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

Commit 01e8f4f0 authored by Marie Janssen's avatar Marie Janssen Committed by Gerrit Code Review
Browse files

Merge "AVRCP: Track players without active media sessions"

parents e1839072 c7f1f24f
Loading
Loading
Loading
Loading
+46 −31
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.bluetooth.avrcp;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothAvrcp;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
@@ -42,15 +44,23 @@ public class AddressedMediaPlayer {
    static private final String TAG = "AddressedMediaPlayer";
    static private final Boolean DEBUG = false;

    static private final long SINGLE_QID = 1;
    static private final String UNKNOWN_TITLE = "(unknown)";

    private AvrcpMediaRspInterface mMediaInterface;
    private List<MediaSession.QueueItem> mNowPlayingList;

    private final List<MediaSession.QueueItem> mUnknownNowPlayingList;

    private long mLastTrackIdSent;

    public AddressedMediaPlayer(AvrcpMediaRspInterface mediaInterface) {
        mNowPlayingList = null;
        mMediaInterface = mediaInterface;
        mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID;
        List<MediaSession.QueueItem> unknown = new ArrayList<MediaSession.QueueItem>();
        unknown.add(getCurrentQueueItem(null, SINGLE_QID));
        mUnknownNowPlayingList = unknown;
    }

    void cleanup() {
@@ -62,7 +72,7 @@ public class AddressedMediaPlayer {

    /* get now playing list from addressed player */
    void getFolderItemsNowPlaying(byte[] bdaddr, AvrcpCmd.FolderItemsCmd reqObj,
            MediaController mediaController) {
            @Nullable MediaController mediaController) {
        if (DEBUG) Log.v(TAG, "getFolderItemsNowPlaying");
        if (mediaController == null) {
            // No players (if a player exists, we would have selected it)
@@ -77,7 +87,7 @@ public class AddressedMediaPlayer {

    /* get item attributes for item in now playing list */
    void getItemAttr(byte[] bdaddr, AvrcpCmd.ItemAttrCmd itemAttr,
            MediaController mediaController) {
            @Nullable MediaController mediaController) {
        int status = AvrcpConstants.RSP_NO_ERROR;
        long mediaId = ByteBuffer.wrap(itemAttr.mUid).getLong();
        List<MediaSession.QueueItem> items = getNowPlayingList(mediaController);
@@ -87,12 +97,6 @@ public class AddressedMediaPlayer {
        if (Arrays.equals(itemAttr.mUid, AvrcpConstants.TRACK_IS_SELECTED)) {
            if (DEBUG) Log.d(TAG, "getItemAttr: Remote requests for now playing contents:");

            if (mediaController == null) {
                Log.e(TAG, "mediaController = null, sending no available players response");
                mMediaInterface.getItemAttrRsp(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY, null);
                return;
            }

            // get the current playing metadata and send.
            getItemAttrFilterAttr(bdaddr, itemAttr, getCurrentQueueItem(mediaController, mediaId),
                    mediaController);
@@ -113,10 +117,10 @@ public class AddressedMediaPlayer {

    /* Refresh and get the queue of now playing.
     */
    private List<MediaSession.QueueItem> getNowPlayingList(MediaController mediaController) {
        if (mediaController == null) return null;
    private List<MediaSession.QueueItem> getNowPlayingList(
            @Nullable MediaController mediaController) {
        if (mediaController == null) return mUnknownNowPlayingList;
        if (mNowPlayingList != null) return mNowPlayingList;

        List<MediaSession.QueueItem> items = mediaController.getQueue();
        if (items == null) {
            Log.i(TAG, "null queue from " + mediaController.getPackageName()
@@ -124,7 +128,7 @@ public class AddressedMediaPlayer {
            MediaMetadata metadata = mediaController.getMetadata();
            // Because we are database-unaware, we can just number the item here whatever we want
            // because they have to re-poll it every time.
            MediaSession.QueueItem current = getCurrentQueueItem(mediaController, 1);
            MediaSession.QueueItem current = getCurrentQueueItem(mediaController, SINGLE_QID);
            items = new ArrayList<MediaSession.QueueItem>();
            items.add(current);
        }
@@ -135,7 +139,14 @@ public class AddressedMediaPlayer {
    /* Constructs a queue item representing the current playing metadata from an
     * active controller with queue id |qid|.
     */
    private MediaSession.QueueItem getCurrentQueueItem(MediaController controller, long qid) {
    private MediaSession.QueueItem getCurrentQueueItem(
            @Nullable MediaController controller, long qid) {
        if (controller == null) {
            MediaDescription.Builder bob = new MediaDescription.Builder();
            bob.setTitle(UNKNOWN_TITLE);
            return new QueueItem(bob.build(), qid);
        }

        MediaMetadata metadata = controller.getMetadata();
        if (metadata == null) {
            Log.w(TAG, "Controller has no metadata!? Making an empty one");
@@ -186,12 +197,12 @@ public class AddressedMediaPlayer {
    }

    /* Instructs media player to play particular media item */
    void playItem(byte[] bdaddr, byte[] uid, MediaController mediaController) {
    void playItem(byte[] bdaddr, byte[] uid, @Nullable MediaController mediaController) {
        long qid = ByteBuffer.wrap(uid).getLong();
        List<MediaSession.QueueItem> items = mNowPlayingList;

        if (mediaController == null) {
            Log.e(TAG, "mediaController is null");
            Log.e(TAG, "No mediaController when PlayItem " + qid + " requested");
            mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR);
            return;
        }
@@ -218,19 +229,19 @@ public class AddressedMediaPlayer {
        mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_INV_ITEM);
    }

    void getTotalNumOfItems(byte[] bdaddr, MediaController mediaController) {
    void getTotalNumOfItems(byte[] bdaddr, @Nullable MediaController mediaController) {
        if (DEBUG) Log.d(TAG, "getTotalNumOfItems");
        List<MediaSession.QueueItem> items = mNowPlayingList;
        if (items != null) {
            // We already have the cached list sending the response to remote
            // We already have the cached list, send the response to remote
            mMediaInterface.getTotalNumOfItemsRsp(
                    bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, items.size());
            return;
        }

        if (mediaController == null) {
            Log.e(TAG, "mediaController = null, sending no available players response");
            mMediaInterface.getItemAttrRsp(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY, null);
            Log.e(TAG, "getTotalNumOfItems with no mediaController, sending no items");
            mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, 0);
            return;
        }

@@ -246,8 +257,9 @@ public class AddressedMediaPlayer {
        mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, items.size());
    }

    boolean sendTrackChangeWithId(boolean requesting, MediaController mediaController) {
        if (DEBUG) Log.d(TAG, "sendTrackChangeWithId");
    boolean sendTrackChangeWithId(boolean requesting, @Nullable MediaController mediaController) {
        if (DEBUG)
            Log.d(TAG, "sendTrackChangeWithId (" + requesting + "): controller " + mediaController);
        byte[] track;
        long qid = MediaSession.QueueItem.UNKNOWN_ID;
        if (mediaController != null) {
@@ -299,7 +311,7 @@ public class AddressedMediaPlayer {
     */
    private void getFolderItemsFilterAttr(byte[] bdaddr, AvrcpCmd.FolderItemsCmd folderItemsReqObj,
            List<MediaSession.QueueItem> items, byte scope, long startItem, long endItem,
            MediaController mediaController) {
            @NonNull MediaController mediaController) {
        if (DEBUG) Log.d(TAG, "getFolderItemsFilterAttr: startItem =" + startItem + ", endItem = "
                + endItem);

@@ -397,7 +409,7 @@ public class AddressedMediaPlayer {
    }

    private String getAttrValue(
            int attr, MediaSession.QueueItem item, MediaController mediaController) {
            int attr, MediaSession.QueueItem item, @Nullable MediaController mediaController) {
        String attrValue = null;
        if (item == null) {
            if (DEBUG) Log.d(TAG, "getAttrValue received null item");
@@ -405,12 +417,14 @@ public class AddressedMediaPlayer {
        }
        try {
            MediaDescription desc = item.getDescription();
            PlaybackState state = mediaController.getPlaybackState();
            Bundle extras = desc.getExtras();
            if (mediaController != null) {
                PlaybackState state = mediaController.getPlaybackState();
                if (state != null && (item.getQueueId() == state.getActiveQueueItemId())) {
                    if (DEBUG) Log.d(TAG, "getAttrValue: item is active, filling extra data");
                    extras = fillBundle(mediaController.getMetadata(), extras);
                }
            }
            if (DEBUG) Log.d(TAG, "getAttrValue: item " + item + " : " + desc);
            switch (attr) {
                case AvrcpConstants.ATTRID_TITLE:
@@ -466,7 +480,7 @@ public class AddressedMediaPlayer {
    }

    private void getItemAttrFilterAttr(byte[] bdaddr, AvrcpCmd.ItemAttrCmd mItemAttrReqObj,
            MediaSession.QueueItem mediaItem, MediaController mediaController) {
            MediaSession.QueueItem mediaItem, @Nullable MediaController mediaController) {
        /* Response parameters */
        int[] attrIds = null; /* array of attr ids */
        String[] attrValues = null; /* array of attr values */
@@ -486,14 +500,15 @@ public class AddressedMediaPlayer {
            } else {
                /* get only the requested attribute ids from the request */
                for (int idx = 0; idx < mItemAttrReqObj.mNumAttr; idx++) {
                    if (DEBUG) Log.d(TAG, "getAttrValue: attr id[" + idx + "] :" +
                        mItemAttrReqObj.mAttrIDs[idx]);
                    if (DEBUG)
                        Log.d(TAG, "getItemAttrFilterAttr: attr id[" + idx + "] :"
                                        + mItemAttrReqObj.mAttrIDs[idx]);
                    attrTempId.add(mItemAttrReqObj.mAttrIDs[idx]);
                }
            }
        }

        if (DEBUG) Log.d(TAG, "getAttrValue: attr id list size:" + attrTempId.size());
        if (DEBUG) Log.d(TAG, "getItemAttrFilterAttr: attr id list size:" + attrTempId.size());
        /* lookup and copy values of attributes for ids requested above */
        for (int idx = 0; idx < attrTempId.size(); idx++) {
            /* check if media player provided requested attributes */
+58 −32
Original line number Diff line number Diff line
@@ -112,6 +112,8 @@ public final class Avrcp {
    private int mAbsVolRetryTimes;
    private int mSkipAmount;

    private static final int NO_PLAYER_ID = 0;

    private int mCurrAddrPlayerID;
    private int mCurrBrowsePlayerID;
    private int mLastUsedPlayerID;
@@ -256,7 +258,7 @@ public final class Avrcp {
        mAbsVolThreshold = 0;
        mVolumeMapping = new HashMap<Integer, Integer>();
        sUIDCounter = AvrcpConstants.DEFAULT_UID_COUNTER;
        mCurrAddrPlayerID = 0;
        mCurrAddrPlayerID = NO_PLAYER_ID;
        mCurrBrowsePlayerID = 0;
        mContext = context;
        mLastUsedPlayerID = 0;
@@ -374,6 +376,11 @@ public final class Avrcp {
        @Override
        public void onSessionDestroyed() {
            Log.v(TAG, "MediaController session destroyed");
            if (mMediaController != null) {
                removeMediaController(mMediaController.getWrappedInstance());
                mMediaController.unregisterCallback(mMediaControllerCb);
                mMediaController = null;
            }
        }

        @Override
@@ -933,12 +940,14 @@ public final class Avrcp {
    }

    private void updateCurrentMediaState() {
        MediaAttributes currentAttributes = mMediaAttributes;
        PlaybackState newState = mCurrentPlayState;
        if (mMediaController == null) {
            // Use A2DP state if we don't have a MediaControlller
            boolean isPlaying = (mA2dpState == BluetoothA2dp.STATE_PLAYING);
            boolean isPlaying =
                    (mA2dpState == BluetoothA2dp.STATE_PLAYING) && mAudioManager.isMusicActive();
            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,
@@ -947,26 +956,21 @@ public final class Avrcp {
                    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;
                newState = builder.build();
            }

        MediaAttributes currentAttributes = mMediaAttributes;

        PlaybackState newState = mMediaController.getPlaybackState();

        // Metadata
            mMediaAttributes = new MediaAttributes(null);
        } else {
            newState = mMediaController.getPlaybackState();
            mMediaAttributes = new MediaAttributes(mMediaController.getMetadata());
        }

        if (currentAttributes.equals(mMediaAttributes)) {
        if (!currentAttributes.equals(mMediaAttributes)) {
            Log.v(TAG, "MediaAttributes Changed to " + mMediaAttributes.toString());
            mTracksPlayed++;
            sendTrackChangedRsp(false);
        }

        updatePlaybackState(mMediaController.getPlaybackState());
        updatePlaybackState(newState);
    }

    private void getRcFeaturesRequestFromNative(byte[] address, int features) {
@@ -1409,6 +1413,7 @@ public final class Avrcp {
        if (DEBUG) Log.d(TAG, "packageName: " + packageName + " removed: " + removed);

        if (removed) {
            removeMediaPlayerInfo(packageName);
            // old package is removed, updating local browsable player's list
            if (isBrowseSupported(packageName)) {
                removePackageFromBrowseList(packageName);
@@ -1562,14 +1567,18 @@ public final class Avrcp {
                            getMediaControllers();
                    for (android.media.session.MediaController controller : currentControllers) {
                        if (!newControllers.contains(controller)) {
                            removeMediaPlayerInfo(controller.getPackageName());
                            playersChanged = true;
                            removeMediaController(controller);
                            if (mMediaController != null && mMediaController.equals(controller)) {
                                if (DEBUG) Log.v(TAG, "Active Controller is gone!");
                                mMediaController.unregisterCallback(mMediaControllerCb);
                                mMediaController = null;
                            }
                        }
                    }

                    if (playersChanged) {
                        mHandler.sendEmptyMessage(MSG_AVAILABLE_PLAYERS_CHANGED_RSP);
                        if (newControllers.size() > 0 && (mMediaController == null)) {
                        if (newControllers.size() > 0 && getAddressedPlayerInfo() == null) {
                            if (DEBUG)
                                Log.v(TAG,
                                        "No addressed player but active sessions, taking first.");
@@ -1794,7 +1803,6 @@ public final class Avrcp {
     *  @return true if an item was updated, false if it was added instead
     */
    private boolean addMediaPlayerInfo(MediaPlayerInfo info) {
        if (DEBUG) Log.d(TAG, "add " + info.toString());
        int updateId = -1;
        boolean updated = false;
        synchronized (mMediaPlayerInfoList) {
@@ -1809,10 +1817,13 @@ public final class Avrcp {
                // New player
                mLastUsedPlayerID++;
                updateId = mLastUsedPlayerID;
            } else if (updateId == mCurrAddrPlayerID) {
                updateCurrentController(mCurrAddrPlayerID, mCurrBrowsePlayerID);
            }
            mMediaPlayerInfoList.put(updateId, info);
            if (DEBUG)
                Log.d(TAG, (updated ? "update #" : "add #") + updateId + ":" + info.toString());
            if (updateId == mCurrAddrPlayerID) {
                updateCurrentController(mCurrAddrPlayerID, mCurrBrowsePlayerID);
            }
        }
        return updated;
    }
@@ -1828,6 +1839,8 @@ public final class Avrcp {
                }
            }
            if (removeKey != -1) {
                if (DEBUG)
                    Log.d(TAG, "remove #" + removeKey + ":" + mMediaPlayerInfoList.get(removeKey));
                return mMediaPlayerInfoList.remove(removeKey);
            }

@@ -1835,6 +1848,15 @@ public final class Avrcp {
        }
    }

    /** Remove the controller referenced by |controller| from any player in the list */
    private void removeMediaController(android.media.session.MediaController controller) {
        synchronized (mMediaPlayerInfoList) {
            for (MediaPlayerInfo info : mMediaPlayerInfoList.values()) {
                if (info.getMediaController().equals(controller)) info.setMediaController(null);
            }
        }
    }

    /*
     * utility function to get the playback state of any media player through
     * media controller APIs.
@@ -2096,11 +2118,10 @@ public final class Avrcp {

        if (DEBUG)
            Log.d(TAG, "updateCurrentController: " + mMediaController + " to " + newController);
        if (mMediaController == null || newController == null
                || (mMediaController.getWrappedInstance() != newController.getWrappedInstance())) {
        if (mMediaController == null || (!mMediaController.equals(newController))) {
            if (mMediaController != null) mMediaController.unregisterCallback(mMediaControllerCb);
            if (newController != null) {
            mMediaController = newController;
            if (mMediaController != null) {
                mMediaController.registerCallback(mMediaControllerCb, mHandler);
                mAddressedMediaPlayer.updateNowPlayingList(mMediaController.getQueue());
            } else {
@@ -2184,12 +2205,16 @@ public final class Avrcp {

    private void handleGetItemAttr(AvrcpCmd.ItemAttrCmd itemAttr) {
        if (itemAttr.mScope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
            if (mCurrAddrPlayerID == NO_PLAYER_ID) {
                getItemAttrRspNative(
                        itemAttr.mAddress, AvrcpConstants.RSP_NO_AVBL_PLAY, (byte) 0, null, null);
            } else {
                mAddressedMediaPlayer.getItemAttr(itemAttr.mAddress, itemAttr, mMediaController);
            }
        else {
            if (mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress) != null)
        } else {
            if (mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress) != null) {
                mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress).getItemAttr(itemAttr);
            else {
            } else {
                Log.e(TAG, "Could not get attributes. mBrowsedMediaPlayer is null");
                getItemAttrRspNative(itemAttr.mAddress, AvrcpConstants.RSP_INTERNAL_ERR,
                        (byte) 0, null, null);
@@ -2279,7 +2304,8 @@ public final class Avrcp {
        ProfileService.println(sb, "mSkipAmount: " + mSkipAmount);
        ProfileService.println(sb, "mVolumeMapping: " + mVolumeMapping.toString());
        if (mMediaController != null)
            ProfileService.println(sb, "mMediaSession pkg: " + mMediaController.getPackageName());
            ProfileService.println(sb, "mMediaController: " + mMediaController.getWrappedInstance()
                            + " pkg " + mMediaController.getPackageName());

        ProfileService.println(sb, "\nMedia Players:");
        synchronized (mMediaPlayerInfoList) {
+16 −0
Original line number Diff line number Diff line
@@ -139,6 +139,22 @@ public class MediaController {
        return mDelegate.controlsSameSession(other);
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof android.media.session.MediaController) {
            return mDelegate.equals(o);
        } else if (o instanceof MediaController) {
            MediaController other = (MediaController) o;
            return mDelegate.equals(other.mDelegate);
        }
        return false;
    }

    @Override
    public String toString() {
        return super.toString() + "(wraps " + mDelegate + ")";
    }

    public static abstract class Callback extends android.media.session.MediaController.Callback { }

    public class TransportControls {