diff options
17 files changed, 126 insertions, 81 deletions
diff --git a/android/app/res/values/config.xml b/android/app/res/values/config.xml index 5668a41199..0bb72b5ca8 100644 --- a/android/app/res/values/config.xml +++ b/android/app/res/values/config.xml @@ -138,9 +138,6 @@ <integer name="a2dp_source_codec_priority_lc3">6001</integer> <integer name="a2dp_source_codec_priority_opus">7001</integer> - <!-- Enable support for URI based images. Off by default due to increased memory usage --> - <bool name="avrcp_target_cover_art_uri_images">false</bool> - <!-- Package that is responsible for user interaction on pairing request, success or cancel. Receives: diff --git a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java index b04f684f34..b98d9e6be8 100644 --- a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java +++ b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java @@ -784,6 +784,10 @@ public class MediaPlayerList { } } + public boolean isVfsCoverArtEnabled() { + return Util.areUriImagesSupported(); + } + /** * Adds a {@link MediaController} to the {@link #mMediaPlayers} map and returns its ID. * diff --git a/android/app/src/com/android/bluetooth/audio_util/helpers/Image.java b/android/app/src/com/android/bluetooth/audio_util/helpers/Image.java index fc106e9a95..d89a6afddf 100644 --- a/android/app/src/com/android/bluetooth/audio_util/helpers/Image.java +++ b/android/app/src/com/android/bluetooth/audio_util/helpers/Image.java @@ -60,7 +60,7 @@ public class Image { Bitmap bmp_album_art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); Bitmap bmp_icon = metadata.getBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON); - if (mContext != null && Util.areUriImagesSupported(mContext)) { + if (Util.areUriImagesSupported()) { uri_art = metadata.getString(MediaMetadata.METADATA_KEY_ART_URI); uri_album_art = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI); uri_icon = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI); @@ -92,7 +92,7 @@ public class Image { Bitmap bmp_album_art = bundle.getParcelable(MediaMetadata.METADATA_KEY_ALBUM_ART); Bitmap bmp_icon = bundle.getParcelable(MediaMetadata.METADATA_KEY_DISPLAY_ICON); - if (mContext != null && Util.areUriImagesSupported(mContext)) { + if (Util.areUriImagesSupported()) { uri_art = bundle.getString(MediaMetadata.METADATA_KEY_ART_URI); uri_album_art = bundle.getString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI); uri_icon = bundle.getString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI); diff --git a/android/app/src/com/android/bluetooth/audio_util/helpers/Metadata.java b/android/app/src/com/android/bluetooth/audio_util/helpers/Metadata.java index 02ca4fbd5c..63791acb29 100644 --- a/android/app/src/com/android/bluetooth/audio_util/helpers/Metadata.java +++ b/android/app/src/com/android/bluetooth/audio_util/helpers/Metadata.java @@ -23,8 +23,6 @@ import android.media.browse.MediaBrowser.MediaItem; import android.media.session.MediaSession; import android.os.Bundle; -import com.android.bluetooth.R; - import java.util.Objects; public class Metadata implements Cloneable { @@ -201,7 +199,7 @@ public class Metadata implements Cloneable { mMetadata.duration = "" + data.getLong(MediaMetadata.METADATA_KEY_DURATION); } if ((mContext != null - && Util.areUriImagesSupported(mContext) + && Util.areUriImagesSupported() && (data.containsKey(MediaMetadata.METADATA_KEY_ART_URI) || data.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART_URI) || data.containsKey( @@ -233,7 +231,7 @@ public class Metadata implements Cloneable { if (desc.getIconBitmap() != null) { mMetadata.image = new Image(mContext, desc.getIconBitmap()); } else if (mContext != null - && Util.areUriImagesSupported(mContext) + && Util.areUriImagesSupported() && desc.getIconUri() != null) { mMetadata.image = new Image(mContext, desc.getIconUri()); } @@ -281,7 +279,7 @@ public class Metadata implements Cloneable { mMetadata.duration = "" + bundle.getLong(MediaMetadata.METADATA_KEY_DURATION); } if ((mContext != null - && Util.areUriImagesSupported(mContext) + && Util.areUriImagesSupported() && (bundle.containsKey(MediaMetadata.METADATA_KEY_ART_URI) || bundle.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART_URI) || bundle.containsKey( @@ -296,13 +294,8 @@ public class Metadata implements Cloneable { /** Elect to use default values in the Metadata in place of any missing values */ public Builder useDefaults() { - if (mMetadata.mediaId == null) { - mMetadata.mediaId = EMPTY_MEDIA_ID; - } - if (mMetadata.title == null) { - mMetadata.title = - mContext != null ? mContext.getString(R.string.not_provided) : EMPTY_TITLE; - } + if (mMetadata.mediaId == null) mMetadata.mediaId = EMPTY_MEDIA_ID; + if (mMetadata.title == null) mMetadata.title = EMPTY_TITLE; if (mMetadata.artist == null) mMetadata.artist = EMPTY_ARTIST; if (mMetadata.album == null) mMetadata.album = EMPTY_ALBUM; if (mMetadata.trackNum == null) mMetadata.trackNum = EMPTY_TRACK_NUM; diff --git a/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java b/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java index 32597edc75..877a315c88 100644 --- a/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java +++ b/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java @@ -21,9 +21,10 @@ import android.content.pm.PackageManager; import android.media.MediaMetadata; import android.media.browse.MediaBrowser.MediaItem; import android.media.session.MediaSession; +import android.os.SystemProperties; import android.util.Log; -import com.android.bluetooth.R; +import androidx.annotation.VisibleForTesting; import java.util.ArrayList; import java.util.List; @@ -31,6 +32,13 @@ import java.util.List; class Util { public static String TAG = "audio_util.Util"; + private static final String VFS_COVER_ART_ENABLED_PROPERTY = + "bluetooth.profile.avrcp.target.vfs_coverart.enabled"; + + @VisibleForTesting + static Boolean sUriImagesSupport = + SystemProperties.getBoolean(VFS_COVER_ART_ENABLED_PROPERTY, false); + // TODO (apanicke): Remove this prefix later, for now it makes debugging easier. public static final String NOW_PLAYING_PREFIX = "NowPlayingId"; @@ -49,13 +57,12 @@ class Util { } /** - * Get whether or not Bluetooth is configured to support URI images or not. + * Get whether or not Bluetooth is configured to support URI images. * * <p>Note that creating URI images will dramatically increase memory usage. */ - public static boolean areUriImagesSupported(Context context) { - if (context == null) return false; - return context.getResources().getBoolean(R.bool.avrcp_target_cover_art_uri_images); + public static boolean areUriImagesSupported() { + return sUriImagesSupport.booleanValue(); } /** Translate a MediaItem to audio_util's Metadata */ diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java index 26d7d3e48d..46805f404c 100644 --- a/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java +++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java @@ -62,8 +62,9 @@ public class AvrcpCoverArtService { // Native interface private AvrcpNativeInterface mNativeInterface; - public AvrcpCoverArtService() { - mNativeInterface = AvrcpNativeInterface.getInstance(); + // The native interface must be a parameter here in order to be able to mock AvrcpTargetService + public AvrcpCoverArtService(AvrcpNativeInterface nativeInterface) { + mNativeInterface = nativeInterface; mAcceptThread = new SocketAcceptor(); mStorage = new AvrcpCoverArtStorage(COVER_ART_STORAGE_MAX_ITEMS); } diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java index d0d6b89c60..068db0f024 100644 --- a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java +++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java @@ -37,6 +37,7 @@ import android.view.KeyEvent; import com.android.bluetooth.BluetoothEventLogger; import com.android.bluetooth.BluetoothMetricsProto; import com.android.bluetooth.a2dp.A2dpService; +import com.android.bluetooth.audio_util.ListItem; import com.android.bluetooth.audio_util.MediaData; import com.android.bluetooth.audio_util.MediaPlayerList; import com.android.bluetooth.audio_util.MediaPlayerWrapper; @@ -97,6 +98,8 @@ public class AvrcpTargetService extends ProfileService { private static AvrcpTargetService sInstance = null; + private final boolean mIsVfsCoverArtEnabled; + public AvrcpTargetService(AdapterService adapterService) { this( requireNonNull(adapterService), @@ -145,7 +148,7 @@ public class AvrcpTargetService extends ProfileService { Log.e(TAG, "Please use AVRCP version 1.6 to enable cover art"); mAvrcpCoverArtService = null; } else { - AvrcpCoverArtService coverArtService = new AvrcpCoverArtService(); + AvrcpCoverArtService coverArtService = new AvrcpCoverArtService(mNativeInterface); if (coverArtService.start()) { mAvrcpCoverArtService = coverArtService; } else { @@ -154,6 +157,8 @@ public class AvrcpTargetService extends ProfileService { } } + mIsVfsCoverArtEnabled = mMediaPlayerList.isVfsCoverArtEnabled(); + mReceiver = new AvrcpBroadcastReceiver(); IntentFilter filter = new IntentFilter(); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); @@ -396,8 +401,7 @@ public class AvrcpTargetService extends ProfileService { Metadata getCurrentSongInfo() { Metadata metadata = mMediaPlayerList.getCurrentSongInfo(); if (mAvrcpCoverArtService != null && metadata.image != null) { - String imageHandle = mAvrcpCoverArtService.storeImage(metadata.image); - if (imageHandle != null) metadata.image.setImageHandle(imageHandle); + metadata.image.setImageHandle(mAvrcpCoverArtService.storeImage(metadata.image)); } return metadata; } @@ -430,26 +434,20 @@ public class AvrcpTargetService extends ProfileService { List<Metadata> getNowPlayingList() { String currentMediaId = getCurrentMediaId(); Metadata currentTrack = null; - String imageHandle = null; List<Metadata> nowPlayingList = mMediaPlayerList.getNowPlayingList(); if (mAvrcpCoverArtService != null) { for (Metadata metadata : nowPlayingList) { if (TextUtils.equals(metadata.mediaId, currentMediaId)) { currentTrack = metadata; } else if (metadata.image != null) { - imageHandle = mAvrcpCoverArtService.storeImage(metadata.image); - if (imageHandle != null) { - metadata.image.setImageHandle(imageHandle); - } + metadata.image.setImageHandle(mAvrcpCoverArtService.storeImage(metadata.image)); } } // Always store the current item from the queue last so we know the image is in storage if (currentTrack != null) { - imageHandle = mAvrcpCoverArtService.storeImage(currentTrack.image); - if (imageHandle != null) { - currentTrack.image.setImageHandle(imageHandle); - } + currentTrack.image.setImageHandle( + mAvrcpCoverArtService.storeImage(currentTrack.image)); } } return nowPlayingList; @@ -488,7 +486,20 @@ public class AvrcpTargetService extends ProfileService { /** See {@link MediaPlayerList#getFolderItems}. */ void getFolderItems(int playerId, String mediaId, MediaPlayerList.GetFolderItemsCallback cb) { - mMediaPlayerList.getFolderItems(playerId, mediaId, cb); + mMediaPlayerList.getFolderItems( + playerId, + mediaId, + (id, results) -> { + if (mIsVfsCoverArtEnabled && mAvrcpCoverArtService != null) { + for (ListItem item : results) { + if (item != null && item.song != null && item.song.image != null) { + item.song.image.setImageHandle( + mAvrcpCoverArtService.storeImage(item.song.image)); + } + } + } + cb.run(id, results); + }); } /** See {@link MediaPlayerList#playItem}. */ diff --git a/android/app/src/com/android/bluetooth/avrcp/helpers/CoverArt.java b/android/app/src/com/android/bluetooth/avrcp/helpers/CoverArt.java index 95388baf78..dd720077a0 100644 --- a/android/app/src/com/android/bluetooth/avrcp/helpers/CoverArt.java +++ b/android/app/src/com/android/bluetooth/avrcp/helpers/CoverArt.java @@ -40,7 +40,12 @@ import java.security.NoSuchAlgorithmException; */ public class CoverArt { private static final String TAG = CoverArt.class.getSimpleName(); - private static final BipPixel PIXEL_THUMBNAIL = BipPixel.createFixed(200, 200); + + // The size in pixels of the thumbnail sides. + private static final int THUMBNAIL_SIZE = 200; + + private static final BipPixel PIXEL_THUMBNAIL = + BipPixel.createFixed(THUMBNAIL_SIZE, THUMBNAIL_SIZE); private String mImageHandle = null; private Bitmap mImage = null; @@ -50,7 +55,7 @@ public class CoverArt { // Create a scaled version of the image for now, as consumers don't need // anything larger than this at the moment. Also makes each image gathered // the same dimensions for hashing purposes. - mImage = Bitmap.createScaledBitmap(image.getImage(), 200, 200, false); + mImage = Bitmap.createScaledBitmap(image.getImage(), THUMBNAIL_SIZE, THUMBNAIL_SIZE, false); } /** @@ -133,13 +138,15 @@ public class CoverArt { BipEncoding encoding = descriptor.getEncoding(); BipPixel pixel = descriptor.getPixel(); - if (encoding.getType() == BipEncoding.JPEG && PIXEL_THUMBNAIL.equals(pixel)) { + int encodingType = encoding.getType(); + if ((encodingType == BipEncoding.JPEG || encodingType == BipEncoding.PNG) + && PIXEL_THUMBNAIL.equals(pixel)) { return true; } return false; } - /** Get the cover artwork image bytes as a 200 x 200 JPEG thumbnail */ + /** Get the cover artwork image bytes as a THUMBNAIL_SIZE x THUMBNAIL_SIZE JPEG thumbnail */ public byte[] getThumbnail() { debug("GetImageThumbnail()"); if (mImage == null) return null; @@ -160,12 +167,19 @@ public class CoverArt { return null; } BipImageProperties.Builder builder = new BipImageProperties.Builder(); - BipEncoding encoding = new BipEncoding(BipEncoding.JPEG); - BipPixel pixel = BipPixel.createFixed(200, 200); - BipImageFormat format = BipImageFormat.createNative(encoding, pixel, -1); + + BipEncoding jpgEncoding = new BipEncoding(BipEncoding.JPEG); + BipEncoding pngEncoding = new BipEncoding(BipEncoding.PNG); + BipPixel jpgPixel = BipPixel.createFixed(THUMBNAIL_SIZE, THUMBNAIL_SIZE); + BipPixel pngPixel = BipPixel.createFixed(THUMBNAIL_SIZE, THUMBNAIL_SIZE); + + BipImageFormat jpgNativeFormat = BipImageFormat.createNative(jpgEncoding, jpgPixel, -1); + BipImageFormat pngVariantFormat = + BipImageFormat.createVariant(pngEncoding, pngPixel, THUMBNAIL_SIZE, null); builder.setImageHandle(mImageHandle); - builder.addNativeFormat(format); + builder.addNativeFormat(jpgNativeFormat); + builder.addVariantFormat(pngVariantFormat); BipImageProperties properties = builder.build(); return properties; diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java index cd9ad4dd9a..b3d61e33af 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java @@ -40,7 +40,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; import org.junit.After; @@ -75,7 +74,6 @@ public class BrowserPlayerWrapperTest { private HandlerThread mThread; @Mock Context mMockContext; - @Mock Resources mMockResources; private Context mTargetContext; private Resources mTestResources; private MockContentResolver mTestContentResolver; @@ -116,8 +114,7 @@ public class BrowserPlayerWrapperTest { }); when(mMockContext.getContentResolver()).thenReturn(mTestContentResolver); - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(true); - when(mMockContext.getResources()).thenReturn(mMockResources); + Util.sUriImagesSupport = true; // Set up Looper thread for the timeout handler mThread = new HandlerThread("MediaPlayerWrapperTestThread"); @@ -138,6 +135,7 @@ public class BrowserPlayerWrapperTest { mTestBitmap = null; mTestResources = null; mTargetContext = null; + Util.sUriImagesSupport = false; } private Bitmap loadImage(int resId) { diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/ImageTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/ImageTest.java index 60ca5a5aef..6c6e6e78fc 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/ImageTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/ImageTest.java @@ -36,7 +36,6 @@ import android.test.mock.MockContentResolver; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; import org.junit.After; @@ -57,7 +56,6 @@ public class ImageTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); private @Mock Context mMockContext; - private @Mock Resources mMockResources; private Resources mTestResources; private MockContentResolver mTestContentResolver; @@ -108,8 +106,7 @@ public class ImageTest { }); when(mMockContext.getContentResolver()).thenReturn(mTestContentResolver); - when(mMockContext.getResources()).thenReturn(mMockResources); - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(true); + Util.sUriImagesSupport = true; } @After @@ -119,6 +116,7 @@ public class ImageTest { mTestResources = null; mTargetContext = null; mMockContext = null; + Util.sUriImagesSupport = false; } private Bitmap loadImage(int resId) { @@ -287,7 +285,7 @@ public class ImageTest { */ @Test public void testCreateImageFromMediaMetadataWithArtUriDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; MediaMetadata metadata = getMediaMetadataWithUri(MediaMetadata.METADATA_KEY_ART_URI, IMAGE_STRING_1); Image artwork = new Image(mMockContext, metadata); @@ -302,7 +300,7 @@ public class ImageTest { */ @Test public void testCreateImageFromMediaMetadataWithAlbumArtUriDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; MediaMetadata metadata = getMediaMetadataWithUri(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, IMAGE_STRING_1); Image artwork = new Image(mMockContext, metadata); @@ -317,7 +315,7 @@ public class ImageTest { */ @Test public void testCreateImageFromMediaMetadataWithDisplayIconUriDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; MediaMetadata metadata = getMediaMetadataWithUri( MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, IMAGE_STRING_1); @@ -459,7 +457,7 @@ public class ImageTest { */ @Test public void testCreateImageFromBundleWithArtUriDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; Bundle bundle = getBundleWithUri(MediaMetadata.METADATA_KEY_ART_URI, IMAGE_STRING_1); Image artwork = new Image(mMockContext, bundle); assertThat(artwork.getImage()).isNull(); @@ -473,7 +471,7 @@ public class ImageTest { */ @Test public void testCreateImageFromBundleWithAlbumArtUriDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; Bundle bundle = getBundleWithUri(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, IMAGE_STRING_1); Image artwork = new Image(mMockContext, bundle); assertThat(artwork.getImage()).isNull(); @@ -487,7 +485,7 @@ public class ImageTest { */ @Test public void testCreateImageFromBundleWithDisplayIconUriDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; Bundle bundle = getBundleWithUri(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, IMAGE_STRING_1); Image artwork = new Image(mMockContext, bundle); 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 2b323d9810..794a1200b3 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 @@ -36,9 +36,9 @@ import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -75,7 +75,6 @@ public class MediaPlayerWrapperTest { @Mock MediaController mMockController; @Mock MediaPlayerWrapper.Callback mTestCbs; @Mock Context mMockContext; - @Mock Resources mMockResources; List<MediaSession.QueueItem> getQueueFromDescriptions( List<MediaDescription.Builder> descriptions) { @@ -98,8 +97,7 @@ public class MediaPlayerWrapperTest { InstrumentationRegistry.getInstrumentation().getTargetContext()); mTestBitmap = loadImage(com.android.bluetooth.tests.R.raw.image_200_200); - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(true); - when(mMockContext.getResources()).thenReturn(mMockResources); + Util.sUriImagesSupport = true; // Set failure handler to capture Log.wtf messages Log.setWtfHandler(mFailHandler); @@ -160,6 +158,14 @@ public class MediaPlayerWrapperTest { MediaPlayerWrapper.sTesting = true; } + @After + public void tearDown() { + if (mThread != null) { + mThread.quitSafely(); + } + Util.sUriImagesSupport = false; + } + private Bitmap loadImage(int resId) { InputStream imageInputStream = mTestResources.openRawResource(resId); return BitmapFactory.decodeStream(imageInputStream); diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MetadataTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MetadataTest.java index 5ba6e0f4e5..4cc12f763d 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MetadataTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MetadataTest.java @@ -38,7 +38,6 @@ import android.test.mock.MockContentResolver; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; import org.junit.After; @@ -59,7 +58,6 @@ public class MetadataTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); private @Mock Context mMockContext; - private @Mock Resources mMockResources; private Resources mTestResources; private MockContentResolver mTestContentResolver; @@ -111,8 +109,7 @@ public class MetadataTest { }); when(mMockContext.getContentResolver()).thenReturn(mTestContentResolver); - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(true); - when(mMockContext.getResources()).thenReturn(mMockResources); + Util.sUriImagesSupport = true; mSongImage = new Image(mMockContext, mTestBitmap); } @@ -125,6 +122,7 @@ public class MetadataTest { mTestResources = null; mTargetContext = null; mMockContext = null; + Util.sUriImagesSupport = false; } private Bitmap loadImage(int resId) { @@ -427,7 +425,7 @@ public class MetadataTest { */ @Test public void testBuildMetadataFromMediaMetadataWithUriAndUrisDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; MediaMetadata m = getMediaMetadataWithUri(MediaMetadata.METADATA_KEY_ART_URI, IMAGE_URI_1); Metadata metadata = new Metadata.Builder().useContext(mMockContext).fromMediaMetadata(m).build(); @@ -737,7 +735,7 @@ public class MetadataTest { */ @Test public void testBuildMetadataFromBundleWithUriAndUrisDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; Bundle bundle = getBundleWithUri(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, IMAGE_URI_1); Metadata metadata = new Metadata.Builder().useContext(mMockContext).fromBundle(bundle).build(); @@ -852,7 +850,7 @@ public class MetadataTest { */ @Test public void testBuildMetadataFromMediaItemWithIconUriAndUrisDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; MediaDescription description = getMediaDescription(null, IMAGE_URI_1, null); MediaItem item = getMediaItem(description); Metadata metadata = @@ -980,7 +978,7 @@ public class MetadataTest { */ @Test public void testBuildMetadataFromQueueItemWithIconUriandUrisDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; MediaDescription description = getMediaDescription(null, IMAGE_URI_1, null); QueueItem queueItem = getQueueItem(description); Metadata metadata = diff --git a/android/pandora/mmi2grpc/mmi2grpc/avrcp.py b/android/pandora/mmi2grpc/mmi2grpc/avrcp.py index 2c4d1ed58d..c64eb2113f 100644 --- a/android/pandora/mmi2grpc/mmi2grpc/avrcp.py +++ b/android/pandora/mmi2grpc/mmi2grpc/avrcp.py @@ -117,6 +117,7 @@ class AVRCPProxy(ProfileProxy): Action: Make sure the IUT is in a connectable state. """ + self.mediaplayer.ResetQueue() return "OK" @assert_description @@ -1086,7 +1087,7 @@ class AVRCPProxy(ProfileProxy): """ Please accept the l2cap channel connection for an OBEX connection. """ - + return "OK" @assert_description @@ -1094,7 +1095,7 @@ class AVRCPProxy(ProfileProxy): """ Please accept the OBEX CONNECT REQ. """ - + return "OK" @@ -1105,7 +1106,7 @@ class AVRCPProxy(ProfileProxy): ready. """ self.mediaplayer.Play() - + return "OK" @assert_description @@ -1113,7 +1114,7 @@ class AVRCPProxy(ProfileProxy): """ Take action to reject the invalid 'get-img' request sent by the tester. """ - + return "OK" @assert_description @@ -1121,7 +1122,7 @@ class AVRCPProxy(ProfileProxy): """ Take action to accept the GetImgProperties operation from the tester. """ - + return "OK" @assert_description @@ -1129,7 +1130,7 @@ class AVRCPProxy(ProfileProxy): """ Take action to accept the GetImg operation from the tester. """ - + return "OK" @assert_description @@ -1137,7 +1138,7 @@ class AVRCPProxy(ProfileProxy): """ Was the currently displayed file or folder sent by the IUT? """ - + return "OK" @assert_description @@ -1148,5 +1149,5 @@ class AVRCPProxy(ProfileProxy): """ self.mediaplayer.UpdateQueue() self.mediaplayer.PlayUpdated() - + return "OK" diff --git a/android/pandora/server/configs/pts_bot_tests_config.json b/android/pandora/server/configs/pts_bot_tests_config.json index 717f76a612..93c26119ec 100644 --- a/android/pandora/server/configs/pts_bot_tests_config.json +++ b/android/pandora/server/configs/pts_bot_tests_config.json @@ -133,7 +133,6 @@ "AVRCP/TG/CA/BI-05-C", "AVRCP/TG/CA/BI-07-C", "AVRCP/TG/CA/BI-10-C", - "AVRCP/TG/CA/BV-06-C", "AVRCP/TG/CA/BV-16-C", "AVRCP/CT/CEC/BV-02-I", "AVRCP/CT/CRC/BV-02-I", @@ -785,6 +784,7 @@ "AVRCP/TG/CA/BV-02-I", "AVRCP/TG/CA/BV-03-I", "AVRCP/TG/CA/BV-04-C", + "AVRCP/TG/CA/BV-06-C", "AVRCP/TG/CA/BV-08-C", "AVRCP/TG/CA/BV-10-C", "AVRCP/TG/CA/BV-12-C", @@ -3246,7 +3246,6 @@ "AVRCP/TG/CA/BI-05-C", "AVRCP/TG/CA/BI-07-C", "AVRCP/TG/CA/BI-10-C", - "AVRCP/TG/CA/BV-06-C", "AVRCP/TG/CA/BV-16-C" ] }, diff --git a/android/pandora/server/src/MediaPlayer.kt b/android/pandora/server/src/MediaPlayer.kt index d0cdb6d7fb..a2c981f881 100644 --- a/android/pandora/server/src/MediaPlayer.kt +++ b/android/pandora/server/src/MediaPlayer.kt @@ -118,6 +118,13 @@ class MediaPlayer(val context: Context) : MediaPlayerImplBase(), Closeable { } } + override fun resetQueue(request: Empty, responseObserver: StreamObserver<Empty>) { + grpcUnary<Empty>(scope, responseObserver) { + MediaPlayerBrowserService.instance.resetQueue() + Empty.getDefaultInstance() + } + } + override fun getShuffleMode( request: Empty, responseObserver: StreamObserver<GetShuffleModeResponse> diff --git a/android/pandora/server/src/MediaPlayerBrowserService.kt b/android/pandora/server/src/MediaPlayerBrowserService.kt index 0e32617de4..eaab8a9bbe 100644 --- a/android/pandora/server/src/MediaPlayerBrowserService.kt +++ b/android/pandora/server/src/MediaPlayerBrowserService.kt @@ -185,6 +185,16 @@ class MediaPlayerBrowserService : MediaBrowserServiceCompat() { mediaSession.setQueue(queue) } + fun resetQueue() { + if (metadataItems.contains("" + NEW_QUEUE_ITEM_INDEX)) { + metadataItems.remove("" + NEW_QUEUE_ITEM_INDEX) + queue.removeLast() + mediaSession.setQueue(queue) + stop() + currentTrack = QUEUE_START_INDEX + } + } + fun getShuffleMode(): Int { val controller = mediaSession.getController() return controller.getShuffleMode() diff --git a/pandora/interfaces/pandora_experimental/mediaplayer.proto b/pandora/interfaces/pandora_experimental/mediaplayer.proto index 06519f4130..69ead553d8 100644 --- a/pandora/interfaces/pandora_experimental/mediaplayer.proto +++ b/pandora/interfaces/pandora_experimental/mediaplayer.proto @@ -18,6 +18,7 @@ service MediaPlayer { rpc Backward(google.protobuf.Empty) returns (google.protobuf.Empty); rpc SetLargeMetadata(google.protobuf.Empty) returns (google.protobuf.Empty); rpc UpdateQueue(google.protobuf.Empty) returns (google.protobuf.Empty); + rpc ResetQueue(google.protobuf.Empty) returns (google.protobuf.Empty); rpc GetShuffleMode(google.protobuf.Empty) returns (GetShuffleModeResponse); rpc SetShuffleMode(SetShuffleModeRequest) returns (google.protobuf.Empty); rpc StartTestPlayback(google.protobuf.Empty) returns (google.protobuf.Empty); |