Merge tag 'android-8.0.0_r17' of https://android.googlesource.com/platform/packages/apps/Bluetooth into HEAD
Android 8.0.0 Release 17 (OPR5.170623.007)
diff --git a/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java b/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java
index 8c94027..0835aef 100644
--- a/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java
+++ b/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java
@@ -48,20 +48,21 @@
static private final long SINGLE_QID = 1;
static private final String UNKNOWN_TITLE = "(unknown)";
+ static private final String GPM_BUNDLE_METADATA_KEY =
+ "com.google.android.music.mediasession.music_metadata";
+
private AvrcpMediaRspInterface mMediaInterface;
private @NonNull List<MediaSession.QueueItem> mNowPlayingList;
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() {
@@ -69,7 +70,6 @@
mNowPlayingList = mEmptyNowPlayingList;
mMediaInterface = null;
mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID;
- mNowPlayingListUpdated = false;
}
/* get now playing list from addressed player */
@@ -81,7 +81,7 @@
mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY, null);
return;
}
- List<MediaSession.QueueItem> items = getNowPlayingList(mediaController);
+ List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
getFolderItemsFilterAttr(bdaddr, reqObj, items, AvrcpConstants.BTRC_SCOPE_NOW_PLAYING,
reqObj.mStartItem, reqObj.mEndItem, mediaController);
}
@@ -91,7 +91,7 @@
@Nullable MediaController mediaController) {
int status = AvrcpConstants.RSP_NO_ERROR;
long mediaId = ByteBuffer.wrap(itemAttr.mUid).getLong();
- List<MediaSession.QueueItem> items = getNowPlayingList(mediaController);
+ List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
// NOTE: this is out-of-spec (AVRCP 1.6.1 sec 6.10.4.3, p90) but we answer it anyway
// because some CTs ask for it.
@@ -118,14 +118,10 @@
/* Refresh and get the queue of now playing.
*/
- private @NonNull List<MediaSession.QueueItem> getNowPlayingList(
- @Nullable MediaController mediaController) {
+ @NonNull
+ List<MediaSession.QueueItem> updateNowPlayingList(@Nullable MediaController mediaController) {
if (mediaController == null) return mEmptyNowPlayingList;
List<MediaSession.QueueItem> items = mediaController.getQueue();
- if (items != null && !mNowPlayingListUpdated) {
- mNowPlayingList = items;
- return mNowPlayingList;
- }
if (items == null) {
Log.i(TAG, "null queue from " + mediaController.getPackageName()
+ ", constructing single-item list");
@@ -137,18 +133,17 @@
items.add(current);
}
+ if (!items.equals(mNowPlayingList)) sendNowPlayingListChanged();
mNowPlayingList = items;
- if (mNowPlayingListUpdated) sendNowPlayingListChanged();
-
return mNowPlayingList;
}
private void sendNowPlayingListChanged() {
if (mMediaInterface == null) return;
+ if (DEBUG) Log.d(TAG, "sendNowPlayingListChanged()");
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
@@ -186,6 +181,7 @@
private Bundle fillBundle(MediaMetadata metadata, Bundle currentExtras) {
if (metadata == null) {
+ Log.i(TAG, "fillBundle: metadata is null");
return currentExtras;
}
@@ -207,15 +203,10 @@
return bundle;
}
- void updateNowPlayingList(@Nullable MediaController mediaController) {
- mNowPlayingListUpdated = true;
- getNowPlayingList(mediaController);
- }
-
/* Instructs media player to play particular media item */
void playItem(byte[] bdaddr, byte[] uid, @Nullable MediaController mediaController) {
long qid = ByteBuffer.wrap(uid).getLong();
- List<MediaSession.QueueItem> items = getNowPlayingList(mediaController);
+ List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
if (mediaController == null) {
Log.e(TAG, "No mediaController when PlayItem " + qid + " requested");
@@ -246,7 +237,7 @@
}
void getTotalNumOfItems(byte[] bdaddr, @Nullable MediaController mediaController) {
- List<MediaSession.QueueItem> items = getNowPlayingList(mediaController);
+ List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
if (DEBUG) Log.d(TAG, "getTotalNumOfItems: " + items.size() + " items.");
mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, items.size());
}
@@ -256,7 +247,6 @@
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;
}
@@ -391,10 +381,22 @@
MediaDescription desc = item.getDescription();
Bundle extras = desc.getExtras();
boolean isCurrentTrack = item.getQueueId() == getActiveQueueItemId(mediaController);
+ MediaMetadata data = null;
if (isCurrentTrack) {
if (DEBUG) Log.d(TAG, "getAttrValue: item is active, using current data");
- extras = fillBundle(mediaController.getMetadata(), extras);
+ data = mediaController.getMetadata();
+ if (data == null)
+ Log.e(TAG, "getMetadata didn't give us any metadata for the current track");
}
+
+ if (data == null) {
+ // TODO: This code can be removed when b/63117921 is resolved
+ data = (MediaMetadata) extras.get(GPM_BUNDLE_METADATA_KEY);
+ extras = null; // We no longer need the data in here
+ }
+
+ extras = fillBundle(data, extras);
+
if (DEBUG) Log.d(TAG, "getAttrValue: item " + item + " : " + desc);
switch (attr) {
case AvrcpConstants.ATTRID_TITLE:
@@ -511,7 +513,9 @@
private long getActiveQueueItemId(@Nullable MediaController controller) {
if (controller == null) return MediaSession.QueueItem.UNKNOWN_ID;
PlaybackState state = controller.getPlaybackState();
- if (state == null) return MediaSession.QueueItem.UNKNOWN_ID;
+ if (state == null || state.getState() == PlaybackState.STATE_BUFFERING
+ || state.getState() == PlaybackState.STATE_NONE)
+ return MediaSession.QueueItem.UNKNOWN_ID;
long qid = state.getActiveQueueItemId();
if (qid != MediaSession.QueueItem.UNKNOWN_ID) return qid;
// Check if we're presenting a "one item queue"
diff --git a/src/com/android/bluetooth/avrcp/Avrcp.java b/src/com/android/bluetooth/avrcp/Avrcp.java
index 712b2a8..08adf81 100644
--- a/src/com/android/bluetooth/avrcp/Avrcp.java
+++ b/src/com/android/bluetooth/avrcp/Avrcp.java
@@ -57,9 +57,11 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -86,11 +88,12 @@
private @NonNull PlaybackState mCurrentPlayState;
private int mA2dpState;
private int mPlayStatusChangedNT;
- private int mReportedPlayStatus;
+ private byte mReportedPlayStatus;
private int mTrackChangedNT;
private int mPlayPosChangedNT;
private int mAddrPlayerChangedNT;
private int mReportedPlayerID;
+ private int mNowPlayingListChangedNT;
private long mPlaybackIntervalMs;
private long mLastReportedPosition;
private long mNextPosMs;
@@ -99,7 +102,6 @@
private int mRemoteVolume;
private int mLastRemoteVolume;
private int mInitialRemoteVolume;
- private BrowsablePlayerListBuilder mBrowsableListBuilder;
/* Local volume in audio index 0-15 */
private int mLocalVolume;
@@ -162,10 +164,8 @@
private static final int MSG_ABS_VOL_TIMEOUT = 17;
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;
@@ -244,6 +244,7 @@
mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
mPlaybackIntervalMs = 0L;
mLastReportedPosition = -1;
mNextPosMs = -1;
@@ -280,8 +281,6 @@
mAbsVolThreshold = resources.getInteger(R.integer.a2dp_absolute_volume_initial_threshold);
}
- mBrowsableListBuilder = new BrowsablePlayerListBuilder();
-
// Register for package removal intent broadcasts for media button receiver persistence
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -292,7 +291,7 @@
context.registerReceiver(mAvrcpReceiver, pkgFilter);
IntentFilter bootFilter = new IntentFilter();
- bootFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
+ bootFilter.addAction(Intent.ACTION_USER_UNLOCKED);
context.registerReceiver(mBootReceiver, bootFilter);
}
@@ -328,7 +327,7 @@
if (manager == null || manager.isUserUnlocked()) {
if (DEBUG) Log.d(TAG, "User already unlocked, initializing player lists");
// initialize browsable player list and build media player list
- mBrowsableListBuilder.start();
+ buildBrowsablePlayerList();
}
}
@@ -357,7 +356,6 @@
mContext.unregisterReceiver(mAvrcpReceiver);
mContext.unregisterReceiver(mBootReceiver);
- mBrowsableListBuilder.cleanup();
mAddressedMediaPlayer.cleanup();
mAvrcpBrowseManager.cleanup();
}
@@ -373,12 +371,13 @@
@Override
public void onMetadataChanged(MediaMetadata metadata) {
if (DEBUG) Log.v(TAG, "onMetadataChanged");
- scheduleMediaUpdate();
+ updateCurrentMediaState(false);
}
@Override
public synchronized void onPlaybackStateChanged(PlaybackState state) {
if (DEBUG) Log.v(TAG, "onPlaybackStateChanged: state " + state.toString());
- scheduleMediaUpdate();
+
+ updateCurrentMediaState(false);
}
@Override
@@ -478,7 +477,7 @@
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);
+ updateCurrentMediaState(false);
break;
case MSG_PLAY_INTERVAL_TIMEOUT:
@@ -692,7 +691,7 @@
case MSG_SET_A2DP_AUDIO_STATE:
if (DEBUG) Log.v(TAG, "MSG_SET_A2DP_AUDIO_STATE:" + msg.arg1);
mA2dpState = msg.arg1;
- scheduleMediaUpdate();
+ updateCurrentMediaState(false);
break;
case MSG_NATIVE_REQ_GET_FOLDER_ITEMS: {
@@ -778,13 +777,6 @@
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;
@@ -834,10 +826,6 @@
if (newState != null) mCurrentPlayState = newState;
- if (mPlayStatusChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM
- && (mReportedPlayStatus != newPlayStatus)) {
- sendPlaybackStatus(AvrcpConstants.NOTIFICATION_TYPE_CHANGED, newPlayStatus);
- }
return mCurrentPlayState;
}
@@ -974,11 +962,6 @@
}
}
- 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) {
@@ -986,9 +969,13 @@
registerNotificationRspAvalPlayerChangedNative(
AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
mAvailablePlayerViewChanged = false;
+ return;
}
if (mAddrPlayerChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM
&& mReportedPlayerID != mCurrAddrPlayerID) {
+ registerNotificationRspAvalPlayerChangedNative(
+ AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
+ mAvailablePlayerViewChanged = false;
registerNotificationRspAddrPlayerChangedNative(
AvrcpConstants.NOTIFICATION_TYPE_CHANGED, mCurrAddrPlayerID, sUIDCounter);
mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
@@ -997,6 +984,8 @@
mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ mAddressedMediaPlayer.updateNowPlayingList(mMediaController);
// If the player changed, they need to re-request anything here again
// so we can skip the rest of the update.
return;
@@ -1010,25 +999,63 @@
if (mMediaController == null) {
currentAttributes = new MediaAttributes(null);
} else {
- newState = mMediaController.getPlaybackState();
currentAttributes = new MediaAttributes(mMediaController.getMetadata());
}
}
- long newQueueId = MediaSession.QueueItem.UNKNOWN_ID;
- if (newState != null) newQueueId = newState.getActiveQueueItemId();
- Log.v(TAG, "Media update: id " + mLastQueueId + "➡" + newQueueId + "? "
- + currentAttributes.toRedactedString());
- // Notify track changed if:
- // - The CT is registering for the notification
- // - Queue ID is UNKNOWN and MediaMetadata is different
- // - Queue ID is valid and different and MediaMetadata is different
- if (registering || (((newQueueId == -1) || (newQueueId != mLastQueueId))
- && !currentAttributes.equals(mMediaAttributes))) {
- sendTrackChangedRsp(registering);
- mMediaAttributes = currentAttributes;
- mLastQueueId = newQueueId;
+ byte newPlayStatus = getBluetoothPlayState(newState);
+
+ if (newState.getState() != PlaybackState.STATE_BUFFERING
+ && newState.getState() != PlaybackState.STATE_NONE) {
+ long newQueueId = MediaSession.QueueItem.UNKNOWN_ID;
+ if (newState != null) newQueueId = newState.getActiveQueueItemId();
+ Log.v(TAG, "Media update: id " + mLastQueueId + "➡" + newQueueId + "? "
+ + currentAttributes.toRedactedString() + " : "
+ + mMediaAttributes.toRedactedString());
+
+ // Dont send now playing list changed if the player doesn't support browsing
+ MediaPlayerInfo info = getAddressedPlayerInfo();
+ if (info != null && info.isBrowseSupported()) {
+ Log.v(TAG, "Check if NowPlayingList is updated");
+ mAddressedMediaPlayer.updateNowPlayingList(mMediaController);
+ }
+
+ if ((newQueueId == -1 || newQueueId != mLastQueueId)
+ && currentAttributes.equals(mMediaAttributes)
+ && newPlayStatus == PLAYSTATUS_PLAYING
+ && mReportedPlayStatus == PLAYSTATUS_STOPPED) {
+ // Most carkits like seeing the track changed before the
+ // playback state changed, but some controllers are slow
+ // to update their metadata. Hold of on sending the playback state
+ // update until after we know the current metadata is up to date
+ // and track changed has been sent. This was seen on BMW carkits
+ Log.i(TAG, "Waiting for metadata update to send track changed");
+
+ return;
+ }
+
+ // Notify track changed if:
+ // - The CT is registering for the notification
+ // - Queue ID is UNKNOWN and MediaMetadata is different
+ // - Queue ID is valid and different and MediaMetadata is different
+ if (registering || ((newQueueId == -1 || newQueueId != mLastQueueId)
+ && !currentAttributes.equals(mMediaAttributes))) {
+ Log.v(TAG, "Send track changed");
+ mMediaAttributes = currentAttributes;
+ mLastQueueId = newQueueId;
+ sendTrackChangedRsp(registering);
+ }
+ } else {
+ Log.i(TAG, "Skipping update due to invalid playback state");
}
+
+ // still send the updated play state if the playback state is none or buffering
+ Log.e(TAG, "play status change " + mReportedPlayStatus + "➡" + newPlayStatus);
+ if (mPlayStatusChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM
+ && (mReportedPlayStatus != newPlayStatus)) {
+ sendPlaybackStatus(AvrcpConstants.NOTIFICATION_TYPE_CHANGED, newPlayStatus);
+ }
+
sendPlayPosNotificationRsp(false);
}
@@ -1063,14 +1090,13 @@
case EVT_PLAY_STATUS_CHANGED:
mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
updatePlaybackState();
- sendPlaybackStatus(AvrcpConstants.NOTIFICATION_TYPE_INTERIM,
- getBluetoothPlayState(mCurrentPlayState));
+ sendPlaybackStatus(AvrcpConstants.NOTIFICATION_TYPE_INTERIM, mReportedPlayStatus);
break;
case EVT_TRACK_CHANGED:
Log.v(TAG, "Track changed notification enabled");
mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
- updateCurrentMediaState(true);
+ sendTrackChangedRsp(true);
break;
case EVT_PLAY_POS_CHANGED:
@@ -1105,6 +1131,7 @@
case EVENT_NOW_PLAYING_CONTENT_CHANGED:
if (DEBUG) Log.d(TAG, "Now Playing List changed notification enabled");
/* send interim response to remote device */
+ mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
if (!registerNotificationRspNowPlayingChangedNative(
AvrcpConstants.NOTIFICATION_TYPE_INTERIM)) {
Log.e(TAG, "EVENT_NOW_PLAYING_CONTENT_CHANGED: " +
@@ -1160,8 +1187,7 @@
private boolean isPlayingState(@Nullable PlaybackState state) {
if (state == null) return false;
- return (state != null) && (state.getState() == PlaybackState.STATE_PLAYING)
- || (state.getState() == PlaybackState.STATE_BUFFERING);
+ return (state != null) && (state.getState() == PlaybackState.STATE_PLAYING);
}
/**
@@ -1390,10 +1416,10 @@
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
- if (DEBUG) Log.d(TAG, "Boot completed, initializing player lists");
+ if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
+ if (DEBUG) Log.d(TAG, "User unlocked, initializing player lists");
/* initializing media player's list */
- mBrowsableListBuilder.start();
+ buildBrowsablePlayerList();
}
}
}
@@ -1439,7 +1465,7 @@
// new package has been added.
if (isBrowsableListUpdated(packageName)) {
// Rebuilding browsable players list
- mBrowsableListBuilder.start();
+ buildBrowsablePlayerList();
}
}
}
@@ -1548,8 +1574,15 @@
// checking for error cases
if (mMediaPlayerInfoList.isEmpty()) {
status = AvrcpConstants.RSP_NO_AVBL_PLAY;
- Log.w(TAG, " No Available Players to set, sending response back ");
+ Log.w(TAG, "setBrowsedPlayer: No available players! ");
} else {
+ // Workaround for broken controllers selecting ID 0
+ // Seen at least on Ford, Chevrolet MyLink
+ if (selectedId == 0) {
+ Log.w(TAG, "setBrowsedPlayer: workaround invalid id 0");
+ selectedId = mCurrAddrPlayerID;
+ }
+
// update current browse player id and start browsing service
updateNewIds(mCurrAddrPlayerID, selectedId);
String browsedPackage = getPackageName(selectedId);
@@ -1582,9 +1615,15 @@
@Override
public void onActiveSessionsChanged(
List<android.media.session.MediaController> newControllers) {
+ Set<String> updatedPackages = new HashSet<String>();
// Update the current players
for (android.media.session.MediaController controller : newControllers) {
+ String packageName = controller.getPackageName();
+ if (DEBUG) Log.v(TAG, "ActiveSession: " + MediaController.wrap(controller));
+ // Only use the first (highest priority) controller from each package
+ if (updatedPackages.contains(packageName)) continue;
addMediaPlayerController(controller);
+ updatedPackages.add(packageName);
}
if (newControllers.size() > 0 && getAddressedPlayerInfo() == null) {
@@ -1592,7 +1631,7 @@
Log.v(TAG, "No addressed player but active sessions, taking first.");
setAddressedMediaSessionPackage(newControllers.get(0).getPackageName());
}
- scheduleMediaUpdate();
+ updateCurrentMediaState(false);
}
};
@@ -1602,13 +1641,17 @@
updateCurrentController(0, mCurrBrowsePlayerID);
return;
}
+ if (packageName.equals("com.android.server.telecom")) {
+ Log.d(TAG, "Ignore addressed media session change to telecom");
+ return;
+ }
// No change.
if (getPackageName(mCurrAddrPlayerID).equals(packageName)) return;
if (DEBUG) Log.v(TAG, "Changing addressed media session to " + packageName);
// If the player doesn't exist, we need to add it.
if (getMediaPlayerInfo(packageName) == null) {
addMediaPlayerPackage(packageName);
- scheduleMediaUpdate();
+ updateCurrentMediaState(false);
}
synchronized (mMediaPlayerInfoList) {
for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
@@ -1616,7 +1659,7 @@
int newAddrID = entry.getKey();
if (DEBUG) Log.v(TAG, "Set addressed #" + newAddrID + " " + entry.getValue());
updateCurrentController(newAddrID, mCurrBrowsePlayerID);
- scheduleMediaUpdate();
+ updateCurrentMediaState(false);
return;
}
}
@@ -1628,6 +1671,10 @@
private void setActiveMediaSession(MediaSession.Token token) {
android.media.session.MediaController activeController =
new android.media.session.MediaController(mContext, token);
+ if (activeController.getPackageName().equals("com.android.server.telecom")) {
+ Log.d(TAG, "Ignore active media session change to telecom");
+ return;
+ }
if (DEBUG) Log.v(TAG, "Set active media session " + activeController.getPackageName());
addMediaPlayerController(activeController);
setAddressedMediaSessionPackage(activeController.getPackageName());
@@ -1667,71 +1714,33 @@
return browseServiceName;
}
- private class BrowsablePlayerListBuilder extends MediaBrowser.ConnectionCallback {
- List<ResolveInfo> mWaiting;
- BrowsePlayerInfo mCurrentPlayer;
- MediaBrowser mCurrentBrowser;
- boolean mPlayersChanged;
-
- public BrowsablePlayerListBuilder() {}
-
- public void start() {
+ void buildBrowsablePlayerList() {
+ synchronized (mBrowsePlayerInfoList) {
mBrowsePlayerInfoList.clear();
- cleanup();
Intent intent = new Intent(android.service.media.MediaBrowserService.SERVICE_INTERFACE);
- mWaiting = mPackageManager.queryIntentServices(intent, PackageManager.MATCH_ALL);
- connectNextPlayer();
- }
+ List<ResolveInfo> playerList =
+ mPackageManager.queryIntentServices(intent, PackageManager.MATCH_ALL);
- public void cleanup() {
- if (mWaiting != null) mWaiting.clear();
- mPlayersChanged = false;
- if (mCurrentBrowser != null) mCurrentBrowser.disconnect();
- }
+ for (ResolveInfo info : playerList) {
+ String displayableName = info.loadLabel(mPackageManager).toString();
+ String serviceName = info.serviceInfo.name;
+ String packageName = info.serviceInfo.packageName;
- private void connectNextPlayer() {
- if (mWaiting.isEmpty()) {
- // Done. Send players changed if needed.
- if (mPlayersChanged) {
- registerNotificationRspAvalPlayerChangedNative(
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
+ if (DEBUG) Log.d(TAG, "Adding " + serviceName + " to list of browsable players");
+ BrowsePlayerInfo currentPlayer =
+ new BrowsePlayerInfo(packageName, displayableName, serviceName);
+ mBrowsePlayerInfoList.add(currentPlayer);
+ MediaPlayerInfo playerInfo = getMediaPlayerInfo(packageName);
+ MediaController controller =
+ (playerInfo == null) ? null : playerInfo.getMediaController();
+ // Refresh the media player entry so it notices we can browse
+ if (controller != null) {
+ addMediaPlayerController(controller.getWrappedInstance());
+ } else {
+ addMediaPlayerPackage(packageName);
}
- return;
}
- ResolveInfo info = mWaiting.remove(0);
- String displayableName = info.loadLabel(mPackageManager).toString();
- String serviceName = info.serviceInfo.name;
- String packageName = info.serviceInfo.packageName;
-
- mCurrentPlayer = new BrowsePlayerInfo(packageName, displayableName, serviceName);
- mCurrentBrowser = new MediaBrowser(
- mContext, new ComponentName(packageName, serviceName), this, null);
- if (DEBUG) Log.d(TAG, "Trying to connect to " + serviceName);
- mCurrentBrowser.connect();
- }
-
- @Override
- public void onConnected() {
- Log.d(TAG, "BrowsablePlayerListBuilder: " + mCurrentPlayer.packageName + " OK");
- mCurrentBrowser.disconnect();
- mCurrentBrowser = null;
- mBrowsePlayerInfoList.add(mCurrentPlayer);
- MediaPlayerInfo info = getMediaPlayerInfo(mCurrentPlayer.packageName);
- MediaController controller = (info == null) ? null : info.getMediaController();
- // Refresh the media player entry so it notices we can browse
- if (controller != null) {
- addMediaPlayerController(controller.getWrappedInstance());
- } else {
- addMediaPlayerPackage(mCurrentPlayer.packageName);
- }
- mPlayersChanged = true;
- connectNextPlayer();
- }
-
- @Override
- public void onConnectionFailed() {
- Log.d(TAG, "BrowsablePlayerListBuilder: " + mCurrentPlayer.packageName + " FAIL");
- connectNextPlayer();
+ updateCurrentMediaState(false);
}
}
@@ -1755,7 +1764,7 @@
addMediaPlayerController(controller);
}
- scheduleMediaUpdate();
+ updateCurrentMediaState(false);
if (mMediaPlayerInfoList.size() > 0) {
// Set the first one as the Addressed Player
@@ -1804,6 +1813,10 @@
int updateId = -1;
boolean updated = false;
boolean currentRemoved = false;
+ if (info.getPackageName().equals("com.android.server.telecom")) {
+ Log.d(TAG, "Skip adding telecom to the media player info list");
+ return updated;
+ }
synchronized (mMediaPlayerInfoList) {
for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
MediaPlayerInfo current = entry.getValue();
@@ -1828,11 +1841,10 @@
mAvailablePlayerViewChanged = true;
}
mMediaPlayerInfoList.put(updateId, info);
- if (DEBUG)
- Log.d(TAG, (updated ? "update #" : "add #") + updateId + ":" + info.toString());
- if (currentRemoved || updateId == mCurrAddrPlayerID) {
- updateCurrentController(updateId, mCurrBrowsePlayerID);
- }
+ }
+ if (DEBUG) Log.d(TAG, (updated ? "update #" : "add #") + updateId + ":" + info.toString());
+ if (currentRemoved || updateId == mCurrAddrPlayerID) {
+ updateCurrentController(updateId, mCurrBrowsePlayerID);
}
return updated;
}
@@ -1887,9 +1899,9 @@
switch (pbState.getState()) {
case PlaybackState.STATE_PLAYING:
- case PlaybackState.STATE_BUFFERING:
return PLAYSTATUS_PLAYING;
+ case PlaybackState.STATE_BUFFERING:
case PlaybackState.STATE_STOPPED:
case PlaybackState.STATE_NONE:
case PlaybackState.STATE_CONNECTING:
@@ -2041,14 +2053,18 @@
short[] featureBitMaskValues =
new short[numPlayers * AvrcpConstants.AVRC_FEATURE_MASK_SIZE];
- int players = 0;
+ // Reserve the first spot for the currently addressed player if
+ // we have one
+ int players = mMediaPlayerInfoList.containsKey(mCurrAddrPlayerID) ? 1 : 0;
for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
+ int idx = players;
+ if (entry.getKey() == mCurrAddrPlayerID) idx = 0;
MediaPlayerInfo info = entry.getValue();
- playerIds[players] = entry.getKey();
- playerTypes[players] = info.getMajorType();
- playerSubTypes[players] = info.getSubType();
- displayableNameArray[players] = info.getDisplayableName();
- playStatusValues[players] = info.getPlayStatus();
+ playerIds[idx] = entry.getKey();
+ playerTypes[idx] = info.getMajorType();
+ playerSubTypes[idx] = info.getSubType();
+ displayableNameArray[idx] = info.getDisplayableName();
+ playStatusValues[idx] = info.getPlayStatus();
short[] featureBits = info.getFeatureBitMask();
for (int numBit = 0; numBit < featureBits.length; numBit++) {
@@ -2056,19 +2072,18 @@
byte octet = (byte) (featureBits[numBit] / 8);
/* gives the bit position within the octet */
byte bit = (byte) (featureBits[numBit] % 8);
- featureBitMaskValues[(players * AvrcpConstants.AVRC_FEATURE_MASK_SIZE)
- + octet] |= (1 << bit);
+ featureBitMaskValues[(idx * AvrcpConstants.AVRC_FEATURE_MASK_SIZE) + octet] |=
+ (1 << bit);
}
/* printLogs */
if (DEBUG) {
- Log.d(TAG, "Player " + playerIds[players] + ": " + displayableNameArray[players]
- + " type: " + playerTypes[players] + ", "
- + playerSubTypes[players] + " status: "
- + playStatusValues[players]);
+ Log.d(TAG, "Player " + playerIds[idx] + ": " + displayableNameArray[idx]
+ + " type: " + playerTypes[idx] + ", " + playerSubTypes[idx]
+ + " status: " + playStatusValues[idx]);
}
- players++;
+ if (idx != 0) players++;
}
if (DEBUG) Log.d(TAG, "prepareMediaPlayerRspObj: numPlayers = " + numPlayers);
@@ -2125,14 +2140,12 @@
mMediaController = newController;
if (mMediaController != null) {
mMediaController.registerCallback(mMediaControllerCb, mHandler);
- mAddressedMediaPlayer.updateNowPlayingList(mMediaController);
} else {
- mAddressedMediaPlayer.updateNowPlayingList(null);
registerRsp = false;
}
}
}
- scheduleMediaUpdate();
+ updateCurrentMediaState(false);
return registerRsp;
}
@@ -2207,6 +2220,12 @@
}
private void handleGetItemAttr(AvrcpCmd.ItemAttrCmd itemAttr) {
+ if (itemAttr.mUidCounter != sUIDCounter) {
+ Log.e(TAG, "handleGetItemAttr: invaild uid counter.");
+ getItemAttrRspNative(
+ itemAttr.mAddress, AvrcpConstants.RSP_UID_CHANGED, (byte) 0, null, null);
+ return;
+ }
if (itemAttr.mScope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
if (mCurrAddrPlayerID == NO_PLAYER_ID) {
getItemAttrRspNative(
@@ -2503,9 +2522,15 @@
}
public void nowPlayingChangedRsp(int type) {
+ if (mNowPlayingListChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+ if (DEBUG) Log.d(TAG, "NowPlayingListChanged: Not registered or requesting.");
+ return;
+ }
+
if (!registerNotificationRspNowPlayingChangedNative(type)) {
Log.e(TAG, "registerNotificationRspNowPlayingChangedNative failed!");
}
+ mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
}
public void trackChangedRsp(int type, byte[] uid) {
diff --git a/src/com/android/bluetooth/avrcp/BrowsedMediaPlayer.java b/src/com/android/bluetooth/avrcp/BrowsedMediaPlayer.java
index c430ea9..397e17a 100644
--- a/src/com/android/bluetooth/avrcp/BrowsedMediaPlayer.java
+++ b/src/com/android/bluetooth/avrcp/BrowsedMediaPlayer.java
@@ -53,6 +53,7 @@
/* package and service name of target Media Player which is set for browsing */
private String mPackageName;
+ private String mConnectingPackageName;
private String mClassName;
private Context mContext;
private AvrcpMediaRspInterface mMediaInterface;
@@ -83,20 +84,34 @@
private List<MediaBrowser.MediaItem> mFolderItems = null;
/* Connection state callback handler */
- private MediaBrowser.ConnectionCallback browseMediaConnectionCallback =
- new MediaBrowser.ConnectionCallback() {
+ class MediaConnectionCallback extends MediaBrowser.ConnectionCallback {
+ private String mCallbackPackageName;
+ private MediaBrowser mBrowser;
+
+ public MediaConnectionCallback(String packageName) {
+ this.mCallbackPackageName = packageName;
+ }
+
+ public void setBrowser(MediaBrowser b) {
+ mBrowser = b;
+ }
@Override
public void onConnected() {
mConnState = CONNECTED;
if (DEBUG) Log.d(TAG, "mediaBrowser CONNECTED to " + mPackageName);
/* perform init tasks and set player as browsed player on successful connection */
- onBrowseConnect();
+ onBrowseConnect(mCallbackPackageName, mBrowser);
+
+ // Remove what could be a circular dependency causing GC to never happen on this object
+ mBrowser = null;
}
@Override
public void onConnectionFailed() {
mConnState = DISCONNECTED;
+ // Remove what could be a circular dependency causing GC to never happen on this object
+ mBrowser = null;
Log.e(TAG, "mediaBrowser Connection failed with " + mPackageName
+ ", Sending fail response!");
mMediaInterface.setBrowsedPlayerRsp(mBDAddr, AvrcpConstants.RSP_INTERNAL_ERR,
@@ -105,10 +120,11 @@
@Override
public void onConnectionSuspended() {
+ mBrowser = null;
mConnState = SUSPENDED;
Log.e(TAG, "mediaBrowser SUSPENDED connection with " + mPackageName);
}
- };
+ }
/* Subscription callback handler. Subscribe to a folder to get its contents */
private MediaBrowser.SubscriptionCallback folderItemsCb =
@@ -251,26 +267,44 @@
}
/* initialize mediacontroller in order to communicate with media player. */
- private void onBrowseConnect() {
- boolean isError = false;
+ private void onBrowseConnect(String connectedPackage, MediaBrowser browser) {
+ if (!connectedPackage.equals(mConnectingPackageName)) {
+ Log.w(TAG, "onBrowseConnect: recieved callback for package we aren't connecting to "
+ + connectedPackage);
+ return;
+ }
+ mConnectingPackageName = null;
+
+ if (browser == null) {
+ Log.e(TAG, "onBrowseConnect: received a null browser for " + connectedPackage);
+ mMediaInterface.setBrowsedPlayerRsp(
+ mBDAddr, AvrcpConstants.RSP_INTERNAL_ERR, (byte) 0x00, 0, null);
+ return;
+ }
+
MediaSession.Token token = null;
try {
- /* get rootfolder uid from media player */
- if (mMediaId == null) {
- mMediaId = mMediaBrowser.getRoot();
- /*
- * assuming that root folder uid will not change on uids changed
- */
- mRootFolderUid = mMediaId;
- /* store root folder uid to stack */
- mPathStack.push(mMediaId);
- }
-
- if (!mMediaBrowser.isConnected()) {
+ if (!browser.isConnected()) {
Log.e(TAG, "setBrowsedPlayer: " + mPackageName + "not connected");
- } else if ((token = mMediaBrowser.getSessionToken()) == null) {
+ } else if ((token = browser.getSessionToken()) == null) {
Log.e(TAG, "setBrowsedPlayer: " + mPackageName + "no Session token");
} else {
+ /* update to the new MediaBrowser */
+ if (mMediaBrowser != null) mMediaBrowser.disconnect();
+ mMediaBrowser = browser;
+ mPackageName = connectedPackage;
+
+ /* get rootfolder uid from media player */
+ if (mMediaId == null) {
+ mMediaId = mMediaBrowser.getRoot();
+ /*
+ * assuming that root folder uid will not change on uids changed
+ */
+ mRootFolderUid = mMediaId;
+ /* store root folder uid to stack */
+ mPathStack.push(mMediaId);
+ }
+
mMediaController = MediaController.wrap(
new android.media.session.MediaController(mContext, token));
/* get root folder items */
@@ -287,7 +321,7 @@
}
public void setBrowsed(String packageName, String cls) {
- mPackageName = packageName;
+ mConnectingPackageName = packageName;
mClassName = cls;
/* cleanup variables from previous browsed calls */
mFolderItems = null;
@@ -298,10 +332,14 @@
* will be required while navigating up the folder
*/
mPathStack = new Stack<String>();
+
/* Bind to MediaBrowseService of MediaPlayer */
- mMediaBrowser = new MediaBrowser(mContext, new ComponentName(mPackageName, mClassName),
- browseMediaConnectionCallback, null);
- mMediaBrowser.connect();
+ MediaConnectionCallback callback = new MediaConnectionCallback(packageName);
+ MediaBrowser tempBrowser = new MediaBrowser(
+ mContext, new ComponentName(packageName, mClassName), callback, null);
+ callback.setBrowser(tempBrowser);
+
+ tempBrowser.connect();
}
/* called when connection to media player is closed */
@@ -494,7 +532,7 @@
*/
private List<MediaBrowser.MediaItem> checkIndexOutofBounds(
byte[] bdaddr, List<MediaBrowser.MediaItem> children, long startItem, long endItem) {
- if (endItem > children.size()) endItem = children.size() - 1;
+ if (endItem >= children.size()) endItem = children.size() - 1;
if (startItem >= Integer.MAX_VALUE) startItem = Integer.MAX_VALUE;
try {
List<MediaBrowser.MediaItem> childrenSubList =
@@ -651,9 +689,11 @@
case AvrcpConstants.ATTRID_GENRE:
attrValue = extras.getString(MediaMetadata.METADATA_KEY_GENRE);
+ break;
case AvrcpConstants.ATTRID_PLAY_TIME:
attrValue = extras.getString(MediaMetadata.METADATA_KEY_DURATION);
+ break;
case AvrcpConstants.ATTRID_COVER_ART:
Log.e(TAG, "getAttrValue: Cover art attribute not supported");