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;
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() {
/* 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)
/* 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);
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);
/* 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()
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);
}
/* 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");
}
/* 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;
}
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;
}
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) {
*/
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);
}
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");
}
try {
MediaDescription desc = item.getDescription();
- PlaybackState state = mediaController.getPlaybackState();
Bundle extras = desc.getExtras();
- 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 (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) {
}
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 */
} 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 */
private int mAbsVolRetryTimes;
private int mSkipAmount;
+ private static final int NO_PLAYER_ID = 0;
+
private int mCurrAddrPlayerID;
private int mCurrBrowsePlayerID;
private int mLastUsedPlayerID;
mAbsVolThreshold = 0;
mVolumeMapping = new HashMap<Integer, Integer>();
sUIDCounter = AvrcpConstants.DEFAULT_UID_COUNTER;
- mCurrAddrPlayerID = 0;
+ mCurrAddrPlayerID = NO_PLAYER_ID;
mCurrBrowsePlayerID = 0;
mContext = context;
mLastUsedPlayerID = 0;
@Override
public void onSessionDestroyed() {
Log.v(TAG, "MediaController session destroyed");
+ if (mMediaController != null) {
+ removeMediaController(mMediaController.getWrappedInstance());
+ mMediaController.unregisterCallback(mMediaControllerCb);
+ mMediaController = null;
+ }
}
@Override
}
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,
builder.setState(PlaybackState.STATE_PAUSED,
PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f);
}
- updatePlaybackState(builder.build());
+ newState = builder.build();
}
- // Can't get metadata from A2dp so we're done.
- return;
+ mMediaAttributes = new MediaAttributes(null);
+ } else {
+ newState = mMediaController.getPlaybackState();
+ mMediaAttributes = new MediaAttributes(mMediaController.getMetadata());
}
- MediaAttributes currentAttributes = mMediaAttributes;
-
- PlaybackState newState = mMediaController.getPlaybackState();
-
- // Metadata
- 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) {
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);
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.");
* @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) {
// 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;
}
}
}
if (removeKey != -1) {
+ if (DEBUG)
+ Log.d(TAG, "remove #" + removeKey + ":" + mMediaPlayerInfoList.get(removeKey));
return mMediaPlayerInfoList.remove(removeKey);
}
}
}
+ /** 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.
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;
+ mMediaController = newController;
+ if (mMediaController != null) {
mMediaController.registerCallback(mMediaControllerCb, mHandler);
mAddressedMediaPlayer.updateNowPlayingList(mMediaController.getQueue());
} else {
}
private void handleGetItemAttr(AvrcpCmd.ItemAttrCmd itemAttr) {
- if(itemAttr.mScope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
- mAddressedMediaPlayer.getItemAttr(itemAttr.mAddress, itemAttr, mMediaController);
- }
- else {
- if (mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress) != null)
+ 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) {
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);
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) {