Loading services/core/java/com/android/server/notification/ManagedServices.java +28 −0 Original line number Diff line number Diff line Loading @@ -280,6 +280,10 @@ abstract public class ManagedServices { } } UserProfiles getUserProfiles() { return mUserProfiles; } /** * When resetting a package, we need to enable default components that belong to that packages * we also need to disable components that are not default to return the managed service state Loading Loading @@ -2340,6 +2344,30 @@ abstract public class ManagedServices { } } boolean isManagedProfileUser(int userId) { synchronized (mCurrentProfiles) { UserInfo user = mCurrentProfiles.get(userId); if (user == null) { return false; } return user.isManagedProfile(); } } int getProfileParentId(int userId, Context context) { final long identity = Binder.clearCallingIdentity(); try { UserManager um = context.getSystemService(UserManager.class); UserInfo parent = um.getProfileParent(userId); if (parent != null) { return parent.id; } return userId; // if no parent, return itself } finally { Binder.restoreCallingIdentity(identity); } } boolean hasParent(UserInfo profile, Context context) { final long identity = Binder.clearCallingIdentity(); try { Loading services/core/java/com/android/server/notification/NotificationManagerService.java +64 −14 Original line number Diff line number Diff line Loading @@ -12230,6 +12230,9 @@ public class NotificationManagerService extends SystemService { private static final String TAG_ENABLED_TYPES = "enabled_classification_types"; private static final String ATT_NAS_UNSUPPORTED = "unsupported_adjustments"; private static final String ATT_USER_ID = "user"; // for classification only, but named a bit more generally in case this ever gets expanded private static final String TAG_SET_BY_USERS = "adjustment_pref_set_by_users"; private static final String ATT_USER_LIST = "users"; private final Object mLock = new Object(); Loading @@ -12247,6 +12250,12 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mLock") private Map<Integer, HashSet<String>> mNasUnsupported = new ArrayMap<>(); // Set of user IDs for which the classification setting was ever explicitly changed (in // other words, the current setting -- allowed or disallowed -- is not default). Used for // handling default behavior for profiles until the user sets a preference. @GuardedBy("mLock") private Set<Integer> mClassificationPrefSetByUsers = new ArraySet<>(); // Map of user ID -> the disallowed packages for each adjustment key. // Inner map key: Adjustment key. value - list of pkgs that we shouldn't apply // adjustments with that key to Loading Loading @@ -12419,18 +12428,28 @@ public class NotificationManagerService extends SystemService { // be effectively denied for that user. In particular: // - if an adjustment is denied for that profile user's parent, then it is also effectively // denied for that profile regardless of what the profile's current setting is. // - for classification (KEY_TYPE) only, if a user hasn't explicitly enabled the adjustment // for their managed/work profile, default the setting to off. @GuardedBy("mLock") private @NonNull Set<String> deniedAdjustmentsForUser(@UserIdInt int userId) { Set<String> denied = new HashSet<>(); if (mDeniedAdjustments.containsKey(userId)) { denied.addAll(mDeniedAdjustments.get(userId)); } final @UserIdInt int parentId = mUmInternal.getProfileParentId(userId); if ((parentId != userId) && mDeniedAdjustments.containsKey(parentId)) { if (getUserProfiles().isProfileUser(userId, mContext)) { final @UserIdInt int parentId = getUserProfiles().getProfileParentId(userId, mContext); if (mDeniedAdjustments.containsKey(parentId)) { denied.addAll(mDeniedAdjustments.get(parentId)); } // TODO: b/415768865 - add any cases where a (work/managed) profile should default to // off unless explicitly turned on // Managed profiles only: if the setting hasn't been explicitly set for this // profile, then also consider KEY_TYPE (classification) denied. if (getUserProfiles().isManagedProfileUser(userId) && !mClassificationPrefSetByUsers.contains(userId)) { denied.add(KEY_TYPE); } } return denied; } Loading Loading @@ -12934,7 +12953,7 @@ public class NotificationManagerService extends SystemService { return Log.isLoggable("notification_assistant", Log.VERBOSE); } @GuardedBy("mNotificationLock") @GuardedBy("mLock") private void addDefaultClassificationTypes(int userId) { // Add the default classification types if the list is empty or not present. // Will do so for the profile's parent if the user ID is a profile user. Loading @@ -12953,16 +12972,19 @@ public class NotificationManagerService extends SystemService { if (!android.service.notification.Flags.notificationClassification()) { return; } synchronized (mLock) { if (mDeniedAdjustments.containsKey(userId)) { mDeniedAdjustments.get(userId).remove(key); } for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { mHandler.post(() -> notifyCapabilitiesChanged(info)); } if (KEY_TYPE.equals(key)) { mClassificationPrefSetByUsers.add(userId); addDefaultClassificationTypes(userId); } } for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { mHandler.post(() -> notifyCapabilitiesChanged(info)); } } @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) @GuardedBy("mNotificationLock") Loading @@ -12970,8 +12992,13 @@ public class NotificationManagerService extends SystemService { if (!android.service.notification.Flags.notificationClassification()) { return; } synchronized (mLock) { mDeniedAdjustments.putIfAbsent(userId, new ArraySet<>()); mDeniedAdjustments.get(userId).add(key); if (KEY_TYPE.equals(key)) { mClassificationPrefSetByUsers.add(userId); } } for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { mHandler.post(() -> notifyCapabilitiesChanged(info)); } Loading Loading @@ -13084,6 +13111,11 @@ public class NotificationManagerService extends SystemService { TextUtils.join(",", mAllowedClassificationTypes.get(user))); out.endTag(null, TAG_ENABLED_TYPES); } out.startTag(null, TAG_SET_BY_USERS); out.attribute(null, ATT_USER_LIST, TextUtils.join(",", mClassificationPrefSetByUsers)); out.endTag(null, TAG_SET_BY_USERS); } } Loading Loading @@ -13120,7 +13152,7 @@ public class NotificationManagerService extends SystemService { try { userAllowedTypes.add(Integer.parseInt(type)); } catch (NumberFormatException e) { Slog.wtf(TAG, "Bad type specified", e); Slog.wtf(TAG, "Bad integer specified", e); } } mAllowedClassificationTypes.put(user, userAllowedTypes); Loading @@ -13138,6 +13170,18 @@ public class NotificationManagerService extends SystemService { userDeniedPackages.put(key, new ArraySet<>(pkgList)); mAdjustmentKeyDeniedPackages.put(user, userDeniedPackages); } } else if (TAG_SET_BY_USERS.equals(tag)) { final String users = XmlUtils.readStringAttribute(parser, ATT_USER_LIST); if (!TextUtils.isEmpty(users)) { for (String userIdString : Arrays.asList(users.split(","))) { try { mClassificationPrefSetByUsers.add( Integer.parseInt(userIdString)); } catch (NumberFormatException e) { Slog.wtf(TAG, "Bad type specified", e); } } } } } Loading @@ -13158,6 +13202,12 @@ public class NotificationManagerService extends SystemService { protected void pullAdjustmentPreferencesStats(List<StatsEvent> events) { final List<UserInfo> allUsers = mUm.getUsers(); for (UserInfo ui : allUsers) { // only log for full users and managed profiles, which are the only users that have // separate switches available in settings if (!(ui.isFull() || ui.isManagedProfile())) { continue; } int userId = ui.getUserHandle().getIdentifier(); boolean bundlesSupported, summariesSupported; services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +91 −27 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.notification; import static android.os.UserHandle.USER_ALL; import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED; import static android.service.notification.Adjustment.KEY_IMPORTANCE; import static android.service.notification.Adjustment.KEY_SUMMARIZATION; import static android.service.notification.Adjustment.KEY_TYPE; Loading Loading @@ -131,9 +132,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase { private TestableContext mContext = spy(getContext()); Object mLock = new Object(); UserInfo mZero = new UserInfo(0, "zero", 0); UserInfo mTen = new UserInfo(10, "ten", 0); UserInfo mZero = new UserInfo(0, "zero", UserInfo.FLAG_FULL); UserInfo mTen = new UserInfo(10, "ten", UserInfo.FLAG_PROFILE); ComponentName mCn = new ComponentName("a", "b"); Loading Loading @@ -199,11 +199,19 @@ public class NotificationAssistantsTest extends UiServiceTestCase { List<UserInfo> users = new ArrayList<>(); users.add(mZero); users.add(mTen); users.add(new UserInfo(11, "11", 0)); users.add(new UserInfo(12, "12", 0)); users.add(new UserInfo(13, "13", 0)); users.add(new UserInfo(11, "11", null, UserInfo.FLAG_PROFILE, USER_TYPE_PROFILE_MANAGED)); users.add(new UserInfo(12, "12", UserInfo.FLAG_PROFILE)); users.add(new UserInfo(13, "13", UserInfo.FLAG_FULL)); for (UserInfo user : users) { when(mUm.getUserInfo(eq(user.id))).thenReturn(user); if (user.isProfile()) { when(mNm.hasParent(user)).thenReturn(true); when(mNm.isProfileUser(user)).thenReturn(true); when(mUserProfiles.isProfileUser(eq(user.id), any())).thenReturn(true); if (user.isManagedProfile()) { when(mUserProfiles.isManagedProfileUser(user.id)).thenReturn(true); } } } when(mUm.getUsers()).thenReturn(users); when(mUm.getAliveUsers()).thenReturn(users); Loading @@ -213,8 +221,10 @@ public class NotificationAssistantsTest extends UiServiceTestCase { profileIds.add(10); profileIds.add(12); when(mUmInternal.getProfileParentId(13)).thenReturn(13); // 13 is a full user when(mUserProfiles.getProfileParentId(eq(13), any())).thenReturn(13); when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds); when(mUmInternal.getProfileParentId(11)).thenReturn(mZero.id); when(mUserProfiles.getProfileParentId(eq(11), any())).thenReturn(mZero.id); when(mNm.isNASMigrationDone(anyInt())).thenReturn(true); when(mNm.canUseManagedServices(any(), anyInt(), any())).thenReturn(true); mRegistry = ExtensionRegistryLite.newInstance(); Loading Loading @@ -704,11 +714,13 @@ public class NotificationAssistantsTest extends UiServiceTestCase { // if the profile's parent has that adjustment disabled. // User 11 is set up as a profile user of mZero in setup; user 13 is not mAssistants.allowAdjustmentType(11, Adjustment.KEY_TYPE); mAssistants.allowAdjustmentType(13, Adjustment.KEY_TYPE); mAssistants.disallowAdjustmentType(mZero.id, Adjustment.KEY_TYPE); assertThat(mAssistants.getAllowedAssistantAdjustments(11)).doesNotContain( Adjustment.KEY_TYPE); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isFalse(); assertThat(mAssistants.isAdjustmentAllowed(13, Adjustment.KEY_TYPE)).isTrue(); // Now turn it back on for the parent; it should be considered allowed for the profile // (for which it was already on). Loading @@ -717,6 +729,21 @@ public class NotificationAssistantsTest extends UiServiceTestCase { assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isTrue(); } @Test public void testClassificationAdjustment_managedProfileDefaultsOff() { // Turn on KEY_TYPE classification for mZero (parent) but not 11 (managed profile) mAssistants.allowAdjustmentType(mZero.id, Adjustment.KEY_TYPE); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isFalse(); // Check this doesn't apply to other adjustments if they default to allowed mAssistants.allowAdjustmentType(mZero.id, Adjustment.KEY_SUMMARIZATION); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_SUMMARIZATION)).isTrue(); // now turn on classification for the profile user directly mAssistants.allowAdjustmentType(11, Adjustment.KEY_TYPE); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isTrue(); } @Test @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void testAllowAdjustmentType_classifListEmpty_resetDefaultClassificationTypes() { Loading Loading @@ -747,6 +774,28 @@ public class NotificationAssistantsTest extends UiServiceTestCase { .containsExactly(TYPE_NEWS); } @Test @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void testClassificationAdjustments_readWriteXml_userSetStateMaintained() throws Exception { // Turn on KEY_TYPE classification for mZero (parent) but not 11 (managed profile) mAssistants.allowAdjustmentType(mZero.id, Adjustment.KEY_TYPE); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isFalse(); // reload from XML; default state should persist writeXmlAndReload(USER_ALL); assertThat(mAssistants.isAdjustmentAllowed(mZero.id, Adjustment.KEY_TYPE)).isTrue(); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isFalse(); mAssistants.allowAdjustmentType(11, Adjustment.KEY_TYPE); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isTrue(); // now that it's been set, we should still retain this information after XML reload writeXmlAndReload(USER_ALL); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isTrue(); } @Test @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) public void testDisallowAdjustmentType_readWriteXml_entries() throws Exception { Loading Loading @@ -1009,22 +1058,30 @@ public class NotificationAssistantsTest extends UiServiceTestCase { throws Exception { mAssistants.loadDefaultsFromConfig(true); // Scenario setup: the total list of users is 0, 10, 11 (profile of 0), 12, 13 // Scenario setup: the total list of users is 0, 10, 11 (managed profile of 0), 12, 13 // * user 0 has both bundles (KEY_TYPE) + summaries (KEY_SUMMARIZATION) supported // * KEY_TYPE is allowed; KEY_SUMMARIZATION disallowed // * user 13 has only KEY_TYPE, which is disallowed // * other users have neither supported mAssistants.setAdjustmentTypeSupportedState(mZero.id, KEY_TYPE, true); mAssistants.allowAdjustmentType(mZero.id, KEY_TYPE); mAssistants.setAdjustmentTypeSupportedState(mZero.id, KEY_SUMMARIZATION, true); mAssistants.disallowAdjustmentType(mZero.id, KEY_SUMMARIZATION); mAssistants.setAdjustmentTypeSupportedState(13, KEY_TYPE, true); mAssistants.disallowAdjustmentType(13, KEY_TYPE); mAssistants.setAdjustmentTypeSupportedState(13, KEY_SUMMARIZATION, false); for (int user : List.of(mTen.id, 11, 12)) { mAssistants.setAdjustmentTypeSupportedState(user, KEY_TYPE, false); mAssistants.setAdjustmentTypeSupportedState(user, KEY_SUMMARIZATION, false); // * KEY_TYPE is disallowed; KEY_SUMMARIZATION allowed // * user 13 has only KEY_TYPE, which is allowed // * user 11 (managed profile) has only KEY_SUMMARIZATION, disallowed // * other users are non-managed profiles; should not be logged even if both types are // supported for (int user : List.of(mZero.id, mTen.id, 12, 13)) { // users with KEY_TYPE supported: all but 11 mAssistants.setAdjustmentTypeSupportedState(user, KEY_TYPE, true); } mAssistants.setAdjustmentTypeSupportedState(11, KEY_TYPE, false); for (int user : List.of(mZero.id, mTen.id, 11, 12)) { // users with KEY_SUMMARIZATION supported: all but 13 mAssistants.setAdjustmentTypeSupportedState(user, KEY_SUMMARIZATION, true); } mAssistants.setAdjustmentTypeSupportedState(13, KEY_SUMMARIZATION, false); // permissions for adjustments as described above mAssistants.disallowAdjustmentType(mZero.id, KEY_TYPE); mAssistants.allowAdjustmentType(mZero.id, KEY_SUMMARIZATION); mAssistants.disallowAdjustmentType(11, KEY_SUMMARIZATION); mAssistants.allowAdjustmentType(13, KEY_TYPE); // Enable specific bundle types for user 0 mAssistants.setAssistantClassificationTypeState(mZero.id, TYPE_SOCIAL_MEDIA, false); Loading @@ -1033,7 +1090,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { mAssistants.setAssistantClassificationTypeState(mZero.id, TYPE_CONTENT_RECOMMENDATION, true); // and different ones for user 12 // and different ones for user 13 mAssistants.setAssistantClassificationTypeState(13, TYPE_SOCIAL_MEDIA, true); mAssistants.setAssistantClassificationTypeState(13, TYPE_PROMOTION, false); mAssistants.setAssistantClassificationTypeState(13, TYPE_NEWS, false); Loading @@ -1045,8 +1102,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase { mAssistants.pullAdjustmentPreferencesStats(events); // The StatsEvent is filled out with the expected NotificationAdjustmentPreferences values. // We expect 2 atoms for user 0 and 1 atom for user 10. assertThat(events.size()).isEqualTo(3); // We expect 2 atoms for user 0, 1 atom for user 11, and 1 for user 13. assertThat(events.size()).isEqualTo(4); // Collect all the resulting atoms in a map of user ID -> adjustment type (enum) -> atom to // confirm that we have the correct set of atoms without enforcing anything about ordering. Loading @@ -1065,7 +1122,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { Map<Integer, NotificationAdjustmentPreferences> userZeroAtoms = atoms.get(mZero.id); assertThat(userZeroAtoms).containsKey(NotificationProtoEnums.KEY_TYPE); NotificationAdjustmentPreferences p0b = userZeroAtoms.get(NotificationProtoEnums.KEY_TYPE); assertThat(p0b.getAdjustmentAllowed()).isTrue(); assertThat(p0b.getAdjustmentAllowed()).isFalse(); assertThat(p0b.getAllowedBundleTypesList()).containsExactly( BundleTypes.forNumber(NotificationProtoEnums.TYPE_NEWS), BundleTypes.forNumber(NotificationProtoEnums.TYPE_CONTENT_RECOMMENDATION)); Loading @@ -1074,14 +1131,21 @@ public class NotificationAssistantsTest extends UiServiceTestCase { assertThat(userZeroAtoms).containsKey(NotificationProtoEnums.KEY_SUMMARIZATION); NotificationAdjustmentPreferences p0s = userZeroAtoms.get( NotificationProtoEnums.KEY_SUMMARIZATION); assertThat(p0s.getAdjustmentAllowed()).isFalse(); assertThat(p0s.getAdjustmentAllowed()).isTrue(); // user 11, KEY_SUMMARIZATION assertThat(atoms).containsKey(11); assertThat(atoms.get(11)).containsKey(NotificationProtoEnums.KEY_SUMMARIZATION); NotificationAdjustmentPreferences p11s = atoms.get(11).get( NotificationProtoEnums.KEY_SUMMARIZATION); assertThat(p11s.getAdjustmentAllowed()).isFalse(); // user 12, KEY_TYPE (bundles) // user 13, KEY_TYPE (bundles) assertThat(atoms).containsKey(13); assertThat(atoms.get(13)).containsKey(NotificationProtoEnums.KEY_TYPE); NotificationAdjustmentPreferences p13b = atoms.get(13).get( NotificationProtoEnums.KEY_TYPE); assertThat(p13b.getAdjustmentAllowed()).isFalse(); assertThat(p13b.getAdjustmentAllowed()).isTrue(); assertThat(p13b.getAllowedBundleTypesList()) .containsExactly(BundleTypes.forNumber(NotificationProtoEnums.TYPE_SOCIAL_MEDIA)); } Loading Loading
services/core/java/com/android/server/notification/ManagedServices.java +28 −0 Original line number Diff line number Diff line Loading @@ -280,6 +280,10 @@ abstract public class ManagedServices { } } UserProfiles getUserProfiles() { return mUserProfiles; } /** * When resetting a package, we need to enable default components that belong to that packages * we also need to disable components that are not default to return the managed service state Loading Loading @@ -2340,6 +2344,30 @@ abstract public class ManagedServices { } } boolean isManagedProfileUser(int userId) { synchronized (mCurrentProfiles) { UserInfo user = mCurrentProfiles.get(userId); if (user == null) { return false; } return user.isManagedProfile(); } } int getProfileParentId(int userId, Context context) { final long identity = Binder.clearCallingIdentity(); try { UserManager um = context.getSystemService(UserManager.class); UserInfo parent = um.getProfileParent(userId); if (parent != null) { return parent.id; } return userId; // if no parent, return itself } finally { Binder.restoreCallingIdentity(identity); } } boolean hasParent(UserInfo profile, Context context) { final long identity = Binder.clearCallingIdentity(); try { Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +64 −14 Original line number Diff line number Diff line Loading @@ -12230,6 +12230,9 @@ public class NotificationManagerService extends SystemService { private static final String TAG_ENABLED_TYPES = "enabled_classification_types"; private static final String ATT_NAS_UNSUPPORTED = "unsupported_adjustments"; private static final String ATT_USER_ID = "user"; // for classification only, but named a bit more generally in case this ever gets expanded private static final String TAG_SET_BY_USERS = "adjustment_pref_set_by_users"; private static final String ATT_USER_LIST = "users"; private final Object mLock = new Object(); Loading @@ -12247,6 +12250,12 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mLock") private Map<Integer, HashSet<String>> mNasUnsupported = new ArrayMap<>(); // Set of user IDs for which the classification setting was ever explicitly changed (in // other words, the current setting -- allowed or disallowed -- is not default). Used for // handling default behavior for profiles until the user sets a preference. @GuardedBy("mLock") private Set<Integer> mClassificationPrefSetByUsers = new ArraySet<>(); // Map of user ID -> the disallowed packages for each adjustment key. // Inner map key: Adjustment key. value - list of pkgs that we shouldn't apply // adjustments with that key to Loading Loading @@ -12419,18 +12428,28 @@ public class NotificationManagerService extends SystemService { // be effectively denied for that user. In particular: // - if an adjustment is denied for that profile user's parent, then it is also effectively // denied for that profile regardless of what the profile's current setting is. // - for classification (KEY_TYPE) only, if a user hasn't explicitly enabled the adjustment // for their managed/work profile, default the setting to off. @GuardedBy("mLock") private @NonNull Set<String> deniedAdjustmentsForUser(@UserIdInt int userId) { Set<String> denied = new HashSet<>(); if (mDeniedAdjustments.containsKey(userId)) { denied.addAll(mDeniedAdjustments.get(userId)); } final @UserIdInt int parentId = mUmInternal.getProfileParentId(userId); if ((parentId != userId) && mDeniedAdjustments.containsKey(parentId)) { if (getUserProfiles().isProfileUser(userId, mContext)) { final @UserIdInt int parentId = getUserProfiles().getProfileParentId(userId, mContext); if (mDeniedAdjustments.containsKey(parentId)) { denied.addAll(mDeniedAdjustments.get(parentId)); } // TODO: b/415768865 - add any cases where a (work/managed) profile should default to // off unless explicitly turned on // Managed profiles only: if the setting hasn't been explicitly set for this // profile, then also consider KEY_TYPE (classification) denied. if (getUserProfiles().isManagedProfileUser(userId) && !mClassificationPrefSetByUsers.contains(userId)) { denied.add(KEY_TYPE); } } return denied; } Loading Loading @@ -12934,7 +12953,7 @@ public class NotificationManagerService extends SystemService { return Log.isLoggable("notification_assistant", Log.VERBOSE); } @GuardedBy("mNotificationLock") @GuardedBy("mLock") private void addDefaultClassificationTypes(int userId) { // Add the default classification types if the list is empty or not present. // Will do so for the profile's parent if the user ID is a profile user. Loading @@ -12953,16 +12972,19 @@ public class NotificationManagerService extends SystemService { if (!android.service.notification.Flags.notificationClassification()) { return; } synchronized (mLock) { if (mDeniedAdjustments.containsKey(userId)) { mDeniedAdjustments.get(userId).remove(key); } for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { mHandler.post(() -> notifyCapabilitiesChanged(info)); } if (KEY_TYPE.equals(key)) { mClassificationPrefSetByUsers.add(userId); addDefaultClassificationTypes(userId); } } for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { mHandler.post(() -> notifyCapabilitiesChanged(info)); } } @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) @GuardedBy("mNotificationLock") Loading @@ -12970,8 +12992,13 @@ public class NotificationManagerService extends SystemService { if (!android.service.notification.Flags.notificationClassification()) { return; } synchronized (mLock) { mDeniedAdjustments.putIfAbsent(userId, new ArraySet<>()); mDeniedAdjustments.get(userId).add(key); if (KEY_TYPE.equals(key)) { mClassificationPrefSetByUsers.add(userId); } } for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { mHandler.post(() -> notifyCapabilitiesChanged(info)); } Loading Loading @@ -13084,6 +13111,11 @@ public class NotificationManagerService extends SystemService { TextUtils.join(",", mAllowedClassificationTypes.get(user))); out.endTag(null, TAG_ENABLED_TYPES); } out.startTag(null, TAG_SET_BY_USERS); out.attribute(null, ATT_USER_LIST, TextUtils.join(",", mClassificationPrefSetByUsers)); out.endTag(null, TAG_SET_BY_USERS); } } Loading Loading @@ -13120,7 +13152,7 @@ public class NotificationManagerService extends SystemService { try { userAllowedTypes.add(Integer.parseInt(type)); } catch (NumberFormatException e) { Slog.wtf(TAG, "Bad type specified", e); Slog.wtf(TAG, "Bad integer specified", e); } } mAllowedClassificationTypes.put(user, userAllowedTypes); Loading @@ -13138,6 +13170,18 @@ public class NotificationManagerService extends SystemService { userDeniedPackages.put(key, new ArraySet<>(pkgList)); mAdjustmentKeyDeniedPackages.put(user, userDeniedPackages); } } else if (TAG_SET_BY_USERS.equals(tag)) { final String users = XmlUtils.readStringAttribute(parser, ATT_USER_LIST); if (!TextUtils.isEmpty(users)) { for (String userIdString : Arrays.asList(users.split(","))) { try { mClassificationPrefSetByUsers.add( Integer.parseInt(userIdString)); } catch (NumberFormatException e) { Slog.wtf(TAG, "Bad type specified", e); } } } } } Loading @@ -13158,6 +13202,12 @@ public class NotificationManagerService extends SystemService { protected void pullAdjustmentPreferencesStats(List<StatsEvent> events) { final List<UserInfo> allUsers = mUm.getUsers(); for (UserInfo ui : allUsers) { // only log for full users and managed profiles, which are the only users that have // separate switches available in settings if (!(ui.isFull() || ui.isManagedProfile())) { continue; } int userId = ui.getUserHandle().getIdentifier(); boolean bundlesSupported, summariesSupported;
services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +91 −27 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.notification; import static android.os.UserHandle.USER_ALL; import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED; import static android.service.notification.Adjustment.KEY_IMPORTANCE; import static android.service.notification.Adjustment.KEY_SUMMARIZATION; import static android.service.notification.Adjustment.KEY_TYPE; Loading Loading @@ -131,9 +132,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase { private TestableContext mContext = spy(getContext()); Object mLock = new Object(); UserInfo mZero = new UserInfo(0, "zero", 0); UserInfo mTen = new UserInfo(10, "ten", 0); UserInfo mZero = new UserInfo(0, "zero", UserInfo.FLAG_FULL); UserInfo mTen = new UserInfo(10, "ten", UserInfo.FLAG_PROFILE); ComponentName mCn = new ComponentName("a", "b"); Loading Loading @@ -199,11 +199,19 @@ public class NotificationAssistantsTest extends UiServiceTestCase { List<UserInfo> users = new ArrayList<>(); users.add(mZero); users.add(mTen); users.add(new UserInfo(11, "11", 0)); users.add(new UserInfo(12, "12", 0)); users.add(new UserInfo(13, "13", 0)); users.add(new UserInfo(11, "11", null, UserInfo.FLAG_PROFILE, USER_TYPE_PROFILE_MANAGED)); users.add(new UserInfo(12, "12", UserInfo.FLAG_PROFILE)); users.add(new UserInfo(13, "13", UserInfo.FLAG_FULL)); for (UserInfo user : users) { when(mUm.getUserInfo(eq(user.id))).thenReturn(user); if (user.isProfile()) { when(mNm.hasParent(user)).thenReturn(true); when(mNm.isProfileUser(user)).thenReturn(true); when(mUserProfiles.isProfileUser(eq(user.id), any())).thenReturn(true); if (user.isManagedProfile()) { when(mUserProfiles.isManagedProfileUser(user.id)).thenReturn(true); } } } when(mUm.getUsers()).thenReturn(users); when(mUm.getAliveUsers()).thenReturn(users); Loading @@ -213,8 +221,10 @@ public class NotificationAssistantsTest extends UiServiceTestCase { profileIds.add(10); profileIds.add(12); when(mUmInternal.getProfileParentId(13)).thenReturn(13); // 13 is a full user when(mUserProfiles.getProfileParentId(eq(13), any())).thenReturn(13); when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds); when(mUmInternal.getProfileParentId(11)).thenReturn(mZero.id); when(mUserProfiles.getProfileParentId(eq(11), any())).thenReturn(mZero.id); when(mNm.isNASMigrationDone(anyInt())).thenReturn(true); when(mNm.canUseManagedServices(any(), anyInt(), any())).thenReturn(true); mRegistry = ExtensionRegistryLite.newInstance(); Loading Loading @@ -704,11 +714,13 @@ public class NotificationAssistantsTest extends UiServiceTestCase { // if the profile's parent has that adjustment disabled. // User 11 is set up as a profile user of mZero in setup; user 13 is not mAssistants.allowAdjustmentType(11, Adjustment.KEY_TYPE); mAssistants.allowAdjustmentType(13, Adjustment.KEY_TYPE); mAssistants.disallowAdjustmentType(mZero.id, Adjustment.KEY_TYPE); assertThat(mAssistants.getAllowedAssistantAdjustments(11)).doesNotContain( Adjustment.KEY_TYPE); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isFalse(); assertThat(mAssistants.isAdjustmentAllowed(13, Adjustment.KEY_TYPE)).isTrue(); // Now turn it back on for the parent; it should be considered allowed for the profile // (for which it was already on). Loading @@ -717,6 +729,21 @@ public class NotificationAssistantsTest extends UiServiceTestCase { assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isTrue(); } @Test public void testClassificationAdjustment_managedProfileDefaultsOff() { // Turn on KEY_TYPE classification for mZero (parent) but not 11 (managed profile) mAssistants.allowAdjustmentType(mZero.id, Adjustment.KEY_TYPE); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isFalse(); // Check this doesn't apply to other adjustments if they default to allowed mAssistants.allowAdjustmentType(mZero.id, Adjustment.KEY_SUMMARIZATION); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_SUMMARIZATION)).isTrue(); // now turn on classification for the profile user directly mAssistants.allowAdjustmentType(11, Adjustment.KEY_TYPE); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isTrue(); } @Test @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void testAllowAdjustmentType_classifListEmpty_resetDefaultClassificationTypes() { Loading Loading @@ -747,6 +774,28 @@ public class NotificationAssistantsTest extends UiServiceTestCase { .containsExactly(TYPE_NEWS); } @Test @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void testClassificationAdjustments_readWriteXml_userSetStateMaintained() throws Exception { // Turn on KEY_TYPE classification for mZero (parent) but not 11 (managed profile) mAssistants.allowAdjustmentType(mZero.id, Adjustment.KEY_TYPE); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isFalse(); // reload from XML; default state should persist writeXmlAndReload(USER_ALL); assertThat(mAssistants.isAdjustmentAllowed(mZero.id, Adjustment.KEY_TYPE)).isTrue(); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isFalse(); mAssistants.allowAdjustmentType(11, Adjustment.KEY_TYPE); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isTrue(); // now that it's been set, we should still retain this information after XML reload writeXmlAndReload(USER_ALL); assertThat(mAssistants.isAdjustmentAllowed(11, Adjustment.KEY_TYPE)).isTrue(); } @Test @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) public void testDisallowAdjustmentType_readWriteXml_entries() throws Exception { Loading Loading @@ -1009,22 +1058,30 @@ public class NotificationAssistantsTest extends UiServiceTestCase { throws Exception { mAssistants.loadDefaultsFromConfig(true); // Scenario setup: the total list of users is 0, 10, 11 (profile of 0), 12, 13 // Scenario setup: the total list of users is 0, 10, 11 (managed profile of 0), 12, 13 // * user 0 has both bundles (KEY_TYPE) + summaries (KEY_SUMMARIZATION) supported // * KEY_TYPE is allowed; KEY_SUMMARIZATION disallowed // * user 13 has only KEY_TYPE, which is disallowed // * other users have neither supported mAssistants.setAdjustmentTypeSupportedState(mZero.id, KEY_TYPE, true); mAssistants.allowAdjustmentType(mZero.id, KEY_TYPE); mAssistants.setAdjustmentTypeSupportedState(mZero.id, KEY_SUMMARIZATION, true); mAssistants.disallowAdjustmentType(mZero.id, KEY_SUMMARIZATION); mAssistants.setAdjustmentTypeSupportedState(13, KEY_TYPE, true); mAssistants.disallowAdjustmentType(13, KEY_TYPE); mAssistants.setAdjustmentTypeSupportedState(13, KEY_SUMMARIZATION, false); for (int user : List.of(mTen.id, 11, 12)) { mAssistants.setAdjustmentTypeSupportedState(user, KEY_TYPE, false); mAssistants.setAdjustmentTypeSupportedState(user, KEY_SUMMARIZATION, false); // * KEY_TYPE is disallowed; KEY_SUMMARIZATION allowed // * user 13 has only KEY_TYPE, which is allowed // * user 11 (managed profile) has only KEY_SUMMARIZATION, disallowed // * other users are non-managed profiles; should not be logged even if both types are // supported for (int user : List.of(mZero.id, mTen.id, 12, 13)) { // users with KEY_TYPE supported: all but 11 mAssistants.setAdjustmentTypeSupportedState(user, KEY_TYPE, true); } mAssistants.setAdjustmentTypeSupportedState(11, KEY_TYPE, false); for (int user : List.of(mZero.id, mTen.id, 11, 12)) { // users with KEY_SUMMARIZATION supported: all but 13 mAssistants.setAdjustmentTypeSupportedState(user, KEY_SUMMARIZATION, true); } mAssistants.setAdjustmentTypeSupportedState(13, KEY_SUMMARIZATION, false); // permissions for adjustments as described above mAssistants.disallowAdjustmentType(mZero.id, KEY_TYPE); mAssistants.allowAdjustmentType(mZero.id, KEY_SUMMARIZATION); mAssistants.disallowAdjustmentType(11, KEY_SUMMARIZATION); mAssistants.allowAdjustmentType(13, KEY_TYPE); // Enable specific bundle types for user 0 mAssistants.setAssistantClassificationTypeState(mZero.id, TYPE_SOCIAL_MEDIA, false); Loading @@ -1033,7 +1090,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { mAssistants.setAssistantClassificationTypeState(mZero.id, TYPE_CONTENT_RECOMMENDATION, true); // and different ones for user 12 // and different ones for user 13 mAssistants.setAssistantClassificationTypeState(13, TYPE_SOCIAL_MEDIA, true); mAssistants.setAssistantClassificationTypeState(13, TYPE_PROMOTION, false); mAssistants.setAssistantClassificationTypeState(13, TYPE_NEWS, false); Loading @@ -1045,8 +1102,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase { mAssistants.pullAdjustmentPreferencesStats(events); // The StatsEvent is filled out with the expected NotificationAdjustmentPreferences values. // We expect 2 atoms for user 0 and 1 atom for user 10. assertThat(events.size()).isEqualTo(3); // We expect 2 atoms for user 0, 1 atom for user 11, and 1 for user 13. assertThat(events.size()).isEqualTo(4); // Collect all the resulting atoms in a map of user ID -> adjustment type (enum) -> atom to // confirm that we have the correct set of atoms without enforcing anything about ordering. Loading @@ -1065,7 +1122,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { Map<Integer, NotificationAdjustmentPreferences> userZeroAtoms = atoms.get(mZero.id); assertThat(userZeroAtoms).containsKey(NotificationProtoEnums.KEY_TYPE); NotificationAdjustmentPreferences p0b = userZeroAtoms.get(NotificationProtoEnums.KEY_TYPE); assertThat(p0b.getAdjustmentAllowed()).isTrue(); assertThat(p0b.getAdjustmentAllowed()).isFalse(); assertThat(p0b.getAllowedBundleTypesList()).containsExactly( BundleTypes.forNumber(NotificationProtoEnums.TYPE_NEWS), BundleTypes.forNumber(NotificationProtoEnums.TYPE_CONTENT_RECOMMENDATION)); Loading @@ -1074,14 +1131,21 @@ public class NotificationAssistantsTest extends UiServiceTestCase { assertThat(userZeroAtoms).containsKey(NotificationProtoEnums.KEY_SUMMARIZATION); NotificationAdjustmentPreferences p0s = userZeroAtoms.get( NotificationProtoEnums.KEY_SUMMARIZATION); assertThat(p0s.getAdjustmentAllowed()).isFalse(); assertThat(p0s.getAdjustmentAllowed()).isTrue(); // user 11, KEY_SUMMARIZATION assertThat(atoms).containsKey(11); assertThat(atoms.get(11)).containsKey(NotificationProtoEnums.KEY_SUMMARIZATION); NotificationAdjustmentPreferences p11s = atoms.get(11).get( NotificationProtoEnums.KEY_SUMMARIZATION); assertThat(p11s.getAdjustmentAllowed()).isFalse(); // user 12, KEY_TYPE (bundles) // user 13, KEY_TYPE (bundles) assertThat(atoms).containsKey(13); assertThat(atoms.get(13)).containsKey(NotificationProtoEnums.KEY_TYPE); NotificationAdjustmentPreferences p13b = atoms.get(13).get( NotificationProtoEnums.KEY_TYPE); assertThat(p13b.getAdjustmentAllowed()).isFalse(); assertThat(p13b.getAdjustmentAllowed()).isTrue(); assertThat(p13b.getAllowedBundleTypesList()) .containsExactly(BundleTypes.forNumber(NotificationProtoEnums.TYPE_SOCIAL_MEDIA)); } Loading