OSDN Git Service

AVRCP: update metadata and state more consistently
authorMarie Janssen <jamuraa@google.com>
Tue, 25 Apr 2017 01:36:19 +0000 (18:36 -0700)
committerMarie Janssen <jamuraa@google.com>
Thu, 27 Apr 2017 20:12:12 +0000 (13:12 -0700)
When we get onMetadata it's not updated quite right, and the
PlaybackState gives the wrong answer for getActiveQueueItemId, which
means that we don't send the correct updated data.  Update the metadata
when we get a onPlaybackStateChanged as well, so we refresh the
metadata and notify correctly for EVENT_TRACK_CHANGED.

Keep track of the last queue item we sent and don't send an update if
the queue item has not changed.

Use A2DP streaming state only when we don't have strong signals from
Media framework.

Use PlaybackState.getLastPositionUpdateTime() instead of trying to
figure it out on our own.

Trust MediaAttributes with the track length instead of duplicating it.

Test: connect to BMW kit and skip some tracks, new data will show up
correctly
Bug: 37209435
Bug: 36016671
Bug: 36357185
Bug: 36055995
Change-Id: I332aaadeabcfe8284084a34c964e5f0d07bfeef4

(cherry picked from commit 0a429916782c20980e7f0893c503c633b8341f88)

src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java
src/com/android/bluetooth/avrcp/Avrcp.java

index 02d077f..d122675 100644 (file)
@@ -45,15 +45,19 @@ public class AddressedMediaPlayer {
     private AvrcpMediaRspInterface mMediaInterface;
     private List<MediaSession.QueueItem> mNowPlayingList;
 
+    private long mLastTrackIdSent;
+
     public AddressedMediaPlayer(AvrcpMediaRspInterface mediaInterface) {
         mNowPlayingList = null;
         mMediaInterface = mediaInterface;
+        mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID;
     }
 
     void cleanup() {
         if (DEBUG) Log.v(TAG, "cleanup");
         mNowPlayingList = null;
         mMediaInterface = null;
+        mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID;
     }
 
     /* get now playing list from addressed player */
@@ -242,22 +246,27 @@ public class AddressedMediaPlayer {
         mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, items.size());
     }
 
