|
| 1 | +From 361c828f654b646f968644dbadf8a1f5f8ad67d8 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Valentin Iftime < [email protected]> |
| 3 | +Date: Thu, 1 Feb 2024 13:58:49 +0100 |
| 4 | +Subject: [PATCH] [BACKPORT] Verify URI permission for channel sound update |
| 5 | + from NotificationListenerService |
| 6 | + |
| 7 | + Check that a privileged NotificationListenerService (CDM) has the permission to access the sound URI |
| 8 | + when updating a notification channel. |
| 9 | + |
| 10 | +Test: atest com.android.server.notification.NotificationManagerServiceTest#testUpdateNotificationChannelFromPrivilegedListener_noSoundUriPermission |
| 11 | +Bug: 317357401 |
| 12 | +(cherry picked from commit 9b7bbbf5ad542ecf9ecbf8cd819b468791b443c0) |
| 13 | +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f090c0538a27d8658d8a860046d5c5e931302341) |
| 14 | +Merged-In: Ic7d2e96e43565e98d2aa29b8f2ba35c142387ba9 |
| 15 | +Change-Id: Ic7d2e96e43565e98d2aa29b8f2ba35c142387ba9 |
| 16 | +--- |
| 17 | + .../NotificationManagerService.java | 22 +++++++ |
| 18 | + .../NotificationManagerServiceTest.java | 57 +++++++++++++++++++ |
| 19 | + 2 files changed, 79 insertions(+) |
| 20 | + |
| 21 | +diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java |
| 22 | +index a1e8cd15fd7ee..e793dc024156a 100755 |
| 23 | +--- a/services/core/java/com/android/server/notification/NotificationManagerService.java |
| 24 | ++++ b/services/core/java/com/android/server/notification/NotificationManagerService.java |
| 25 | +@@ -3668,6 +3668,10 @@ public void updateNotificationChannelFromPrivilegedListener(INotificationListene |
| 26 | + Preconditions.checkNotNull(user); |
| 27 | + |
| 28 | + verifyPrivilegedListener(token, user); |
| 29 | ++ |
| 30 | ++ final NotificationChannel originalChannel = mRankingHelper.getNotificationChannel( |
| 31 | ++ pkg, getUidForPackageAndUser(pkg, user), channel.getId(), true); |
| 32 | ++ verifyPrivilegedListenerUriPermission(Binder.getCallingUid(), channel, originalChannel); |
| 33 | + updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true); |
| 34 | + } |
| 35 | + |
| 36 | +@@ -3709,6 +3713,24 @@ private void verifyPrivilegedListener(INotificationListener token, UserHandle us |
| 37 | + } |
| 38 | + } |
| 39 | + |
| 40 | ++ private void verifyPrivilegedListenerUriPermission(int sourceUid, |
| 41 | ++ @NonNull NotificationChannel updateChannel, |
| 42 | ++ @Nullable NotificationChannel originalChannel) { |
| 43 | ++ // Check that the NLS has the required permissions to access the channel |
| 44 | ++ final Uri soundUri = updateChannel.getSound(); |
| 45 | ++ final Uri originalSoundUri = |
| 46 | ++ (originalChannel != null) ? originalChannel.getSound() : null; |
| 47 | ++ if (soundUri != null && !Objects.equals(originalSoundUri, soundUri)) { |
| 48 | ++ Binder.withCleanCallingIdentity(() -> { |
| 49 | ++ mAm.checkGrantUriPermission(sourceUid, null, |
| 50 | ++ ContentProvider.getUriWithoutUserId(soundUri), |
| 51 | ++ Intent.FLAG_GRANT_READ_URI_PERMISSION, |
| 52 | ++ ContentProvider.getUserIdFromUri(soundUri, |
| 53 | ++ UserHandle.getUserId(sourceUid))); |
| 54 | ++ }); |
| 55 | ++ } |
| 56 | ++ } |
| 57 | ++ |
| 58 | + private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException { |
| 59 | + int uid = 0; |
| 60 | + long identity = Binder.clearCallingIdentity(); |
| 61 | +diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java |
| 62 | +index 379290bcf0ad0..db83d8f1a4f07 100644 |
| 63 | +--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java |
| 64 | ++++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java |
| 65 | +@@ -1681,6 +1681,63 @@ public void testUpdateNotificationChannelFromPrivilegedListener_badUser() throws |
| 66 | + eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED)); |
| 67 | + } |
| 68 | + |
| 69 | ++ @Test |
| 70 | ++ public void testUpdateNotificationChannelFromPrivilegedListener_noSoundUriPermission() |
| 71 | ++ throws Exception { |
| 72 | ++ mService.setPreferencesHelper(mPreferencesHelper); |
| 73 | ++ List<String> associations = new ArrayList<>(); |
| 74 | ++ associations.add("a"); |
| 75 | ++ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid))) |
| 76 | ++ .thenReturn(associations); |
| 77 | ++ when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(), |
| 78 | ++ eq(mTestNotificationChannel.getId()), anyBoolean())) |
| 79 | ++ .thenReturn(mTestNotificationChannel); |
| 80 | ++ final Uri soundUri = Uri.parse("content://media/test/sound/uri"); |
| 81 | ++ final NotificationChannel updatedNotificationChannel = new NotificationChannel( |
| 82 | ++ TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT); |
| 83 | ++ updatedNotificationChannel.setSound(soundUri, |
| 84 | ++ updatedNotificationChannel.getAudioAttributes()); |
| 85 | ++ doThrow(new SecurityException("no access")).when(mUgmInternal) |
| 86 | ++ .checkGrantUriPermission(eq(Process.myUid()), any(), eq(soundUri), |
| 87 | ++ anyInt(), eq(Process.myUserHandle().getIdentifier())); |
| 88 | ++ assertThrows(SecurityException.class, |
| 89 | ++ () -> mBinderService.updateNotificationChannelFromPrivilegedListener(null, PKG, |
| 90 | ++ Process.myUserHandle(), updatedNotificationChannel)); |
| 91 | ++ verify(mPreferencesHelper, never()).updateNotificationChannel( |
| 92 | ++ anyString(), anyInt(), any(), anyBoolean()); |
| 93 | ++ verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG), |
| 94 | ++ eq(Process.myUserHandle()), eq(mTestNotificationChannel), |
| 95 | ++ eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED)); |
| 96 | ++ } |
| 97 | ++ |
| 98 | ++ @Test |
| 99 | ++ public void testUpdateNotificationChannelFromPrivilegedListener_noSoundUriPermission_sameSound() |
| 100 | ++ throws Exception { |
| 101 | ++ mService.setPreferencesHelper(mPreferencesHelper); |
| 102 | ++ List<String> associations = new ArrayList<>(); |
| 103 | ++ associations.add("a"); |
| 104 | ++ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid))) |
| 105 | ++ .thenReturn(associations); |
| 106 | ++ when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(), |
| 107 | ++ eq(mTestNotificationChannel.getId()), anyBoolean())) |
| 108 | ++ .thenReturn(mTestNotificationChannel); |
| 109 | ++ final Uri soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; |
| 110 | ++ final NotificationChannel updatedNotificationChannel = new NotificationChannel( |
| 111 | ++ TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT); |
| 112 | ++ updatedNotificationChannel.setSound(soundUri, |
| 113 | ++ updatedNotificationChannel.getAudioAttributes()); |
| 114 | ++ doThrow(new SecurityException("no access")).when(mUgmInternal) |
| 115 | ++ .checkGrantUriPermission(eq(Process.myUid()), any(), eq(soundUri), |
| 116 | ++ anyInt(), eq(Process.myUserHandle().getIdentifier())); |
| 117 | ++ mBinderService.updateNotificationChannelFromPrivilegedListener( |
| 118 | ++ null, PKG, Process.myUserHandle(), updatedNotificationChannel); |
| 119 | ++ verify(mPreferencesHelper, times(1)).updateNotificationChannel( |
| 120 | ++ anyString(), anyInt(), any(), anyBoolean()); |
| 121 | ++ verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG), |
| 122 | ++ eq(Process.myUserHandle()), eq(mTestNotificationChannel), |
| 123 | ++ eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED)); |
| 124 | ++ } |
| 125 | ++ |
| 126 | + @Test |
| 127 | + public void testGetNotificationChannelFromPrivilegedListener_success() throws Exception { |
| 128 | + mService.setRankingHelper(mRankingHelper); |
0 commit comments