Loading services/core/java/com/android/server/notification/NotificationManagerService.java +2 −7 Original line number Diff line number Diff line Loading @@ -2333,6 +2333,7 @@ public class NotificationManagerService extends SystemService { mZenModeHelper, new NotificationChannelLoggerImpl(), mAppOps, mUgmInternal, new SysUiStatsEvent.BuilderFactory()); mRankingHelper = new RankingHelper(getContext(), mRankingHandler, Loading Loading @@ -5647,13 +5648,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))); }); PreferencesHelper.grantUriPermission(mUgmInternal, soundUri, sourceUid); } } Loading services/core/java/com/android/server/notification/PreferencesHelper.java +27 −0 Original line number Diff line number Diff line Loading @@ -40,10 +40,15 @@ import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.content.Context; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.metrics.LogMaker; import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.UserHandle; import android.provider.Settings; Loading @@ -68,6 +73,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.uri.UriGrantsManagerInternal; import org.json.JSONArray; import org.json.JSONException; Loading Loading @@ -173,6 +179,7 @@ public class PreferencesHelper implements RankingConfig { private final ZenModeHelper mZenModeHelper; private final NotificationChannelLogger mNotificationChannelLogger; private final AppOpsManager mAppOps; private final UriGrantsManagerInternal mUgmInternal; private SparseBooleanArray mBadgingEnabled; private SparseBooleanArray mBubblesEnabled; Loading @@ -191,6 +198,7 @@ public class PreferencesHelper implements RankingConfig { public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger, AppOpsManager appOpsManager, UriGrantsManagerInternal ugmInternal, SysUiStatsEvent.BuilderFactory statsEventBuilderFactory) { mContext = context; mZenModeHelper = zenHelper; Loading @@ -199,6 +207,7 @@ public class PreferencesHelper implements RankingConfig { mNotificationChannelLogger = notificationChannelLogger; mAppOps = appOpsManager; mStatsEventBuilderFactory = statsEventBuilderFactory; mUgmInternal = ugmInternal; updateBadgingEnabled(); updateBubblesEnabled(); Loading Loading @@ -955,6 +964,12 @@ public class PreferencesHelper implements RankingConfig { : NotificationChannel.DEFAULT_ALLOW_BUBBLE); } 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} grantUriPermission(mUgmInternal, channel.getSound(), uid); channel.setImportanceLockedByOEM(r.oemLockedImportance); if (!channel.isImportanceLockedByOEM()) { if (r.oemLockedChannels.contains(channel.getId())) { Loading Loading @@ -1009,6 +1024,18 @@ public class PreferencesHelper implements RankingConfig { } } 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))); }); } @Override public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel, Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +38 −1 Original line number Diff line number Diff line Loading @@ -2710,6 +2710,43 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .checkGrantUriPermission(eq(Process.myUid()), any(), eq(soundUri), anyInt(), eq(Process.myUserHandle().getIdentifier())); mBinderService.updateNotificationChannelFromPrivilegedListener( null, mPkg, Process.myUserHandle(), updatedNotificationChannel); verify(mPreferencesHelper, times(1)).updateNotificationChannel( anyString(), anyInt(), any(), 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); List<String> associations = new ArrayList<>(); associations.add("a"); when(mCompanionMgr.getAssociations(mPkg, mUserId)) .thenReturn(associations); 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); Loading services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +81 −22 Original line number Diff line number Diff line Loading @@ -29,6 +29,9 @@ 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.content.ContentResolver.SCHEME_ANDROID_RESOURCE; import static android.content.ContentResolver.SCHEME_CONTENT; import static android.content.ContentResolver.SCHEME_FILE; import static android.util.StatsLog.ANNOTATION_ID_IS_UID; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; Loading @@ -53,6 +56,7 @@ import static junit.framework.Assert.fail; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; Loading @@ -60,6 +64,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; 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; Loading Loading @@ -88,6 +93,7 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; Loading Loading @@ -270,7 +276,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory(); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); resetZenModeHelper(); mAudioAttributes = new AudioAttributes.Builder() Loading Loading @@ -1567,7 +1573,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any()); resetZenModeHelper(); Loading @@ -1579,7 +1585,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, never()).setNotificationPolicy(any()); resetZenModeHelper(); Loading Loading @@ -1683,6 +1689,59 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList().size()); } @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)); 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); 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); assertThat(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true) .getSound()).isEqualTo(sound); } @Test public void testDeleteGroup() throws Exception { NotificationChannelGroup notDeleted = new NotificationChannelGroup("not", "deleted"); Loading Loading @@ -2398,7 +2457,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "</package>\n" + "</ranking>\n"; mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadByteArrayXml(preQXml.getBytes(), true, UserHandle.USER_SYSTEM); assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS, Loading @@ -2411,7 +2470,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS, Loading Loading @@ -2508,7 +2567,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); Loading @@ -2520,7 +2579,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O)); Loading @@ -2533,7 +2592,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); Loading @@ -2546,7 +2605,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); // appears disabled Loading @@ -2565,7 +2624,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); // appears disabled Loading @@ -2584,7 +2643,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O)); Loading Loading @@ -2639,7 +2698,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O)); Loading Loading @@ -2676,7 +2735,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE); Loading Loading @@ -3324,7 +3383,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testPlaceholderConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" Loading @@ -3343,7 +3402,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testNormalConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" Loading @@ -3362,7 +3421,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testNoConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" Loading @@ -3381,7 +3440,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDeleted_noTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" Loading @@ -3400,7 +3459,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDeleted_twice() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); mHelper.createNotificationChannel( PKG_P, UID_P, createNotificationChannel("id", "id", 2), true, false); Loading @@ -3411,7 +3470,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDeleted_recentTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); mHelper.createNotificationChannel( PKG_P, UID_P, createNotificationChannel("id", "id", 2), true, false); Loading @@ -3428,7 +3487,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { null); parser.nextTag(); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); mHelper.readXml(parser, true, UserHandle.USER_SYSTEM); NotificationChannel nc = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true); Loading @@ -3439,7 +3498,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testUnDelete_time() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); mHelper.createNotificationChannel( PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false); Loading @@ -3458,7 +3517,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDeleted_longTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30); Loading Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +2 −7 Original line number Diff line number Diff line Loading @@ -2333,6 +2333,7 @@ public class NotificationManagerService extends SystemService { mZenModeHelper, new NotificationChannelLoggerImpl(), mAppOps, mUgmInternal, new SysUiStatsEvent.BuilderFactory()); mRankingHelper = new RankingHelper(getContext(), mRankingHandler, Loading Loading @@ -5647,13 +5648,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))); }); PreferencesHelper.grantUriPermission(mUgmInternal, soundUri, sourceUid); } } Loading
services/core/java/com/android/server/notification/PreferencesHelper.java +27 −0 Original line number Diff line number Diff line Loading @@ -40,10 +40,15 @@ import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.content.Context; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.metrics.LogMaker; import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.UserHandle; import android.provider.Settings; Loading @@ -68,6 +73,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.uri.UriGrantsManagerInternal; import org.json.JSONArray; import org.json.JSONException; Loading Loading @@ -173,6 +179,7 @@ public class PreferencesHelper implements RankingConfig { private final ZenModeHelper mZenModeHelper; private final NotificationChannelLogger mNotificationChannelLogger; private final AppOpsManager mAppOps; private final UriGrantsManagerInternal mUgmInternal; private SparseBooleanArray mBadgingEnabled; private SparseBooleanArray mBubblesEnabled; Loading @@ -191,6 +198,7 @@ public class PreferencesHelper implements RankingConfig { public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger, AppOpsManager appOpsManager, UriGrantsManagerInternal ugmInternal, SysUiStatsEvent.BuilderFactory statsEventBuilderFactory) { mContext = context; mZenModeHelper = zenHelper; Loading @@ -199,6 +207,7 @@ public class PreferencesHelper implements RankingConfig { mNotificationChannelLogger = notificationChannelLogger; mAppOps = appOpsManager; mStatsEventBuilderFactory = statsEventBuilderFactory; mUgmInternal = ugmInternal; updateBadgingEnabled(); updateBubblesEnabled(); Loading Loading @@ -955,6 +964,12 @@ public class PreferencesHelper implements RankingConfig { : NotificationChannel.DEFAULT_ALLOW_BUBBLE); } 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} grantUriPermission(mUgmInternal, channel.getSound(), uid); channel.setImportanceLockedByOEM(r.oemLockedImportance); if (!channel.isImportanceLockedByOEM()) { if (r.oemLockedChannels.contains(channel.getId())) { Loading Loading @@ -1009,6 +1024,18 @@ public class PreferencesHelper implements RankingConfig { } } 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))); }); } @Override public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel, Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +38 −1 Original line number Diff line number Diff line Loading @@ -2710,6 +2710,43 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .checkGrantUriPermission(eq(Process.myUid()), any(), eq(soundUri), anyInt(), eq(Process.myUserHandle().getIdentifier())); mBinderService.updateNotificationChannelFromPrivilegedListener( null, mPkg, Process.myUserHandle(), updatedNotificationChannel); verify(mPreferencesHelper, times(1)).updateNotificationChannel( anyString(), anyInt(), any(), 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); List<String> associations = new ArrayList<>(); associations.add("a"); when(mCompanionMgr.getAssociations(mPkg, mUserId)) .thenReturn(associations); 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); Loading
services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +81 −22 Original line number Diff line number Diff line Loading @@ -29,6 +29,9 @@ 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.content.ContentResolver.SCHEME_ANDROID_RESOURCE; import static android.content.ContentResolver.SCHEME_CONTENT; import static android.content.ContentResolver.SCHEME_FILE; import static android.util.StatsLog.ANNOTATION_ID_IS_UID; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; Loading @@ -53,6 +56,7 @@ import static junit.framework.Assert.fail; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; Loading @@ -60,6 +64,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; 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; Loading Loading @@ -88,6 +93,7 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; Loading Loading @@ -270,7 +276,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory(); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); resetZenModeHelper(); mAudioAttributes = new AudioAttributes.Builder() Loading Loading @@ -1567,7 +1573,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any()); resetZenModeHelper(); Loading @@ -1579,7 +1585,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, never()).setNotificationPolicy(any()); resetZenModeHelper(); Loading Loading @@ -1683,6 +1689,59 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList().size()); } @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)); 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); 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); assertThat(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true) .getSound()).isEqualTo(sound); } @Test public void testDeleteGroup() throws Exception { NotificationChannelGroup notDeleted = new NotificationChannelGroup("not", "deleted"); Loading Loading @@ -2398,7 +2457,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "</package>\n" + "</ranking>\n"; mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadByteArrayXml(preQXml.getBytes(), true, UserHandle.USER_SYSTEM); assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS, Loading @@ -2411,7 +2470,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS, Loading Loading @@ -2508,7 +2567,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); Loading @@ -2520,7 +2579,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O)); Loading @@ -2533,7 +2592,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); Loading @@ -2546,7 +2605,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); // appears disabled Loading @@ -2565,7 +2624,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); // appears disabled Loading @@ -2584,7 +2643,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O)); Loading Loading @@ -2639,7 +2698,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O)); Loading Loading @@ -2676,7 +2735,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE); Loading Loading @@ -3324,7 +3383,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testPlaceholderConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" Loading @@ -3343,7 +3402,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testNormalConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" Loading @@ -3362,7 +3421,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testNoConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" Loading @@ -3381,7 +3440,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDeleted_noTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" Loading @@ -3400,7 +3459,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDeleted_twice() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); mHelper.createNotificationChannel( PKG_P, UID_P, createNotificationChannel("id", "id", 2), true, false); Loading @@ -3411,7 +3470,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDeleted_recentTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); mHelper.createNotificationChannel( PKG_P, UID_P, createNotificationChannel("id", "id", 2), true, false); Loading @@ -3428,7 +3487,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { null); parser.nextTag(); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); mHelper.readXml(parser, true, UserHandle.USER_SYSTEM); NotificationChannel nc = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true); Loading @@ -3439,7 +3498,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testUnDelete_time() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); mHelper.createNotificationChannel( PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false); Loading @@ -3458,7 +3517,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDeleted_longTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); mAppOpsManager, mUgmInternal, mStatsEventBuilderFactory); long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30); Loading