-    void sendTrackChangeWithId(int trackChangedNT, MediaController mediaController) {
+    boolean sendTrackChangeWithId(boolean requesting, MediaController mediaController) {
         if (DEBUG) Log.d(TAG, "sendTrackChangeWithId");
         byte[] track;
-        if (mediaController == null) {
-            mMediaInterface.trackChangedRsp(trackChangedNT, AvrcpConstants.NO_TRACK_SELECTED);
-            return;
-        }
         long qid = MediaSession.QueueItem.UNKNOWN_ID;
-        PlaybackState state = mediaController.getPlaybackState();
-        if (state != null) {
-            qid = state.getActiveQueueItemId();
+        if (mediaController != null) {
+            PlaybackState state = mediaController.getPlaybackState();
+            /* for any item associated with NowPlaying, uid is queueId */
+            if (state != null) qid = state.getActiveQueueItemId();
+        }
+        if (!requesting && qid == mLastTrackIdSent) {
+            if (DEBUG) Log.d(TAG, "not sending duplicate track changed id " + qid);
+            return false;
         }
-        /* for any item associated with NowPlaying, uid is queueId */
         track = ByteBuffer.allocate(AvrcpConstants.UID_SIZE).putLong(qid).array();
         if (DEBUG) Log.d(TAG, "trackChangedRsp: 0x" + Utils.byteArrayToString(track));
+
+        int trackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+        if (requesting) trackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
         mMediaInterface.trackChangedRsp(trackChangedNT, track);
+        mLastTrackIdSent = qid;
+        return (trackChangedNT == AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
     }
 
     /*
@@ -418,11 +427,13 @@ public class AddressedMediaPlayer {
                     break;
 
                 case AvrcpConstants.ATTRID_TRACK_NUM:
-                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_TRACK_NUMBER);
+                    attrValue =
+                            Long.toString(extras.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER));
                     break;
 
                 case AvrcpConstants.ATTRID_NUM_TRACKS:
-                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_NUM_TRACKS);
+                    attrValue =
+                            Long.toString(extras.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS));
                     break;
 
                 case AvrcpConstants.ATTRID_GENRE:
@@ -430,7 +441,7 @@ public class AddressedMediaPlayer {
                     break;
 
                 case AvrcpConstants.ATTRID_PLAY_TIME:
-                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_DURATION);
+                    attrValue = Long.toString(extras.getLong(MediaMetadata.METADATA_KEY_DURATION));
                     break;
 
                 case AvrcpConstants.ATTRID_COVER_ART:
index 2c084d0..f589026 100644 (file)
@@ -81,12 +81,11 @@ public final class Avrcp {
     private PackageManager mPackageManager;
     private int mTransportControlFlags;
     private PlaybackState mCurrentPlayState;
-    private long mLastStateUpdate;
+    private int mA2dpState;
     private int mPlayStatusChangedNT;
     private int mTrackChangedNT;
     private int mPlayPosChangedNT;
     private long mTracksPlayed;
-    private long mSongLengthMs;
     private long mPlaybackIntervalMs;
     private long mLastReportedPosition;
     private long mNextPosMs;
@@ -235,11 +234,10 @@ public final class Avrcp {
     private Avrcp(Context context) {
         mMediaAttributes = new MediaAttributes(null);
         mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();
+        mA2dpState = BluetoothA2dp.STATE_NOT_PLAYING;
         mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
         mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
         mTracksPlayed = 0;
-        mLastStateUpdate = -1L;
-        mSongLengthMs = 0L;
         mPlaybackIntervalMs = 0L;
         mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
         mLastReportedPosition = -1;
@@ -364,24 +362,13 @@ public final class Avrcp {
         @Override
         public void onMetadataChanged(MediaMetadata metadata) {
             Log.v(TAG, "MediaController metadata changed");
-            updateMetadata(metadata);
+            updateCurrentMediaState();
         }
 
         @Override
         public synchronized void onPlaybackStateChanged(PlaybackState state) {
             if (DEBUG) Log.v(TAG, "onPlaybackStateChanged: state " + state.toString());
-
-            updatePlaybackState(state);
-
-            byte stateBytes = (byte) convertPlayStateToBytes(state.getState());
-
-            /* updating 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);
-            }
+            updateCurrentMediaState();
         }
 
         @Override
@@ -434,7 +421,7 @@ public final class Avrcp {
                 byte[] address = (byte[]) msg.obj;
                 if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_GET_PLAY_STATUS");
                 getPlayStatusRspNative(address, convertPlayStateToPlayStatus(mCurrentPlayState),
-                        (int) mSongLengthMs, (int) getPlayPosition());
+                        (int) mMediaAttributes.getLength(), (int) getPlayPosition());
                 break;
             }
 
@@ -699,7 +686,8 @@ public final class Avrcp {
 
             case MSG_SET_A2DP_AUDIO_STATE:
                 if (DEBUG) Log.v(TAG, "MSG_SET_A2DP_AUDIO_STATE:" + msg.arg1);
-                updateA2dpAudioState(msg.arg1);
+                mA2dpState = msg.arg1;
+                updateCurrentMediaState();
                 break;
 
             case MSG_NATIVE_REQ_GET_FOLDER_ITEMS: {
@@ -792,30 +780,22 @@ public final class Avrcp {
         }
     }
 
-    private void updateA2dpAudioState(int state) {
-        boolean isPlaying = (state == BluetoothA2dp.STATE_PLAYING);
-        if (isPlaying != isPlayingState(mCurrentPlayState)) {
-            /* if a2dp is streaming, check to make sure music is active */
-            if (isPlaying && !mAudioManager.isMusicActive())
-                return;
-            PlaybackState.Builder builder = new PlaybackState.Builder();
-            if (isPlaying) {
-                builder.setState(PlaybackState.STATE_PLAYING,
-                        PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f);
-            } else {
-                builder.setState(PlaybackState.STATE_PAUSED,
-                        PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f);
-            }
-            updatePlaybackState(builder.build());
-        }
-    }
-
     private void updatePlaybackState(PlaybackState state) {
         if (state == null) {
           state = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE,
                 PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f).build();
         }
 
+        byte stateBytes = (byte) convertPlayStateToBytes(state.getState());
+
+        /* updating 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);
+        }
+
         int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState);
         int newPlayStatus = convertPlayStateToPlayStatus(state);
 
@@ -826,7 +806,6 @@ public final class Avrcp {
         }
 
         mCurrentPlayState = state;
-        mLastStateUpdate = SystemClock.elapsedRealtime();
 
         sendPlayPosNotificationRsp(false);
 
@@ -849,7 +828,7 @@ public final class Avrcp {
         private String mediaNumber;
         private String mediaTotalNumber;
         private String genre;
-        private String playingTimeMs;
+        private long playingTimeMs;
 
         private static final int ATTR_TITLE = 1;
         private static final int ATTR_ARTIST_NAME = 2;
@@ -870,7 +849,7 @@ public final class Avrcp {
             mediaNumber = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER));
             mediaTotalNumber = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS));
             genre = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_GENRE));
-            playingTimeMs = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_DURATION));
+            playingTimeMs = data.getLong(MediaMetadata.METADATA_KEY_DURATION);
 
             // Try harder for the title.
             title = data.getString(MediaMetadata.METADATA_KEY_TITLE);
@@ -888,6 +867,11 @@ public final class Avrcp {
                 title = new String();
         }
 
+        public long getLength() {
+            if (!exists) return 0L;
+            return playingTimeMs;
+        }
+
         public boolean equals(MediaAttributes other) {
             if (other == null)
                 return false;
@@ -898,13 +882,11 @@ public final class Avrcp {
             if (exists == false)
                 return true;
 
-            return (title.equals(other.title)) &&
-                (artistName.equals(other.artistName)) &&
-                (albumName.equals(other.albumName)) &&
-                (mediaNumber.equals(other.mediaNumber)) &&
-                (mediaTotalNumber.equals(other.mediaTotalNumber)) &&
-                (genre.equals(other.genre)) &&
-                (playingTimeMs.equals(other.playingTimeMs));
+            return (title.equals(other.title)) && (artistName.equals(other.artistName))
+                    && (albumName.equals(other.albumName))
+                    && (mediaNumber.equals(other.mediaNumber))
+                    && (mediaTotalNumber.equals(other.mediaTotalNumber))
+                    && (genre.equals(other.genre)) && (playingTimeMs == other.playingTimeMs);
         }
 
         public String getString(int attrId) {
@@ -925,7 +907,7 @@ public final class Avrcp {
                 case ATTR_GENRE:
                     return genre;
                 case ATTR_PLAYING_TIME_MS:
-                    return playingTimeMs;
+                    return Long.toString(playingTimeMs);
                 default:
                     return new String();
             }
@@ -944,40 +926,47 @@ public final class Avrcp {
                 return "[MediaAttributes: none]";
             }
 
-            return "[MediaAttributes: " + title + " - " + albumName + " by "
-                    + artistName + " (" + mediaNumber + "/" + mediaTotalNumber + ") "
-                    + genre + "]";
+            return "[MediaAttributes: " + title + " - " + albumName + " by " + artistName + " ("
+                    + playingTimeMs + " " + mediaNumber + "/" + mediaTotalNumber + ") " + genre
+                    + "]";
         }
     }
 
-    private void updateMetadata(MediaMetadata data) {
-        MediaAttributes oldAttributes = mMediaAttributes;
-        mMediaAttributes = new MediaAttributes(data);
-        if (data == null) {
-            mSongLengthMs = 0L;
-        } else {
-            mSongLengthMs = data.getLong(MediaMetadata.METADATA_KEY_DURATION);
+    private void updateCurrentMediaState() {
+        if (mMediaController == null) {
+            // Use A2DP state if we don't have a MediaControlller
+            boolean isPlaying = (mA2dpState == BluetoothA2dp.STATE_PLAYING);
+            if (isPlaying != isPlayingState(mCurrentPlayState)) {
+                /* if a2dp is streaming, check to make sure music is active */
+                if (isPlaying && !mAudioManager.isMusicActive()) return;
+                PlaybackState.Builder builder = new PlaybackState.Builder();
+                if (isPlaying) {
+                    builder.setState(PlaybackState.STATE_PLAYING,
+                            PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f);
+                } else {
+                    builder.setState(PlaybackState.STATE_PAUSED,
+                            PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f);
+                }
+                updatePlaybackState(builder.build());
+            }
+            // Can't get metadata from A2dp so we're done.
+            return;
         }
 
