Loading services/core/java/com/android/server/notification/NotificationManagerService.java +2 −7 Original line number Diff line number Diff line Loading @@ -2346,6 +2346,7 @@ public class NotificationManagerService extends SystemService { mPermissionHelper, mNotificationChannelLogger, mAppOps, mUgmInternal, new SysUiStatsEvent.BuilderFactory(), mShowReviewPermissionsNotification); mRankingHelper = new RankingHelper(getContext(), Loading Loading @@ -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); } } Loading services/core/java/com/android/server/notification/NotificationRecord.java +10 −14 Original line number Diff line number Diff line Loading @@ -30,10 +30,7 @@ import android.app.IActivityManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.Person; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ShortcutInfo; Loading @@ -42,7 +39,6 @@ import android.media.AudioAttributes; import android.media.AudioSystem; import android.metrics.LogMaker; import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.IBinder; Loading Loading @@ -1405,20 +1401,22 @@ public final class NotificationRecord { * {@link #mGrantableUris}. Otherwise, this will either log or throw * {@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; private void visitGrantableUri(Uri uri, boolean userOverriddenUri, boolean isSound) { if (uri == null) { 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<>(); Loading @@ -1438,8 +1436,6 @@ public final class NotificationRecord { } } } } finally { Binder.restoreCallingIdentity(ident); } } Loading services/core/java/com/android/server/notification/PermissionHelper.java +20 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading services/core/java/com/android/server/notification/PreferencesHelper.java +15 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading @@ -211,6 +214,7 @@ public class PreferencesHelper implements RankingConfig { mNotificationChannelLogger = notificationChannelLogger; mAppOps = appOpsManager; mStatsEventBuilderFactory = statsEventBuilderFactory; mUgmInternal = ugmInternal; mShowReviewPermissionsNotification = showReviewPermissionsNotification; XML_VERSION = 4; Loading Loading @@ -999,6 +1003,17 @@ 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} try { PermissionHelper.grantUriPermission(mUgmInternal, channel.getSound(), uid); } catch (SecurityException e) { // Fallback to default Uri to prevent app crashes channel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI, Notification.AUDIO_ATTRIBUTES_DEFAULT); } channel.setImportanceLockedByCriticalDeviceFunction( r.defaultAppLockedImportance || r.fixedImportance); Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +35 −0 Original line number Diff line number Diff line Loading @@ -3659,6 +3659,41 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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, 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 testGetNotificationChannelFromPrivilegedListener_cdm_success() throws Exception { mService.setPreferencesHelper(mPreferencesHelper); Loading Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +2 −7 Original line number Diff line number Diff line Loading @@ -2346,6 +2346,7 @@ public class NotificationManagerService extends SystemService { mPermissionHelper, mNotificationChannelLogger, mAppOps, mUgmInternal, new SysUiStatsEvent.BuilderFactory(), mShowReviewPermissionsNotification); mRankingHelper = new RankingHelper(getContext(), Loading Loading @@ -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); } } Loading
services/core/java/com/android/server/notification/NotificationRecord.java +10 −14 Original line number Diff line number Diff line Loading @@ -30,10 +30,7 @@ import android.app.IActivityManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.Person; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ShortcutInfo; Loading @@ -42,7 +39,6 @@ import android.media.AudioAttributes; import android.media.AudioSystem; import android.metrics.LogMaker; import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.IBinder; Loading Loading @@ -1405,20 +1401,22 @@ public final class NotificationRecord { * {@link #mGrantableUris}. Otherwise, this will either log or throw * {@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; private void visitGrantableUri(Uri uri, boolean userOverriddenUri, boolean isSound) { if (uri == null) { 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<>(); Loading @@ -1438,8 +1436,6 @@ public final class NotificationRecord { } } } } finally { Binder.restoreCallingIdentity(ident); } } Loading
services/core/java/com/android/server/notification/PermissionHelper.java +20 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading
services/core/java/com/android/server/notification/PreferencesHelper.java +15 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading @@ -211,6 +214,7 @@ public class PreferencesHelper implements RankingConfig { mNotificationChannelLogger = notificationChannelLogger; mAppOps = appOpsManager; mStatsEventBuilderFactory = statsEventBuilderFactory; mUgmInternal = ugmInternal; mShowReviewPermissionsNotification = showReviewPermissionsNotification; XML_VERSION = 4; Loading Loading @@ -999,6 +1003,17 @@ 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} try { PermissionHelper.grantUriPermission(mUgmInternal, channel.getSound(), uid); } catch (SecurityException e) { // Fallback to default Uri to prevent app crashes channel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI, Notification.AUDIO_ATTRIBUTES_DEFAULT); } channel.setImportanceLockedByCriticalDeviceFunction( r.defaultAppLockedImportance || r.fixedImportance); Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +35 −0 Original line number Diff line number Diff line Loading @@ -3659,6 +3659,41 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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, 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 testGetNotificationChannelFromPrivilegedListener_cdm_success() throws Exception { mService.setPreferencesHelper(mPreferencesHelper); Loading