Merge cherrypicks of [2455372, 2455337, 2455612, 2455497] into oc-release
Change-Id: I05e7158e88bbdba30984eac8f4ab0eaf8cb0a55c
diff --git a/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java b/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java
index 28a901a..8c94027 100644
--- a/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java
+++ b/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java
@@ -54,12 +54,14 @@
private final List<MediaSession.QueueItem> mEmptyNowPlayingList;
private long mLastTrackIdSent;
+ private boolean mNowPlayingListUpdated;
public AddressedMediaPlayer(AvrcpMediaRspInterface mediaInterface) {
mEmptyNowPlayingList = new ArrayList<MediaSession.QueueItem>();
mNowPlayingList = mEmptyNowPlayingList;
mMediaInterface = mediaInterface;
mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID;
+ mNowPlayingListUpdated = false;
}
void cleanup() {
@@ -67,12 +69,12 @@
mNowPlayingList = mEmptyNowPlayingList;
mMediaInterface = null;
mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID;
+ mNowPlayingListUpdated = false;
}
/* get now playing list from addressed player */
void getFolderItemsNowPlaying(byte[] bdaddr, AvrcpCmd.FolderItemsCmd reqObj,
@Nullable MediaController mediaController) {
- if (DEBUG) Log.v(TAG, "getFolderItemsNowPlaying");
if (mediaController == null) {
// No players (if a player exists, we would have selected it)
Log.e(TAG, "mediaController = null, sending no available players response");
@@ -120,7 +122,10 @@
@Nullable MediaController mediaController) {
if (mediaController == null) return mEmptyNowPlayingList;
List<MediaSession.QueueItem> items = mediaController.getQueue();
- if (items == mNowPlayingList) return mNowPlayingList;
+ if (items != null && !mNowPlayingListUpdated) {
+ mNowPlayingList = items;
+ return mNowPlayingList;
+ }
if (items == null) {
Log.i(TAG, "null queue from " + mediaController.getPackageName()
+ ", constructing single-item list");
@@ -131,12 +136,19 @@
items = new ArrayList<MediaSession.QueueItem>();
items.add(current);
}
+
mNowPlayingList = items;
- // TODO (jamuraa): test to see if the single-item queue is the same and don't send
- if (mMediaInterface != null) {
- mMediaInterface.nowPlayingChangedRsp(AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
- }
- return items;
+
+ if (mNowPlayingListUpdated) sendNowPlayingListChanged();
+
+ return mNowPlayingList;
+ }
+
+ private void sendNowPlayingListChanged() {
+ if (mMediaInterface == null) return;
+ mMediaInterface.uidsChangedRsp(AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
+ mMediaInterface.nowPlayingChangedRsp(AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
+ mNowPlayingListUpdated = false;
}
/* Constructs a queue item representing the current playing metadata from an
@@ -196,6 +208,7 @@
}
void updateNowPlayingList(@Nullable MediaController mediaController) {
+ mNowPlayingListUpdated = true;
getNowPlayingList(mediaController);
}
@@ -239,14 +252,13 @@
}
void sendTrackChangeWithId(int type, @Nullable MediaController mediaController) {
- if (DEBUG)
- Log.d(TAG, "sendTrackChangeWithId (" + type + "): controller " + mediaController);
+ Log.d(TAG, "sendTrackChangeWithId (" + type + "): controller " + mediaController);
long qid = getActiveQueueItemId(mediaController);
byte[] track = ByteBuffer.allocate(AvrcpConstants.UID_SIZE).putLong(qid).array();
+ // The nowPlayingList changed: the new list has the full data for the current item
+ if (type == AvrcpConstants.NOTIFICATION_TYPE_CHANGED) sendNowPlayingListChanged();
mMediaInterface.trackChangedRsp(type, track);
mLastTrackIdSent = qid;
- // The nowPlaying might have changed.
- updateNowPlayingList(mediaController);
}
/*
diff --git a/src/com/android/bluetooth/avrcp/Avrcp.java b/src/com/android/bluetooth/avrcp/Avrcp.java
index 9c286e2..712b2a8 100644
--- a/src/com/android/bluetooth/avrcp/Avrcp.java
+++ b/src/com/android/bluetooth/avrcp/Avrcp.java
@@ -86,8 +86,11 @@
private @NonNull PlaybackState mCurrentPlayState;
private int mA2dpState;
private int mPlayStatusChangedNT;
+ private int mReportedPlayStatus;
private int mTrackChangedNT;
private int mPlayPosChangedNT;
+ private int mAddrPlayerChangedNT;
+ private int mReportedPlayerID;
private long mPlaybackIntervalMs;
private long mLastReportedPosition;
private long mNextPosMs;
@@ -121,7 +124,7 @@
private AvrcpMediaRsp mAvrcpMediaRsp;
/* UID counter to be shared across different files. */
- static short sUIDCounter;
+ static short sUIDCounter = AvrcpConstants.DEFAULT_UID_COUNTER;
/* BTRC features */
public static final int BTRC_FEAT_METADATA = 0x01;
@@ -157,14 +160,12 @@
private static final int MSG_ADJUST_VOLUME = 15;
private static final int MSG_SET_ABSOLUTE_VOLUME = 16;
private static final int MSG_ABS_VOL_TIMEOUT = 17;
- private static final int MSG_FAST_FORWARD = 18;
- private static final int MSG_REWIND = 19;
- private static final int MSG_SET_A2DP_AUDIO_STATE = 20;
- private static final int MSG_ADDRESSED_PLAYER_CHANGED_RSP = 21;
- private static final int MSG_AVAILABLE_PLAYERS_CHANGED_RSP = 22;
- private static final int MSG_NOW_PLAYING_CHANGED_RSP = 23;
+ private static final int MSG_SET_A2DP_AUDIO_STATE = 18;
+ private static final int MSG_NOW_PLAYING_CHANGED_RSP = 19;
+ private static final int MSG_UPDATE_MEDIA = 20;
private static final int CMD_TIMEOUT_DELAY = 2000;
+ private static final int MEDIA_DWELL_TIME = 1000;
private static final int MAX_ERROR_RETRY_TIMES = 6;
private static final int AVRCP_MAX_VOL = 127;
private static final int AVRCP_BASE_VOLUME_STEP = 1;
@@ -177,6 +178,7 @@
/* List of Media player instances, useful for retrieving MediaPlayerList or MediaPlayerInfo */
private SortedMap<Integer, MediaPlayerInfo> mMediaPlayerInfoList;
+ private boolean mAvailablePlayerViewChanged;
/* List of media players which supports browse */
private List<BrowsePlayerInfo> mBrowsePlayerInfoList;
@@ -236,11 +238,13 @@
mMediaAttributes = new MediaAttributes(null);
mLastQueueId = MediaSession.QueueItem.UNKNOWN_ID;
mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();
+ mReportedPlayStatus = PLAYSTATUS_ERROR;
mA2dpState = BluetoothA2dp.STATE_NOT_PLAYING;
mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- mPlaybackIntervalMs = 0L;
mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ mPlaybackIntervalMs = 0L;
mLastReportedPosition = -1;
mNextPosMs = -1;
mPrevPosMs = -1;
@@ -256,8 +260,8 @@
mLastLocalVolume = -1;
mAbsVolThreshold = 0;
mVolumeMapping = new HashMap<Integer, Integer>();
- sUIDCounter = AvrcpConstants.DEFAULT_UID_COUNTER;
mCurrAddrPlayerID = NO_PLAYER_ID;
+ mReportedPlayerID = mCurrAddrPlayerID;
mCurrBrowsePlayerID = 0;
mContext = context;
mLastUsedPlayerID = 0;
@@ -300,6 +304,7 @@
mMediaControllerCb = new MediaControllerListener();
mAvrcpMediaRsp = new AvrcpMediaRsp();
mMediaPlayerInfoList = new TreeMap<Integer, MediaPlayerInfo>();
+ mAvailablePlayerViewChanged = false;
mBrowsePlayerInfoList = Collections.synchronizedList(new ArrayList<BrowsePlayerInfo>());
mPassthroughDispatched = 0;
mPassthroughLogs = new EvictingQueue<MediaKeyLog>(PASSTHROUGH_LOG_MAX_SIZE);
@@ -368,12 +373,12 @@
@Override
public void onMetadataChanged(MediaMetadata metadata) {
if (DEBUG) Log.v(TAG, "onMetadataChanged");
- updateCurrentMediaState(false);
+ scheduleMediaUpdate();
}
@Override
public synchronized void onPlaybackStateChanged(PlaybackState state) {
if (DEBUG) Log.v(TAG, "onPlaybackStateChanged: state " + state.toString());
- updateCurrentMediaState(false);
+ scheduleMediaUpdate();
}
@Override
@@ -427,7 +432,7 @@
case MSG_NATIVE_REQ_GET_PLAY_STATUS:
{
byte[] address = (byte[]) msg.obj;
- int btstate = convertPlayStateToPlayStatus(mCurrentPlayState);
+ int btstate = getBluetoothPlayState(mCurrentPlayState);
int length = (int) mMediaAttributes.getLength();
int position = (int) getPlayPosition();
if (DEBUG)
@@ -470,31 +475,12 @@
processRegisterNotification((byte[]) msg.obj, msg.arg1, msg.arg2);
break;
- case MSG_AVAILABLE_PLAYERS_CHANGED_RSP:
- if (DEBUG) Log.v(TAG, "MSG_AVAILABLE_PLAYERS_CHANGED_RSP");
- removeMessages(MSG_AVAILABLE_PLAYERS_CHANGED_RSP);
- registerNotificationRspAvalPlayerChangedNative(
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
- break;
-
case MSG_NOW_PLAYING_CHANGED_RSP:
if (DEBUG) Log.v(TAG, "MSG_NOW_PLAYING_CHANGED_RSP");
removeMessages(MSG_NOW_PLAYING_CHANGED_RSP);
mAddressedMediaPlayer.updateNowPlayingList(mMediaController);
break;
- case MSG_ADDRESSED_PLAYER_CHANGED_RSP:
- // Later addressed players override earlier ones.
- if (hasMessages(MSG_ADDRESSED_PLAYER_CHANGED_RSP)) {
- Log.i(TAG, "MSG_ADDRESSED_PLAYER_CHANGED_RSP: skip, more changes in queue");
- break;
- }
- if (DEBUG)
- Log.v(TAG, "MSG_ADDRESSED_PLAYER_CHANGED_RSP: newAddrPlayer = " + msg.arg1);
- registerNotificationRspAddrPlayerChangedNative(
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED, msg.arg1, sUIDCounter);
- break;
-
case MSG_PLAY_INTERVAL_TIMEOUT:
sendPlayPosNotificationRsp(false);
break;
@@ -706,7 +692,7 @@
case MSG_SET_A2DP_AUDIO_STATE:
if (DEBUG) Log.v(TAG, "MSG_SET_A2DP_AUDIO_STATE:" + msg.arg1);
mA2dpState = msg.arg1;
- updateCurrentMediaState(false);
+ scheduleMediaUpdate();
break;
case MSG_NATIVE_REQ_GET_FOLDER_ITEMS: {
@@ -792,6 +778,13 @@
handlePassthroughCmd(msg.arg1, msg.arg2);
break;
+ case MSG_UPDATE_MEDIA:
+ if (DEBUG) Log.v(TAG, "MSG_UPDATE_MEDIA");
+ // Throttle to once per MEDIA_DWELL_TIME
+ removeMessages(MSG_UPDATE_MEDIA);
+ updateCurrentMediaState(false);
+ break;
+
default:
Log.e(TAG, "unknown message! msg.what=" + msg.what);
break;
@@ -799,40 +792,59 @@
}
}
- private void updatePlaybackState(PlaybackState state) {
- if (state == null) {
- state = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE,
- PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f).build();
+ private PlaybackState updatePlaybackState() {
+ PlaybackState newState = new PlaybackState.Builder()
+ .setState(PlaybackState.STATE_NONE,
+ PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f)
+ .build();
+ synchronized (this) {
+ PlaybackState controllerState = null;
+ if (mMediaController != null) {
+ controllerState = mMediaController.getPlaybackState();
+ }
+
+ if (controllerState != null) {
+ newState = controllerState;
+ } else if (mAudioManager != null && mAudioManager.isMusicActive()) {
+ // Use A2DP state if we don't have a state from MediaControlller
+ PlaybackState.Builder builder = new PlaybackState.Builder();
+ if (mA2dpState == BluetoothA2dp.STATE_PLAYING) {
+ builder.setState(PlaybackState.STATE_PLAYING,
+ PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f);
+ } else {
+ builder.setState(PlaybackState.STATE_PAUSED,
+ PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f);
+ }
+ newState = builder.build();
+ }
}
- byte stateBytes = (byte) convertPlayStateToBytes(state.getState());
+ byte newPlayStatus = getBluetoothPlayState(newState);
- /* updating play status in global media player list */
+ /* update play status in global media player list */
MediaPlayerInfo player = getAddressedPlayerInfo();
if (player != null) {
- player.setPlayStatus(stateBytes);
- } else {
- Log.w(TAG, "onPlaybackStateChanged: no addressed player id=" + mCurrAddrPlayerID);
+ player.setPlayStatus(newPlayStatus);
}
- int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState);
- int newPlayStatus = convertPlayStateToPlayStatus(state);
-
if (DEBUG) {
- Log.v(TAG, "updatePlaybackState (" + mPlayStatusChangedNT + "): "+
- "old=" + mCurrentPlayState + "(" + oldPlayStatus + "), "+
- "new=" + state + "(" + newPlayStatus + ")");
+ Log.v(TAG, "updatePlaybackState (" + mPlayStatusChangedNT + "): " + mReportedPlayStatus
+ + "➡" + newPlayStatus + "(" + newState + ")");
}
- mCurrentPlayState = state;
+ if (newState != null) mCurrentPlayState = newState;
- sendPlayPosNotificationRsp(false);
-
- if (mPlayStatusChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM &&
- (oldPlayStatus != newPlayStatus)) {
- mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, newPlayStatus);
+ if (mPlayStatusChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM
+ && (mReportedPlayStatus != newPlayStatus)) {
+ sendPlaybackStatus(AvrcpConstants.NOTIFICATION_TYPE_CHANGED, newPlayStatus);
}
+ return mCurrentPlayState;
+ }
+
+ private void sendPlaybackStatus(int playStatusChangedNT, byte playbackState) {
+ registerNotificationRspPlayStatusNative(playStatusChangedNT, playbackState);
+ mPlayStatusChangedNT = playStatusChangedNT;
+ mReportedPlayStatus = playbackState;
}
private void updateTransportControls(int transportControlFlags) {
@@ -962,23 +974,40 @@
}
}
+ private void scheduleMediaUpdate() {
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_MEDIA);
+ mHandler.sendMessageDelayed(msg, MEDIA_DWELL_TIME);
+ }
+
private void updateCurrentMediaState(boolean registering) {
+ // Only do player updates when we aren't registering for track changes.
+ if (!registering) {
+ if (mAvailablePlayerViewChanged) {
+ registerNotificationRspAvalPlayerChangedNative(
+ AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
+ mAvailablePlayerViewChanged = false;
+ }
+ if (mAddrPlayerChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM
+ && mReportedPlayerID != mCurrAddrPlayerID) {
+ registerNotificationRspAddrPlayerChangedNative(
+ AvrcpConstants.NOTIFICATION_TYPE_CHANGED, mCurrAddrPlayerID, sUIDCounter);
+ mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ // Changing player sends reject to anything else we would notify...
+ mReportedPlayerID = mCurrAddrPlayerID;
+ mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ // If the player changed, they need to re-request anything here again
+ // so we can skip the rest of the update.
+ return;
+ }
+ }
+
MediaAttributes currentAttributes;
- PlaybackState newState = null;
+ PlaybackState newState = updatePlaybackState();
+
synchronized (this) {
if (mMediaController == null) {
- // Use A2DP state if we don't have a MediaControlller
- boolean isPlaying = (mA2dpState == BluetoothA2dp.STATE_PLAYING)
- && mAudioManager.isMusicActive();
- PlaybackState.Builder builder = new PlaybackState.Builder();
- if (isPlaying) {
- builder.setState(PlaybackState.STATE_PLAYING,
- PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f);
- } else {
- builder.setState(PlaybackState.STATE_PAUSED,
- PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f);
- }
- newState = builder.build();
currentAttributes = new MediaAttributes(null);
} else {
newState = mMediaController.getPlaybackState();
@@ -1000,8 +1029,7 @@
mMediaAttributes = currentAttributes;
mLastQueueId = newQueueId;
}
-
- updatePlaybackState(newState);
+ sendPlayPosNotificationRsp(false);
}
private void getRcFeaturesRequestFromNative(byte[] address, int features) {
@@ -1033,9 +1061,10 @@
private void processRegisterNotification(byte[] address, int eventId, int param) {
switch (eventId) {
case EVT_PLAY_STATUS_CHANGED:
- mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
- registerNotificationRspPlayStatusNative(mPlayStatusChangedNT,
- convertPlayStateToPlayStatus(mCurrentPlayState));
+ mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ updatePlaybackState();
+ sendPlaybackStatus(AvrcpConstants.NOTIFICATION_TYPE_INTERIM,
+ getBluetoothPlayState(mCurrentPlayState));
break;
case EVT_TRACK_CHANGED:
@@ -1063,6 +1092,8 @@
registerNotificationRspAddrPlayerChangedNative(
AvrcpConstants.NOTIFICATION_TYPE_INTERIM,
mCurrAddrPlayerID, sUIDCounter);
+ mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+ mReportedPlayerID = mCurrAddrPlayerID;
break;
case EVENT_UIDS_CHANGED:
@@ -1127,45 +1158,10 @@
return mCurrentPlayState.getPosition();
}
- private int convertPlayStateToPlayStatus(PlaybackState state) {
- int playStatus = PLAYSTATUS_ERROR;
- switch (state.getState()) {
- case PlaybackState.STATE_PLAYING:
- case PlaybackState.STATE_BUFFERING:
- playStatus = PLAYSTATUS_PLAYING;
- break;
-
- case PlaybackState.STATE_STOPPED:
- case PlaybackState.STATE_NONE:
- playStatus = PLAYSTATUS_STOPPED;
- break;
-
- case PlaybackState.STATE_PAUSED:
- playStatus = PLAYSTATUS_PAUSED;
- break;
-
- case PlaybackState.STATE_FAST_FORWARDING:
- case PlaybackState.STATE_SKIPPING_TO_NEXT:
- case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM:
- playStatus = PLAYSTATUS_FWD_SEEK;
- break;
-
- case PlaybackState.STATE_REWINDING:
- case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
- playStatus = PLAYSTATUS_REV_SEEK;
- break;
-
- case PlaybackState.STATE_ERROR:
- playStatus = PLAYSTATUS_ERROR;
- break;
-
- }
- return playStatus;
- }
-
- private boolean isPlayingState(PlaybackState state) {
- return (state.getState() == PlaybackState.STATE_PLAYING) ||
- (state.getState() == PlaybackState.STATE_BUFFERING);
+ private boolean isPlayingState(@Nullable PlaybackState state) {
+ if (state == null) return false;
+ return (state != null) && (state.getState() == PlaybackState.STATE_PLAYING)
+ || (state.getState() == PlaybackState.STATE_BUFFERING);
}
/**
@@ -1586,23 +1582,17 @@
@Override
public void onActiveSessionsChanged(
List<android.media.session.MediaController> newControllers) {
- boolean playersChanged = false;
-
// Update the current players
for (android.media.session.MediaController controller : newControllers) {
addMediaPlayerController(controller);
- playersChanged = true;
}
- if (playersChanged) {
- mHandler.sendEmptyMessage(MSG_AVAILABLE_PLAYERS_CHANGED_RSP);
- if (newControllers.size() > 0 && getAddressedPlayerInfo() == null) {
- if (DEBUG)
- Log.v(TAG,
- "No addressed player but active sessions, taking first.");
- setAddressedMediaSessionPackage(newControllers.get(0).getPackageName());
- }
+ if (newControllers.size() > 0 && getAddressedPlayerInfo() == null) {
+ if (DEBUG)
+ Log.v(TAG, "No addressed player but active sessions, taking first.");
+ setAddressedMediaSessionPackage(newControllers.get(0).getPackageName());
}
+ scheduleMediaUpdate();
}
};
@@ -1618,7 +1608,7 @@
// If the player doesn't exist, we need to add it.
if (getMediaPlayerInfo(packageName) == null) {
addMediaPlayerPackage(packageName);
- mHandler.sendEmptyMessage(MSG_AVAILABLE_PLAYERS_CHANGED_RSP);
+ scheduleMediaUpdate();
}
synchronized (mMediaPlayerInfoList) {
for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
@@ -1626,8 +1616,7 @@
int newAddrID = entry.getKey();
if (DEBUG) Log.v(TAG, "Set addressed #" + newAddrID + " " + entry.getValue());
updateCurrentController(newAddrID, mCurrBrowsePlayerID);
- mHandler.obtainMessage(MSG_ADDRESSED_PLAYER_CHANGED_RSP, newAddrID, 0)
- .sendToTarget();
+ scheduleMediaUpdate();
return;
}
}
@@ -1765,9 +1754,8 @@
for (android.media.session.MediaController controller : controllers) {
addMediaPlayerController(controller);
}
- if (controllers.size() > 0) {
- mHandler.sendEmptyMessage(MSG_AVAILABLE_PLAYERS_CHANGED_RSP);
- }
+
+ scheduleMediaUpdate();
if (mMediaPlayerInfoList.size() > 0) {
// Set the first one as the Addressed Player
@@ -1793,7 +1781,7 @@
/** Add (or update) a player to the media player list without a controller */
private boolean addMediaPlayerPackage(String packageName) {
MediaPlayerInfo info = new MediaPlayerInfo(null, AvrcpConstants.PLAYER_TYPE_AUDIO,
- AvrcpConstants.PLAYER_SUBTYPE_NONE, getPlayStateBytes(null),
+ AvrcpConstants.PLAYER_SUBTYPE_NONE, PLAYSTATUS_STOPPED,
getFeatureBitMask(packageName), packageName, getAppLabel(packageName));
return addMediaPlayerInfo(info);
}
@@ -1803,8 +1791,9 @@
String packageName = controller.getPackageName();
MediaPlayerInfo info = new MediaPlayerInfo(MediaController.wrap(controller),
AvrcpConstants.PLAYER_TYPE_AUDIO, AvrcpConstants.PLAYER_SUBTYPE_NONE,
- getPlayStateBytes(controller.getPlaybackState()), getFeatureBitMask(packageName),
- controller.getPackageName(), getAppLabel(packageName));
+ getBluetoothPlayState(controller.getPlaybackState()),
+ getFeatureBitMask(packageName), controller.getPackageName(),
+ getAppLabel(packageName));
return addMediaPlayerInfo(info);
}
@@ -1814,10 +1803,20 @@
private boolean addMediaPlayerInfo(MediaPlayerInfo info) {
int updateId = -1;
boolean updated = false;
+ boolean currentRemoved = false;
synchronized (mMediaPlayerInfoList) {
for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
- if (info.getPackageName().equals(entry.getValue().getPackageName())) {
- updateId = entry.getKey();
+ MediaPlayerInfo current = entry.getValue();
+ int id = entry.getKey();
+ if (info.getPackageName().equals(current.getPackageName())) {
+ if (!current.equalView(info)) {
+ // If we would present a different player, make it a new player
+ // so that controllers know whether a player is browsable or not.
+ mMediaPlayerInfoList.remove(id);
+ currentRemoved = (mCurrAddrPlayerID == id);
+ break;
+ }
+ updateId = id;
updated = true;
break;
}
@@ -1826,12 +1825,13 @@
// New player
mLastUsedPlayerID++;
updateId = mLastUsedPlayerID;
+ mAvailablePlayerViewChanged = true;
}
mMediaPlayerInfoList.put(updateId, info);
if (DEBUG)
Log.d(TAG, (updated ? "update #" : "add #") + updateId + ":" + info.toString());
- if (updateId == mCurrAddrPlayerID) {
- updateCurrentController(mCurrAddrPlayerID, mCurrBrowsePlayerID);
+ if (currentRemoved || updateId == mCurrAddrPlayerID) {
+ updateCurrentController(updateId, mCurrBrowsePlayerID);
}
}
return updated;
@@ -1850,6 +1850,7 @@
if (removeKey != -1) {
if (DEBUG)
Log.d(TAG, "remove #" + removeKey + ":" + mMediaPlayerInfoList.get(removeKey));
+ mAvailablePlayerViewChanged = true;
return mMediaPlayerInfoList.remove(removeKey);
}
@@ -1878,25 +1879,13 @@
* utility function to get the playback state of any media player through
* media controller APIs.
*/
- private byte getPlayStateBytes(PlaybackState pbState) {
- byte playStateBytes = PLAYSTATUS_STOPPED;
-
- if (pbState != null) {
- playStateBytes = (byte)convertPlayStateToBytes(pbState.getState());
- Log.v(TAG, "getPlayBackState: playStateBytes = " + playStateBytes);
- } else {
- Log.w(TAG, "playState object null, sending playStateBytes = " + playStateBytes);
+ private byte getBluetoothPlayState(PlaybackState pbState) {
+ if (pbState == null) {
+ Log.w(TAG, "playState object null, sending STOPPED");
+ return PLAYSTATUS_STOPPED;
}
- return playStateBytes;
- }
-
- /*
- * utility function to map framework's play state values to AVRCP spec
- * defined play status values
- */
- private int convertPlayStateToBytes(int playState) {
- switch (playState) {
+ switch (pbState.getState()) {
case PlaybackState.STATE_PLAYING:
case PlaybackState.STATE_BUFFERING:
return PLAYSTATUS_PLAYING;
@@ -2143,7 +2132,7 @@
}
}
}
- updateCurrentMediaState(false);
+ scheduleMediaUpdate();
return registerRsp;
}
@@ -2507,7 +2496,7 @@
}
}
- public void uidsChangedRsp(byte[] address, int type, int uidCounter) {
+ public void uidsChangedRsp(int type) {
if (!registerNotificationRspUIDsChangedNative(type, sUIDCounter)) {
Log.e(TAG, "registerNotificationRspUIDsChangedNative failed!");
}
@@ -2720,12 +2709,12 @@
// Do not modify without updating the HAL bt_rc.h files.
// match up with btrc_play_status_t enum of bt_rc.h
- final static int PLAYSTATUS_STOPPED = 0;
- final static int PLAYSTATUS_PLAYING = 1;
- final static int PLAYSTATUS_PAUSED = 2;
- final static int PLAYSTATUS_FWD_SEEK = 3;
- final static int PLAYSTATUS_REV_SEEK = 4;
- final static int PLAYSTATUS_ERROR = 255;
+ final static byte PLAYSTATUS_STOPPED = 0;
+ final static byte PLAYSTATUS_PLAYING = 1;
+ final static byte PLAYSTATUS_PAUSED = 2;
+ final static byte PLAYSTATUS_FWD_SEEK = 3;
+ final static byte PLAYSTATUS_REV_SEEK = 4;
+ final static byte PLAYSTATUS_ERROR = (byte) 255;
// match up with btrc_media_attr_t enum of bt_rc.h
final static int MEDIA_ATTR_TITLE = 1;
diff --git a/src/com/android/bluetooth/avrcp/AvrcpHelperClasses.java b/src/com/android/bluetooth/avrcp/AvrcpHelperClasses.java
index 5a5b5f7..b0a4c5d 100644
--- a/src/com/android/bluetooth/avrcp/AvrcpHelperClasses.java
+++ b/src/com/android/bluetooth/avrcp/AvrcpHelperClasses.java
@@ -16,6 +16,8 @@
package com.android.bluetooth.avrcp;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.media.session.MediaSession;
import com.android.bluetooth.Utils;
@@ -200,17 +202,19 @@
private int subType;
private byte playStatus;
private short[] featureBitMask;
- private String packageName;
- private String displayableName;
- private MediaController mediaController;
+ private @NonNull String packageName;
+ private @NonNull String displayableName;
+ private @Nullable MediaController mediaController;
- MediaPlayerInfo(MediaController controller, byte majorType, int subType, byte playStatus,
- short[] featureBitMask, String packageName, String displayableName) {
+ MediaPlayerInfo(@Nullable MediaController controller, byte majorType, int subType,
+ byte playStatus, short[] featureBitMask, @NonNull String packageName,
+ @Nullable String displayableName) {
this.setMajorType(majorType);
this.setSubType(subType);
this.playStatus = playStatus;
// store a copy the FeatureBitMask array
this.featureBitMask = Arrays.copyOf(featureBitMask, featureBitMask.length);
+ Arrays.sort(this.featureBitMask);
this.setPackageName(packageName);
this.setDisplayableName(displayableName);
this.setMediaController(controller);
@@ -236,7 +240,7 @@
this.mediaController = mediaController;
}
- void setPackageName(String name) {
+ void setPackageName(@NonNull String name) {
// Controller determines package name when it is set.
if (mediaController != null) return;
this.packageName = name;
@@ -271,7 +275,8 @@
return displayableName;
}
- void setDisplayableName(String displayableName) {
+ void setDisplayableName(@Nullable String displayableName) {
+ if (displayableName == null) displayableName = "";
this.displayableName = displayableName;
}
@@ -282,6 +287,7 @@
void setFeatureBitMask(short[] featureBitMask) {
synchronized (this) {
this.featureBitMask = Arrays.copyOf(featureBitMask, featureBitMask.length);
+ Arrays.sort(this.featureBitMask);
}
}
@@ -295,6 +301,14 @@
return false;
}
+ /** Tests if the view of this player presented to the controller is different enough to
+ * justify sending an Available Players Changed update */
+ public boolean equalView(MediaPlayerInfo other) {
+ return (this.majorType == other.getMajorType()) && (this.subType == other.getSubType())
+ && Arrays.equals(this.featureBitMask, other.getFeatureBitMask())
+ && this.displayableName.equals(other.getDisplayableName());
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/src/com/android/bluetooth/avrcp/AvrcpMediaRspInterface.java b/src/com/android/bluetooth/avrcp/AvrcpMediaRspInterface.java
index 71493b6..1c7b87c 100644
--- a/src/com/android/bluetooth/avrcp/AvrcpMediaRspInterface.java
+++ b/src/com/android/bluetooth/avrcp/AvrcpMediaRspInterface.java
@@ -45,7 +45,7 @@
public void avalPlayerChangedRsp(byte[] address, int type);
- public void uidsChangedRsp(byte[] address, int type, int uidCounter);
+ public void uidsChangedRsp(int type);
public void nowPlayingChangedRsp(int type);