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

Commit c5cab327 authored by Ajay Panicker's avatar Ajay Panicker
Browse files

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
parent 1c7b1bd0
Loading
Loading
Loading
Loading
+28 −24
Original line number Diff line number Diff line
@@ -48,20 +48,21 @@ public class AddressedMediaPlayer {
    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 @@ public class AddressedMediaPlayer {
        mNowPlayingList = mEmptyNowPlayingList;
        mMediaInterface = null;
        mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID;
        mNowPlayingListUpdated = false;
    }

    /* get now playing list from addressed player */
@@ -81,7 +81,7 @@ public class AddressedMediaPlayer {
            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 @@ public class AddressedMediaPlayer {
            @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 @@ public class AddressedMediaPlayer {

    /* 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 @@ public class AddressedMediaPlayer {
            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 @@ public class AddressedMediaPlayer {

    private Bundle fillBundle(MediaMetadata metadata, Bundle currentExtras) {
        if (metadata == null) {
            Log.i(TAG, "fillBundle: metadata is null");
            return currentExtras;
        }

@@ -207,15 +203,10 @@ public class AddressedMediaPlayer {
        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 @@ public class AddressedMediaPlayer {
    }

    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 @@ public class AddressedMediaPlayer {
        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 @@ public class AddressedMediaPlayer {
            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 @@ public class AddressedMediaPlayer {
    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"
+165 −140

File changed.

Preview size limit exceeded, changes collapsed.

+64 −24
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ class BrowsedMediaPlayer {

    /*  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 @@ class BrowsedMediaPlayer {
    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 @@ class BrowsedMediaPlayer {

        @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,10 +267,33 @@ class BrowsedMediaPlayer {
    }

    /* 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 {
            if (!browser.isConnected()) {
                Log.e(TAG, "setBrowsedPlayer: " + mPackageName + "not connected");
            } 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();
@@ -266,11 +305,6 @@ class BrowsedMediaPlayer {
                    mPathStack.push(mMediaId);
                }

            if (!mMediaBrowser.isConnected()) {
                Log.e(TAG, "setBrowsedPlayer: " + mPackageName + "not connected");
            } else if ((token = mMediaBrowser.getSessionToken()) == null) {
                Log.e(TAG, "setBrowsedPlayer: " + mPackageName + "no Session token");
            } else {
                mMediaController = MediaController.wrap(
                    new android.media.session.MediaController(mContext, token));
                /* get root folder items */
@@ -287,7 +321,7 @@ class BrowsedMediaPlayer {
    }

    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 @@ class BrowsedMediaPlayer {
         * 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 @@ class BrowsedMediaPlayer {
     */
    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 @@ class BrowsedMediaPlayer {

                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");