androidx / media

Jetpack Media3 support libraries for media use cases, including ExoPlayer, an extensible media player for Android
https://developer.android.com/media/media3
Apache License 2.0
1.74k stars 416 forks source link

MediaSessionCompat#setMetadata causing bluetooth disconnect on some devices #564

Open morganch01 opened 1 year ago

morganch01 commented 1 year ago

Hi Android Media team,

Question: Are there any further documentation or information you can share about the limitation of MediaSessionCompat#setMetadata(MediaMetadataCompat)? And in what situation it could break bluetooth connection?

Background: Recently we got customer reports that when our app tries play audio via bluetooth, it will break the bluetooth connection on some {phone, bluetooth device} pair. The experience is like:

It is not happening to all phone/BT device pair, only some of them.

Example of pairs that would disconnect the bluetooth:

Example of pairs that playback on BT will work normally:

Narrowing down the root cause: We eventually nail down the issue to that there is issue in one of the String data field we passed in MediaMetadataCompact for MediaSessionCompat#setMedadata(MediaMetadataCompat). We don't know if it is because the String is too long or is null or any other issue yet. Given we can't reproduce locally and only has report from customer. If we pass a nice short string for that field in MediaMetadataCompact, it fixes the issue for the customer.

We still would like to get more info from Android Media team to understand the root cause. And could you add more documentation on the API on the things need to be cautious?

tianyif commented 1 year ago

Hi @morganch01,

Thanks for reporting! I've seen the similar issue before when there is a large Bitmap is set via MediaSessionCompat#setMetadata(MediaMetadataCompat), where a RuntimeException is thrown when the Bundle is written the Parcel. How long is your original String data? And would it be possible that you can provide us a stack trace of this issue?

morganch01 commented 1 year ago

Hi @tianyif ,

The string size is different based on item. It could be between 500 - 3000 characters in a string, mostly should be ASCII characters.

I don't have a stack trace unfortunately as we couldn't repro it in house, and it is not part of the customer log we capture as it isn't crashing our app and we don't collect system trace. In the log from customer, we can only tell the bluetooth is disconnected.

Is there a way I can send you private message about the build and credential, so that if you happen to have the device/headset pair you can try to reproduce the issue?

tianyif commented 1 year ago

Hi @morganch01,

Would you mind to email the info to android-media-github@google.com? You can mentioned the 'Issue #' in the subject, and note the details to reproduce. Also please leave a message here after you've done it.

morganch01 commented 1 year ago

Hi @tianyif

I've sent an email with title "Supplemental Info for issue #564" to the above account. You can try to test the playback with some bluetooth headset and see if you can get a repro.

Thanks,

rikukala commented 1 year ago

Hi @tianyif

I've successfully replicated and confirmed the issue on my device. It seems to be tied to the Bluetooth audio utility class, as suggested by the error logs. Notably, we previously used serializable extras with the MediaMetadata bundle without any problems when using ExoPlayer. However, since our migration to Media3 last week, this issue has surfaced.

When the bluetooth disconnects, following error message is thrown.

