From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Michael Mikhail Date: Fri, 28 Apr 2023 16:17:16 +0000 Subject: [PATCH] Verify URI permissions in MediaMetadata Add a check for URI permission to make sure that user can access the URI set in MediaMetadata. If permission is denied, clear the URI string set in metadata. Bug: 271851153 Test: atest MediaSessionTest Test: Verified by POC app attached in bug, image of second user is not the UMO background of the first user. (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f95b7fc61d6b3bf49420ded0357bec031f8cbdcf) Merged-In: I384f8e230c909d8fc8e5f147e2fd3558fec44626 Change-Id: I384f8e230c909d8fc8e5f147e2fd3558fec44626 --- .../server/media/MediaSessionRecord.java | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index e2087e6ca822..5ebbfe92dc20 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -17,6 +17,8 @@ package com.android.server.media; import android.app.PendingIntent; +import android.content.ContentProvider; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ParceledListSlice; @@ -48,11 +50,13 @@ import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemClock; +import android.text.TextUtils; import android.util.Log; import android.util.Slog; import android.view.KeyEvent; import com.android.server.LocalServices; +import com.android.server.uri.UriGrantsManagerInternal; import java.io.PrintWriter; import java.util.ArrayList; @@ -64,6 +68,10 @@ import java.util.List; */ public class MediaSessionRecord implements IBinder.DeathRecipient { private static final String TAG = "MediaSessionRecord"; + private static final String[] ART_URIS = new String[] { + MediaMetadata.METADATA_KEY_ALBUM_ART_URI, + MediaMetadata.METADATA_KEY_ART_URI, + MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI}; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); /** @@ -85,6 +93,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private final SessionStub mSession; private final SessionCb mSessionCb; private final MediaSessionService mService; + private final UriGrantsManagerInternal mUgmInternal; private final Context mContext; private final Object mLock = new Object(); @@ -142,6 +151,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(); + mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); } /** @@ -870,21 +880,45 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { public void setMetadata(MediaMetadata metadata, long duration, String metadataDescription) throws RemoteException { synchronized (mLock) { - MediaMetadata temp = metadata == null ? null : new MediaMetadata.Builder(metadata) - .build(); - // This is to guarantee that the underlying bundle is unparceled - // before we set it to prevent concurrent reads from throwing an - // exception - if (temp != null) { - temp.size(); - } - mMetadata = temp; mDuration = duration; mMetadataDescription = metadataDescription; + mMetadata = sanitizeMediaMetadata(metadata); } mHandler.post(MessageHandler.MSG_UPDATE_METADATA); } + private MediaMetadata sanitizeMediaMetadata(MediaMetadata metadata) { + if (metadata == null) { + return null; + } + MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder(metadata); + for (String key: ART_URIS) { + String uriString = metadata.getString(key); + if (TextUtils.isEmpty(uriString)) { + continue; + } + Uri uri = Uri.parse(uriString); + if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { + continue; + } + try { + mUgmInternal.checkGrantUriPermission(getUid(), + getPackageName(), + ContentProvider.getUriWithoutUserId(uri), + Intent.FLAG_GRANT_READ_URI_PERMISSION, + ContentProvider.getUserIdFromUri(uri, getUserId())); + } catch (SecurityException e) { + metadataBuilder.putString(key, null); + } + } + MediaMetadata sanitizedMetadata = metadataBuilder.build(); + // sanitizedMetadata.size() guarantees that the underlying bundle is unparceled + // before we set it to prevent concurrent reads from throwing an + // exception + sanitizedMetadata.size(); + return sanitizedMetadata; + } + @Override public void setPlaybackState(PlaybackState state) throws RemoteException { int oldState = mPlaybackState == null