Loading core/java/android/app/INotificationManager.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -88,6 +88,8 @@ interface INotificationManager ParceledListSlice getRecentNotifyingAppsForUser(int userId); int getBlockedAppCount(int userId); boolean areChannelsBypassingDnd(); int getAppsBypassingDndCount(int uid); ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId); // TODO: Remove this when callers have been migrated to the equivalent // INotificationListener method. Loading services/core/java/com/android/server/notification/NotificationManagerService.java +15 −0 Original line number Diff line number Diff line Loading @@ -1163,6 +1163,7 @@ public class NotificationManagerService extends SystemService { mConditionProviders.onUserSwitched(userId); mListeners.onUserSwitched(userId); mZenModeHelper.onUserSwitched(userId); mPreferencesHelper.onUserSwitched(userId); } // assistant is the only thing that cares about managed profiles specifically mAssistants.onUserSwitched(userId); Loading Loading @@ -1191,6 +1192,7 @@ public class NotificationManagerService extends SystemService { mConditionProviders.onUserUnlocked(userId); mListeners.onUserUnlocked(userId); mZenModeHelper.onUserUnlocked(userId); mPreferencesHelper.onUserUnlocked(userId); } } } Loading Loading @@ -2527,6 +2529,19 @@ public class NotificationManagerService extends SystemService { return mPreferencesHelper.getBlockedAppCount(userId); } @Override public int getAppsBypassingDndCount(int userId) { checkCallerIsSystem(); return mPreferencesHelper.getAppsBypassingDndCount(userId); } @Override public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd( String pkg, int userId) { checkCallerIsSystem(); return mPreferencesHelper.getNotificationChannelsBypassingDnd(pkg, userId); } @Override public boolean areChannelsBypassingDnd() { return mPreferencesHelper.areChannelsBypassingDnd(); Loading services/core/java/com/android/server/notification/PreferencesHelper.java +117 −25 Original line number Diff line number Diff line Loading @@ -111,7 +111,6 @@ public class PreferencesHelper implements RankingConfig { // pkg => PackagePreferences private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>(); private final Context mContext; private final PackageManager mPm; private final RankingHandler mRankingHandler; Loading @@ -120,7 +119,6 @@ public class PreferencesHelper implements RankingConfig { private SparseBooleanArray mBadgingEnabled; private boolean mAreChannelsBypassingDnd; public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, ZenModeHelper zenHelper) { mContext = context; Loading @@ -129,11 +127,7 @@ public class PreferencesHelper implements RankingConfig { mPm = pm; updateBadgingEnabled(); mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1; updateChannelsBypassingDnd(); syncChannelsBypassingDnd(mContext.getUserId()); } public void readXml(XmlPullParser parser, boolean forRestore) Loading Loading @@ -525,6 +519,7 @@ public class PreferencesHelper implements RankingConfig { // but the system can if (group.isBlocked() != oldGroup.isBlocked()) { group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE); updateChannelsBypassingDnd(mContext.getUserId()); } if (group.canOverlayApps() != oldGroup.canOverlayApps()) { group.lockFields(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY); Loading Loading @@ -571,6 +566,7 @@ public class PreferencesHelper implements RankingConfig { // Apps are allowed to downgrade channel importance if the user has not changed any // fields on this channel yet. final int previousExistingImportance = existing.getImportance(); if (existing.getUserLockedFields() == 0 && channel.getImportance() < existing.getImportance()) { existing.setImportance(channel.getImportance()); Loading @@ -582,8 +578,9 @@ public class PreferencesHelper implements RankingConfig { boolean bypassDnd = channel.canBypassDnd(); existing.setBypassDnd(bypassDnd); if (bypassDnd != mAreChannelsBypassingDnd) { updateChannelsBypassingDnd(); if (bypassDnd != mAreChannelsBypassingDnd || previousExistingImportance != existing.getImportance()) { updateChannelsBypassingDnd(mContext.getUserId()); } } Loading Loading @@ -613,7 +610,7 @@ public class PreferencesHelper implements RankingConfig { r.channels.put(channel.getId(), channel); if (channel.canBypassDnd() != mAreChannelsBypassingDnd) { updateChannelsBypassingDnd(); updateChannelsBypassingDnd(mContext.getUserId()); } MetricsLogger.action(getChannelLog(channel, pkg).setType( com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN)); Loading Loading @@ -663,8 +660,9 @@ public class PreferencesHelper implements RankingConfig { MetricsLogger.action(getChannelLog(updatedChannel, pkg)); } if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd) { updateChannelsBypassingDnd(); if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd || channel.getImportance() != updatedChannel.getImportance()) { updateChannelsBypassingDnd(mContext.getUserId()); } updateConfig(); } Loading Loading @@ -701,7 +699,7 @@ public class PreferencesHelper implements RankingConfig { MetricsLogger.action(lm); if (mAreChannelsBypassingDnd && channel.canBypassDnd()) { updateChannelsBypassingDnd(); updateChannelsBypassingDnd(mContext.getUserId()); } } } Loading Loading @@ -858,6 +856,27 @@ public class PreferencesHelper implements RankingConfig { return new ParceledListSlice<>(channels); } /** * Gets all notification channels associated with the given pkg and userId that can bypass dnd */ public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg, int userId) { List<NotificationChannel> channels = new ArrayList<>(); synchronized (mPackagePreferences) { final PackagePreferences r = mPackagePreferences.get( packagePreferencesKey(pkg, userId)); // notifications from this package aren't blocked if (r != null && r.importance != IMPORTANCE_NONE) { for (NotificationChannel channel : r.channels.values()) { if (channelIsLive(r, channel) && channel.canBypassDnd()) { channels.add(channel); } } } } return new ParceledListSlice<>(channels); } /** * True for pre-O apps that only have the default channel, or pre O apps that have no * channels yet. This method will create the default channel for pre-O apps that don't have it. Loading Loading @@ -922,18 +941,62 @@ public class PreferencesHelper implements RankingConfig { return count; } public void updateChannelsBypassingDnd() { /** * Returns the number of apps that have at least one notification channel that can bypass DND * for given particular user */ public int getAppsBypassingDndCount(int userId) { int count = 0; synchronized (mPackagePreferences) { final int numPackagePreferencess = mPackagePreferences.size(); for (int PackagePreferencesIndex = 0; PackagePreferencesIndex < numPackagePreferencess; PackagePreferencesIndex++) { final PackagePreferences r = mPackagePreferences.valueAt(PackagePreferencesIndex); final int numChannels = r.channels.size(); for (int channelIndex = 0; channelIndex < numChannels; channelIndex++) { NotificationChannel channel = r.channels.valueAt(channelIndex); if (!channel.isDeleted() && channel.canBypassDnd()) { // If any channel bypasses DND, synchronize state and return early. final int numPackagePreferences = mPackagePreferences.size(); for (int i = 0; i < numPackagePreferences; i++) { final PackagePreferences r = mPackagePreferences.valueAt(i); // Package isn't associated with this userId or notifications from this package are // blocked if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) { continue; } for (NotificationChannel channel : r.channels.values()) { if (channelIsLive(r, channel) && channel.canBypassDnd()) { count++; break; } } } } return count; } /** * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before * updating * @param userId */ private void syncChannelsBypassingDnd(int userId) { mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1; updateChannelsBypassingDnd(userId); } /** * Updates the user's NotificationPolicy based on whether the given userId * has channels bypassing DND * @param userId */ private void updateChannelsBypassingDnd(int userId) { synchronized (mPackagePreferences) { final int numPackagePreferences = mPackagePreferences.size(); for (int i = 0; i < numPackagePreferences; i++) { final PackagePreferences r = mPackagePreferences.valueAt(i); // Package isn't associated with this userId or notifications from this package are // blocked if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) { continue; } for (NotificationChannel channel : r.channels.values()) { if (channelIsLive(r, channel) && channel.canBypassDnd()) { if (!mAreChannelsBypassingDnd) { mAreChannelsBypassingDnd = true; updateZenPolicy(true); Loading @@ -943,7 +1006,6 @@ public class PreferencesHelper implements RankingConfig { } } } // If no channels bypass DND, update the zen policy once to disable DND bypass. if (mAreChannelsBypassingDnd) { mAreChannelsBypassingDnd = false; Loading @@ -951,6 +1013,22 @@ public class PreferencesHelper implements RankingConfig { } } private boolean channelIsLive(PackagePreferences pkgPref, NotificationChannel channel) { // Channel is in a group that's blocked if (!TextUtils.isEmpty(channel.getGroup())) { if (pkgPref.groups.get(channel.getGroup()).isBlocked()) { return false; } } // Channel is deleted or is blocked if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) { return false; } return true; } public void updateZenPolicy(boolean areChannelsBypassingDnd) { NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy(); mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy( Loading Loading @@ -1329,6 +1407,20 @@ public class PreferencesHelper implements RankingConfig { return packageChannels; } /** * Called when user switches */ public void onUserSwitched(int userId) { syncChannelsBypassingDnd(userId); } /** * Called when user is unlocked */ public void onUserUnlocked(int userId) { syncChannelsBypassingDnd(userId); } public void onUserRemoved(int userId) { synchronized (mPackagePreferences) { int N = mPackagePreferences.size(); Loading services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +153 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import static junit.framework.Assert.fail; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; Loading Loading @@ -1090,6 +1089,158 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(0, mHelper.getBlockedChannelCount("pkg2", UID_O)); } @Test public void testGetChannelsBypassingDndCount_noChannelsBypassing() throws Exception { assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, USER.getIdentifier()).getList().size()); } @Test public void testGetChannelsBypassingDnd_noChannelsForUserIdBypassing() throws Exception { int user = 9; NotificationChannel channel = new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_MAX); channel.setBypassDnd(true); mHelper.createNotificationChannel(PKG_N_MR1, 111, channel, true, true); assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); } @Test public void testGetChannelsBypassingDndCount_oneChannelBypassing_groupBlocked() { int user = USER.getIdentifier(); NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); NotificationChannel channel1 = new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MAX); channel1.setBypassDnd(true); channel1.setGroup(ncg.getId()); mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ true); mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true); assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); // disable group ncg.setBlocked(true); mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ false); assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); } @Test public void testGetChannelsBypassingDndCount_multipleChannelsBypassing() { int user = USER.getIdentifier(); NotificationChannel channel1 = new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MAX); NotificationChannel channel2 = new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_MAX); NotificationChannel channel3 = new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_MAX); channel1.setBypassDnd(true); channel2.setBypassDnd(true); channel3.setBypassDnd(true); // has DND access, so can set bypassDnd attribute mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true); mHelper.createNotificationChannel(PKG_N_MR1, user, channel2, true, true); mHelper.createNotificationChannel(PKG_N_MR1, user, channel3, true, true); assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); // block notifications from this app mHelper.setEnabled(PKG_N_MR1, user, false); assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); // re-enable notifications from this app mHelper.setEnabled(PKG_N_MR1, user, true); assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); // setBypassDnd false for some channels channel1.setBypassDnd(false); channel2.setBypassDnd(false); assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); // setBypassDnd false for rest of the channels channel3.setBypassDnd(false); assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); } @Test public void testGetAppsBypassingDndCount_noAppsBypassing() throws Exception { assertEquals(0, mHelper.getAppsBypassingDndCount(USER.getIdentifier())); } @Test public void testGetAppsBypassingDndCount_noAppsForUserIdBypassing() throws Exception { int user = 9; NotificationChannel channel = new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_MAX); channel.setBypassDnd(true); mHelper.createNotificationChannel(PKG_N_MR1, 111, channel, true, true); assertEquals(0, mHelper.getAppsBypassingDndCount(user)); } @Test public void testGetAppsBypassingDndCount_oneChannelBypassing_groupBlocked() { int user = USER.getIdentifier(); NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); NotificationChannel channel1 = new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MAX); channel1.setBypassDnd(true); channel1.setGroup(ncg.getId()); mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ true); mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true); assertEquals(1, mHelper.getAppsBypassingDndCount(user)); // disable group ncg.setBlocked(true); mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ false); assertEquals(0, mHelper.getAppsBypassingDndCount(user)); } @Test public void testGetAppsBypassingDndCount_oneAppBypassing() { int user = USER.getIdentifier(); NotificationChannel channel1 = new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MAX); NotificationChannel channel2 = new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_MAX); NotificationChannel channel3 = new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_MAX); channel1.setBypassDnd(true); channel2.setBypassDnd(true); channel3.setBypassDnd(true); // has DND access, so can set bypassDnd attribute mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true); mHelper.createNotificationChannel(PKG_N_MR1, user, channel2, true, true); mHelper.createNotificationChannel(PKG_N_MR1, user, channel3, true, true); assertEquals(1, mHelper.getAppsBypassingDndCount(user)); // block notifications from this app mHelper.setEnabled(PKG_N_MR1, user, false); assertEquals(0, mHelper.getAppsBypassingDndCount(user)); // no apps can bypass dnd // re-enable notifications from this app mHelper.setEnabled(PKG_N_MR1, user, true); assertEquals(1, mHelper.getAppsBypassingDndCount(user)); // setBypassDnd false for some channels channel1.setBypassDnd(false); channel2.setBypassDnd(false); assertEquals(1, mHelper.getAppsBypassingDndCount(user)); // setBypassDnd false for rest of the channels channel3.setBypassDnd(false); assertEquals(0, mHelper.getAppsBypassingDndCount(user)); } @Test public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception { // create notification channel that can't bypass dnd Loading Loading
core/java/android/app/INotificationManager.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -88,6 +88,8 @@ interface INotificationManager ParceledListSlice getRecentNotifyingAppsForUser(int userId); int getBlockedAppCount(int userId); boolean areChannelsBypassingDnd(); int getAppsBypassingDndCount(int uid); ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId); // TODO: Remove this when callers have been migrated to the equivalent // INotificationListener method. Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +15 −0 Original line number Diff line number Diff line Loading @@ -1163,6 +1163,7 @@ public class NotificationManagerService extends SystemService { mConditionProviders.onUserSwitched(userId); mListeners.onUserSwitched(userId); mZenModeHelper.onUserSwitched(userId); mPreferencesHelper.onUserSwitched(userId); } // assistant is the only thing that cares about managed profiles specifically mAssistants.onUserSwitched(userId); Loading Loading @@ -1191,6 +1192,7 @@ public class NotificationManagerService extends SystemService { mConditionProviders.onUserUnlocked(userId); mListeners.onUserUnlocked(userId); mZenModeHelper.onUserUnlocked(userId); mPreferencesHelper.onUserUnlocked(userId); } } } Loading Loading @@ -2527,6 +2529,19 @@ public class NotificationManagerService extends SystemService { return mPreferencesHelper.getBlockedAppCount(userId); } @Override public int getAppsBypassingDndCount(int userId) { checkCallerIsSystem(); return mPreferencesHelper.getAppsBypassingDndCount(userId); } @Override public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd( String pkg, int userId) { checkCallerIsSystem(); return mPreferencesHelper.getNotificationChannelsBypassingDnd(pkg, userId); } @Override public boolean areChannelsBypassingDnd() { return mPreferencesHelper.areChannelsBypassingDnd(); Loading
services/core/java/com/android/server/notification/PreferencesHelper.java +117 −25 Original line number Diff line number Diff line Loading @@ -111,7 +111,6 @@ public class PreferencesHelper implements RankingConfig { // pkg => PackagePreferences private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>(); private final Context mContext; private final PackageManager mPm; private final RankingHandler mRankingHandler; Loading @@ -120,7 +119,6 @@ public class PreferencesHelper implements RankingConfig { private SparseBooleanArray mBadgingEnabled; private boolean mAreChannelsBypassingDnd; public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, ZenModeHelper zenHelper) { mContext = context; Loading @@ -129,11 +127,7 @@ public class PreferencesHelper implements RankingConfig { mPm = pm; updateBadgingEnabled(); mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1; updateChannelsBypassingDnd(); syncChannelsBypassingDnd(mContext.getUserId()); } public void readXml(XmlPullParser parser, boolean forRestore) Loading Loading @@ -525,6 +519,7 @@ public class PreferencesHelper implements RankingConfig { // but the system can if (group.isBlocked() != oldGroup.isBlocked()) { group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE); updateChannelsBypassingDnd(mContext.getUserId()); } if (group.canOverlayApps() != oldGroup.canOverlayApps()) { group.lockFields(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY); Loading Loading @@ -571,6 +566,7 @@ public class PreferencesHelper implements RankingConfig { // Apps are allowed to downgrade channel importance if the user has not changed any // fields on this channel yet. final int previousExistingImportance = existing.getImportance(); if (existing.getUserLockedFields() == 0 && channel.getImportance() < existing.getImportance()) { existing.setImportance(channel.getImportance()); Loading @@ -582,8 +578,9 @@ public class PreferencesHelper implements RankingConfig { boolean bypassDnd = channel.canBypassDnd(); existing.setBypassDnd(bypassDnd); if (bypassDnd != mAreChannelsBypassingDnd) { updateChannelsBypassingDnd(); if (bypassDnd != mAreChannelsBypassingDnd || previousExistingImportance != existing.getImportance()) { updateChannelsBypassingDnd(mContext.getUserId()); } } Loading Loading @@ -613,7 +610,7 @@ public class PreferencesHelper implements RankingConfig { r.channels.put(channel.getId(), channel); if (channel.canBypassDnd() != mAreChannelsBypassingDnd) { updateChannelsBypassingDnd(); updateChannelsBypassingDnd(mContext.getUserId()); } MetricsLogger.action(getChannelLog(channel, pkg).setType( com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN)); Loading Loading @@ -663,8 +660,9 @@ public class PreferencesHelper implements RankingConfig { MetricsLogger.action(getChannelLog(updatedChannel, pkg)); } if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd) { updateChannelsBypassingDnd(); if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd || channel.getImportance() != updatedChannel.getImportance()) { updateChannelsBypassingDnd(mContext.getUserId()); } updateConfig(); } Loading Loading @@ -701,7 +699,7 @@ public class PreferencesHelper implements RankingConfig { MetricsLogger.action(lm); if (mAreChannelsBypassingDnd && channel.canBypassDnd()) { updateChannelsBypassingDnd(); updateChannelsBypassingDnd(mContext.getUserId()); } } } Loading Loading @@ -858,6 +856,27 @@ public class PreferencesHelper implements RankingConfig { return new ParceledListSlice<>(channels); } /** * Gets all notification channels associated with the given pkg and userId that can bypass dnd */ public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg, int userId) { List<NotificationChannel> channels = new ArrayList<>(); synchronized (mPackagePreferences) { final PackagePreferences r = mPackagePreferences.get( packagePreferencesKey(pkg, userId)); // notifications from this package aren't blocked if (r != null && r.importance != IMPORTANCE_NONE) { for (NotificationChannel channel : r.channels.values()) { if (channelIsLive(r, channel) && channel.canBypassDnd()) { channels.add(channel); } } } } return new ParceledListSlice<>(channels); } /** * True for pre-O apps that only have the default channel, or pre O apps that have no * channels yet. This method will create the default channel for pre-O apps that don't have it. Loading Loading @@ -922,18 +941,62 @@ public class PreferencesHelper implements RankingConfig { return count; } public void updateChannelsBypassingDnd() { /** * Returns the number of apps that have at least one notification channel that can bypass DND * for given particular user */ public int getAppsBypassingDndCount(int userId) { int count = 0; synchronized (mPackagePreferences) { final int numPackagePreferencess = mPackagePreferences.size(); for (int PackagePreferencesIndex = 0; PackagePreferencesIndex < numPackagePreferencess; PackagePreferencesIndex++) { final PackagePreferences r = mPackagePreferences.valueAt(PackagePreferencesIndex); final int numChannels = r.channels.size(); for (int channelIndex = 0; channelIndex < numChannels; channelIndex++) { NotificationChannel channel = r.channels.valueAt(channelIndex); if (!channel.isDeleted() && channel.canBypassDnd()) { // If any channel bypasses DND, synchronize state and return early. final int numPackagePreferences = mPackagePreferences.size(); for (int i = 0; i < numPackagePreferences; i++) { final PackagePreferences r = mPackagePreferences.valueAt(i); // Package isn't associated with this userId or notifications from this package are // blocked if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) { continue; } for (NotificationChannel channel : r.channels.values()) { if (channelIsLive(r, channel) && channel.canBypassDnd()) { count++; break; } } } } return count; } /** * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before * updating * @param userId */ private void syncChannelsBypassingDnd(int userId) { mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1; updateChannelsBypassingDnd(userId); } /** * Updates the user's NotificationPolicy based on whether the given userId * has channels bypassing DND * @param userId */ private void updateChannelsBypassingDnd(int userId) { synchronized (mPackagePreferences) { final int numPackagePreferences = mPackagePreferences.size(); for (int i = 0; i < numPackagePreferences; i++) { final PackagePreferences r = mPackagePreferences.valueAt(i); // Package isn't associated with this userId or notifications from this package are // blocked if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) { continue; } for (NotificationChannel channel : r.channels.values()) { if (channelIsLive(r, channel) && channel.canBypassDnd()) { if (!mAreChannelsBypassingDnd) { mAreChannelsBypassingDnd = true; updateZenPolicy(true); Loading @@ -943,7 +1006,6 @@ public class PreferencesHelper implements RankingConfig { } } } // If no channels bypass DND, update the zen policy once to disable DND bypass. if (mAreChannelsBypassingDnd) { mAreChannelsBypassingDnd = false; Loading @@ -951,6 +1013,22 @@ public class PreferencesHelper implements RankingConfig { } } private boolean channelIsLive(PackagePreferences pkgPref, NotificationChannel channel) { // Channel is in a group that's blocked if (!TextUtils.isEmpty(channel.getGroup())) { if (pkgPref.groups.get(channel.getGroup()).isBlocked()) { return false; } } // Channel is deleted or is blocked if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) { return false; } return true; } public void updateZenPolicy(boolean areChannelsBypassingDnd) { NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy(); mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy( Loading Loading @@ -1329,6 +1407,20 @@ public class PreferencesHelper implements RankingConfig { return packageChannels; } /** * Called when user switches */ public void onUserSwitched(int userId) { syncChannelsBypassingDnd(userId); } /** * Called when user is unlocked */ public void onUserUnlocked(int userId) { syncChannelsBypassingDnd(userId); } public void onUserRemoved(int userId) { synchronized (mPackagePreferences) { int N = mPackagePreferences.size(); Loading
services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +153 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import static junit.framework.Assert.fail; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; Loading Loading @@ -1090,6 +1089,158 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(0, mHelper.getBlockedChannelCount("pkg2", UID_O)); } @Test public void testGetChannelsBypassingDndCount_noChannelsBypassing() throws Exception { assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, USER.getIdentifier()).getList().size()); } @Test public void testGetChannelsBypassingDnd_noChannelsForUserIdBypassing() throws Exception { int user = 9; NotificationChannel channel = new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_MAX); channel.setBypassDnd(true); mHelper.createNotificationChannel(PKG_N_MR1, 111, channel, true, true); assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); } @Test public void testGetChannelsBypassingDndCount_oneChannelBypassing_groupBlocked() { int user = USER.getIdentifier(); NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); NotificationChannel channel1 = new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MAX); channel1.setBypassDnd(true); channel1.setGroup(ncg.getId()); mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ true); mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true); assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); // disable group ncg.setBlocked(true); mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ false); assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); } @Test public void testGetChannelsBypassingDndCount_multipleChannelsBypassing() { int user = USER.getIdentifier(); NotificationChannel channel1 = new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MAX); NotificationChannel channel2 = new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_MAX); NotificationChannel channel3 = new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_MAX); channel1.setBypassDnd(true); channel2.setBypassDnd(true); channel3.setBypassDnd(true); // has DND access, so can set bypassDnd attribute mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true); mHelper.createNotificationChannel(PKG_N_MR1, user, channel2, true, true); mHelper.createNotificationChannel(PKG_N_MR1, user, channel3, true, true); assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); // block notifications from this app mHelper.setEnabled(PKG_N_MR1, user, false); assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); // re-enable notifications from this app mHelper.setEnabled(PKG_N_MR1, user, true); assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); // setBypassDnd false for some channels channel1.setBypassDnd(false); channel2.setBypassDnd(false); assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); // setBypassDnd false for rest of the channels channel3.setBypassDnd(false); assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1, user).getList().size()); } @Test public void testGetAppsBypassingDndCount_noAppsBypassing() throws Exception { assertEquals(0, mHelper.getAppsBypassingDndCount(USER.getIdentifier())); } @Test public void testGetAppsBypassingDndCount_noAppsForUserIdBypassing() throws Exception { int user = 9; NotificationChannel channel = new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_MAX); channel.setBypassDnd(true); mHelper.createNotificationChannel(PKG_N_MR1, 111, channel, true, true); assertEquals(0, mHelper.getAppsBypassingDndCount(user)); } @Test public void testGetAppsBypassingDndCount_oneChannelBypassing_groupBlocked() { int user = USER.getIdentifier(); NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); NotificationChannel channel1 = new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MAX); channel1.setBypassDnd(true); channel1.setGroup(ncg.getId()); mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ true); mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true); assertEquals(1, mHelper.getAppsBypassingDndCount(user)); // disable group ncg.setBlocked(true); mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg, /* fromTargetApp */ false); assertEquals(0, mHelper.getAppsBypassingDndCount(user)); } @Test public void testGetAppsBypassingDndCount_oneAppBypassing() { int user = USER.getIdentifier(); NotificationChannel channel1 = new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MAX); NotificationChannel channel2 = new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_MAX); NotificationChannel channel3 = new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_MAX); channel1.setBypassDnd(true); channel2.setBypassDnd(true); channel3.setBypassDnd(true); // has DND access, so can set bypassDnd attribute mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true); mHelper.createNotificationChannel(PKG_N_MR1, user, channel2, true, true); mHelper.createNotificationChannel(PKG_N_MR1, user, channel3, true, true); assertEquals(1, mHelper.getAppsBypassingDndCount(user)); // block notifications from this app mHelper.setEnabled(PKG_N_MR1, user, false); assertEquals(0, mHelper.getAppsBypassingDndCount(user)); // no apps can bypass dnd // re-enable notifications from this app mHelper.setEnabled(PKG_N_MR1, user, true); assertEquals(1, mHelper.getAppsBypassingDndCount(user)); // setBypassDnd false for some channels channel1.setBypassDnd(false); channel2.setBypassDnd(false); assertEquals(1, mHelper.getAppsBypassingDndCount(user)); // setBypassDnd false for rest of the channels channel3.setBypassDnd(false); assertEquals(0, mHelper.getAppsBypassingDndCount(user)); } @Test public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception { // create notification channel that can't bypass dnd Loading