Allow the NAS to update channels and add @SystemApi to NotificationChannel#setImportantConversation.
This is to support the new conversation notification channel management feature.
Design doc: go/nas-25q2-blue
Bug: 374268500
Bug: 373599715
Change-Id: I044bf6800d092ec59be0d664912c3358b3dba60f
Flag: android.service.notification.notification_conversation_channel_management
API-Coverage-Bug: 374688623
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7ba5a4b..89f1a4b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1047,6 +1047,7 @@
method public int getUserLockedFields();
method public boolean isDeleted();
method public void populateFromXml(org.xmlpull.v1.XmlPullParser);
+ method @FlaggedApi("android.service.notification.notification_conversation_channel_management") public void setImportantConversation(boolean);
method public org.json.JSONObject toJson() throws org.json.JSONException;
method public void writeXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
field public static final int USER_LOCKED_SOUND = 32; // 0x20
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1766449..a5b5ef3 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -377,7 +377,7 @@
method public void setDeletedTimeMs(long);
method public void setDemoted(boolean);
method public void setImportanceLockedByCriticalDeviceFunction(boolean);
- method public void setImportantConversation(boolean);
+ method @FlaggedApi("android.service.notification.notification_conversation_channel_management") public void setImportantConversation(boolean);
method public void setOriginalImportance(int);
method public void setUserVisibleTaskShown(boolean);
field @FlaggedApi("android.service.notification.notification_classification") public static final String NEWS_ID = "android.app.news";
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 794636d..73d26b8 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -551,10 +551,10 @@
mDeletedTime = time;
}
- /**
- * @hide
- */
+ /** @hide */
@TestApi
+ @SystemApi
+ @FlaggedApi(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
public void setImportantConversation(boolean importantConvo) {
mImportantConvo = importantConvo;
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 2cf31e1..a8ab211 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -920,7 +920,7 @@
* <p>This method will throw a security exception if you don't have access to notifications
* for the given user.</p>
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
- * device} in order to use this method.
+ * device} or be the notification assistant in order to use this method.
*
* @param pkg The package the channel belongs to.
* @param user The user the channel belongs to.
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5b998eb..849f236 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6726,7 +6726,7 @@
Objects.requireNonNull(pkg);
Objects.requireNonNull(user);
- verifyPrivilegedListener(token, user, false);
+ verifyPrivilegedListener(token, user, true);
final NotificationChannel originalChannel = mPreferencesHelper.getNotificationChannel(
pkg, getUidForPackageAndUser(pkg, user), channel.getId(), true);
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 f6f35ab..ac021e1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4836,7 +4836,8 @@
}
@Test
- public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception {
+ public void updateNotificationChannelFromPrivilegedListener_cdm_success() throws Exception {
+
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(mPkg, mUserId))
.thenReturn(singletonList(mock(AssociationInfo.class)));
@@ -4856,7 +4857,7 @@
}
@Test
- public void testUpdateNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
+ public void updateNotificationChannelFromPrivilegedListener_cdm_noAccess() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(mPkg, mUserId))
.thenReturn(emptyList());
@@ -4878,7 +4879,51 @@
}
@Test
- public void testUpdateNotificationChannelFromPrivilegedListener_badUser() throws Exception {
+ public void updateNotificationChannelFromPrivilegedListener_assistant_success() throws Exception {
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mCompanionMgr.getAssociations(mPkg, mUserId))
+ .thenReturn(emptyList());
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+ when(mPreferencesHelper.getNotificationChannel(eq(mPkg), anyInt(),
+ eq(mTestNotificationChannel.getId()), anyBoolean()))
+ .thenReturn(mTestNotificationChannel);
+
+ mBinderService.updateNotificationChannelFromPrivilegedListener(
+ null, mPkg, Process.myUserHandle(), mTestNotificationChannel);
+
+ verify(mPreferencesHelper, times(1)).updateNotificationChannel(
+ anyString(), anyInt(), any(), anyBoolean(), anyInt(), anyBoolean());
+
+ verify(mListeners, never()).notifyNotificationChannelChanged(eq(mPkg),
+ eq(Process.myUserHandle()), eq(mTestNotificationChannel),
+ eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
+ }
+
+ @Test
+ public void updateNotificationChannelFromPrivilegedListener_assistant_noAccess() throws Exception {
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mCompanionMgr.getAssociations(mPkg, mUserId))
+ .thenReturn(emptyList());
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
+
+ try {
+ mBinderService.updateNotificationChannelFromPrivilegedListener(
+ null, mPkg, Process.myUserHandle(), mTestNotificationChannel);
+ fail("listeners that don't have a companion device shouldn't be able to call this");
+ } catch (SecurityException e) {
+ // pass
+ }
+
+ verify(mPreferencesHelper, never()).updateNotificationChannel(
+ anyString(), anyInt(), any(), anyBoolean(), anyInt(), anyBoolean());
+
+ verify(mListeners, never()).notifyNotificationChannelChanged(eq(mPkg),
+ eq(Process.myUserHandle()), eq(mTestNotificationChannel),
+ eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
+ }
+
+ @Test
+ public void updateNotificationChannelFromPrivilegedListener_badUser() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(mPkg, mUserId))
.thenReturn(singletonList(mock(AssociationInfo.class)));
@@ -4904,7 +4949,7 @@
}
@Test
- public void testUpdateNotificationChannelFromPrivilegedListener_noSoundUriPermission()
+ public void updateNotificationChannelFromPrivilegedListener_noSoundUriPermission()
throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(mPkg, mUserId))
@@ -4936,7 +4981,7 @@
}
@Test
- public void testUpdateNotificationChannelFromPrivilegedListener_noSoundUriPermission_sameSound()
+ public void updateNotificationChannelFromPrivilegedListener_noSoundUriPermission_sameSound()
throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(mPkg, mUserId))
@@ -4968,7 +5013,7 @@
@Test
public void
- testUpdateNotificationChannelFromPrivilegedListener_oldSoundNoUriPerm_newSoundHasUriPerm()
+ updateNotificationChannelFromPrivilegedListener_oldSoundNoUriPerm_newSoundHasUriPerm()
throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mCompanionMgr.getAssociations(mPkg, mUserId))