From b4da73a935a8c906ff5df562155824d63ac849ab Mon Sep 17 00:00:00 2001 From: Iavor-Valentin Iftime Date: Tue, 10 Dec 2024 13:11:54 +0000 Subject: Check sound Uri permission when creating a notification channel Verify that the calling app has the right permissions to read/grant the sound Uri for a channel it creates. Previously, Uri permissions would only be checked by NotificationRecord when a notification was posted. Flag: EXEMPT security fix Test: atest PreferencesHelperTest Test: atest NotificationManagerServiceTest Bug: 337775777 Merged-In: If5a6c80e3bd665a9e67655a230e43d704422082b Change-Id: If5a6c80e3bd665a9e67655a230e43d704422082b (cherry picked from commit 94fc92cf20b897f016c3a9718edf40818de4a11b) --- .../notification/NotificationManagerService.java | 9 +- .../server/notification/NotificationRecord.java | 15 +-- .../server/notification/PermissionHelper.java | 21 +++- .../server/notification/PreferencesHelper.java | 9 ++ .../NotificationManagerServiceTest.java | 37 +++++- .../notification/NotificationRecordTest.java | 25 ++-- .../server/notification/PreferencesHelperTest.java | 133 +++++++++++++++------ 7 files changed, 179 insertions(+), 70 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index b62fd616d299..9752299bca01 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2346,6 +2346,7 @@ public class NotificationManagerService extends SystemService { mPermissionHelper, mNotificationChannelLogger, mAppOps, + mUgmInternal, new SysUiStatsEvent.BuilderFactory(), mShowReviewPermissionsNotification); mRankingHelper = new RankingHelper(getContext(), @@ -5875,13 +5876,7 @@ public class NotificationManagerService extends SystemService { final Uri originalSoundUri = (originalChannel != null) ? originalChannel.getSound() : null; if (soundUri != null && !Objects.equals(originalSoundUri, soundUri)) { - Binder.withCleanCallingIdentity(() -> { - mUgmInternal.checkGrantUriPermission(sourceUid, null, - ContentProvider.getUriWithoutUserId(soundUri), - Intent.FLAG_GRANT_READ_URI_PERMISSION, - ContentProvider.getUserIdFromUri(soundUri, - UserHandle.getUserId(sourceUid))); - }); + PermissionHelper.grantUriPermission(mUgmInternal, soundUri, sourceUid); } } diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index c9a6c630d41b..672130eef969 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -1406,19 +1406,14 @@ public final class NotificationRecord { * {@link SecurityException} depending on target SDK of enqueuing app. */ private void visitGrantableUri(Uri uri, boolean userOverriddenUri, boolean isSound) { - if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return; + if (mGrantableUris != null && mGrantableUris.contains(uri)) { + return; // already verified this URI + } - // We can't grant Uri permissions from system final int sourceUid = getSbn().getUid(); if (sourceUid == android.os.Process.SYSTEM_UID) return; - - final long ident = Binder.clearCallingIdentity(); try { - // This will throw SecurityException if caller can't grant - mUgmInternal.checkGrantUriPermission(sourceUid, null, - ContentProvider.getUriWithoutUserId(uri), - Intent.FLAG_GRANT_READ_URI_PERMISSION, - ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid))); + PermissionHelper.grantUriPermission(mUgmInternal, uri, sourceUid); if (mGrantableUris == null) { mGrantableUris = new ArraySet<>(); @@ -1438,8 +1433,6 @@ public final class NotificationRecord { } } } - } finally { - Binder.restoreCallingIdentity(ident); } } diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java index 93c83e181ec1..e62613f92e1c 100644 --- a/services/core/java/com/android/server/notification/PermissionHelper.java +++ b/services/core/java/com/android/server/notification/PermissionHelper.java @@ -25,19 +25,25 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.Manifest; import android.annotation.NonNull; import android.annotation.UserIdInt; +import android.content.ContentProvider; +import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; +import android.net.Uri; import android.os.Binder; import android.os.RemoteException; +import android.os.UserHandle; import android.permission.IPermissionManager; import android.util.ArrayMap; import android.util.Pair; import android.util.Slog; import com.android.internal.util.ArrayUtils; +import com.android.server.uri.UriGrantsManagerInternal; import java.util.Collections; import java.util.HashSet; @@ -58,7 +64,7 @@ public final class PermissionHelper { private final IPermissionManager mPermManager; public PermissionHelper(Context context, IPackageManager packageManager, - IPermissionManager permManager) { + IPermissionManager permManager) { mContext = context; mPackageManager = packageManager; mPermManager = permManager; @@ -296,6 +302,19 @@ public final class PermissionHelper { return false; } + static void grantUriPermission(final UriGrantsManagerInternal ugmInternal, Uri uri, + int sourceUid) { + if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return; + + Binder.withCleanCallingIdentity(() -> { + // This will throw a SecurityException if the caller can't grant. + ugmInternal.checkGrantUriPermission(sourceUid, null, + ContentProvider.getUriWithoutUserId(uri), + Intent.FLAG_GRANT_READ_URI_PERMISSION, + ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid))); + }); + } + public static class PackagePermission { public final String packageName; public final @UserIdInt int userId; diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 4399a3ca46c5..1aa3fcf25a59 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -78,6 +78,7 @@ import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import com.android.server.notification.PermissionHelper.PackagePermission; +import com.android.server.uri.UriGrantsManagerInternal; import org.json.JSONArray; import org.json.JSONException; @@ -185,6 +186,7 @@ public class PreferencesHelper implements RankingConfig { private final PermissionHelper mPermissionHelper; private final NotificationChannelLogger mNotificationChannelLogger; private final AppOpsManager mAppOps; + private final UriGrantsManagerInternal mUgmInternal; private SparseBooleanArray mBadgingEnabled; private SparseBooleanArray mBubblesEnabled; @@ -201,6 +203,7 @@ public class PreferencesHelper implements RankingConfig { ZenModeHelper zenHelper, PermissionHelper permHelper, NotificationChannelLogger notificationChannelLogger, AppOpsManager appOpsManager, + UriGrantsManagerInternal ugmInternal, SysUiStatsEvent.BuilderFactory statsEventBuilderFactory, boolean showReviewPermissionsNotification) { mContext = context; @@ -211,6 +214,7 @@ public class PreferencesHelper implements RankingConfig { mNotificationChannelLogger = notificationChannelLogger; mAppOps = appOpsManager; mStatsEventBuilderFactory = statsEventBuilderFactory; + mUgmInternal = ugmInternal; mShowReviewPermissionsNotification = showReviewPermissionsNotification; XML_VERSION = 4; @@ -999,6 +1003,11 @@ public class PreferencesHelper implements RankingConfig { } clearLockedFieldsLocked(channel); + // Verify that the app has permission to read the sound Uri + // Only check for new channels, as regular apps can only set sound + // before creating. See: {@link NotificationChannel#setSound} + PermissionHelper.grantUriPermission(mUgmInternal, channel.getSound(), uid); + channel.setImportanceLockedByCriticalDeviceFunction( r.defaultAppLockedImportance || r.fixedImportance); 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 f203cfa9e442..63688d1242c6 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3646,7 +3646,42 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { doThrow(new SecurityException("no access")).when(mUgmInternal) .checkGrantUriPermission(eq(Process.myUid()), any(), eq(soundUri), - anyInt(), eq(Process.myUserHandle().getIdentifier())); + anyInt(), eq(Process.myUserHandle().getIdentifier())); + + mBinderService.updateNotificationChannelFromPrivilegedListener( + null, mPkg, Process.myUserHandle(), updatedNotificationChannel); + + 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 + testUpdateNotificationChannelFromPrivilegedListener_oldSoundNoUriPerm_newSoundHasUriPerm() + throws Exception { + mService.setPreferencesHelper(mPreferencesHelper); + when(mCompanionMgr.getAssociations(mPkg, mUserId)) + .thenReturn(singletonList(mock(AssociationInfo.class))); + when(mPreferencesHelper.getNotificationChannel(eq(mPkg), anyInt(), + eq(mTestNotificationChannel.getId()), anyBoolean())) + .thenReturn(mTestNotificationChannel); + + // Missing Uri permissions for the old channel sound + final Uri oldSoundUri = Settings.System.DEFAULT_NOTIFICATION_URI; + doThrow(new SecurityException("no access")).when(mUgmInternal) + .checkGrantUriPermission(eq(Process.myUid()), any(), eq(oldSoundUri), + anyInt(), eq(Process.myUserHandle().getIdentifier())); + + // Has Uri permissions for the old channel sound + final Uri newSoundUri = Uri.parse("content://media/test/sound/uri"); + final NotificationChannel updatedNotificationChannel = new NotificationChannel( + TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT); + updatedNotificationChannel.setSound(newSoundUri, + updatedNotificationChannel.getAudioAttributes()); mBinderService.updateNotificationChannelFromPrivilegedListener( null, PKG, Process.myUserHandle(), updatedNotificationChannel); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java index fae92d9ac738..bcdbd227d248 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java @@ -36,6 +36,7 @@ import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -76,6 +77,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.server.LocalServices; import com.android.server.UiServiceTestCase; import com.android.server.uri.UriGrantsManagerInternal; @@ -855,22 +857,19 @@ public class NotificationRecordTest extends UiServiceTestCase { when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class), anyInt(), anyInt())).thenThrow(new SecurityException()); + LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); + LocalServices.addService(UriGrantsManagerInternal.class, ugm); + channel.setSound(null, null); Notification n = new Notification.Builder(mContext, channel.getId()) .setSmallIcon(Icon.createWithContentUri(Uri.parse("content://something"))) .build(); StatusBarNotification sbn = new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); - record.mAm = am; - record.mUgmInternal = ugm; - try { - record.calculateGrantableUris(); - fail("App provided uri for p targeting app should throw exception"); - } catch (SecurityException e) { - // expected - } + assertThrows("App provided uri for p targeting app should throw exception", + SecurityException.class, + () -> new NotificationRecord(mMockContext, sbn, channel)); } @Test @@ -880,17 +879,17 @@ public class NotificationRecordTest extends UiServiceTestCase { when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class), anyInt(), anyInt())).thenThrow(new SecurityException()); + LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); + LocalServices.addService(UriGrantsManagerInternal.class, ugm); + channel.setSound(Uri.parse("content://something"), mock(AudioAttributes.class)); Notification n = mock(Notification.class); when(n.getChannelId()).thenReturn(channel.getId()); StatusBarNotification sbn = new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); - record.mAm = am; - record.mUgmInternal = ugm; - record.calculateGrantableUris(); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, record.getSound()); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 48ad86da1bc5..53bdce6718d1 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -29,6 +29,10 @@ import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MAX; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; +import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE; +import static android.content.ContentResolver.SCHEME_ANDROID_RESOURCE; +import static android.content.ContentResolver.SCHEME_CONTENT; +import static android.content.ContentResolver.SCHEME_FILE; import static android.media.AudioAttributes.CONTENT_TYPE_SONIFICATION; import static android.media.AudioAttributes.USAGE_NOTIFICATION; import static android.os.UserHandle.USER_SYSTEM; @@ -67,6 +71,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -98,6 +103,7 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Parcel; +import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; @@ -303,7 +309,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory(); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); resetZenModeHelper(); mAudioAttributes = new AudioAttributes.Builder() @@ -645,7 +651,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_oldXml_migrates() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true); + mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, true); String xml = "\n" + "\n" + "\n" @@ -846,7 +852,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_newXml_permissionNotificationOff() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); String xml = "\n" + "\n" @@ -903,7 +909,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true); + mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, true); String xml = "\n" + "\n" @@ -959,7 +965,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_oldXml_migration_NoUid() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID); String xml = "\n" @@ -992,7 +998,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_newXml_noMigration_NoUid() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID); String xml = "\n" @@ -1024,7 +1030,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForNonBackup_postMigration() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); ArrayMap, Pair> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false)); @@ -1110,7 +1116,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForBackup_postMigration() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); ArrayMap, Pair> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false)); @@ -1202,7 +1208,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForBackup_postMigration_noExternal() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); ArrayMap, Pair> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair<>(UID_P, PKG_P), new Pair<>(true, false)); @@ -1287,7 +1293,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForBackup_postMigration_noLocalSettings() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); ArrayMap, Pair> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false)); @@ -1432,7 +1438,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertTrue(actualChannel.isSoundRestored()); } - /** * Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to * handle its restore properly. @@ -1498,7 +1503,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { new FileNotFoundException("")).thenReturn(resId); mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mLogger, mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); @@ -2444,8 +2449,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); - + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); // create notification channel that can bypass dnd, but app is blocked // expected result: areChannelsBypassingDnd = false @@ -2475,7 +2479,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); // create notification channel that can bypass dnd, but app is blocked // expected result: areChannelsBypassingDnd = false @@ -2499,7 +2503,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); // create notification channel that can bypass dnd, but app is blocked // expected result: areChannelsBypassingDnd = false @@ -2553,7 +2557,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean()); resetZenModeHelper(); @@ -2566,7 +2570,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean()); resetZenModeHelper(); @@ -2662,6 +2666,61 @@ public class PreferencesHelperTest extends UiServiceTestCase { PKG_N_MR1, UID_N_MR1, channel.getId(), false).getSound()); } + @Test + public void testCreateChannel_noSoundUriPermission_contentSchemeVerified() { + final Uri sound = Uri.parse(SCHEME_CONTENT + "://media/test/sound/uri"); + + doThrow(new SecurityException("no access")).when(mUgmInternal) + .checkGrantUriPermission(eq(UID_N_MR1), any(), eq(sound), + anyInt(), eq(Process.myUserHandle().getIdentifier())); + + final NotificationChannel channel = new NotificationChannel("id2", "name2", + NotificationManager.IMPORTANCE_DEFAULT); + channel.setSound(sound, mAudioAttributes); + + assertThrows(SecurityException.class, + () -> mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, + true, false, UID_N_MR1, false)); + assertThat(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true)) + .isNull(); + } + + @Test + public void testCreateChannel_noSoundUriPermission_fileSchemaIgnored() { + final Uri sound = Uri.parse(SCHEME_FILE + "://path/sound"); + + doThrow(new SecurityException("no access")).when(mUgmInternal) + .checkGrantUriPermission(eq(UID_N_MR1), any(), any(), + anyInt(), eq(Process.myUserHandle().getIdentifier())); + + final NotificationChannel channel = new NotificationChannel("id2", "name2", + NotificationManager.IMPORTANCE_DEFAULT); + channel.setSound(sound, mAudioAttributes); + + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false, UID_N_MR1, + false); + assertThat(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true) + .getSound()).isEqualTo(sound); + } + + @Test + public void testCreateChannel_noSoundUriPermission_resourceSchemaIgnored() { + final Uri sound = Uri.parse(SCHEME_ANDROID_RESOURCE + "://resId/sound"); + + doThrow(new SecurityException("no access")).when(mUgmInternal) + .checkGrantUriPermission(eq(UID_N_MR1), any(), any(), + anyInt(), eq(Process.myUserHandle().getIdentifier())); + + final NotificationChannel channel = new NotificationChannel("id2", "name2", + NotificationManager.IMPORTANCE_DEFAULT); + channel.setSound(sound, mAudioAttributes); + + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false, UID_N_MR1, + false); + assertThat(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true) + .getSound()).isEqualTo(sound); + } + @Test public void testPermanentlyDeleteChannels() throws Exception { NotificationChannel channel1 = @@ -3669,7 +3728,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "\n"; mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); loadByteArrayXml(preQXml.getBytes(), true, USER_SYSTEM); assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS, @@ -3683,7 +3742,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS, @@ -3753,7 +3812,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); @@ -3766,7 +3825,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O)); @@ -3780,7 +3839,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); @@ -3796,7 +3855,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O)); @@ -3852,7 +3911,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O)); @@ -3890,7 +3949,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE); @@ -4548,7 +4607,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testPlaceholderConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); final String xml = "\n" + "\n" @@ -4568,7 +4627,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testNormalConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); final String xml = "\n" + "\n" @@ -4588,7 +4647,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testNoConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); final String xml = "\n" + "\n" @@ -4608,7 +4667,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testDeleted_noTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); final String xml = "\n" + "\n" @@ -4628,7 +4687,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testDeleted_twice() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); mHelper.createNotificationChannel( PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false, @@ -4643,7 +4702,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testDeleted_recentTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); mHelper.createNotificationChannel( PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false, @@ -4662,7 +4721,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { parser.nextTag(); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); mHelper.readXml(parser, true, USER_SYSTEM); NotificationChannel nc = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true); @@ -4674,7 +4733,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testUnDelete_time() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); mHelper.createNotificationChannel( PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false, @@ -4696,7 +4755,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testDeleted_longTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory, false); + mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory, false); long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30); -- cgit v1.2.3-59-g8ed1b