-        if (!oldAttributes.equals(mMediaAttributes)) {
+        MediaAttributes currentAttributes = mMediaAttributes;
+
+        PlaybackState newState = mMediaController.getPlaybackState();
+
+        // Metadata
+        mMediaAttributes = new MediaAttributes(mMediaController.getMetadata());
+
+        if (currentAttributes.equals(mMediaAttributes)) {
             Log.v(TAG, "MediaAttributes Changed to " + mMediaAttributes.toString());
             mTracksPlayed++;
-
-            if (mTrackChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
-                mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-                sendTrackChangedRsp();
-            }
-        } else {
-            Log.v(TAG, "Updated " + mMediaAttributes.toString() + " but no change!");
+            sendTrackChangedRsp(false);
         }
 
-        // Update the play state, which sends play state and play position
-        // notifications if needed.
-        if (mMediaController != null) {
-            updatePlaybackState(mMediaController.getPlaybackState());
-        } else {
-            updatePlaybackState(null);
-        }
+        updatePlaybackState(mMediaController.getPlaybackState());
     }
 
     private void getRcFeaturesRequestFromNative(byte[] address, int features) {
@@ -1021,7 +1010,7 @@ public final class Avrcp {
             case EVT_TRACK_CHANGED:
                 Log.v(TAG, "Track changed notification enabled");
                 mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
-                sendTrackChangedRsp();
+                sendTrackChangedRsp(true);
                 break;
 
             case EVT_PLAY_POS_CHANGED:
@@ -1068,22 +1057,25 @@ public final class Avrcp {
         mHandler.sendMessage(msg);
     }
 
-    private void sendTrackChangedRsp() {
+    private void sendTrackChangedRsp(boolean requested) {
         MediaPlayerInfo info = getAddressedPlayerInfo();
         if (info != null && !info.isBrowseSupported()) {
             // for players which does not support Browse or when no track is currently selected
-            trackChangeRspForBrowseUnsupported();
+            trackChangeRspForBrowseUnsupported(requested);
         } else {
+            boolean changed =
+                    mAddressedMediaPlayer.sendTrackChangeWithId(requested, mMediaController);
             // for players which support browsing
-            mAddressedMediaPlayer.sendTrackChangeWithId(mTrackChangedNT, mMediaController);
+            if (changed) mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
         }
     }
 
-    private void trackChangeRspForBrowseUnsupported() {
+    private void trackChangeRspForBrowseUnsupported(boolean requested) {
         byte[] track = AvrcpConstants.TRACK_IS_SELECTED;
-        if (mTrackChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM
-                && !mMediaAttributes.exists) {
+        if (requested && !mMediaAttributes.exists) {
             track = AvrcpConstants.NO_TRACK_SELECTED;
+        } else if (!requested) {
+            mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
         }
         registerNotificationRspTrackChangeNative(mTrackChangedNT, track);
     }
@@ -1098,7 +1090,9 @@ public final class Avrcp {
         }
 
         if (isPlayingState(mCurrentPlayState)) {
-            return SystemClock.elapsedRealtime() - mLastStateUpdate + mCurrentPlayState.getPosition();
+            long sinceUpdate =
+                    (SystemClock.elapsedRealtime() - mCurrentPlayState.getLastPositionUpdateTime());
+            return sinceUpdate + mCurrentPlayState.getPosition();
         }
 
         return mCurrentPlayState.getPosition();
@@ -2105,13 +2099,12 @@ public final class Avrcp {
             if (newController != null) {
                 mMediaController = newController;
                 mMediaController.registerCallback(mMediaControllerCb, mHandler);
-                updateMetadata(mMediaController.getMetadata());
                 mAddressedMediaPlayer.updateNowPlayingList(mMediaController.getQueue());
             } else {
-                updateMetadata(null);
                 mAddressedMediaPlayer.updateNowPlayingList(null);
                 registerRsp = false;
             }
+            updateCurrentMediaState();
         }
         return registerRsp;
     }
@@ -2264,10 +2257,8 @@ public final class Avrcp {
         ProfileService.println(sb, "mTransportControlFlags: " + mTransportControlFlags);
         ProfileService.println(sb, "mTracksPlayed: " + mTracksPlayed);
         ProfileService.println(sb, "mCurrentPlayState: " + mCurrentPlayState);
-        ProfileService.println(sb, "mLastStateUpdate: " + mLastStateUpdate);
         ProfileService.println(sb, "mPlayStatusChangedNT: " + mPlayStatusChangedNT);
         ProfileService.println(sb, "mTrackChangedNT: " + mTrackChangedNT);
-        ProfileService.println(sb, "mSongLengthMs: " + mSongLengthMs);
         ProfileService.println(sb, "mPlaybackIntervalMs: " + mPlaybackIntervalMs);
         ProfileService.println(sb, "mPlayPosChangedNT: " + mPlayPosChangedNT);
         ProfileService.println(sb, "mNextPosMs: " + mNextPosMs);