FATAL EXCEPTION: main
Process: com.android.bluetooth, PID: 21368
java.lang.RuntimeException: Parcelable encountered ClassNotFoundException reading a Serializable object (name = com.mypackage.items.SomeSerializable)
    at android.os.Parcel.readSerializable(Parcel.java:3513)
    at android.os.Parcel.readValue(Parcel.java:3277)
    at android.os.Parcel.readArrayMapInternal(Parcel.java:3623)
    at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292)
    at android.os.BaseBundle.unparcel(BaseBundle.java:236)
    at android.os.BaseBundle.containsKey(BaseBundle.java:516)
    at com.android.bluetooth.audio_util.Metadata$Builder.fromBundle(Metadata.java:190)
    at com.android.bluetooth.audio_util.Metadata$Builder.fromMediaDescription(Metadata.java:173)
    at com.android.bluetooth.audio_util.Metadata$Builder.fromQueueItem(Metadata.java:181)
    at com.android.bluetooth.audio_util.Util.toMetadata(Util.java:105)
    at com.android.bluetooth.audio_util.Util.toMetadataList(Util.java:135)
    at com.android.bluetooth.audio_util.MediaPlayerWrapper$MediaControllerListener.onQueueChanged(MediaPlayerWrapper.java:486)
    at android.media.session.MediaController$MessageHandler.handleMessage(MediaController.java:1239)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:201)
    at android.os.Looper.loop(Looper.java:288)
    at android.app.ActivityThread.main(ActivityThread.java:7839)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.lang.ClassNotFoundException: com.mypackage.items.SomeSerializable
    at java.lang.Class.classForName(Native Method)
    at java.lang.Class.forName(Class.java:454)
    at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:682)
    at android.os.Parcel$2.resolveClass(Parcel.java:3504)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1703)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1594)
    at java.io.ObjectInputStream.readEnum(ObjectInputStream.java:1824)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1409)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
    at android.os.Parcel.readSerializable(Parcel.java:3507)
    at android.os.Parcel.readValue(Parcel.java:3277)
    at android.os.Parcel.readArrayMapInternal(Parcel.java:3623)
    at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292)
    at android.os.BaseBundle.unparcel(BaseBundle.java:236)
    at android.os.BaseBundle.containsKey(BaseBundle.java:516)
    at com.android.bluetooth.audio_util.Metadata$Builder.fromBundle(Metadata.java:190)
    at com.android.bluetooth.audio_util.Metadata$Builder.fromMediaDescription(Metadata.java:173)
    at com.android.bluetooth.audio_util.Metadata$Builder.fromQueueItem(Metadata.java:181)
    at com.android.bluetooth.audio_util.Util.toMetadata(Util.java:105)
    at com.android.bluetooth.audio_util.Util.toMetadataList(Util.java:135)
    at com.android.bluetooth.audio_util.MediaPlayerWrapper$MediaControllerListener.onQueueChanged(MediaPlayerWrapper.java:486)
    at android.media.session.MediaController$MessageHandler.handleMessage(MediaController.java:1239)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:201)
    at android.os.Looper.loop(Looper.java:288)
    at android.app.ActivityThread.main(ActivityThread.java:7839)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.lang.ClassNotFoundException: com.mypackage.items.SomeSerializable
    at java.lang.Class.classForName(Native Method)
    at java.lang.Class.forName(Class.java:454)
    at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:682)
    at android.os.Parcel$2.resolveClass(Parcel.java:3504)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1703)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1594)
    at java.io.ObjectInputStream.readEnum(ObjectInputStream.java:1824)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1409)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
    at android.os.Parcel.readSerializable(Parcel.java:3507)
    at android.os.Parcel.readValue(Parcel.java:3277)
    at android.os.Parcel.readArrayMapInternal(Parcel.java:3623)
    at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292)
    at android.os.BaseBundle.unparcel(BaseBundle.java:236)
    at android.os.BaseBundle.containsKey(BaseBundle.java:516)
    at com.android.bluetooth.audio_util.Metadata$Builder.fromBundle(Metadata.java:190)
    at com.android.bluetooth.audio_util.Metadata$Builder.fromMediaDescription(Metadata.java:173)
    at com.android.bluetooth.audio_util.Metadata$Builder.fromQueueItem(Metadata.java:181)
    at com.android.bluetooth.audio_util.Util.toMetadata(Util.java:105)
    at com.android.bluetooth.audio_util.Util.toMetadataList(Util.java:135)
    at com.android.bluetooth.audio_util.MediaPlayerWrapper$MediaControllerListener.onQueueChanged(MediaPlayerWrapper.java:486)
    at android.media.session.MediaController$MessageHandler.handleMessage(MediaController.java:1239)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:201)
    at android.os.Looper.loop(Looper.java:288)
    at android.app.ActivityThread.main(ActivityThread.java:7839)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)