Loading android/app/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java +46 −31 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.bluetooth.avrcp; import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothAvrcp; import android.media.session.MediaSession; import android.media.session.PlaybackState; Loading @@ -42,15 +44,23 @@ public class AddressedMediaPlayer { static private final String TAG = "AddressedMediaPlayer"; static private final Boolean DEBUG = false; static private final long SINGLE_QID = 1; static private final String UNKNOWN_TITLE = "(unknown)"; private AvrcpMediaRspInterface mMediaInterface; private List<MediaSession.QueueItem> mNowPlayingList; private final List<MediaSession.QueueItem> mUnknownNowPlayingList; private long mLastTrackIdSent; public AddressedMediaPlayer(AvrcpMediaRspInterface mediaInterface) { mNowPlayingList = null; mMediaInterface = mediaInterface; mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID; List<MediaSession.QueueItem> unknown = new ArrayList<MediaSession.QueueItem>(); unknown.add(getCurrentQueueItem(null, SINGLE_QID)); mUnknownNowPlayingList = unknown; } void cleanup() { Loading @@ -62,7 +72,7 @@ public class AddressedMediaPlayer { /* get now playing list from addressed player */ void getFolderItemsNowPlaying(byte[] bdaddr, AvrcpCmd.FolderItemsCmd reqObj, MediaController mediaController) { @Nullable MediaController mediaController) { if (DEBUG) Log.v(TAG, "getFolderItemsNowPlaying"); if (mediaController == null) { // No players (if a player exists, we would have selected it) Loading @@ -77,7 +87,7 @@ public class AddressedMediaPlayer { /* get item attributes for item in now playing list */ void getItemAttr(byte[] bdaddr, AvrcpCmd.ItemAttrCmd itemAttr, MediaController mediaController) { @Nullable MediaController mediaController) { int status = AvrcpConstants.RSP_NO_ERROR; long mediaId = ByteBuffer.wrap(itemAttr.mUid).getLong(); List<MediaSession.QueueItem> items = getNowPlayingList(mediaController); Loading @@ -87,12 +97,6 @@ public class AddressedMediaPlayer { if (Arrays.equals(itemAttr.mUid, AvrcpConstants.TRACK_IS_SELECTED)) { if (DEBUG) Log.d(TAG, "getItemAttr: Remote requests for now playing contents:"); if (mediaController == null) { Log.e(TAG, "mediaController = null, sending no available players response"); mMediaInterface.getItemAttrRsp(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY, null); return; } // get the current playing metadata and send. getItemAttrFilterAttr(bdaddr, itemAttr, getCurrentQueueItem(mediaController, mediaId), mediaController); Loading @@ -113,10 +117,10 @@ public class AddressedMediaPlayer { /* Refresh and get the queue of now playing. */ private List<MediaSession.QueueItem> getNowPlayingList(MediaController mediaController) { if (mediaController == null) return null; private List<MediaSession.QueueItem> getNowPlayingList( @Nullable MediaController mediaController) { if (mediaController == null) return mUnknownNowPlayingList; if (mNowPlayingList != null) return mNowPlayingList; List<MediaSession.QueueItem> items = mediaController.getQueue(); if (items == null) { Log.i(TAG, "null queue from " + mediaController.getPackageName() Loading @@ -124,7 +128,7 @@ public class AddressedMediaPlayer { MediaMetadata metadata = mediaController.getMetadata(); // Because we are database-unaware, we can just number the item here whatever we want // because they have to re-poll it every time. MediaSession.QueueItem current = getCurrentQueueItem(mediaController, 1); MediaSession.QueueItem current = getCurrentQueueItem(mediaController, SINGLE_QID); items = new ArrayList<MediaSession.QueueItem>(); items.add(current); } Loading @@ -135,7 +139,14 @@ public class AddressedMediaPlayer { /* Constructs a queue item representing the current playing metadata from an * active controller with queue id |qid|. */ private MediaSession.QueueItem getCurrentQueueItem(MediaController controller, long qid) { private MediaSession.QueueItem getCurrentQueueItem( @Nullable MediaController controller, long qid) { if (controller == null) { MediaDescription.Builder bob = new MediaDescription.Builder(); bob.setTitle(UNKNOWN_TITLE); return new QueueItem(bob.build(), qid); } MediaMetadata metadata = controller.getMetadata(); if (metadata == null) { Log.w(TAG, "Controller has no metadata!? Making an empty one"); Loading Loading @@ -186,12 +197,12 @@ public class AddressedMediaPlayer { } /* Instructs media player to play particular media item */ void playItem(byte[] bdaddr, byte[] uid, MediaController mediaController) { void playItem(byte[] bdaddr, byte[] uid, @Nullable MediaController mediaController) { long qid = ByteBuffer.wrap(uid).getLong(); List<MediaSession.QueueItem> items = mNowPlayingList; if (mediaController == null) { Log.e(TAG, "mediaController is null"); Log.e(TAG, "No mediaController when PlayItem " + qid + " requested"); mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR); return; } Loading @@ -218,19 +229,19 @@ public class AddressedMediaPlayer { mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_INV_ITEM); } void getTotalNumOfItems(byte[] bdaddr, MediaController mediaController) { void getTotalNumOfItems(byte[] bdaddr, @Nullable MediaController mediaController) { if (DEBUG) Log.d(TAG, "getTotalNumOfItems"); List<MediaSession.QueueItem> items = mNowPlayingList; if (items != null) { // We already have the cached list sending the response to remote // We already have the cached list, send the response to remote mMediaInterface.getTotalNumOfItemsRsp( bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, items.size()); return; } if (mediaController == null) { Log.e(TAG, "mediaController = null, sending no available players response"); mMediaInterface.getItemAttrRsp(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY, null); Log.e(TAG, "getTotalNumOfItems with no mediaController, sending no items"); mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, 0); return; } Loading @@ -246,8 +257,9 @@ public class AddressedMediaPlayer { mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, items.size()); } boolean sendTrackChangeWithId(boolean requesting, MediaController mediaController) { if (DEBUG) Log.d(TAG, "sendTrackChangeWithId"); boolean sendTrackChangeWithId(boolean requesting, @Nullable MediaController mediaController) { if (DEBUG) Log.d(TAG, "sendTrackChangeWithId (" + requesting + "): controller " + mediaController); byte[] track; long qid = MediaSession.QueueItem.UNKNOWN_ID; if (mediaController != null) { Loading Loading @@ -299,7 +311,7 @@ public class AddressedMediaPlayer { */ private void getFolderItemsFilterAttr(byte[] bdaddr, AvrcpCmd.FolderItemsCmd folderItemsReqObj, List<MediaSession.QueueItem> items, byte scope, long startItem, long endItem, MediaController mediaController) { @NonNull MediaController mediaController) { if (DEBUG) Log.d(TAG, "getFolderItemsFilterAttr: startItem =" + startItem + ", endItem = " + endItem); Loading Loading @@ -397,7 +409,7 @@ public class AddressedMediaPlayer { } private String getAttrValue( int attr, MediaSession.QueueItem item, MediaController mediaController) { int attr, MediaSession.QueueItem item, @Nullable MediaController mediaController) { String attrValue = null; if (item == null) { if (DEBUG) Log.d(TAG, "getAttrValue received null item"); Loading @@ -405,12 +417,14 @@ public class AddressedMediaPlayer { } try { MediaDescription desc = item.getDescription(); PlaybackState state = mediaController.getPlaybackState(); Bundle extras = desc.getExtras(); if (mediaController != null) { PlaybackState state = mediaController.getPlaybackState(); if (state != null && (item.getQueueId() == state.getActiveQueueItemId())) { if (DEBUG) Log.d(TAG, "getAttrValue: item is active, filling extra data"); extras = fillBundle(mediaController.getMetadata(), extras); } } if (DEBUG) Log.d(TAG, "getAttrValue: item " + item + " : " + desc); switch (attr) { case AvrcpConstants.ATTRID_TITLE: Loading Loading @@ -466,7 +480,7 @@ public class AddressedMediaPlayer { } private void getItemAttrFilterAttr(byte[] bdaddr, AvrcpCmd.ItemAttrCmd mItemAttrReqObj, MediaSession.QueueItem mediaItem, MediaController mediaController) { MediaSession.QueueItem mediaItem, @Nullable MediaController mediaController) { /* Response parameters */ int[] attrIds = null; /* array of attr ids */ String[] attrValues = null; /* array of attr values */ Loading @@ -486,14 +500,15 @@ public class AddressedMediaPlayer { } else { /* get only the requested attribute ids from the request */ for (int idx = 0; idx < mItemAttrReqObj.mNumAttr; idx++) { if (DEBUG) Log.d(TAG, "getAttrValue: attr id[" + idx + "] :" + mItemAttrReqObj.mAttrIDs[idx]); if (DEBUG) Log.d(TAG, "getItemAttrFilterAttr: attr id[" + idx + "] :" + mItemAttrReqObj.mAttrIDs[idx]); attrTempId.add(mItemAttrReqObj.mAttrIDs[idx]); } } } if (DEBUG) Log.d(TAG, "getAttrValue: attr id list size:" + attrTempId.size()); if (DEBUG) Log.d(TAG, "getItemAttrFilterAttr: attr id list size:" + attrTempId.size()); /* lookup and copy values of attributes for ids requested above */ for (int idx = 0; idx < attrTempId.size(); idx++) { /* check if media player provided requested attributes */ Loading android/app/src/com/android/bluetooth/avrcp/Avrcp.java +58 −32 Original line number Diff line number Diff line Loading @@ -112,6 +112,8 @@ public final class Avrcp { private int mAbsVolRetryTimes; private int mSkipAmount; private static final int NO_PLAYER_ID = 0; private int mCurrAddrPlayerID; private int mCurrBrowsePlayerID; private int mLastUsedPlayerID; Loading Loading @@ -256,7 +258,7 @@ public final class Avrcp { mAbsVolThreshold = 0; mVolumeMapping = new HashMap<Integer, Integer>(); sUIDCounter = AvrcpConstants.DEFAULT_UID_COUNTER; mCurrAddrPlayerID = 0; mCurrAddrPlayerID = NO_PLAYER_ID; mCurrBrowsePlayerID = 0; mContext = context; mLastUsedPlayerID = 0; Loading Loading @@ -374,6 +376,11 @@ public final class Avrcp { @Override public void onSessionDestroyed() { Log.v(TAG, "MediaController session destroyed"); if (mMediaController != null) { removeMediaController(mMediaController.getWrappedInstance()); mMediaController.unregisterCallback(mMediaControllerCb); mMediaController = null; } } @Override Loading Loading @@ -933,12 +940,14 @@ public final class Avrcp { } private void updateCurrentMediaState() { MediaAttributes currentAttributes = mMediaAttributes; PlaybackState newState = mCurrentPlayState; if (mMediaController == null) { // Use A2DP state if we don't have a MediaControlller boolean isPlaying = (mA2dpState == BluetoothA2dp.STATE_PLAYING); boolean isPlaying = (mA2dpState == BluetoothA2dp.STATE_PLAYING) && mAudioManager.isMusicActive(); if (isPlaying != isPlayingState(mCurrentPlayState)) { /* if a2dp is streaming, check to make sure music is active */ if (isPlaying && !mAudioManager.isMusicActive()) return; PlaybackState.Builder builder = new PlaybackState.Builder(); if (isPlaying) { builder.setState(PlaybackState.STATE_PLAYING, Loading @@ -947,26 +956,21 @@ public final class Avrcp { builder.setState(PlaybackState.STATE_PAUSED, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f); } updatePlaybackState(builder.build()); } // Can't get metadata from A2dp so we're done. return; newState = builder.build(); } MediaAttributes currentAttributes = mMediaAttributes; PlaybackState newState = mMediaController.getPlaybackState(); // Metadata mMediaAttributes = new MediaAttributes(null); } else { newState = mMediaController.getPlaybackState(); mMediaAttributes = new MediaAttributes(mMediaController.getMetadata()); } if (currentAttributes.equals(mMediaAttributes)) { if (!currentAttributes.equals(mMediaAttributes)) { Log.v(TAG, "MediaAttributes Changed to " + mMediaAttributes.toString()); mTracksPlayed++; sendTrackChangedRsp(false); } updatePlaybackState(mMediaController.getPlaybackState()); updatePlaybackState(newState); } private void getRcFeaturesRequestFromNative(byte[] address, int features) { Loading Loading @@ -1409,6 +1413,7 @@ public final class Avrcp { if (DEBUG) Log.d(TAG, "packageName: " + packageName + " removed: " + removed); if (removed) { removeMediaPlayerInfo(packageName); // old package is removed, updating local browsable player's list if (isBrowseSupported(packageName)) { removePackageFromBrowseList(packageName); Loading Loading @@ -1562,14 +1567,18 @@ public final class Avrcp { getMediaControllers(); for (android.media.session.MediaController controller : currentControllers) { if (!newControllers.contains(controller)) { removeMediaPlayerInfo(controller.getPackageName()); playersChanged = true; removeMediaController(controller); if (mMediaController != null && mMediaController.equals(controller)) { if (DEBUG) Log.v(TAG, "Active Controller is gone!"); mMediaController.unregisterCallback(mMediaControllerCb); mMediaController = null; } } } if (playersChanged) { mHandler.sendEmptyMessage(MSG_AVAILABLE_PLAYERS_CHANGED_RSP); if (newControllers.size() > 0 && (mMediaController == null)) { if (newControllers.size() > 0 && getAddressedPlayerInfo() == null) { if (DEBUG) Log.v(TAG, "No addressed player but active sessions, taking first."); Loading Loading @@ -1794,7 +1803,6 @@ public final class Avrcp { * @return true if an item was updated, false if it was added instead */ private boolean addMediaPlayerInfo(MediaPlayerInfo info) { if (DEBUG) Log.d(TAG, "add " + info.toString()); int updateId = -1; boolean updated = false; synchronized (mMediaPlayerInfoList) { Loading @@ -1809,10 +1817,13 @@ public final class Avrcp { // New player mLastUsedPlayerID++; updateId = mLastUsedPlayerID; } else if (updateId == mCurrAddrPlayerID) { updateCurrentController(mCurrAddrPlayerID, mCurrBrowsePlayerID); } mMediaPlayerInfoList.put(updateId, info); if (DEBUG) Log.d(TAG, (updated ? "update #" : "add #") + updateId + ":" + info.toString()); if (updateId == mCurrAddrPlayerID) { updateCurrentController(mCurrAddrPlayerID, mCurrBrowsePlayerID); } } return updated; } Loading @@ -1828,6 +1839,8 @@ public final class Avrcp { } } if (removeKey != -1) { if (DEBUG) Log.d(TAG, "remove #" + removeKey + ":" + mMediaPlayerInfoList.get(removeKey)); return mMediaPlayerInfoList.remove(removeKey); } Loading @@ -1835,6 +1848,15 @@ public final class Avrcp { } } /** Remove the controller referenced by |controller| from any player in the list */ private void removeMediaController(android.media.session.MediaController controller) { synchronized (mMediaPlayerInfoList) { for (MediaPlayerInfo info : mMediaPlayerInfoList.values()) { if (info.getMediaController().equals(controller)) info.setMediaController(null); } } } /* * utility function to get the playback state of any media player through * media controller APIs. Loading Loading @@ -2096,11 +2118,10 @@ public final class Avrcp { if (DEBUG) Log.d(TAG, "updateCurrentController: " + mMediaController + " to " + newController); if (mMediaController == null || newController == null || (mMediaController.getWrappedInstance() != newController.getWrappedInstance())) { if (mMediaController == null || (!mMediaController.equals(newController))) { if (mMediaController != null) mMediaController.unregisterCallback(mMediaControllerCb); if (newController != null) { mMediaController = newController; if (mMediaController != null) { mMediaController.registerCallback(mMediaControllerCb, mHandler); mAddressedMediaPlayer.updateNowPlayingList(mMediaController.getQueue()); } else { Loading Loading @@ -2184,12 +2205,16 @@ public final class Avrcp { private void handleGetItemAttr(AvrcpCmd.ItemAttrCmd itemAttr) { if (itemAttr.mScope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) { if (mCurrAddrPlayerID == NO_PLAYER_ID) { getItemAttrRspNative( itemAttr.mAddress, AvrcpConstants.RSP_NO_AVBL_PLAY, (byte) 0, null, null); } else { mAddressedMediaPlayer.getItemAttr(itemAttr.mAddress, itemAttr, mMediaController); } else { if (mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress) != null) } else { if (mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress) != null) { mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress).getItemAttr(itemAttr); else { } else { Log.e(TAG, "Could not get attributes. mBrowsedMediaPlayer is null"); getItemAttrRspNative(itemAttr.mAddress, AvrcpConstants.RSP_INTERNAL_ERR, (byte) 0, null, null); Loading Loading @@ -2279,7 +2304,8 @@ public final class Avrcp { ProfileService.println(sb, "mSkipAmount: " + mSkipAmount); ProfileService.println(sb, "mVolumeMapping: " + mVolumeMapping.toString()); if (mMediaController != null) ProfileService.println(sb, "mMediaSession pkg: " + mMediaController.getPackageName()); ProfileService.println(sb, "mMediaController: " + mMediaController.getWrappedInstance() + " pkg " + mMediaController.getPackageName()); ProfileService.println(sb, "\nMedia Players:"); synchronized (mMediaPlayerInfoList) { Loading android/app/src/com/android/bluetooth/avrcp/mockable/MediaController.java +16 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,22 @@ public class MediaController { return mDelegate.controlsSameSession(other); } @Override public boolean equals(Object o) { if (o instanceof android.media.session.MediaController) { return mDelegate.equals(o); } else if (o instanceof MediaController) { MediaController other = (MediaController) o; return mDelegate.equals(other.mDelegate); } return false; } @Override public String toString() { return super.toString() + "(wraps " + mDelegate + ")"; } public static abstract class Callback extends android.media.session.MediaController.Callback { } public class TransportControls { Loading Loading
android/app/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java +46 −31 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.bluetooth.avrcp; import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothAvrcp; import android.media.session.MediaSession; import android.media.session.PlaybackState; Loading @@ -42,15 +44,23 @@ public class AddressedMediaPlayer { static private final String TAG = "AddressedMediaPlayer"; static private final Boolean DEBUG = false; static private final long SINGLE_QID = 1; static private final String UNKNOWN_TITLE = "(unknown)"; private AvrcpMediaRspInterface mMediaInterface; private List<MediaSession.QueueItem> mNowPlayingList; private final List<MediaSession.QueueItem> mUnknownNowPlayingList; private long mLastTrackIdSent; public AddressedMediaPlayer(AvrcpMediaRspInterface mediaInterface) { mNowPlayingList = null; mMediaInterface = mediaInterface; mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID; List<MediaSession.QueueItem> unknown = new ArrayList<MediaSession.QueueItem>(); unknown.add(getCurrentQueueItem(null, SINGLE_QID)); mUnknownNowPlayingList = unknown; } void cleanup() { Loading @@ -62,7 +72,7 @@ public class AddressedMediaPlayer { /* get now playing list from addressed player */ void getFolderItemsNowPlaying(byte[] bdaddr, AvrcpCmd.FolderItemsCmd reqObj, MediaController mediaController) { @Nullable MediaController mediaController) { if (DEBUG) Log.v(TAG, "getFolderItemsNowPlaying"); if (mediaController == null) { // No players (if a player exists, we would have selected it) Loading @@ -77,7 +87,7 @@ public class AddressedMediaPlayer { /* get item attributes for item in now playing list */ void getItemAttr(byte[] bdaddr, AvrcpCmd.ItemAttrCmd itemAttr, MediaController mediaController) { @Nullable MediaController mediaController) { int status = AvrcpConstants.RSP_NO_ERROR; long mediaId = ByteBuffer.wrap(itemAttr.mUid).getLong(); List<MediaSession.QueueItem> items = getNowPlayingList(mediaController); Loading @@ -87,12 +97,6 @@ public class AddressedMediaPlayer { if (Arrays.equals(itemAttr.mUid, AvrcpConstants.TRACK_IS_SELECTED)) { if (DEBUG) Log.d(TAG, "getItemAttr: Remote requests for now playing contents:"); if (mediaController == null) { Log.e(TAG, "mediaController = null, sending no available players response"); mMediaInterface.getItemAttrRsp(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY, null); return; } // get the current playing metadata and send. getItemAttrFilterAttr(bdaddr, itemAttr, getCurrentQueueItem(mediaController, mediaId), mediaController); Loading @@ -113,10 +117,10 @@ public class AddressedMediaPlayer { /* Refresh and get the queue of now playing. */ private List<MediaSession.QueueItem> getNowPlayingList(MediaController mediaController) { if (mediaController == null) return null; private List<MediaSession.QueueItem> getNowPlayingList( @Nullable MediaController mediaController) { if (mediaController == null) return mUnknownNowPlayingList; if (mNowPlayingList != null) return mNowPlayingList; List<MediaSession.QueueItem> items = mediaController.getQueue(); if (items == null) { Log.i(TAG, "null queue from " + mediaController.getPackageName() Loading @@ -124,7 +128,7 @@ public class AddressedMediaPlayer { MediaMetadata metadata = mediaController.getMetadata(); // Because we are database-unaware, we can just number the item here whatever we want // because they have to re-poll it every time. MediaSession.QueueItem current = getCurrentQueueItem(mediaController, 1); MediaSession.QueueItem current = getCurrentQueueItem(mediaController, SINGLE_QID); items = new ArrayList<MediaSession.QueueItem>(); items.add(current); } Loading @@ -135,7 +139,14 @@ public class AddressedMediaPlayer { /* Constructs a queue item representing the current playing metadata from an * active controller with queue id |qid|. */ private MediaSession.QueueItem getCurrentQueueItem(MediaController controller, long qid) { private MediaSession.QueueItem getCurrentQueueItem( @Nullable MediaController controller, long qid) { if (controller == null) { MediaDescription.Builder bob = new MediaDescription.Builder(); bob.setTitle(UNKNOWN_TITLE); return new QueueItem(bob.build(), qid); } MediaMetadata metadata = controller.getMetadata(); if (metadata == null) { Log.w(TAG, "Controller has no metadata!? Making an empty one"); Loading Loading @@ -186,12 +197,12 @@ public class AddressedMediaPlayer { } /* Instructs media player to play particular media item */ void playItem(byte[] bdaddr, byte[] uid, MediaController mediaController) { void playItem(byte[] bdaddr, byte[] uid, @Nullable MediaController mediaController) { long qid = ByteBuffer.wrap(uid).getLong(); List<MediaSession.QueueItem> items = mNowPlayingList; if (mediaController == null) { Log.e(TAG, "mediaController is null"); Log.e(TAG, "No mediaController when PlayItem " + qid + " requested"); mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR); return; } Loading @@ -218,19 +229,19 @@ public class AddressedMediaPlayer { mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_INV_ITEM); } void getTotalNumOfItems(byte[] bdaddr, MediaController mediaController) { void getTotalNumOfItems(byte[] bdaddr, @Nullable MediaController mediaController) { if (DEBUG) Log.d(TAG, "getTotalNumOfItems"); List<MediaSession.QueueItem> items = mNowPlayingList; if (items != null) { // We already have the cached list sending the response to remote // We already have the cached list, send the response to remote mMediaInterface.getTotalNumOfItemsRsp( bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, items.size()); return; } if (mediaController == null) { Log.e(TAG, "mediaController = null, sending no available players response"); mMediaInterface.getItemAttrRsp(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY, null); Log.e(TAG, "getTotalNumOfItems with no mediaController, sending no items"); mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, 0); return; } Loading @@ -246,8 +257,9 @@ public class AddressedMediaPlayer { mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, items.size()); } boolean sendTrackChangeWithId(boolean requesting, MediaController mediaController) { if (DEBUG) Log.d(TAG, "sendTrackChangeWithId"); boolean sendTrackChangeWithId(boolean requesting, @Nullable MediaController mediaController) { if (DEBUG) Log.d(TAG, "sendTrackChangeWithId (" + requesting + "): controller " + mediaController); byte[] track; long qid = MediaSession.QueueItem.UNKNOWN_ID; if (mediaController != null) { Loading Loading @@ -299,7 +311,7 @@ public class AddressedMediaPlayer { */ private void getFolderItemsFilterAttr(byte[] bdaddr, AvrcpCmd.FolderItemsCmd folderItemsReqObj, List<MediaSession.QueueItem> items, byte scope, long startItem, long endItem, MediaController mediaController) { @NonNull MediaController mediaController) { if (DEBUG) Log.d(TAG, "getFolderItemsFilterAttr: startItem =" + startItem + ", endItem = " + endItem); Loading Loading @@ -397,7 +409,7 @@ public class AddressedMediaPlayer { } private String getAttrValue( int attr, MediaSession.QueueItem item, MediaController mediaController) { int attr, MediaSession.QueueItem item, @Nullable MediaController mediaController) { String attrValue = null; if (item == null) { if (DEBUG) Log.d(TAG, "getAttrValue received null item"); Loading @@ -405,12 +417,14 @@ public class AddressedMediaPlayer { } try { MediaDescription desc = item.getDescription(); PlaybackState state = mediaController.getPlaybackState(); Bundle extras = desc.getExtras(); if (mediaController != null) { PlaybackState state = mediaController.getPlaybackState(); if (state != null && (item.getQueueId() == state.getActiveQueueItemId())) { if (DEBUG) Log.d(TAG, "getAttrValue: item is active, filling extra data"); extras = fillBundle(mediaController.getMetadata(), extras); } } if (DEBUG) Log.d(TAG, "getAttrValue: item " + item + " : " + desc); switch (attr) { case AvrcpConstants.ATTRID_TITLE: Loading Loading @@ -466,7 +480,7 @@ public class AddressedMediaPlayer { } private void getItemAttrFilterAttr(byte[] bdaddr, AvrcpCmd.ItemAttrCmd mItemAttrReqObj, MediaSession.QueueItem mediaItem, MediaController mediaController) { MediaSession.QueueItem mediaItem, @Nullable MediaController mediaController) { /* Response parameters */ int[] attrIds = null; /* array of attr ids */ String[] attrValues = null; /* array of attr values */ Loading @@ -486,14 +500,15 @@ public class AddressedMediaPlayer { } else { /* get only the requested attribute ids from the request */ for (int idx = 0; idx < mItemAttrReqObj.mNumAttr; idx++) { if (DEBUG) Log.d(TAG, "getAttrValue: attr id[" + idx + "] :" + mItemAttrReqObj.mAttrIDs[idx]); if (DEBUG) Log.d(TAG, "getItemAttrFilterAttr: attr id[" + idx + "] :" + mItemAttrReqObj.mAttrIDs[idx]); attrTempId.add(mItemAttrReqObj.mAttrIDs[idx]); } } } if (DEBUG) Log.d(TAG, "getAttrValue: attr id list size:" + attrTempId.size()); if (DEBUG) Log.d(TAG, "getItemAttrFilterAttr: attr id list size:" + attrTempId.size()); /* lookup and copy values of attributes for ids requested above */ for (int idx = 0; idx < attrTempId.size(); idx++) { /* check if media player provided requested attributes */ Loading
android/app/src/com/android/bluetooth/avrcp/Avrcp.java +58 −32 Original line number Diff line number Diff line Loading @@ -112,6 +112,8 @@ public final class Avrcp { private int mAbsVolRetryTimes; private int mSkipAmount; private static final int NO_PLAYER_ID = 0; private int mCurrAddrPlayerID; private int mCurrBrowsePlayerID; private int mLastUsedPlayerID; Loading Loading @@ -256,7 +258,7 @@ public final class Avrcp { mAbsVolThreshold = 0; mVolumeMapping = new HashMap<Integer, Integer>(); sUIDCounter = AvrcpConstants.DEFAULT_UID_COUNTER; mCurrAddrPlayerID = 0; mCurrAddrPlayerID = NO_PLAYER_ID; mCurrBrowsePlayerID = 0; mContext = context; mLastUsedPlayerID = 0; Loading Loading @@ -374,6 +376,11 @@ public final class Avrcp { @Override public void onSessionDestroyed() { Log.v(TAG, "MediaController session destroyed"); if (mMediaController != null) { removeMediaController(mMediaController.getWrappedInstance()); mMediaController.unregisterCallback(mMediaControllerCb); mMediaController = null; } } @Override Loading Loading @@ -933,12 +940,14 @@ public final class Avrcp { } private void updateCurrentMediaState() { MediaAttributes currentAttributes = mMediaAttributes; PlaybackState newState = mCurrentPlayState; if (mMediaController == null) { // Use A2DP state if we don't have a MediaControlller boolean isPlaying = (mA2dpState == BluetoothA2dp.STATE_PLAYING); boolean isPlaying = (mA2dpState == BluetoothA2dp.STATE_PLAYING) && mAudioManager.isMusicActive(); if (isPlaying != isPlayingState(mCurrentPlayState)) { /* if a2dp is streaming, check to make sure music is active */ if (isPlaying && !mAudioManager.isMusicActive()) return; PlaybackState.Builder builder = new PlaybackState.Builder(); if (isPlaying) { builder.setState(PlaybackState.STATE_PLAYING, Loading @@ -947,26 +956,21 @@ public final class Avrcp { builder.setState(PlaybackState.STATE_PAUSED, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f); } updatePlaybackState(builder.build()); } // Can't get metadata from A2dp so we're done. return; newState = builder.build(); } MediaAttributes currentAttributes = mMediaAttributes; PlaybackState newState = mMediaController.getPlaybackState(); // Metadata mMediaAttributes = new MediaAttributes(null); } else { newState = mMediaController.getPlaybackState(); mMediaAttributes = new MediaAttributes(mMediaController.getMetadata()); } if (currentAttributes.equals(mMediaAttributes)) { if (!currentAttributes.equals(mMediaAttributes)) { Log.v(TAG, "MediaAttributes Changed to " + mMediaAttributes.toString()); mTracksPlayed++; sendTrackChangedRsp(false); } updatePlaybackState(mMediaController.getPlaybackState()); updatePlaybackState(newState); } private void getRcFeaturesRequestFromNative(byte[] address, int features) { Loading Loading @@ -1409,6 +1413,7 @@ public final class Avrcp { if (DEBUG) Log.d(TAG, "packageName: " + packageName + " removed: " + removed); if (removed) { removeMediaPlayerInfo(packageName); // old package is removed, updating local browsable player's list if (isBrowseSupported(packageName)) { removePackageFromBrowseList(packageName); Loading Loading @@ -1562,14 +1567,18 @@ public final class Avrcp { getMediaControllers(); for (android.media.session.MediaController controller : currentControllers) { if (!newControllers.contains(controller)) { removeMediaPlayerInfo(controller.getPackageName()); playersChanged = true; removeMediaController(controller); if (mMediaController != null && mMediaController.equals(controller)) { if (DEBUG) Log.v(TAG, "Active Controller is gone!"); mMediaController.unregisterCallback(mMediaControllerCb); mMediaController = null; } } } if (playersChanged) { mHandler.sendEmptyMessage(MSG_AVAILABLE_PLAYERS_CHANGED_RSP); if (newControllers.size() > 0 && (mMediaController == null)) { if (newControllers.size() > 0 && getAddressedPlayerInfo() == null) { if (DEBUG) Log.v(TAG, "No addressed player but active sessions, taking first."); Loading Loading @@ -1794,7 +1803,6 @@ public final class Avrcp { * @return true if an item was updated, false if it was added instead */ private boolean addMediaPlayerInfo(MediaPlayerInfo info) { if (DEBUG) Log.d(TAG, "add " + info.toString()); int updateId = -1; boolean updated = false; synchronized (mMediaPlayerInfoList) { Loading @@ -1809,10 +1817,13 @@ public final class Avrcp { // New player mLastUsedPlayerID++; updateId = mLastUsedPlayerID; } else if (updateId == mCurrAddrPlayerID) { updateCurrentController(mCurrAddrPlayerID, mCurrBrowsePlayerID); } mMediaPlayerInfoList.put(updateId, info); if (DEBUG) Log.d(TAG, (updated ? "update #" : "add #") + updateId + ":" + info.toString()); if (updateId == mCurrAddrPlayerID) { updateCurrentController(mCurrAddrPlayerID, mCurrBrowsePlayerID); } } return updated; } Loading @@ -1828,6 +1839,8 @@ public final class Avrcp { } } if (removeKey != -1) { if (DEBUG) Log.d(TAG, "remove #" + removeKey + ":" + mMediaPlayerInfoList.get(removeKey)); return mMediaPlayerInfoList.remove(removeKey); } Loading @@ -1835,6 +1848,15 @@ public final class Avrcp { } } /** Remove the controller referenced by |controller| from any player in the list */ private void removeMediaController(android.media.session.MediaController controller) { synchronized (mMediaPlayerInfoList) { for (MediaPlayerInfo info : mMediaPlayerInfoList.values()) { if (info.getMediaController().equals(controller)) info.setMediaController(null); } } } /* * utility function to get the playback state of any media player through * media controller APIs. Loading Loading @@ -2096,11 +2118,10 @@ public final class Avrcp { if (DEBUG) Log.d(TAG, "updateCurrentController: " + mMediaController + " to " + newController); if (mMediaController == null || newController == null || (mMediaController.getWrappedInstance() != newController.getWrappedInstance())) { if (mMediaController == null || (!mMediaController.equals(newController))) { if (mMediaController != null) mMediaController.unregisterCallback(mMediaControllerCb); if (newController != null) { mMediaController = newController; if (mMediaController != null) { mMediaController.registerCallback(mMediaControllerCb, mHandler); mAddressedMediaPlayer.updateNowPlayingList(mMediaController.getQueue()); } else { Loading Loading @@ -2184,12 +2205,16 @@ public final class Avrcp { private void handleGetItemAttr(AvrcpCmd.ItemAttrCmd itemAttr) { if (itemAttr.mScope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) { if (mCurrAddrPlayerID == NO_PLAYER_ID) { getItemAttrRspNative( itemAttr.mAddress, AvrcpConstants.RSP_NO_AVBL_PLAY, (byte) 0, null, null); } else { mAddressedMediaPlayer.getItemAttr(itemAttr.mAddress, itemAttr, mMediaController); } else { if (mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress) != null) } else { if (mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress) != null) { mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress).getItemAttr(itemAttr); else { } else { Log.e(TAG, "Could not get attributes. mBrowsedMediaPlayer is null"); getItemAttrRspNative(itemAttr.mAddress, AvrcpConstants.RSP_INTERNAL_ERR, (byte) 0, null, null); Loading Loading @@ -2279,7 +2304,8 @@ public final class Avrcp { ProfileService.println(sb, "mSkipAmount: " + mSkipAmount); ProfileService.println(sb, "mVolumeMapping: " + mVolumeMapping.toString()); if (mMediaController != null) ProfileService.println(sb, "mMediaSession pkg: " + mMediaController.getPackageName()); ProfileService.println(sb, "mMediaController: " + mMediaController.getWrappedInstance() + " pkg " + mMediaController.getPackageName()); ProfileService.println(sb, "\nMedia Players:"); synchronized (mMediaPlayerInfoList) { Loading
android/app/src/com/android/bluetooth/avrcp/mockable/MediaController.java +16 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,22 @@ public class MediaController { return mDelegate.controlsSameSession(other); } @Override public boolean equals(Object o) { if (o instanceof android.media.session.MediaController) { return mDelegate.equals(o); } else if (o instanceof MediaController) { MediaController other = (MediaController) o; return mDelegate.equals(other.mDelegate); } return false; } @Override public String toString() { return super.toString() + "(wraps " + mDelegate + ")"; } public static abstract class Callback extends android.media.session.MediaController.Callback { } public class TransportControls { Loading