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

Commit 748284b2 authored by Pavlin Radoslavov's avatar Pavlin Radoslavov Committed by Myles Watson
Browse files

Track the AudioManager Playback state to update the AVRCP playback state

* If the AudioManager playback configuration shows that one of the
  player's state is PLAYER_STATE_STARTED (as received in a callback
  from the AudioManager), then set the AVRCP state to
  PlaybackState.STATE_PLAYING
* Don't suppress AVRCP playback state change when no track change

Bug: b/63151260
Bug: b/66029010
Test: Manual with Bose SoundSport FW v1.3.0
Change-Id: I0878ba26a78a7e5ef52ec033386300b777a39c67
(cherry picked from commit 6822e62e)
(cherry picked from commit 98f72525)

Second Cherry-pick due to an accidental skip during a merge-conflict.
parent fad0627e
Loading
Loading
Loading
Loading
+58 −21
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.browse.MediaBrowser;
@@ -78,6 +79,8 @@ public final class Avrcp {
    private Context mContext;
    private final AudioManager mAudioManager;
    private AvrcpMessageHandler mHandler;
    private Handler mAudioManagerPlaybackHandler;
    private AudioManagerPlaybackListener mAudioManagerPlaybackCb;
    private MediaSessionManager mMediaSessionManager;
    private @Nullable MediaController mMediaController;
    private MediaControllerListener mMediaControllerCb;
@@ -87,6 +90,7 @@ public final class Avrcp {
    private int mTransportControlFlags;
    private @NonNull PlaybackState mCurrentPlayState;
    private int mA2dpState;
    private boolean mAudioManagerIsPlaying;
    private int mPlayStatusChangedNT;
    private byte mReportedPlayStatus;
    private int mTrackChangedNT;
@@ -240,6 +244,7 @@ public final class Avrcp {
        mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();
        mReportedPlayStatus = PLAYSTATUS_ERROR;
        mA2dpState = BluetoothA2dp.STATE_NOT_PLAYING;
        mAudioManagerIsPlaying = false;
        mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
        mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
        mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
@@ -307,6 +312,8 @@ public final class Avrcp {
        thread.start();
        Looper looper = thread.getLooper();
        mHandler = new AvrcpMessageHandler(looper);
        mAudioManagerPlaybackHandler = new Handler(looper);
        mAudioManagerPlaybackCb = new AudioManagerPlaybackListener();
        mMediaControllerCb = new MediaControllerListener();
        mAvrcpMediaRsp = new AvrcpMediaRsp();
        mMediaPlayerInfoList = new TreeMap<Integer, MediaPlayerInfo>();
@@ -336,6 +343,9 @@ public final class Avrcp {
            // initialize browsable player list and build media player list
            buildBrowsablePlayerList();
        }

        mAudioManager.registerAudioPlaybackCallback(
                mAudioManagerPlaybackCb, mAudioManagerPlaybackHandler);
    }

    public static Avrcp make(Context context) {
@@ -347,18 +357,23 @@ public final class Avrcp {

    public synchronized void doQuit() {
        if (DEBUG) Log.d(TAG, "doQuit");
        if (mAudioManager != null) {
            mAudioManager.unregisterAudioPlaybackCallback(mAudioManagerPlaybackCb);
        }
        if (mMediaController != null) mMediaController.unregisterCallback(mMediaControllerCb);
        if (mMediaSessionManager != null) {
            mMediaSessionManager.setCallback(null, null);
            mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveSessionListener);
        }

        mAudioManagerPlaybackHandler.removeCallbacksAndMessages(null);
        mHandler.removeCallbacksAndMessages(null);
        Looper looper = mHandler.getLooper();
        if (looper != null) {
            looper.quit();
        }

        mAudioManagerPlaybackHandler = null;
        mHandler = null;
        mContext.unregisterReceiver(mAvrcpReceiver);
        mContext.unregisterReceiver(mBootReceiver);
@@ -374,6 +389,30 @@ public final class Avrcp {
            mVolumeMapping.clear();
    }

    private class AudioManagerPlaybackListener extends AudioManager.AudioPlaybackCallback {
        @Override
        public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {
            super.onPlaybackConfigChanged(configs);
            boolean isPlaying = false;
            for (AudioPlaybackConfiguration config : configs) {
                if (DEBUG) {
                    Log.d(TAG,
                            "AudioManager Player: "
                                    + AudioPlaybackConfiguration.toLogFriendlyString(config));
                }
                if (config.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
                    isPlaying = true;
                    break;
                }
            }
            if (DEBUG) Log.d(TAG, "AudioManager isPlaying: " + isPlaying);
            if (mAudioManagerIsPlaying != isPlaying) {
                mAudioManagerIsPlaying = isPlaying;
                updateCurrentMediaState();
            }
        }
    }

    private class MediaControllerListener extends MediaController.Callback {
        @Override
        public void onMetadataChanged(MediaMetadata metadata) {
@@ -807,10 +846,22 @@ public final class Avrcp {

            if (controllerState != null) {
                newState = controllerState;
            } else if (mAudioManager != null && mAudioManager.isMusicActive()) {
                // Use A2DP state if we don't have a state from MediaControlller
            }
            // Use the AudioManager to update the playback state.
            // NOTE: We cannot use the
            //    (mA2dpState == BluetoothA2dp.STATE_PLAYING)
            // check, because after Pause, the A2DP state remains in
            // STATE_PLAYING for 3 more seconds.
            // As a result of that, if we pause the music, on carkits the
            // Play status indicator will continue to display "Playing"
            // for 3 more seconds which can be confusing.
            if (mAudioManagerIsPlaying
                    || (controllerState == null && mAudioManager != null
                               && mAudioManager.isMusicActive())) {
                // Use AudioManager playback state if we don't have the state
                // from MediaControlller
                PlaybackState.Builder builder = new PlaybackState.Builder();
                if (mA2dpState == BluetoothA2dp.STATE_PLAYING) {
                if (mAudioManagerIsPlaying) {
                    builder.setState(PlaybackState.STATE_PLAYING,
                            PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f);
                } else {
@@ -1026,22 +1077,6 @@ public final class Avrcp {
                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: " + newQueueId + " : "
                                + currentAttributes + " : " + mMediaAttributes);

                return;
            }

            // Notify track changed if:
            //  - The CT is registered for the notification
            //  - Queue ID is UNKNOWN and MediaMetadata is different
@@ -1060,9 +1095,11 @@ public final class Avrcp {
        }

        // still send the updated play state if the playback state is none or buffering
        Log.e(TAG, "play status change " + mReportedPlayStatus + "➡" + newPlayStatus);
        Log.e(TAG,
                "play status change " + mReportedPlayStatus + "➡" + newPlayStatus
                        + " mPlayStatusChangedNT: " + mPlayStatusChangedNT);
        if (mPlayStatusChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM
                && (mReportedPlayStatus != newPlayStatus)) {
                || (mReportedPlayStatus != newPlayStatus)) {
            sendPlaybackStatus(AvrcpConstants.NOTIFICATION_TYPE_CHANGED, newPlayStatus);
        }