From 94b256960c2e022fe40d8d5b00d1c16d5e0cde71 Mon Sep 17 00:00:00 2001 From: Etienne Ruffieux <eruffieux@google.com> Date: Thu, 24 Aug 2023 02:01:09 -0700 Subject: [PATCH] Fix null playstate update from players. Some players will send a null object as parameter for playstate change callback, without updating the MediaSession. These events should still trigger an update by using the Mediasession metadata if it is different. Bug: 297191838 Tag: #feature Test: atest BluetoothInstrumentationTests Change-Id: Ic5a5b6a68e616af939a4e42d7d64476932dc38d3 --- .../audio_util/MediaPlayerWrapper.java | 22 ++++++++----- .../audio_util/MediaPlayerWrapperTest.java | 33 ++++++++++++++++++- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java index 3225244b3be..256326da64b 100644 --- a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java +++ b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java @@ -461,8 +461,11 @@ public class MediaPlayerWrapper { @Override public void onMetadataChanged(@Nullable MediaMetadata mediaMetadata) { if (!isMetadataReady()) { - Log.v(TAG, "onMetadataChanged(): " + mPackageName - + " tried to update with no queue"); + Log.v( + TAG, + "onMetadataChanged(): " + + mPackageName + + " tried to update with no metadata"); return; } @@ -495,13 +498,16 @@ public class MediaPlayerWrapper { @Override public void onPlaybackStateChanged(@Nullable PlaybackState state) { if (!isPlaybackStateReady()) { - Log.v(TAG, "onPlaybackStateChanged(): " + mPackageName - + " tried to update with no queue"); + Log.v( + TAG, + "onPlaybackStateChanged(): " + + mPackageName + + " tried to update with no state"); return; } - mPlaybackStateChangeEventLogger.logv(TAG, "onPlaybackStateChanged(): " - + mPackageName + " : " + state.toString()); + mPlaybackStateChangeEventLogger.logv( + TAG, "onPlaybackStateChanged(): " + mPackageName + " : " + state); if (!playstateEquals(state, getPlaybackState())) { e("The callback playback state doesn't match the current state"); @@ -513,8 +519,8 @@ public class MediaPlayerWrapper { return; } - // If there is no playstate, ignore the update. - if (state.getState() == PlaybackState.STATE_NONE) { + // If state isn't null and there is no playstate, ignore the update. + if (state != null && state.getState() == PlaybackState.STATE_NONE) { Log.v(TAG, "Waiting to send update as controller has no playback state"); return; } diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java index 8b3ceae577c..9ed1fa0c96d 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java @@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.*; import android.content.Context; -import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -377,6 +376,38 @@ public class MediaPlayerWrapperTest { Util.toMetadata(mMockContext, mTestMetadata.build())); } + @Test + public void testNullPlayback() { + // Create the wrapper object and register the looper with the timeout handler + TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); + MediaPlayerWrapper wrapper = + MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper()); + wrapper.registerCallback(mTestCbs); + + // Return null when getting the queue + doReturn(null).when(mMockController).getQueue(); + + // Grab the callbacks the wrapper registered with the controller + verify(mMockController).registerCallback(mControllerCbs.capture(), any()); + MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); + + // Update Metadata returned by controller + mTestState.setState(PlaybackState.STATE_PLAYING, 1000, 1.0f); + doReturn(mTestState.build()).when(mMockController).getPlaybackState(); + + // Call the callback + controllerCallbacks.onPlaybackStateChanged(null); + + // Assert that the metadata returned by getPlaybackState() is used instead of null + + verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); + MediaData data = mMediaUpdateData.getValue(); + Assert.assertEquals( + "Returned PlaybackState is incorrect", + data.state.toString(), + mTestState.build().toString()); + } + @Test public void testNullQueue() { // Create the wrapper object and register the looper with the timeout handler -- GitLab