diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 704e912c3749f..bc7fcf55d0a33 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -98,21 +98,11 @@ public final class NotificationChannel implements Parcelable { */ public static final int USER_LOCKED_SOUND = 0x00000020; - /** - * @hide - */ - public static final int USER_LOCKED_ALLOWED = 0x00000040; - /** * @hide */ public static final int USER_LOCKED_SHOW_BADGE = 0x00000080; - /** - * @hide - */ - public static final int USER_LOCKED_AUDIO_ATTRIBUTES = 0x00000100; - /** * @hide */ @@ -123,9 +113,7 @@ public final class NotificationChannel implements Parcelable { USER_LOCKED_LIGHTS, USER_LOCKED_VIBRATION, USER_LOCKED_SOUND, - USER_LOCKED_ALLOWED, USER_LOCKED_SHOW_BADGE, - USER_LOCKED_AUDIO_ATTRIBUTES }; private static final int DEFAULT_LIGHT_COLOR = 0; @@ -270,6 +258,13 @@ public final class NotificationChannel implements Parcelable { mUserLockedFields |= field; } + /** + * @hide + */ + public void unlockFields(int field) { + mUserLockedFields &= ~field; + } + /** * @hide */ diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index 7758516bfde4f..788f21ddf0929 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -48,11 +48,13 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; public class RankingHelper implements RankingConfig { private static final String TAG = "RankingHelper"; @@ -574,12 +576,8 @@ public class RankingHelper implements RankingConfig { updateConfig(); } - private void clearLockedFields(NotificationChannel channel) { - int clearMask = 0; - for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) { - clearMask |= NotificationChannel.LOCKABLE_FIELDS[i]; - } - channel.lockFields(~clearMask); + void clearLockedFields(NotificationChannel channel) { + channel.unlockFields(channel.getUserLockedFields()); } @Override @@ -597,6 +595,7 @@ public class RankingHelper implements RankingConfig { if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) { updatedChannel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE); } + lockFieldsForUpdate(channel, updatedChannel); r.channels.put(updatedChannel.getId(), updatedChannel); if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(updatedChannel.getId())) { @@ -805,6 +804,35 @@ public class RankingHelper implements RankingConfig { enabled ? DEFAULT_IMPORTANCE : NotificationManager.IMPORTANCE_NONE); } + @VisibleForTesting + void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) { + update.unlockFields(update.getUserLockedFields()); + update.lockFields(original.getUserLockedFields()); + if (original.canBypassDnd() != update.canBypassDnd()) { + update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY); + } + if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) { + update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY); + } + if (original.getImportance() != update.getImportance()) { + update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); + } + if (original.shouldShowLights() != update.shouldShowLights() + || original.getLightColor() != update.getLightColor()) { + update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS); + } + if (!Objects.equals(original.getSound(), update.getSound())) { + update.lockFields(NotificationChannel.USER_LOCKED_SOUND); + } + if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern()) + || original.shouldVibrate() != update.shouldVibrate()) { + update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION); + } + if (original.canShowBadge() != update.canShowBadge()) { + update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE); + } + } + public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) { if (filter == null) { final int N = mSignalExtractors.length; diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java index 7bef0339297db..30d68129c836a 100644 --- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java @@ -55,6 +55,7 @@ import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.util.ArrayMap; +import android.util.Slog; import android.util.Xml; import java.io.BufferedInputStream; @@ -90,6 +91,7 @@ public class RankingHelperTest { @Mock NotificationUsageStats mUsageStats; @Mock RankingHandler mHandler; @Mock PackageManager mPm; + @Mock Context mContext; private Notification mNotiGroupGSortA; private Notification mNotiGroupGSortB; @@ -113,60 +115,6 @@ public class RankingHelperTest { MockitoAnnotations.initMocks(this); UserHandle user = UserHandle.ALL; - mHelper = new RankingHelper(getContext(), mPm, mHandler, mUsageStats, - new String[] {ImportanceExtractor.class.getName()}); - - mNotiGroupGSortA = new Notification.Builder(getContext(), TEST_CHANNEL_ID) - .setContentTitle("A") - .setGroup("G") - .setSortKey("A") - .setWhen(1205) - .build(); - mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", 1, null, 0, 0, mNotiGroupGSortA, user, - null, System.currentTimeMillis()), getDefaultChannel()); - - mNotiGroupGSortB = new Notification.Builder(getContext(), TEST_CHANNEL_ID) - .setContentTitle("B") - .setGroup("G") - .setSortKey("B") - .setWhen(1200) - .build(); - mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", 1, null, 0, 0, mNotiGroupGSortB, user, - null, System.currentTimeMillis()), getDefaultChannel()); - - mNotiNoGroup = new Notification.Builder(getContext(), TEST_CHANNEL_ID) - .setContentTitle("C") - .setWhen(1201) - .build(); - mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", 1, null, 0, 0, mNotiNoGroup, user, - null, System.currentTimeMillis()), getDefaultChannel()); - - mNotiNoGroup2 = new Notification.Builder(getContext(), TEST_CHANNEL_ID) - .setContentTitle("D") - .setWhen(1202) - .build(); - mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", 1, null, 0, 0, mNotiNoGroup2, user, - null, System.currentTimeMillis()), getDefaultChannel()); - - mNotiNoGroupSortA = new Notification.Builder(getContext(), TEST_CHANNEL_ID) - .setContentTitle("E") - .setWhen(1201) - .setSortKey("A") - .build(); - mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification( - "package", "package", 1, null, 0, 0, mNotiNoGroupSortA, user, - null, System.currentTimeMillis()), getDefaultChannel()); - - mAudioAttributes = new AudioAttributes.Builder() - .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) - .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) - .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED) - .build(); - final ApplicationInfo legacy = new ApplicationInfo(); legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1; final ApplicationInfo upgrade = new ApplicationInfo(); @@ -174,6 +122,64 @@ public class RankingHelperTest { when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy); when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(upgrade); when(mPm.getPackageUidAsUser(eq(PKG), anyInt())).thenReturn(UID); + when(mContext.getResources()).thenReturn( + InstrumentationRegistry.getContext().getResources()); + when(mContext.getPackageManager()).thenReturn(mPm); + when(mContext.getApplicationInfo()).thenReturn(legacy); + + mHelper = new RankingHelper(getContext(), mPm, mHandler, mUsageStats, + new String[] {ImportanceExtractor.class.getName()}); + + mNotiGroupGSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID) + .setContentTitle("A") + .setGroup("G") + .setSortKey("A") + .setWhen(1205) + .build(); + mRecordGroupGSortA = new NotificationRecord(mContext, new StatusBarNotification( + PKG, PKG, 1, null, 0, 0, mNotiGroupGSortA, user, + null, System.currentTimeMillis()), getDefaultChannel()); + + mNotiGroupGSortB = new Notification.Builder(mContext, TEST_CHANNEL_ID) + .setContentTitle("B") + .setGroup("G") + .setSortKey("B") + .setWhen(1200) + .build(); + mRecordGroupGSortB = new NotificationRecord(mContext, new StatusBarNotification( + PKG, PKG, 1, null, 0, 0, mNotiGroupGSortB, user, + null, System.currentTimeMillis()), getDefaultChannel()); + + mNotiNoGroup = new Notification.Builder(mContext, TEST_CHANNEL_ID) + .setContentTitle("C") + .setWhen(1201) + .build(); + mRecordNoGroup = new NotificationRecord(mContext, new StatusBarNotification( + PKG, PKG, 1, null, 0, 0, mNotiNoGroup, user, + null, System.currentTimeMillis()), getDefaultChannel()); + + mNotiNoGroup2 = new Notification.Builder(mContext, TEST_CHANNEL_ID) + .setContentTitle("D") + .setWhen(1202) + .build(); + mRecordNoGroup2 = new NotificationRecord(mContext, new StatusBarNotification( + PKG, PKG, 1, null, 0, 0, mNotiNoGroup2, user, + null, System.currentTimeMillis()), getDefaultChannel()); + + mNotiNoGroupSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID) + .setContentTitle("E") + .setWhen(1201) + .setSortKey("A") + .build(); + mRecordNoGroupSortA = new NotificationRecord(mContext, new StatusBarNotification( + PKG, PKG, 1, null, 0, 0, mNotiNoGroupSortA, user, + null, System.currentTimeMillis()), getDefaultChannel()); + + mAudioAttributes = new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) + .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) + .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED) + .build(); } private NotificationChannel getDefaultChannel() { @@ -229,6 +235,10 @@ public class RankingHelperTest { assertEquals(expected.getName(), actual.getName()); } + private NotificationChannel getChannel() { + return new NotificationChannel("id", "name", IMPORTANCE_LOW); + } + @Test public void testFindAfterRankingWithASplitGroup() throws Exception { ArrayList notificationList = new ArrayList(3); @@ -643,6 +653,112 @@ public class RankingHelperTest { assertEquals(channel.canShowBadge(), savedChannel.canShowBadge()); } + @Test + public void testClearLockedFields() throws Exception { + final NotificationChannel channel = getChannel(); + mHelper.clearLockedFields(channel); + assertEquals(0, channel.getUserLockedFields()); + + channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY + | NotificationChannel.USER_LOCKED_IMPORTANCE); + mHelper.clearLockedFields(channel); + assertEquals(0, channel.getUserLockedFields()); + } + + @Test + public void testLockFields_soundAndVibration() throws Exception { + mHelper.createNotificationChannel(PKG, UID, getChannel(), true); + + final NotificationChannel update1 = getChannel(); + update1.setSound(new Uri.Builder().scheme("test").build(), + new AudioAttributes.Builder().build()); + update1.lockFields(NotificationChannel.USER_LOCKED_PRIORITY); // should be ignored + mHelper.updateNotificationChannel(PKG, UID, update1); + assertEquals(NotificationChannel.USER_LOCKED_SOUND, + mHelper.getNotificationChannel(PKG, UID, update1.getId(), false) + .getUserLockedFields()); + + NotificationChannel update2 = getChannel(); + update2.enableVibration(true); + mHelper.updateNotificationChannel(PKG, UID, update2); + assertEquals(NotificationChannel.USER_LOCKED_SOUND + | NotificationChannel.USER_LOCKED_VIBRATION, + mHelper.getNotificationChannel(PKG, UID, update2.getId(), false) + .getUserLockedFields()); + } + + @Test + public void testLockFields_vibrationAndLights() throws Exception { + mHelper.createNotificationChannel(PKG, UID, getChannel(), true); + + final NotificationChannel update1 = getChannel(); + update1.setVibrationPattern(new long[]{7945, 46 ,246}); + mHelper.updateNotificationChannel(PKG, UID, update1); + assertEquals(NotificationChannel.USER_LOCKED_VIBRATION, + mHelper.getNotificationChannel(PKG, UID, update1.getId(), false) + .getUserLockedFields()); + + final NotificationChannel update2 = getChannel(); + update2.enableLights(true); + mHelper.updateNotificationChannel(PKG, UID, update2); + assertEquals(NotificationChannel.USER_LOCKED_VIBRATION + | NotificationChannel.USER_LOCKED_LIGHTS, + mHelper.getNotificationChannel(PKG, UID, update2.getId(), false) + .getUserLockedFields()); + } + + @Test + public void testLockFields_lightsAndImportance() throws Exception { + mHelper.createNotificationChannel(PKG, UID, getChannel(), true); + + final NotificationChannel update1 = getChannel(); + update1.setLightColor(Color.GREEN); + mHelper.updateNotificationChannel(PKG, UID, update1); + assertEquals(NotificationChannel.USER_LOCKED_LIGHTS, + mHelper.getNotificationChannel(PKG, UID, update1.getId(), false) + .getUserLockedFields()); + + final NotificationChannel update2 = getChannel(); + update2.setImportance(IMPORTANCE_DEFAULT); + mHelper.updateNotificationChannel(PKG, UID, update2); + assertEquals(NotificationChannel.USER_LOCKED_LIGHTS + | NotificationChannel.USER_LOCKED_IMPORTANCE, + mHelper.getNotificationChannel(PKG, UID, update2.getId(), false) + .getUserLockedFields()); + } + + @Test + public void testLockFields_visibilityAndDndAndBadge() throws Exception { + mHelper.createNotificationChannel(PKG, UID, getChannel(), true); + assertEquals(0, + mHelper.getNotificationChannel(PKG, UID, getChannel().getId(), false) + .getUserLockedFields()); + + final NotificationChannel update1 = getChannel(); + update1.setBypassDnd(true); + mHelper.updateNotificationChannel(PKG, UID, update1); + assertEquals(NotificationChannel.USER_LOCKED_PRIORITY, + mHelper.getNotificationChannel(PKG, UID, update1.getId(), false) + .getUserLockedFields()); + + final NotificationChannel update2 = getChannel(); + update2.setLockscreenVisibility(Notification.VISIBILITY_SECRET); + mHelper.updateNotificationChannel(PKG, UID, update2); + assertEquals(NotificationChannel.USER_LOCKED_PRIORITY + | NotificationChannel.USER_LOCKED_VISIBILITY, + mHelper.getNotificationChannel(PKG, UID, update2.getId(), false) + .getUserLockedFields()); + + final NotificationChannel update3 = getChannel(); + update3.setShowBadge(false); + mHelper.updateNotificationChannel(PKG, UID, update3); + assertEquals(NotificationChannel.USER_LOCKED_PRIORITY + | NotificationChannel.USER_LOCKED_VISIBILITY + | NotificationChannel.USER_LOCKED_SHOW_BADGE, + mHelper.getNotificationChannel(PKG, UID, update3.getId(), false) + .getUserLockedFields()); + } + @Test public void testDeleteNonExistentChannel() throws Exception { mHelper.deleteNotificationChannelGroup(PKG, UID, "does not exist"); @@ -650,8 +766,7 @@ public class RankingHelperTest { @Test public void testGetDeletedChannel() throws Exception { - NotificationChannel channel = - new NotificationChannel("id2", "name2", IMPORTANCE_LOW); + NotificationChannel channel = getChannel(); channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); channel.enableLights(true); channel.setBypassDnd(true);