Reputation: 652
I'm using a MediaStyle Notification in my app. Its been working fine until Android 11. On Android 11 it crashes the Android UI (not just the app, it takes down Android's UI).
There are no error in logcat on the app, but there are errors from Android itself.
fun buildNotificationAsync(sessionToken: MediaSessionCompat.Token): Deferred<Notification> = GlobalScope.async {
if (shouldCreateNowPlayingChannel()) {
createNowPlayingChannel()
}
val controller = MediaControllerCompat(context, sessionToken)
val description = controller.metadata.description
val playbackState = controller.playbackState
val builder = NotificationCompat.Builder(context, NOW_PLAYING_CHANNEL)
// Only add actions for skip back, play/pause, skip forward, based on what's enabled.
var playPauseIndex = 0
if (playbackState.isSkipToPreviousEnabled) {
builder.addAction(skipToPreviousAction)
++playPauseIndex
}
if (playbackState.isRewindEnabled) {
builder.addAction(rewindAction)
++playPauseIndex
}
if (playbackState.isPlaying) {
builder.addAction(pauseAction)
} else if (playbackState.isPlayEnabled) {
builder.addAction(playAction)
}
if (playbackState.isFastForwardEnabled) {
builder.addAction(fastForwardAction)
}
if (playbackState.isSkipToNextEnabled) {
builder.addAction(skipToNextAction)
}
val isHuaweiLollipop = (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1 ||
Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) &&
Build.MANUFACTURER.toLowerCase(Locale.getDefault()).contains("huawei")
if (!isHuaweiLollipop) {
val mediaStyle = MediaStyle()
.setCancelButtonIntent(stopPendingIntent)
.setMediaSession(sessionToken)
.setShowActionsInCompactView(playPauseIndex)
.setShowCancelButton(true)
builder.setStyle(mediaStyle)
}
description.iconUri?.let { uri ->
val largeIcon = CoverCache.getInstance().fetchLargeIfNecessary(context, uri)
largeIcon?.let { icon -> builder.setLargeIcon(icon) }
}
return@async builder.setContentIntent(controller.sessionActivity)
.setContentText(description.subtitle)
.setContentTitle(description.title)
.setDeleteIntent(stopPendingIntent)
.setOnlyAlertOnce(true)
.setSmallIcon(R.drawable.ic_stat)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.build()
}
I see this exception that I think is related:
AndroidRuntime E FATAL EXCEPTION: SysUiBg
E Process: com.android.systemui, PID: 26311
E java.lang.SecurityException: Permission Denial: opening provider androidx.core.content.FileProvider from ProcessRecord{410d665 26311:com.android.systemui/u0a141} (pid=26311, uid=10141) that is not exported from UID 10151
E at android.os.Parcel.createExceptionOrNull(Parcel.java:2373)
E at android.os.Parcel.createException(Parcel.java:2357)
E at android.os.Parcel.readException(Parcel.java:2340)
E at android.os.Parcel.readException(Parcel.java:2282)
E at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:5702)
E at android.app.ActivityThread.acquireProvider(ActivityThread.java:6813)
E at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2930)
E at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:2481)
E at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1967)
E at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1921)
E at android.graphics.ImageDecoder$ContentResolverSource.createImageDecoder(ImageDecoder.java:274)
E at android.graphics.ImageDecoder.decodeBitmapImpl(ImageDecoder.java:1862)
E at android.graphics.ImageDecoder.decodeBitmap(ImageDecoder.java:1855)
E at com.android.systemui.media.MediaDataManager.loadBitmapFromUri(MediaDataManager.kt:462)
E at com.android.systemui.media.MediaDataManager.loadBitmapFromUri(MediaDataManager.kt:433)
E at com.android.systemui.media.MediaDataManager.loadMediaDataInBg(MediaDataManager.kt:331)
E at com.android.systemui.media.MediaDataManager.access$loadMediaDataInBg(MediaDataManager.kt:89)
E at com.android.systemui.media.MediaDataManager$loadMediaData$1.run(MediaDataManager.kt:241)
E at android.os.Handler.handleCallback(Handler.java:938)
E at android.os.Handler.dispatchMessage(Handler.java:99)
E at android.os.Looper.loop(Looper.java:223)
E at android.os.HandlerThread.run(HandlerThread.java:67)
E Caused by: android.os.RemoteException: Remote stack trace:
E at com.android.server.am.ActivityManagerService.getContentProviderImpl(ActivityManagerService.java:7155)
E at com.android.server.am.ActivityManagerService.getContentProvider(ActivityManagerService.java:7594)
E at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2381)
E at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2883)
E at android.os.Binder.execTransactInternal(Binder.java:1159)
If I remove the line of the builder that sets the small icon, it works fine (but that makes the notification just a plain notification without the media style). You'll note the exception is about lack for permission for fetching something from a file provider, but nothing in the notification uses anything like that.
Upvotes: 3
Views: 1299
Reputation: 17
This bug is still an issue unfortunately. I had the exact issue, with the notification blowing up the phone and your solution
context.grantUriPermission( "com.android.systemui", it.toUri(), Intent.FLAG_GRANT_READ_URI_PERMISSION)
works so thanks! Seems specific to android 11.
I too had to add this in within the media mappers of an audio app Im working on.
Upvotes: 0
Reputation: 652
I found the issue. Since this is a MediaStyle, we set the MediaSession, the session has access to the MediaMetadataCompat items. For building those items, we have:
val builder = MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, doc.title)
.putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, doc.author ?: "Unknown")
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, doc.documentId)
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, folders.firstOrNull { it.folderId == doc.folderId }?.folderName)
.putString(MediaMetadataCompat.METADATA_KEY_GENRE, sourceType?.name ?: "Unknown") // We don't have Genre info, we'll use for the sourceType
.putLong(METADATA_KEY_UAMP_FLAGS, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE.toLong())
if (originalDocumentType != null) {
// We don't have a disc no, so
builder.putLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER, originalDocumentType.ordinal.toLong())
}
// we'll use it doc type.
if (icon != null) {
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, icon.toString())
}
Its that last line that sets Icon uri. That needs permission. adding this right above that line
context.grantUriPermission("com.android.systemui", icon, Intent.FLAG_GRANT_READ_URI_PERMISSION)
Now the app runs correctly. But the fact that is causing the Android UI to crash because that permission was missing is still a bug. I'd expect to fail to show the icon or even crash the app, but certainly not to crash Android itself.
Upvotes: 1