Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ae3d9211 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Check sound Uri permission when creating a notification channel" into udc-dev

parents 5be6bb1a 2d88dd97
Loading
Loading
Loading
Loading
+2 −7
Original line number Diff line number Diff line
@@ -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);
            }
        }
+10 −14
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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<>();
@@ -1438,8 +1436,6 @@ public final class NotificationRecord {
                    }
                }
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

+20 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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;
+15 −0
Original line number Diff line number Diff line
@@ -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,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);

+35 −0
Original line number Diff line number Diff line
@@ -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