Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f3488279 authored by Marie Janssen's avatar Marie Janssen
Browse files

AVRCP: Workaround Queue ID & metadata updates

When a player with a queue updates their track, it comes in two separate
parts from MediaController.Callback:
 - Queue ID is in onPlaybackStateChanged
 - Metadata is in onMetadataChanged

Because the ordering of these two is not specific, both must change
before we have a consistent data state to present the carkit if the
Media client has a queue.

Even though the only thing sent in the Track Changed notification is the
Queue ID, the request for metadata often comes before the Metadata has
been updated after that notification.

Refactor Track Changed notification to keep track of the last data we
sent to the controller, and send new tracks when both Queue ID and
Metadata have changed.

Test: various carkit testing with track skipping
Bug: 37707672
Change-Id: Ibf9f725f6912955a65421786068921489dbf8e45
(cherry picked from commit 5aca05c1d79f3412b6964b3b6335ad6f2d558756)
parent 35f4114a
Loading
Loading
Loading
Loading
+3 −7
Original line number Diff line number Diff line
@@ -236,19 +236,15 @@ public class AddressedMediaPlayer {
        mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, items.size());
    }

    boolean sendTrackChangeWithId(boolean requesting, @Nullable MediaController mediaController) {
    void sendTrackChangeWithId(int type, @Nullable MediaController mediaController) {
        if (DEBUG)
            Log.d(TAG, "sendTrackChangeWithId (" + requesting + "): controller " + mediaController);
            Log.d(TAG, "sendTrackChangeWithId (" + type + "): controller " + mediaController);
        long qid = getActiveQueueItemId(mediaController);
        byte[] 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);
        mMediaInterface.trackChangedRsp(type, track);
        mLastTrackIdSent = qid;
        // The nowPlaying might have changed.
        updateNowPlayingList(mediaController);
        return (trackChangedNT == AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
    }

    /*
+40 −38
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ public final class Avrcp {
    private @Nullable MediaController mMediaController;
    private MediaControllerListener mMediaControllerCb;
    private MediaAttributes mMediaAttributes;
    private long mLastQueueId;
    private PackageManager mPackageManager;
    private int mTransportControlFlags;
    private @NonNull PlaybackState mCurrentPlayState;
@@ -233,6 +234,7 @@ public final class Avrcp {

    private Avrcp(Context context) {
        mMediaAttributes = new MediaAttributes(null);
        mLastQueueId = MediaSession.QueueItem.UNKNOWN_ID;
        mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();
        mA2dpState = BluetoothA2dp.STATE_NOT_PLAYING;
        mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
@@ -368,12 +370,12 @@ public final class Avrcp {
        @Override
        public void onMetadataChanged(MediaMetadata metadata) {
            if (DEBUG) Log.v(TAG, "onMetadataChanged");
            updateCurrentMediaState();
            updateCurrentMediaState(false);
        }
        @Override
        public synchronized void onPlaybackStateChanged(PlaybackState state) {
            if (DEBUG) Log.v(TAG, "onPlaybackStateChanged: state " + state.toString());
            updateCurrentMediaState();
            updateCurrentMediaState(false);
        }

        @Override
@@ -699,7 +701,7 @@ public final class Avrcp {
            case MSG_SET_A2DP_AUDIO_STATE:
                if (DEBUG) Log.v(TAG, "MSG_SET_A2DP_AUDIO_STATE:" + msg.arg1);
                mA2dpState = msg.arg1;
                updateCurrentMediaState();
                updateCurrentMediaState(false);
                break;

            case MSG_NATIVE_REQ_GET_FOLDER_ITEMS: {
@@ -944,8 +946,8 @@ public final class Avrcp {
        }
    }

    private void updateCurrentMediaState() {
        MediaAttributes currentAttributes = mMediaAttributes;
    private void updateCurrentMediaState(boolean registering) {
        MediaAttributes currentAttributes;
        PlaybackState newState = null;
        synchronized (this) {
            if (mMediaController == null) {
@@ -961,20 +963,26 @@ public final class Avrcp {
                            PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f);
                }
                newState = builder.build();
                mMediaAttributes = new MediaAttributes(null);
                currentAttributes = new MediaAttributes(null);
            } else {
                newState = mMediaController.getPlaybackState();
                mMediaAttributes = new MediaAttributes(mMediaController.getMetadata());
                currentAttributes = new MediaAttributes(mMediaController.getMetadata());
            }
        }

        long oldQueueId = mCurrentPlayState.getActiveQueueItemId();
        long newQueueId = MediaSession.QueueItem.UNKNOWN_ID;
        if (newState != null) newQueueId = newState.getActiveQueueItemId();
        Log.v(TAG, "Media update: id " + oldQueueId + "➡" + newQueueId + ":"
                        + mMediaAttributes.toString());
        if (oldQueueId != newQueueId || !currentAttributes.equals(mMediaAttributes)) {
            sendTrackChangedRsp(false);
        Log.v(TAG, "Media update: id " + mLastQueueId + "➡" + newQueueId + "? "
                        + currentAttributes.toString());
        // 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;
        }

        updatePlaybackState(newState);
@@ -1017,7 +1025,7 @@ public final class Avrcp {
            case EVT_TRACK_CHANGED:
                Log.v(TAG, "Track changed notification enabled");
                mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
                sendTrackChangedRsp(true);
                updateCurrentMediaState(true);
                break;

            case EVT_PLAY_POS_CHANGED:
@@ -1028,27 +1036,27 @@ public final class Avrcp {

            case EVT_AVBL_PLAYERS_CHANGED:
                /* Notify remote available players changed */
                if (DEBUG) Log.d (TAG, "sending availablePlayersChanged to remote ");
                if (DEBUG) Log.d(TAG, "Available Players notification enabled");
                registerNotificationRspAvalPlayerChangedNative(
                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM);
                break;

            case EVT_ADDR_PLAYER_CHANGED:
                /* Notify remote addressed players changed */
                if (DEBUG) Log.d (TAG, "sending addressedPlayersChanged to remote ");
                if (DEBUG) Log.d(TAG, "Addressed Player notification enabled");
                registerNotificationRspAddrPlayerChangedNative(
                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM,
                        mCurrAddrPlayerID, sUIDCounter);
                break;

            case EVENT_UIDS_CHANGED:
                if (DEBUG) Log.d(TAG, "sending UIDs changed to remote");
                if (DEBUG) Log.d(TAG, "UIDs changed notification enabled");
                registerNotificationRspUIDsChangedNative(
                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM, sUIDCounter);
                break;

            case EVENT_NOW_PLAYING_CONTENT_CHANGED:
                if (DEBUG) Log.d(TAG, "sending NowPlayingList changed to remote");
                if (DEBUG) Log.d(TAG, "Now Playing List changed notification enabled");
                /* send interim response to remote device */
                if (!registerNotificationRspNowPlayingChangedNative(
                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM)) {
@@ -1064,31 +1072,25 @@ public final class Avrcp {
        mHandler.sendMessage(msg);
    }

    private void sendTrackChangedRsp(boolean requested) {
        MediaPlayerInfo info = getAddressedPlayerInfo();
        if (!requested && mTrackChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
            if (DEBUG) Log.d(TAG, "sendTrackChangedRsp: Not registered or requesting.");
    private void sendTrackChangedRsp(boolean registering) {
        if (!registering && mTrackChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
            if (DEBUG) Log.d(TAG, "sendTrackChangedRsp: Not registered or registering.");
            return;
        }
        if (info != null && !info.isBrowseSupported()) {
            // for players which does not support Browse or when no track is currently selected
            trackChangeRspForBrowseUnsupported(requested);
        } else {
            boolean changed =
                    mAddressedMediaPlayer.sendTrackChangeWithId(requested, mMediaController);
            // for players which support browsing
            if (changed) mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
        }
    }

    private void trackChangeRspForBrowseUnsupported(boolean requested) {
        byte[] track = AvrcpConstants.TRACK_IS_SELECTED;
        if (requested && !mMediaAttributes.exists) {
            track = AvrcpConstants.NO_TRACK_SELECTED;
        } else if (!requested) {
        mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
        }
        if (registering) mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;

        MediaPlayerInfo info = getAddressedPlayerInfo();
        // for non-browsable players or no player
        if (info != null && !info.isBrowseSupported()) {
            byte[] track = AvrcpConstants.TRACK_IS_SELECTED;
            if (!mMediaAttributes.exists) track = AvrcpConstants.NO_TRACK_SELECTED;
            registerNotificationRspTrackChangeNative(mTrackChangedNT, track);
            return;
        }

        mAddressedMediaPlayer.sendTrackChangeWithId(mTrackChangedNT, mMediaController);
    }

    private long getPlayPosition() {
@@ -2125,7 +2127,7 @@ public final class Avrcp {
                }
            }
        }
        updateCurrentMediaState();
        updateCurrentMediaState(false);
        return registerRsp;
    }