johngray1965
johngray1965

Reputation: 652

Android 11 MediaStyle Notification crash

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

Answers (2)

Angel Keene
Angel Keene

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

johngray1965
johngray1965

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

Related Questions