diff options
| author | 2019-04-01 16:04:24 -0700 | |
|---|---|---|
| committer | 2019-04-02 23:00:55 +0000 | |
| commit | be7979616c74493d704bd57f8bcf678cd62c068f (patch) | |
| tree | 824be8b10a5a986512472e620e289f67df443086 | |
| parent | fc02cc3bffcb9f4a66ce2162ac09109ec1ddeb16 (diff) | |
Enforce new bubble policy in NoMan
If the app is foreground & the notification wants & is allowed to bubble
then we bubble.
Otherwise, the notification only gets flag bubble if it:
- is messaging style OR
- phone call with foreground service with person
If the app is foreground & something that typically wouldn't be allowed
to bubble gets to bubble, as long as that bubble is around, subsequent
updates to it will also be flagged as a bubble. Once the bubble / notif
is removed, subsequent notifs will not bubble.
Adds a tests for these various requirements.
Test: atest NotificationManagerServiceTest
Bug: 129147774
Change-Id: I27d28566af4c29ec59430939f82ac2fcedeb75fe
2 files changed, 481 insertions, 16 deletions
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index bfab85b5211b..f2e56b589bd5 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -17,6 +17,7 @@ package com.android.server.notification; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; +import static android.app.Notification.CATEGORY_CALL; import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY; import static android.app.Notification.FLAG_BUBBLE; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; @@ -107,6 +108,7 @@ import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.app.PendingIntent; +import android.app.Person; import android.app.StatusBarManager; import android.app.UriGrantsManager; import android.app.admin.DeviceAdminInfo; @@ -4762,11 +4764,36 @@ public class NotificationManagerService extends SystemService { /** * Updates the flags for this notification to reflect whether it is a bubble or not. */ - private void flagNotificationForBubbles(NotificationRecord r, String pkg, int userId) { + private void flagNotificationForBubbles(NotificationRecord r, String pkg, int userId, + NotificationRecord oldRecord) { Notification notification = r.getNotification(); - boolean canBubble = mPreferencesHelper.areBubblesAllowed(pkg, userId) + + // Does the app want to bubble & have permission to bubble? + boolean canBubble = notification.getBubbleMetadata() != null + && mPreferencesHelper.areBubblesAllowed(pkg, userId) && r.getChannel().canBubble(); - if (notification.getBubbleMetadata() != null && canBubble) { + + // Is the app in the foreground? + final boolean appIsForeground = + mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND; + + // Is the notification something we'd allow to bubble? + // A call with a foreground service + person + ArrayList<Person> peopleList = notification.extras != null + ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST) + : null; + boolean isForegroundCall = CATEGORY_CALL.equals(notification.category) + && (notification.flags & FLAG_FOREGROUND_SERVICE) != 0; + // OR message style (which always has a person) + Class<? extends Notification.Style> style = notification.getNotificationStyle(); + boolean isMessageStyle = Notification.MessagingStyle.class.equals(style); + boolean notificationAppropriateToBubble = isMessageStyle + || (peopleList != null && !peopleList.isEmpty() && isForegroundCall); + // OR something that was previously a bubble & still exists + boolean bubbleUpdate = oldRecord != null + && (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0; + + if (canBubble && (notificationAppropriateToBubble || appIsForeground || bubbleUpdate)) { notification.flags |= FLAG_BUBBLE; } else { notification.flags &= ~FLAG_BUBBLE; @@ -5129,7 +5156,7 @@ public class NotificationManagerService extends SystemService { final String tag = n.getTag(); // We need to fix the notification up a little for bubbles - flagNotificationForBubbles(r, pkg, callingUid); + flagNotificationForBubbles(r, pkg, callingUid, old); // Handle grouped notifications and bail out early if we // can to avoid extracting signals. diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 6f1bd87f14f7..ca7a71ecad75 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -18,6 +18,8 @@ package com.android.server.notification; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE; +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; +import static android.app.Notification.CATEGORY_CALL; import static android.app.Notification.FLAG_BUBBLE; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; import static android.app.NotificationManager.EXTRA_BLOCKED_STATE; @@ -81,6 +83,7 @@ import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.Person; import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.companion.ICompanionDeviceManager; @@ -103,6 +106,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.DeviceConfig; @@ -433,6 +437,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private NotificationRecord generateNotificationRecord(NotificationChannel channel, Notification.TvExtender extender) { + return generateNotificationRecord(channel, extender, false /* isBubble */); + } + + private NotificationRecord generateNotificationRecord(NotificationChannel channel, + Notification.TvExtender extender, boolean isBubble) { if (channel == null) { channel = mTestNotificationChannel; } @@ -442,6 +451,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { if (extender != null) { nb.extend(extender); } + if (isBubble) { + nb.setBubbleMetadata(getBasicBubbleMetadataBuilder().build()); + } StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0, nb.build(), new UserHandle(mUid), null, 0); return new NotificationRecord(mContext, sbn, channel); @@ -4293,7 +4305,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testFlagBubbleNotifs_flagIfAllowed() throws RemoteException { + public void testFlagBubbleNotifs_flag_appForeground() throws RemoteException { // Bubbles are allowed! mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); @@ -4303,29 +4315,353 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( mTestNotificationChannel.getImportance()); - // Notif with bubble metadata + // Notif with bubble metadata but not our other misc requirements + NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, + null /* tvExtender */, true /* isBubble */); + + // Say we're foreground + when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn( + IMPORTANCE_FOREGROUND); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // yes allowed, yes foreground, yes bubble + assertTrue(mService.getNotificationRecord( + nr.sbn.getKey()).getNotification().isBubbleNotification()); + } + + @Test + public void testFlagBubbleNotifs_noFlag_appNotForeground() throws RemoteException { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Notif with bubble metadata but not our other misc requirements + NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, + null /* tvExtender */, true /* isBubble */); + + // Make sure we're NOT foreground + when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn( + IMPORTANCE_VISIBLE); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // yes allowed but NOT foreground, no bubble + assertFalse(mService.getNotificationRecord( + nr.sbn.getKey()).getNotification().isBubbleNotification()); + } + + @Test + public void testFlagBubbleNotifs_flag_previousForegroundFlag() throws RemoteException { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Notif with bubble metadata but not our other misc requirements + NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel, + null /* tvExtender */, true /* isBubble */); + + // Send notif when we're foreground + when(mActivityManager.getPackageImportance(nr1.sbn.getPackageName())).thenReturn( + IMPORTANCE_FOREGROUND); + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr1.sbn.getId(), nr1.sbn.getNotification(), nr1.sbn.getUserId()); + waitForIdle(); + + // yes allowed, yes foreground, yes bubble + assertTrue(mService.getNotificationRecord( + nr1.sbn.getKey()).getNotification().isBubbleNotification()); + + // Send a new update when we're not foreground + NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, + null /* tvExtender */, true /* isBubble */); + + when(mActivityManager.getPackageImportance(nr2.sbn.getPackageName())).thenReturn( + IMPORTANCE_VISIBLE); + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId()); + waitForIdle(); + + // yes allowed, previously foreground / flagged, yes bubble + assertTrue(mService.getNotificationRecord( + nr2.sbn.getKey()).getNotification().isBubbleNotification()); + + StatusBarNotification[] notifs2 = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifs2.length); + assertEquals(1, mService.getNotificationRecordCount()); + } + + @Test + public void testFlagBubbleNotifs_noFlag_previousForegroundFlag_afterRemoval() + throws RemoteException { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Notif with bubble metadata but not our other misc requirements + NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel, + null /* tvExtender */, true /* isBubble */); + + // Send notif when we're foreground + when(mActivityManager.getPackageImportance(nr1.sbn.getPackageName())).thenReturn( + IMPORTANCE_FOREGROUND); + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr1.sbn.getId(), nr1.sbn.getNotification(), nr1.sbn.getUserId()); + waitForIdle(); + + // yes allowed, yes foreground, yes bubble + assertTrue(mService.getNotificationRecord( + nr1.sbn.getKey()).getNotification().isBubbleNotification()); + + // Remove the bubble + mBinderService.cancelNotificationWithTag(PKG, "tag", nr1.sbn.getId(), + nr1.sbn.getUserId()); + waitForIdle(); + + StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG); + assertEquals(0, notifs.length); + assertEquals(0, mService.getNotificationRecordCount()); + + // Send a new update when we're not foreground + NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel, + null /* tvExtender */, true /* isBubble */); + + when(mActivityManager.getPackageImportance(nr2.sbn.getPackageName())).thenReturn( + IMPORTANCE_VISIBLE); + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId()); + waitForIdle(); + + // yes allowed, but was removed & no foreground, so no bubble + assertFalse(mService.getNotificationRecord( + nr2.sbn.getKey()).getNotification().isBubbleNotification()); + + StatusBarNotification[] notifs2 = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifs2.length); + assertEquals(1, mService.getNotificationRecordCount()); + } + + @Test + public void testFlagBubbleNotifs_flag_messaging() throws RemoteException { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Give it bubble metadata + Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build(); + // Give it a person + Person person = new Person.Builder() + .setName("bubblebot") + .build(); + // Make it messaging style + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setContentTitle("foo") + .setBubbleMetadata(data) + .setStyle(new Notification.MessagingStyle(person) + .setConversationTitle("Bubble Chat") + .addMessage("Hello?", + SystemClock.currentThreadTimeMillis() - 300000, person) + .addMessage("Is it me you're looking for?", + SystemClock.currentThreadTimeMillis(), person) + ) + .setSmallIcon(android.R.drawable.sym_def_app_icon); + + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0, + nb.build(), new UserHandle(mUid), null, 0); + NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, null, + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // yes allowed, yes messaging, yes bubble + assertTrue(mService.getNotificationRecord( + sbn.getKey()).getNotification().isBubbleNotification()); + } + + @Test + public void testFlagBubbleNotifs_flag_phonecall() throws RemoteException { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Give it bubble metadata Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build(); + // Give it a person + Person person = new Person.Builder() + .setName("bubblebot") + .build(); + // Make it a phone call Notification.Builder nb = new Notification.Builder(mContext, mTestNotificationChannel.getId()) + .setCategory(CATEGORY_CALL) + .addPerson(person) .setContentTitle("foo") .setBubbleMetadata(data) .setSmallIcon(android.R.drawable.sym_def_app_icon); StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0, nb.build(), new UserHandle(mUid), null, 0); + // Make sure it has foreground service + sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE; NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); mBinderService.enqueueNotificationWithTag(PKG, PKG, null, nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); waitForIdle(); - // yes allowed, yes bubble + // yes phone call, yes person, yes foreground service, yes bubble assertTrue(mService.getNotificationRecord( sbn.getKey()).getNotification().isBubbleNotification()); } @Test - public void testFlagBubbleNotifs_noFlagIfNotAllowed() throws RemoteException { + public void testFlagBubbleNotifs_noFlag_phonecall_noForegroundService() throws RemoteException { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Give it bubble metadata + Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build(); + // Give it a person + Person person = new Person.Builder() + .setName("bubblebot") + .build(); + // Make it a phone call + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setCategory(CATEGORY_CALL) + .addPerson(person) + .setContentTitle("foo") + .setBubbleMetadata(data) + .setSmallIcon(android.R.drawable.sym_def_app_icon); + + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0, + nb.build(), new UserHandle(mUid), null, 0); + NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, null, + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // yes phone call, yes person, NO foreground service, no bubble + assertFalse(mService.getNotificationRecord( + sbn.getKey()).getNotification().isBubbleNotification()); + } + + @Test + public void testFlagBubbleNotifs_noFlag_phonecall_noPerson() throws RemoteException { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Give it bubble metadata + Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build(); + // Make it a phone call + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setCategory(CATEGORY_CALL) + .setContentTitle("foo") + .setBubbleMetadata(data) + .setSmallIcon(android.R.drawable.sym_def_app_icon); + + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0, + nb.build(), new UserHandle(mUid), null, 0); + // Make sure it has foreground service + sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE; + NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, null, + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // yes phone call, yes foreground service, BUT NO person, no bubble + assertFalse(mService.getNotificationRecord( + sbn.getKey()).getNotification().isBubbleNotification()); + } + + @Test + public void testFlagBubbleNotifs_noFlag_phonecall_noCategory() throws RemoteException { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Give it bubble metadata + Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build(); + // Give it a person + Person person = new Person.Builder() + .setName("bubblebot") + .build(); + // No category + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .addPerson(person) + .setContentTitle("foo") + .setBubbleMetadata(data) + .setSmallIcon(android.R.drawable.sym_def_app_icon); + + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0, + nb.build(), new UserHandle(mUid), null, 0); + // Make sure it has foreground service + sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE; + NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, null, + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // yes person, yes foreground service, BUT NO call, no bubble + assertFalse(mService.getNotificationRecord( + sbn.getKey()).getNotification().isBubbleNotification()); + } + + @Test + public void testFlagBubbleNotifs_noFlag_messaging_appNotAllowed() throws RemoteException { // Bubbles are NOT allowed! mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(false); @@ -4335,12 +4671,24 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( mTestNotificationChannel.getImportance()); - // Notif with bubble metadata + // Give it bubble metadata Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build(); + // Give it a person + Person person = new Person.Builder() + .setName("bubblebot") + .build(); + // Make it messaging style Notification.Builder nb = new Notification.Builder(mContext, mTestNotificationChannel.getId()) .setContentTitle("foo") .setBubbleMetadata(data) + .setStyle(new Notification.MessagingStyle(person) + .setConversationTitle("Bubble Chat") + .addMessage("Hello?", + SystemClock.currentThreadTimeMillis() - 300000, person) + .addMessage("Is it me you're looking for?", + SystemClock.currentThreadTimeMillis(), person) + ) .setSmallIcon(android.R.drawable.sym_def_app_icon); StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0, @@ -4358,7 +4706,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testFlagBubbleNotifs_noFlagIfNotBubble() throws RemoteException { + public void testFlagBubbleNotifs_noFlag_notBubble() throws RemoteException { // Bubbles are allowed! mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); @@ -4369,9 +4717,50 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel.getImportance()); // Notif WITHOUT bubble metadata + NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel); + + // Post the notification + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // no bubble metadata, no bubble + assertFalse(mService.getNotificationRecord( + nr.sbn.getKey()).getNotification().isBubbleNotification()); + } + + @Test + public void testFlagBubbleNotifs_noFlag_messaging_channelNotAllowed() throws RemoteException { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // But not on this channel! + mTestNotificationChannel.setAllowBubbles(false); + + // Give it bubble metadata + Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build(); + // Give it a person + Person person = new Person.Builder() + .setName("bubblebot") + .build(); + // Make it messaging style Notification.Builder nb = new Notification.Builder(mContext, mTestNotificationChannel.getId()) .setContentTitle("foo") + .setBubbleMetadata(data) + .setStyle(new Notification.MessagingStyle(person) + .setConversationTitle("Bubble Chat") + .addMessage("Hello?", + SystemClock.currentThreadTimeMillis() - 300000, person) + .addMessage("Is it me you're looking for?", + SystemClock.currentThreadTimeMillis(), person) + ) .setSmallIcon(android.R.drawable.sym_def_app_icon); StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0, @@ -4383,16 +4772,57 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); waitForIdle(); - // no bubble metadata, no bubble + // channel not allowed, no bubble assertFalse(mService.getNotificationRecord( sbn.getKey()).getNotification().isBubbleNotification()); } @Test - public void testFlagBubbleNotifs_noFlagIfChannelNotBubble() throws RemoteException { + public void testFlagBubbleNotifs_noFlag_phonecall_notAllowed() throws RemoteException { // Bubbles are allowed! mService.setPreferencesHelper(mPreferencesHelper); - when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(false); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Give it bubble metadata + Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build(); + // Give it a person + Person person = new Person.Builder() + .setName("bubblebot") + .build(); + // Make it a phone call + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setCategory(CATEGORY_CALL) + .addPerson(person) + .setContentTitle("foo") + .setBubbleMetadata(data) + .setSmallIcon(android.R.drawable.sym_def_app_icon); + + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0, + nb.build(), new UserHandle(mUid), null, 0); + // Make sure it has foreground service + sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE; + NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, null, + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // yes phone call, yes person, yes foreground service, but not allowed, no bubble + assertFalse(mService.getNotificationRecord( + sbn.getKey()).getNotification().isBubbleNotification()); + } + + @Test + public void testFlagBubbleNotifs_noFlag_phonecall_channelNotAllowed() throws RemoteException { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(false); when(mPreferencesHelper.getNotificationChannel( anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( mTestNotificationChannel); @@ -4402,24 +4832,32 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // But not on this channel! mTestNotificationChannel.setAllowBubbles(false); - // Notif with bubble metadata + // Give it bubble metadata Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build(); + // Give it a person + Person person = new Person.Builder() + .setName("bubblebot") + .build(); + // Make it a phone call Notification.Builder nb = new Notification.Builder(mContext, mTestNotificationChannel.getId()) + .setCategory(CATEGORY_CALL) + .addPerson(person) .setContentTitle("foo") .setBubbleMetadata(data) .setSmallIcon(android.R.drawable.sym_def_app_icon); StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0, nb.build(), new UserHandle(mUid), null, 0); + // Make sure it has foreground service + sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE; NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - // Post the notification mBinderService.enqueueNotificationWithTag(PKG, PKG, null, nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); waitForIdle(); - // channel not allowed, no bubble + // yes phone call, yes person, yes foreground service, but channel not allowed, no bubble assertFalse(mService.getNotificationRecord( sbn.getKey()).getNotification().isBubbleNotification()); } |