Android-x86
Fork
Donation

  • R/O
  • HTTP
  • SSH
  • HTTPS

packages-apps-Bluetooth: Commit

packages/apps/Bluetooth


Commit MetaInfo

Revisiónf51e3acc5f680652841b5bdc77e89ec6fedbe69a (tree)
Tiempo2017-08-29 03:42:49
AutorAjay Panicker <apanicke@goog...>
Commiterandroid-build-team Robot

Log Message

Improve AVRCP quality and state handling (1/2)

Bug: 64749777
Test: Play music with various car kits and see that metadata shows up

See go/oc-avrcp-sotu - 8/15/20 OC + Patches for further info

Change-Id: Ia9dd450860f5bc54bd249322bfb9762c6235dc32
(cherry picked from commit 6b95520b8fe8a5bd8bc264e06bec51f3591d2507)

Cambiar Resumen

Diferencia

--- a/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java
+++ b/src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java
@@ -48,20 +48,21 @@ public class AddressedMediaPlayer {
4848 static private final long SINGLE_QID = 1;
4949 static private final String UNKNOWN_TITLE = "(unknown)";
5050
51+ static private final String GPM_BUNDLE_METADATA_KEY =
52+ "com.google.android.music.mediasession.music_metadata";
53+
5154 private AvrcpMediaRspInterface mMediaInterface;
5255 private @NonNull List<MediaSession.QueueItem> mNowPlayingList;
5356
5457 private final List<MediaSession.QueueItem> mEmptyNowPlayingList;
5558
5659 private long mLastTrackIdSent;
57- private boolean mNowPlayingListUpdated;
5860
5961 public AddressedMediaPlayer(AvrcpMediaRspInterface mediaInterface) {
6062 mEmptyNowPlayingList = new ArrayList<MediaSession.QueueItem>();
6163 mNowPlayingList = mEmptyNowPlayingList;
6264 mMediaInterface = mediaInterface;
6365 mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID;
64- mNowPlayingListUpdated = false;
6566 }
6667
6768 void cleanup() {
@@ -69,7 +70,6 @@ public class AddressedMediaPlayer {
6970 mNowPlayingList = mEmptyNowPlayingList;
7071 mMediaInterface = null;
7172 mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID;
72- mNowPlayingListUpdated = false;
7373 }
7474
7575 /* get now playing list from addressed player */
@@ -81,7 +81,7 @@ public class AddressedMediaPlayer {
8181 mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY, null);
8282 return;
8383 }
84- List<MediaSession.QueueItem> items = getNowPlayingList(mediaController);
84+ List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
8585 getFolderItemsFilterAttr(bdaddr, reqObj, items, AvrcpConstants.BTRC_SCOPE_NOW_PLAYING,
8686 reqObj.mStartItem, reqObj.mEndItem, mediaController);
8787 }
@@ -91,7 +91,7 @@ public class AddressedMediaPlayer {
9191 @Nullable MediaController mediaController) {
9292 int status = AvrcpConstants.RSP_NO_ERROR;
9393 long mediaId = ByteBuffer.wrap(itemAttr.mUid).getLong();
94- List<MediaSession.QueueItem> items = getNowPlayingList(mediaController);
94+ List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
9595
9696 // NOTE: this is out-of-spec (AVRCP 1.6.1 sec 6.10.4.3, p90) but we answer it anyway
9797 // because some CTs ask for it.
@@ -118,14 +118,10 @@ public class AddressedMediaPlayer {
118118
119119 /* Refresh and get the queue of now playing.
120120 */
121- private @NonNull List<MediaSession.QueueItem> getNowPlayingList(
122- @Nullable MediaController mediaController) {
121+ @NonNull
122+ List<MediaSession.QueueItem> updateNowPlayingList(@Nullable MediaController mediaController) {
123123 if (mediaController == null) return mEmptyNowPlayingList;
124124 List<MediaSession.QueueItem> items = mediaController.getQueue();
125- if (items != null && !mNowPlayingListUpdated) {
126- mNowPlayingList = items;
127- return mNowPlayingList;
128- }
129125 if (items == null) {
130126 Log.i(TAG, "null queue from " + mediaController.getPackageName()
131127 + ", constructing single-item list");
@@ -137,18 +133,17 @@ public class AddressedMediaPlayer {
137133 items.add(current);
138134 }
139135
136+ if (!items.equals(mNowPlayingList)) sendNowPlayingListChanged();
140137 mNowPlayingList = items;
141138
142- if (mNowPlayingListUpdated) sendNowPlayingListChanged();
143-
144139 return mNowPlayingList;
145140 }
146141
147142 private void sendNowPlayingListChanged() {
148143 if (mMediaInterface == null) return;
144+ if (DEBUG) Log.d(TAG, "sendNowPlayingListChanged()");
149145 mMediaInterface.uidsChangedRsp(AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
150146 mMediaInterface.nowPlayingChangedRsp(AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
151- mNowPlayingListUpdated = false;
152147 }
153148
154149 /* Constructs a queue item representing the current playing metadata from an
@@ -186,6 +181,7 @@ public class AddressedMediaPlayer {
186181
187182 private Bundle fillBundle(MediaMetadata metadata, Bundle currentExtras) {
188183 if (metadata == null) {
184+ Log.i(TAG, "fillBundle: metadata is null");
189185 return currentExtras;
190186 }
191187
@@ -207,15 +203,10 @@ public class AddressedMediaPlayer {
207203 return bundle;
208204 }
209205
210- void updateNowPlayingList(@Nullable MediaController mediaController) {
211- mNowPlayingListUpdated = true;
212- getNowPlayingList(mediaController);
213- }
214-
215206 /* Instructs media player to play particular media item */
216207 void playItem(byte[] bdaddr, byte[] uid, @Nullable MediaController mediaController) {
217208 long qid = ByteBuffer.wrap(uid).getLong();
218- List<MediaSession.QueueItem> items = getNowPlayingList(mediaController);
209+ List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
219210
220211 if (mediaController == null) {
221212 Log.e(TAG, "No mediaController when PlayItem " + qid + " requested");
@@ -246,7 +237,7 @@ public class AddressedMediaPlayer {
246237 }
247238
248239 void getTotalNumOfItems(byte[] bdaddr, @Nullable MediaController mediaController) {
249- List<MediaSession.QueueItem> items = getNowPlayingList(mediaController);
240+ List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
250241 if (DEBUG) Log.d(TAG, "getTotalNumOfItems: " + items.size() + " items.");
251242 mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, items.size());
252243 }
@@ -256,7 +247,6 @@ public class AddressedMediaPlayer {
256247 long qid = getActiveQueueItemId(mediaController);
257248 byte[] track = ByteBuffer.allocate(AvrcpConstants.UID_SIZE).putLong(qid).array();
258249 // The nowPlayingList changed: the new list has the full data for the current item
259- if (type == AvrcpConstants.NOTIFICATION_TYPE_CHANGED) sendNowPlayingListChanged();
260250 mMediaInterface.trackChangedRsp(type, track);
261251 mLastTrackIdSent = qid;
262252 }
@@ -391,10 +381,22 @@ public class AddressedMediaPlayer {
391381 MediaDescription desc = item.getDescription();
392382 Bundle extras = desc.getExtras();
393383 boolean isCurrentTrack = item.getQueueId() == getActiveQueueItemId(mediaController);
384+ MediaMetadata data = null;
394385 if (isCurrentTrack) {
395386 if (DEBUG) Log.d(TAG, "getAttrValue: item is active, using current data");
396- extras = fillBundle(mediaController.getMetadata(), extras);
387+ data = mediaController.getMetadata();
388+ if (data == null)
389+ Log.e(TAG, "getMetadata didn't give us any metadata for the current track");
390+ }
391+
392+ if (data == null) {
393+ // TODO: This code can be removed when b/63117921 is resolved
394+ data = (MediaMetadata) extras.get(GPM_BUNDLE_METADATA_KEY);
395+ extras = null; // We no longer need the data in here
397396 }
397+
398+ extras = fillBundle(data, extras);
399+
398400 if (DEBUG) Log.d(TAG, "getAttrValue: item " + item + " : " + desc);
399401 switch (attr) {
400402 case AvrcpConstants.ATTRID_TITLE:
@@ -511,7 +513,9 @@ public class AddressedMediaPlayer {
511513 private long getActiveQueueItemId(@Nullable MediaController controller) {
512514 if (controller == null) return MediaSession.QueueItem.UNKNOWN_ID;
513515 PlaybackState state = controller.getPlaybackState();
514- if (state == null) return MediaSession.QueueItem.UNKNOWN_ID;
516+ if (state == null || state.getState() == PlaybackState.STATE_BUFFERING
517+ || state.getState() == PlaybackState.STATE_NONE)
518+ return MediaSession.QueueItem.UNKNOWN_ID;
515519 long qid = state.getActiveQueueItemId();
516520 if (qid != MediaSession.QueueItem.UNKNOWN_ID) return qid;
517521 // Check if we're presenting a "one item queue"
--- a/src/com/android/bluetooth/avrcp/Avrcp.java
+++ b/src/com/android/bluetooth/avrcp/Avrcp.java
@@ -57,9 +57,11 @@ import com.android.bluetooth.Utils;
5757 import java.util.ArrayList;
5858 import java.util.Collections;
5959 import java.util.HashMap;
60+import java.util.HashSet;
6061 import java.util.Iterator;
6162 import java.util.List;
6263 import java.util.Map;
64+import java.util.Set;
6365 import java.util.SortedMap;
6466 import java.util.TreeMap;
6567
@@ -86,11 +88,12 @@ public final class Avrcp {
8688 private @NonNull PlaybackState mCurrentPlayState;
8789 private int mA2dpState;
8890 private int mPlayStatusChangedNT;
89- private int mReportedPlayStatus;
91+ private byte mReportedPlayStatus;
9092 private int mTrackChangedNT;
9193 private int mPlayPosChangedNT;
9294 private int mAddrPlayerChangedNT;
9395 private int mReportedPlayerID;
96+ private int mNowPlayingListChangedNT;
9497 private long mPlaybackIntervalMs;
9598 private long mLastReportedPosition;
9699 private long mNextPosMs;
@@ -99,7 +102,6 @@ public final class Avrcp {
99102 private int mRemoteVolume;
100103 private int mLastRemoteVolume;
101104 private int mInitialRemoteVolume;
102- private BrowsablePlayerListBuilder mBrowsableListBuilder;
103105
104106 /* Local volume in audio index 0-15 */
105107 private int mLocalVolume;
@@ -162,10 +164,8 @@ public final class Avrcp {
162164 private static final int MSG_ABS_VOL_TIMEOUT = 17;
163165 private static final int MSG_SET_A2DP_AUDIO_STATE = 18;
164166 private static final int MSG_NOW_PLAYING_CHANGED_RSP = 19;
165- private static final int MSG_UPDATE_MEDIA = 20;
166167
167168 private static final int CMD_TIMEOUT_DELAY = 2000;
168- private static final int MEDIA_DWELL_TIME = 1000;
169169 private static final int MAX_ERROR_RETRY_TIMES = 6;
170170 private static final int AVRCP_MAX_VOL = 127;
171171 private static final int AVRCP_BASE_VOLUME_STEP = 1;
@@ -244,6 +244,7 @@ public final class Avrcp {
244244 mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
245245 mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
246246 mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
247+ mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
247248 mPlaybackIntervalMs = 0L;
248249 mLastReportedPosition = -1;
249250 mNextPosMs = -1;
@@ -280,8 +281,6 @@ public final class Avrcp {
280281 mAbsVolThreshold = resources.getInteger(R.integer.a2dp_absolute_volume_initial_threshold);
281282 }
282283
283- mBrowsableListBuilder = new BrowsablePlayerListBuilder();
284-
285284 // Register for package removal intent broadcasts for media button receiver persistence
286285 IntentFilter pkgFilter = new IntentFilter();
287286 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -292,7 +291,7 @@ public final class Avrcp {
292291 context.registerReceiver(mAvrcpReceiver, pkgFilter);
293292
294293 IntentFilter bootFilter = new IntentFilter();
295- bootFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
294+ bootFilter.addAction(Intent.ACTION_USER_UNLOCKED);
296295 context.registerReceiver(mBootReceiver, bootFilter);
297296 }
298297
@@ -328,7 +327,7 @@ public final class Avrcp {
328327 if (manager == null || manager.isUserUnlocked()) {
329328 if (DEBUG) Log.d(TAG, "User already unlocked, initializing player lists");
330329 // initialize browsable player list and build media player list
331- mBrowsableListBuilder.start();
330+ buildBrowsablePlayerList();
332331 }
333332 }
334333
@@ -357,7 +356,6 @@ public final class Avrcp {
357356 mContext.unregisterReceiver(mAvrcpReceiver);
358357 mContext.unregisterReceiver(mBootReceiver);
359358
360- mBrowsableListBuilder.cleanup();
361359 mAddressedMediaPlayer.cleanup();
362360 mAvrcpBrowseManager.cleanup();
363361 }
@@ -373,12 +371,13 @@ public final class Avrcp {
373371 @Override
374372 public void onMetadataChanged(MediaMetadata metadata) {
375373 if (DEBUG) Log.v(TAG, "onMetadataChanged");
376- scheduleMediaUpdate();
374+ updateCurrentMediaState(false);
377375 }
378376 @Override
379377 public synchronized void onPlaybackStateChanged(PlaybackState state) {
380378 if (DEBUG) Log.v(TAG, "onPlaybackStateChanged: state " + state.toString());
381- scheduleMediaUpdate();
379+
380+ updateCurrentMediaState(false);
382381 }
383382
384383 @Override
@@ -478,7 +477,7 @@ public final class Avrcp {
478477 case MSG_NOW_PLAYING_CHANGED_RSP:
479478 if (DEBUG) Log.v(TAG, "MSG_NOW_PLAYING_CHANGED_RSP");
480479 removeMessages(MSG_NOW_PLAYING_CHANGED_RSP);
481- mAddressedMediaPlayer.updateNowPlayingList(mMediaController);
480+ updateCurrentMediaState(false);
482481 break;
483482
484483 case MSG_PLAY_INTERVAL_TIMEOUT:
@@ -692,7 +691,7 @@ public final class Avrcp {
692691 case MSG_SET_A2DP_AUDIO_STATE:
693692 if (DEBUG) Log.v(TAG, "MSG_SET_A2DP_AUDIO_STATE:" + msg.arg1);
694693 mA2dpState = msg.arg1;
695- scheduleMediaUpdate();
694+ updateCurrentMediaState(false);
696695 break;
697696
698697 case MSG_NATIVE_REQ_GET_FOLDER_ITEMS: {
@@ -778,13 +777,6 @@ public final class Avrcp {
778777 handlePassthroughCmd(msg.arg1, msg.arg2);
779778 break;
780779
781- case MSG_UPDATE_MEDIA:
782- if (DEBUG) Log.v(TAG, "MSG_UPDATE_MEDIA");
783- // Throttle to once per MEDIA_DWELL_TIME
784- removeMessages(MSG_UPDATE_MEDIA);
785- updateCurrentMediaState(false);
786- break;
787-
788780 default:
789781 Log.e(TAG, "unknown message! msg.what=" + msg.what);
790782 break;
@@ -834,10 +826,6 @@ public final class Avrcp {
834826
835827 if (newState != null) mCurrentPlayState = newState;
836828
837- if (mPlayStatusChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM
838- && (mReportedPlayStatus != newPlayStatus)) {
839- sendPlaybackStatus(AvrcpConstants.NOTIFICATION_TYPE_CHANGED, newPlayStatus);
840- }
841829 return mCurrentPlayState;
842830 }
843831
@@ -974,11 +962,6 @@ public final class Avrcp {
974962 }
975963 }
976964
977- private void scheduleMediaUpdate() {
978- Message msg = mHandler.obtainMessage(MSG_UPDATE_MEDIA);
979- mHandler.sendMessageDelayed(msg, MEDIA_DWELL_TIME);
980- }
981-
982965 private void updateCurrentMediaState(boolean registering) {
983966 // Only do player updates when we aren't registering for track changes.
984967 if (!registering) {
@@ -986,9 +969,13 @@ public final class Avrcp {
986969 registerNotificationRspAvalPlayerChangedNative(
987970 AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
988971 mAvailablePlayerViewChanged = false;
972+ return;
989973 }
990974 if (mAddrPlayerChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM
991975 && mReportedPlayerID != mCurrAddrPlayerID) {
976+ registerNotificationRspAvalPlayerChangedNative(
977+ AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
978+ mAvailablePlayerViewChanged = false;
992979 registerNotificationRspAddrPlayerChangedNative(
993980 AvrcpConstants.NOTIFICATION_TYPE_CHANGED, mCurrAddrPlayerID, sUIDCounter);
994981 mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
@@ -997,6 +984,8 @@ public final class Avrcp {
997984 mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
998985 mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
999986 mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
987+ mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
988+ mAddressedMediaPlayer.updateNowPlayingList(mMediaController);
1000989 // If the player changed, they need to re-request anything here again
1001990 // so we can skip the rest of the update.
1002991 return;
@@ -1010,25 +999,63 @@ public final class Avrcp {
1010999 if (mMediaController == null) {
10111000 currentAttributes = new MediaAttributes(null);
10121001 } else {
1013- newState = mMediaController.getPlaybackState();
10141002 currentAttributes = new MediaAttributes(mMediaController.getMetadata());
10151003 }
10161004 }
10171005
1018- long newQueueId = MediaSession.QueueItem.UNKNOWN_ID;
1019- if (newState != null) newQueueId = newState.getActiveQueueItemId();
1020- Log.v(TAG, "Media update: id " + mLastQueueId + "➡" + newQueueId + "? "
1021- + currentAttributes.toRedactedString());
1022- // Notify track changed if:
1023- // - The CT is registering for the notification
1024- // - Queue ID is UNKNOWN and MediaMetadata is different
1025- // - Queue ID is valid and different and MediaMetadata is different
1026- if (registering || (((newQueueId == -1) || (newQueueId != mLastQueueId))
1027- && !currentAttributes.equals(mMediaAttributes))) {
1028- sendTrackChangedRsp(registering);
1029- mMediaAttributes = currentAttributes;
1030- mLastQueueId = newQueueId;
1006+ byte newPlayStatus = getBluetoothPlayState(newState);
1007+
1008+ if (newState.getState() != PlaybackState.STATE_BUFFERING
1009+ && newState.getState() != PlaybackState.STATE_NONE) {
1010+ long newQueueId = MediaSession.QueueItem.UNKNOWN_ID;
1011+ if (newState != null) newQueueId = newState.getActiveQueueItemId();
1012+ Log.v(TAG, "Media update: id " + mLastQueueId + "➡" + newQueueId + "? "
1013+ + currentAttributes.toRedactedString() + " : "
1014+ + mMediaAttributes.toRedactedString());
1015+
1016+ // Dont send now playing list changed if the player doesn't support browsing
1017+ MediaPlayerInfo info = getAddressedPlayerInfo();
1018+ if (info != null && info.isBrowseSupported()) {
1019+ Log.v(TAG, "Check if NowPlayingList is updated");
1020+ mAddressedMediaPlayer.updateNowPlayingList(mMediaController);
1021+ }
1022+
1023+ if ((newQueueId == -1 || newQueueId != mLastQueueId)
1024+ && currentAttributes.equals(mMediaAttributes)
1025+ && newPlayStatus == PLAYSTATUS_PLAYING
1026+ && mReportedPlayStatus == PLAYSTATUS_STOPPED) {
1027+ // Most carkits like seeing the track changed before the
1028+ // playback state changed, but some controllers are slow
1029+ // to update their metadata. Hold of on sending the playback state
1030+ // update until after we know the current metadata is up to date
1031+ // and track changed has been sent. This was seen on BMW carkits
1032+ Log.i(TAG, "Waiting for metadata update to send track changed");
1033+
1034+ return;
1035+ }
1036+
1037+ // Notify track changed if:
1038+ // - The CT is registering for the notification
1039+ // - Queue ID is UNKNOWN and MediaMetadata is different
1040+ // - Queue ID is valid and different and MediaMetadata is different
1041+ if (registering || ((newQueueId == -1 || newQueueId != mLastQueueId)
1042+ && !currentAttributes.equals(mMediaAttributes))) {
1043+ Log.v(TAG, "Send track changed");
1044+ mMediaAttributes = currentAttributes;
1045+ mLastQueueId = newQueueId;
1046+ sendTrackChangedRsp(registering);
1047+ }
1048+ } else {
1049+ Log.i(TAG, "Skipping update due to invalid playback state");
10311050 }
1051+
1052+ // still send the updated play state if the playback state is none or buffering
1053+ Log.e(TAG, "play status change " + mReportedPlayStatus + "➡" + newPlayStatus);
1054+ if (mPlayStatusChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM
1055+ && (mReportedPlayStatus != newPlayStatus)) {
1056+ sendPlaybackStatus(AvrcpConstants.NOTIFICATION_TYPE_CHANGED, newPlayStatus);
1057+ }
1058+
10321059 sendPlayPosNotificationRsp(false);
10331060 }
10341061
@@ -1063,14 +1090,13 @@ public final class Avrcp {
10631090 case EVT_PLAY_STATUS_CHANGED:
10641091 mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
10651092 updatePlaybackState();
1066- sendPlaybackStatus(AvrcpConstants.NOTIFICATION_TYPE_INTERIM,
1067- getBluetoothPlayState(mCurrentPlayState));
1093+ sendPlaybackStatus(AvrcpConstants.NOTIFICATION_TYPE_INTERIM, mReportedPlayStatus);
10681094 break;
10691095
10701096 case EVT_TRACK_CHANGED:
10711097 Log.v(TAG, "Track changed notification enabled");
10721098 mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
1073- updateCurrentMediaState(true);
1099+ sendTrackChangedRsp(true);
10741100 break;
10751101
10761102 case EVT_PLAY_POS_CHANGED:
@@ -1105,6 +1131,7 @@ public final class Avrcp {
11051131 case EVENT_NOW_PLAYING_CONTENT_CHANGED:
11061132 if (DEBUG) Log.d(TAG, "Now Playing List changed notification enabled");
11071133 /* send interim response to remote device */
1134+ mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
11081135 if (!registerNotificationRspNowPlayingChangedNative(
11091136 AvrcpConstants.NOTIFICATION_TYPE_INTERIM)) {
11101137 Log.e(TAG, "EVENT_NOW_PLAYING_CONTENT_CHANGED: " +
@@ -1160,8 +1187,7 @@ public final class Avrcp {
11601187
11611188 private boolean isPlayingState(@Nullable PlaybackState state) {
11621189 if (state == null) return false;
1163- return (state != null) && (state.getState() == PlaybackState.STATE_PLAYING)
1164- || (state.getState() == PlaybackState.STATE_BUFFERING);
1190+ return (state != null) && (state.getState() == PlaybackState.STATE_PLAYING);
11651191 }
11661192
11671193 /**
@@ -1390,10 +1416,10 @@ public final class Avrcp {
13901416 @Override
13911417 public void onReceive(Context context, Intent intent) {
13921418 String action = intent.getAction();
1393- if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
1394- if (DEBUG) Log.d(TAG, "Boot completed, initializing player lists");
1419+ if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
1420+ if (DEBUG) Log.d(TAG, "User unlocked, initializing player lists");
13951421 /* initializing media player's list */
1396- mBrowsableListBuilder.start();
1422+ buildBrowsablePlayerList();
13971423 }
13981424 }
13991425 }
@@ -1439,7 +1465,7 @@ public final class Avrcp {
14391465 // new package has been added.
14401466 if (isBrowsableListUpdated(packageName)) {
14411467 // Rebuilding browsable players list
1442- mBrowsableListBuilder.start();
1468+ buildBrowsablePlayerList();
14431469 }
14441470 }
14451471 }
@@ -1548,8 +1574,15 @@ public final class Avrcp {
15481574 // checking for error cases
15491575 if (mMediaPlayerInfoList.isEmpty()) {
15501576 status = AvrcpConstants.RSP_NO_AVBL_PLAY;
1551- Log.w(TAG, " No Available Players to set, sending response back ");
1577+ Log.w(TAG, "setBrowsedPlayer: No available players! ");
15521578 } else {
1579+ // Workaround for broken controllers selecting ID 0
1580+ // Seen at least on Ford, Chevrolet MyLink
1581+ if (selectedId == 0) {
1582+ Log.w(TAG, "setBrowsedPlayer: workaround invalid id 0");
1583+ selectedId = mCurrAddrPlayerID;
1584+ }
1585+
15531586 // update current browse player id and start browsing service
15541587 updateNewIds(mCurrAddrPlayerID, selectedId);
15551588 String browsedPackage = getPackageName(selectedId);
@@ -1582,9 +1615,15 @@ public final class Avrcp {
15821615 @Override
15831616 public void onActiveSessionsChanged(
15841617 List<android.media.session.MediaController> newControllers) {
1618+ Set<String> updatedPackages = new HashSet<String>();
15851619 // Update the current players
15861620 for (android.media.session.MediaController controller : newControllers) {
1621+ String packageName = controller.getPackageName();
1622+ if (DEBUG) Log.v(TAG, "ActiveSession: " + MediaController.wrap(controller));
1623+ // Only use the first (highest priority) controller from each package
1624+ if (updatedPackages.contains(packageName)) continue;
15871625 addMediaPlayerController(controller);
1626+ updatedPackages.add(packageName);
15881627 }
15891628
15901629 if (newControllers.size() > 0 && getAddressedPlayerInfo() == null) {
@@ -1592,7 +1631,7 @@ public final class Avrcp {
15921631 Log.v(TAG, "No addressed player but active sessions, taking first.");
15931632 setAddressedMediaSessionPackage(newControllers.get(0).getPackageName());
15941633 }
1595- scheduleMediaUpdate();
1634+ updateCurrentMediaState(false);
15961635 }
15971636 };
15981637
@@ -1602,13 +1641,17 @@ public final class Avrcp {
16021641 updateCurrentController(0, mCurrBrowsePlayerID);
16031642 return;
16041643 }
1644+ if (packageName.equals("com.android.server.telecom")) {
1645+ Log.d(TAG, "Ignore addressed media session change to telecom");
1646+ return;
1647+ }
16051648 // No change.
16061649 if (getPackageName(mCurrAddrPlayerID).equals(packageName)) return;
16071650 if (DEBUG) Log.v(TAG, "Changing addressed media session to " + packageName);
16081651 // If the player doesn't exist, we need to add it.
16091652 if (getMediaPlayerInfo(packageName) == null) {
16101653 addMediaPlayerPackage(packageName);
1611- scheduleMediaUpdate();
1654+ updateCurrentMediaState(false);
16121655 }
16131656 synchronized (mMediaPlayerInfoList) {
16141657 for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
@@ -1616,7 +1659,7 @@ public final class Avrcp {
16161659 int newAddrID = entry.getKey();
16171660 if (DEBUG) Log.v(TAG, "Set addressed #" + newAddrID + " " + entry.getValue());
16181661 updateCurrentController(newAddrID, mCurrBrowsePlayerID);
1619- scheduleMediaUpdate();
1662+ updateCurrentMediaState(false);
16201663 return;
16211664 }
16221665 }
@@ -1628,6 +1671,10 @@ public final class Avrcp {
16281671 private void setActiveMediaSession(MediaSession.Token token) {
16291672 android.media.session.MediaController activeController =
16301673 new android.media.session.MediaController(mContext, token);
1674+ if (activeController.getPackageName().equals("com.android.server.telecom")) {
1675+ Log.d(TAG, "Ignore active media session change to telecom");
1676+ return;
1677+ }
16311678 if (DEBUG) Log.v(TAG, "Set active media session " + activeController.getPackageName());
16321679 addMediaPlayerController(activeController);
16331680 setAddressedMediaSessionPackage(activeController.getPackageName());
@@ -1667,71 +1714,33 @@ public final class Avrcp {
16671714 return browseServiceName;
16681715 }
16691716
1670- private class BrowsablePlayerListBuilder extends MediaBrowser.ConnectionCallback {
1671- List<ResolveInfo> mWaiting;
1672- BrowsePlayerInfo mCurrentPlayer;
1673- MediaBrowser mCurrentBrowser;
1674- boolean mPlayersChanged;
1675-
1676- public BrowsablePlayerListBuilder() {}
1677-
1678- public void start() {
1717+ void buildBrowsablePlayerList() {
1718+ synchronized (mBrowsePlayerInfoList) {
16791719 mBrowsePlayerInfoList.clear();
1680- cleanup();
16811720 Intent intent = new Intent(android.service.media.MediaBrowserService.SERVICE_INTERFACE);
1682- mWaiting = mPackageManager.queryIntentServices(intent, PackageManager.MATCH_ALL);
1683- connectNextPlayer();
1684- }
1685-
1686- public void cleanup() {
1687- if (mWaiting != null) mWaiting.clear();
1688- mPlayersChanged = false;
1689- if (mCurrentBrowser != null) mCurrentBrowser.disconnect();
1690- }
1691-
1692- private void connectNextPlayer() {
1693- if (mWaiting.isEmpty()) {
1694- // Done. Send players changed if needed.
1695- if (mPlayersChanged) {
1696- registerNotificationRspAvalPlayerChangedNative(
1697- AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
1721+ List<ResolveInfo> playerList =
1722+ mPackageManager.queryIntentServices(intent, PackageManager.MATCH_ALL);
1723+
1724+ for (ResolveInfo info : playerList) {
1725+ String displayableName = info.loadLabel(mPackageManager).toString();
1726+ String serviceName = info.serviceInfo.name;
1727+ String packageName = info.serviceInfo.packageName;
1728+
1729+ if (DEBUG) Log.d(TAG, "Adding " + serviceName + " to list of browsable players");
1730+ BrowsePlayerInfo currentPlayer =
1731+ new BrowsePlayerInfo(packageName, displayableName, serviceName);
1732+ mBrowsePlayerInfoList.add(currentPlayer);
1733+ MediaPlayerInfo playerInfo = getMediaPlayerInfo(packageName);
1734+ MediaController controller =
1735+ (playerInfo == null) ? null : playerInfo.getMediaController();
1736+ // Refresh the media player entry so it notices we can browse
1737+ if (controller != null) {
1738+ addMediaPlayerController(controller.getWrappedInstance());
1739+ } else {
1740+ addMediaPlayerPackage(packageName);
16981741 }
1699- return;
1700- }
1701- ResolveInfo info = mWaiting.remove(0);
1702- String displayableName = info.loadLabel(mPackageManager).toString();
1703- String serviceName = info.serviceInfo.name;
1704- String packageName = info.serviceInfo.packageName;
1705-
1706- mCurrentPlayer = new BrowsePlayerInfo(packageName, displayableName, serviceName);
1707- mCurrentBrowser = new MediaBrowser(
1708- mContext, new ComponentName(packageName, serviceName), this, null);
1709- if (DEBUG) Log.d(TAG, "Trying to connect to " + serviceName);
1710- mCurrentBrowser.connect();
1711- }
1712-
1713- @Override
1714- public void onConnected() {
1715- Log.d(TAG, "BrowsablePlayerListBuilder: " + mCurrentPlayer.packageName + " OK");
1716- mCurrentBrowser.disconnect();
1717- mCurrentBrowser = null;
1718- mBrowsePlayerInfoList.add(mCurrentPlayer);
1719- MediaPlayerInfo info = getMediaPlayerInfo(mCurrentPlayer.packageName);
1720- MediaController controller = (info == null) ? null : info.getMediaController();
1721- // Refresh the media player entry so it notices we can browse
1722- if (controller != null) {
1723- addMediaPlayerController(controller.getWrappedInstance());
1724- } else {
1725- addMediaPlayerPackage(mCurrentPlayer.packageName);
17261742 }
1727- mPlayersChanged = true;
1728- connectNextPlayer();
1729- }
1730-
1731- @Override
1732- public void onConnectionFailed() {
1733- Log.d(TAG, "BrowsablePlayerListBuilder: " + mCurrentPlayer.packageName + " FAIL");
1734- connectNextPlayer();
1743+ updateCurrentMediaState(false);
17351744 }
17361745 }
17371746
@@ -1755,7 +1764,7 @@ public final class Avrcp {
17551764 addMediaPlayerController(controller);
17561765 }
17571766
1758- scheduleMediaUpdate();
1767+ updateCurrentMediaState(false);
17591768
17601769 if (mMediaPlayerInfoList.size() > 0) {
17611770 // Set the first one as the Addressed Player
@@ -1804,6 +1813,10 @@ public final class Avrcp {
18041813 int updateId = -1;
18051814 boolean updated = false;
18061815 boolean currentRemoved = false;
1816+ if (info.getPackageName().equals("com.android.server.telecom")) {
1817+ Log.d(TAG, "Skip adding telecom to the media player info list");
1818+ return updated;
1819+ }
18071820 synchronized (mMediaPlayerInfoList) {
18081821 for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
18091822 MediaPlayerInfo current = entry.getValue();
@@ -1828,11 +1841,10 @@ public final class Avrcp {
18281841 mAvailablePlayerViewChanged = true;
18291842 }
18301843 mMediaPlayerInfoList.put(updateId, info);
1831- if (DEBUG)
1832- Log.d(TAG, (updated ? "update #" : "add #") + updateId + ":" + info.toString());
1833- if (currentRemoved || updateId == mCurrAddrPlayerID) {
1834- updateCurrentController(updateId, mCurrBrowsePlayerID);
1835- }
1844+ }
1845+ if (DEBUG) Log.d(TAG, (updated ? "update #" : "add #") + updateId + ":" + info.toString());
1846+ if (currentRemoved || updateId == mCurrAddrPlayerID) {
1847+ updateCurrentController(updateId, mCurrBrowsePlayerID);
18361848 }
18371849 return updated;
18381850 }
@@ -1887,9 +1899,9 @@ public final class Avrcp {
18871899
18881900 switch (pbState.getState()) {
18891901 case PlaybackState.STATE_PLAYING:
1890- case PlaybackState.STATE_BUFFERING:
18911902 return PLAYSTATUS_PLAYING;
18921903
1904+ case PlaybackState.STATE_BUFFERING:
18931905 case PlaybackState.STATE_STOPPED:
18941906 case PlaybackState.STATE_NONE:
18951907 case PlaybackState.STATE_CONNECTING:
@@ -2041,14 +2053,18 @@ public final class Avrcp {
20412053 short[] featureBitMaskValues =
20422054 new short[numPlayers * AvrcpConstants.AVRC_FEATURE_MASK_SIZE];
20432055
2044- int players = 0;
2056+ // Reserve the first spot for the currently addressed player if
2057+ // we have one
2058+ int players = mMediaPlayerInfoList.containsKey(mCurrAddrPlayerID) ? 1 : 0;
20452059 for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
2060+ int idx = players;
2061+ if (entry.getKey() == mCurrAddrPlayerID) idx = 0;
20462062 MediaPlayerInfo info = entry.getValue();
2047- playerIds[players] = entry.getKey();
2048- playerTypes[players] = info.getMajorType();
2049- playerSubTypes[players] = info.getSubType();
2050- displayableNameArray[players] = info.getDisplayableName();
2051- playStatusValues[players] = info.getPlayStatus();
2063+ playerIds[idx] = entry.getKey();
2064+ playerTypes[idx] = info.getMajorType();
2065+ playerSubTypes[idx] = info.getSubType();
2066+ displayableNameArray[idx] = info.getDisplayableName();
2067+ playStatusValues[idx] = info.getPlayStatus();
20522068
20532069 short[] featureBits = info.getFeatureBitMask();
20542070 for (int numBit = 0; numBit < featureBits.length; numBit++) {
@@ -2056,19 +2072,18 @@ public final class Avrcp {
20562072 byte octet = (byte) (featureBits[numBit] / 8);
20572073 /* gives the bit position within the octet */
20582074 byte bit = (byte) (featureBits[numBit] % 8);
2059- featureBitMaskValues[(players * AvrcpConstants.AVRC_FEATURE_MASK_SIZE)
2060- + octet] |= (1 << bit);
2075+ featureBitMaskValues[(idx * AvrcpConstants.AVRC_FEATURE_MASK_SIZE) + octet] |=
2076+ (1 << bit);
20612077 }
20622078
20632079 /* printLogs */
20642080 if (DEBUG) {
2065- Log.d(TAG, "Player " + playerIds[players] + ": " + displayableNameArray[players]
2066- + " type: " + playerTypes[players] + ", "
2067- + playerSubTypes[players] + " status: "
2068- + playStatusValues[players]);
2081+ Log.d(TAG, "Player " + playerIds[idx] + ": " + displayableNameArray[idx]
2082+ + " type: " + playerTypes[idx] + ", " + playerSubTypes[idx]
2083+ + " status: " + playStatusValues[idx]);
20692084 }
20702085
2071- players++;
2086+ if (idx != 0) players++;
20722087 }
20732088
20742089 if (DEBUG) Log.d(TAG, "prepareMediaPlayerRspObj: numPlayers = " + numPlayers);
@@ -2125,14 +2140,12 @@ public final class Avrcp {
21252140 mMediaController = newController;
21262141 if (mMediaController != null) {
21272142 mMediaController.registerCallback(mMediaControllerCb, mHandler);
2128- mAddressedMediaPlayer.updateNowPlayingList(mMediaController);
21292143 } else {
2130- mAddressedMediaPlayer.updateNowPlayingList(null);
21312144 registerRsp = false;
21322145 }
21332146 }
21342147 }
2135- scheduleMediaUpdate();
2148+ updateCurrentMediaState(false);
21362149 return registerRsp;
21372150 }
21382151
@@ -2207,6 +2220,12 @@ public final class Avrcp {
22072220 }
22082221
22092222 private void handleGetItemAttr(AvrcpCmd.ItemAttrCmd itemAttr) {
2223+ if (itemAttr.mUidCounter != sUIDCounter) {
2224+ Log.e(TAG, "handleGetItemAttr: invaild uid counter.");
2225+ getItemAttrRspNative(
2226+ itemAttr.mAddress, AvrcpConstants.RSP_UID_CHANGED, (byte) 0, null, null);
2227+ return;
2228+ }
22102229 if (itemAttr.mScope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
22112230 if (mCurrAddrPlayerID == NO_PLAYER_ID) {
22122231 getItemAttrRspNative(
@@ -2503,9 +2522,15 @@ public final class Avrcp {
25032522 }
25042523
25052524 public void nowPlayingChangedRsp(int type) {
2525+ if (mNowPlayingListChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
2526+ if (DEBUG) Log.d(TAG, "NowPlayingListChanged: Not registered or requesting.");
2527+ return;
2528+ }
2529+
25062530 if (!registerNotificationRspNowPlayingChangedNative(type)) {
25072531 Log.e(TAG, "registerNotificationRspNowPlayingChangedNative failed!");
25082532 }
2533+ mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
25092534 }
25102535
25112536 public void trackChangedRsp(int type, byte[] uid) {
--- a/src/com/android/bluetooth/avrcp/BrowsedMediaPlayer.java
+++ b/src/com/android/bluetooth/avrcp/BrowsedMediaPlayer.java
@@ -53,6 +53,7 @@ class BrowsedMediaPlayer {
5353
5454 /* package and service name of target Media Player which is set for browsing */
5555 private String mPackageName;
56+ private String mConnectingPackageName;
5657 private String mClassName;
5758 private Context mContext;
5859 private AvrcpMediaRspInterface mMediaInterface;
@@ -83,20 +84,34 @@ class BrowsedMediaPlayer {
8384 private List<MediaBrowser.MediaItem> mFolderItems = null;
8485
8586 /* Connection state callback handler */
86- private MediaBrowser.ConnectionCallback browseMediaConnectionCallback =
87- new MediaBrowser.ConnectionCallback() {
87+ class MediaConnectionCallback extends MediaBrowser.ConnectionCallback {
88+ private String mCallbackPackageName;
89+ private MediaBrowser mBrowser;
90+
91+ public MediaConnectionCallback(String packageName) {
92+ this.mCallbackPackageName = packageName;
93+ }
94+
95+ public void setBrowser(MediaBrowser b) {
96+ mBrowser = b;
97+ }
8898
8999 @Override
90100 public void onConnected() {
91101 mConnState = CONNECTED;
92102 if (DEBUG) Log.d(TAG, "mediaBrowser CONNECTED to " + mPackageName);
93103 /* perform init tasks and set player as browsed player on successful connection */
94- onBrowseConnect();
104+ onBrowseConnect(mCallbackPackageName, mBrowser);
105+
106+ // Remove what could be a circular dependency causing GC to never happen on this object
107+ mBrowser = null;
95108 }
96109
97110 @Override
98111 public void onConnectionFailed() {
99112 mConnState = DISCONNECTED;
113+ // Remove what could be a circular dependency causing GC to never happen on this object
114+ mBrowser = null;
100115 Log.e(TAG, "mediaBrowser Connection failed with " + mPackageName
101116 + ", Sending fail response!");
102117 mMediaInterface.setBrowsedPlayerRsp(mBDAddr, AvrcpConstants.RSP_INTERNAL_ERR,
@@ -105,10 +120,11 @@ class BrowsedMediaPlayer {
105120
106121 @Override
107122 public void onConnectionSuspended() {
123+ mBrowser = null;
108124 mConnState = SUSPENDED;
109125 Log.e(TAG, "mediaBrowser SUSPENDED connection with " + mPackageName);
110126 }
111- };
127+ }
112128
113129 /* Subscription callback handler. Subscribe to a folder to get its contents */
114130 private MediaBrowser.SubscriptionCallback folderItemsCb =
@@ -251,26 +267,44 @@ class BrowsedMediaPlayer {
251267 }
252268
253269 /* initialize mediacontroller in order to communicate with media player. */
254- private void onBrowseConnect() {
255- boolean isError = false;
270+ private void onBrowseConnect(String connectedPackage, MediaBrowser browser) {
271+ if (!connectedPackage.equals(mConnectingPackageName)) {
272+ Log.w(TAG, "onBrowseConnect: recieved callback for package we aren't connecting to "
273+ + connectedPackage);
274+ return;
275+ }
276+ mConnectingPackageName = null;
277+
278+ if (browser == null) {
279+ Log.e(TAG, "onBrowseConnect: received a null browser for " + connectedPackage);
280+ mMediaInterface.setBrowsedPlayerRsp(
281+ mBDAddr, AvrcpConstants.RSP_INTERNAL_ERR, (byte) 0x00, 0, null);
282+ return;
283+ }
284+
256285 MediaSession.Token token = null;
257286 try {
258- /* get rootfolder uid from media player */
259- if (mMediaId == null) {
260- mMediaId = mMediaBrowser.getRoot();
261- /*
262- * assuming that root folder uid will not change on uids changed
263- */
264- mRootFolderUid = mMediaId;
265- /* store root folder uid to stack */
266- mPathStack.push(mMediaId);
267- }
268-
269- if (!mMediaBrowser.isConnected()) {
287+ if (!browser.isConnected()) {
270288 Log.e(TAG, "setBrowsedPlayer: " + mPackageName + "not connected");
271- } else if ((token = mMediaBrowser.getSessionToken()) == null) {
289+ } else if ((token = browser.getSessionToken()) == null) {
272290 Log.e(TAG, "setBrowsedPlayer: " + mPackageName + "no Session token");
273291 } else {
292+ /* update to the new MediaBrowser */
293+ if (mMediaBrowser != null) mMediaBrowser.disconnect();
294+ mMediaBrowser = browser;
295+ mPackageName = connectedPackage;
296+
297+ /* get rootfolder uid from media player */
298+ if (mMediaId == null) {
299+ mMediaId = mMediaBrowser.getRoot();
300+ /*
301+ * assuming that root folder uid will not change on uids changed
302+ */
303+ mRootFolderUid = mMediaId;
304+ /* store root folder uid to stack */
305+ mPathStack.push(mMediaId);
306+ }
307+
274308 mMediaController = MediaController.wrap(
275309 new android.media.session.MediaController(mContext, token));
276310 /* get root folder items */
@@ -287,7 +321,7 @@ class BrowsedMediaPlayer {
287321 }
288322
289323 public void setBrowsed(String packageName, String cls) {
290- mPackageName = packageName;
324+ mConnectingPackageName = packageName;
291325 mClassName = cls;
292326 /* cleanup variables from previous browsed calls */
293327 mFolderItems = null;
@@ -298,10 +332,14 @@ class BrowsedMediaPlayer {
298332 * will be required while navigating up the folder
299333 */
300334 mPathStack = new Stack<String>();
335+
301336 /* Bind to MediaBrowseService of MediaPlayer */
302- mMediaBrowser = new MediaBrowser(mContext, new ComponentName(mPackageName, mClassName),
303- browseMediaConnectionCallback, null);
304- mMediaBrowser.connect();
337+ MediaConnectionCallback callback = new MediaConnectionCallback(packageName);
338+ MediaBrowser tempBrowser = new MediaBrowser(
339+ mContext, new ComponentName(packageName, mClassName), callback, null);
340+ callback.setBrowser(tempBrowser);
341+
342+ tempBrowser.connect();
305343 }
306344
307345 /* called when connection to media player is closed */
@@ -494,7 +532,7 @@ class BrowsedMediaPlayer {
494532 */
495533 private List<MediaBrowser.MediaItem> checkIndexOutofBounds(
496534 byte[] bdaddr, List<MediaBrowser.MediaItem> children, long startItem, long endItem) {
497- if (endItem > children.size()) endItem = children.size() - 1;
535+ if (endItem >= children.size()) endItem = children.size() - 1;
498536 if (startItem >= Integer.MAX_VALUE) startItem = Integer.MAX_VALUE;
499537 try {
500538 List<MediaBrowser.MediaItem> childrenSubList =
@@ -651,9 +689,11 @@ class BrowsedMediaPlayer {
651689
652690 case AvrcpConstants.ATTRID_GENRE:
653691 attrValue = extras.getString(MediaMetadata.METADATA_KEY_GENRE);
692+ break;
654693
655694 case AvrcpConstants.ATTRID_PLAY_TIME:
656695 attrValue = extras.getString(MediaMetadata.METADATA_KEY_DURATION);
696+ break;
657697
658698 case AvrcpConstants.ATTRID_COVER_ART:
659699 Log.e(TAG, "getAttrValue: Cover art attribute not supported");
Show on old repository browser