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

Commit 46468069 authored by Sal Savage's avatar Sal Savage
Browse files

Keep player set up to date, assign addressed player from it

This change makes it so the addressed player will *always* be in and
come from our known set of available players.

On devices that don't support browsing, this means a default player is
added to the available player set with the addressed player's ID and
reasonable set of supported actions.

On devices that do support browsing this means that we now assume the
addressed player id will always correspond to a player ID in our
available player set. If that player isn't in the set, a default player
is used, similar to on devices without browsing. When available players
change and are downloaded we also update out addressed player to point
to the newly downloaded player. This is because the player metadata
could have changed. On some devices the player ID is reused for new
players too!

Each time the addressed player object is changed we request to fetch the
current playback status, metadata and now player list (if supported) so
we can stay in sync.

Tag: #stability
Bug: 187107189
Test: atest BluetoothInstrumentationTests
Change-Id: I4b929fedae1747e63de2aaf236811bb042e0fd57
parent fe642416
Loading
Loading
Loading
Loading
+94 −18
Original line number Diff line number Diff line
@@ -126,9 +126,11 @@ class AvrcpControllerStateMachine extends StateMachine {
    boolean mRemoteControlConnected = false;
    boolean mBrowsingConnected = false;
    final BrowseTree mBrowseTree;

    private AvrcpPlayer mAddressedPlayer;
    private int mAddressedPlayerId;
    private SparseArray<AvrcpPlayer> mAvailablePlayerList = new SparseArray<AvrcpPlayer>();
    private SparseArray<AvrcpPlayer> mAvailablePlayerList;

    private int mVolumeChangedNotificationsToIgnore = 0;
    private int mVolumeNotificationLabel = -1;

@@ -148,6 +150,7 @@ class AvrcpControllerStateMachine extends StateMachine {
        mCoverArtManager = service.getCoverArtManager();
        logD(device.toString());

        mAvailablePlayerList = new SparseArray<AvrcpPlayer>();
        mAddressedPlayerId = AvrcpPlayer.DEFAULT_ID;

        AvrcpPlayer.Builder apb = new AvrcpPlayer.Builder();
@@ -159,6 +162,7 @@ class AvrcpControllerStateMachine extends StateMachine {
        apb.setSupportedFeature(AvrcpPlayer.FEATURE_FORWARD);
        apb.setSupportedFeature(AvrcpPlayer.FEATURE_PREVIOUS);
        mAddressedPlayer = apb.build();
        mAvailablePlayerList.put(mAddressedPlayerId, mAddressedPlayer);

        mBrowseTree = new BrowseTree(mDevice);
        mDisconnected = new Disconnected();
@@ -228,6 +232,16 @@ class AvrcpControllerStateMachine extends StateMachine {
        return mAddressedPlayer.getCurrentTrack();
    }

    @VisibleForTesting
    int getAddressedPlayerId() {
        return mAddressedPlayerId;
    }

    @VisibleForTesting
    SparseArray<AvrcpPlayer> getAvailablePlayers() {
        return mAvailablePlayerList;
    }

    /**
     * Dump the current State Machine to the string builder.
     *
@@ -239,6 +253,8 @@ class AvrcpControllerStateMachine extends StateMachine {
        ProfileService.println(sb, "isActive: " + isActive());
        ProfileService.println(sb, "Control: " + mRemoteControlConnected);
        ProfileService.println(sb, "Browsing: " + mBrowsingConnected);
        ProfileService.println(sb, "Cover Art: "
                + (mCoverArtManager.getState(mDevice) == BluetoothProfile.STATE_CONNECTED));

        ProfileService.println(sb, "Addressed Player ID: " + mAddressedPlayerId);
        ProfileService.println(sb, "Available Players (" + mAvailablePlayerList.size() + "): ");
@@ -252,8 +268,6 @@ class AvrcpControllerStateMachine extends StateMachine {
        if (mBrowseTree.mNowPlayingNode != null) {
            queue = mBrowseTree.mNowPlayingNode.getContents();
        }
        ProfileService.println(sb, "Current Track: " + mAddressedPlayer.getCurrentTrack());
        ProfileService.println(sb, "Playback State: " + mAddressedPlayer.getPlaybackState());
        ProfileService.println(sb, "Queue (" + (queue == null ? 0 : queue.size()) + "): " + queue);
    }

@@ -563,8 +577,10 @@ class AvrcpControllerStateMachine extends StateMachine {
                    return true;

                case MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED:
                    int oldAddressedPlayerId = mAddressedPlayerId;
                    mAddressedPlayerId = msg.arg1;
                    logD("AddressedPlayer = " + mAddressedPlayerId);
                    logD("AddressedPlayer changed " + oldAddressedPlayerId + " -> "
                            + mAddressedPlayerId);

                    // The now playing list is tied to the addressed player by specification in
                    // AVRCP 5.9.1. A new addressed player means our now playing content is now
@@ -573,22 +589,37 @@ class AvrcpControllerStateMachine extends StateMachine {
                    if (isActive()) {
                        BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode);
                    }
                    removeUnusedArtworkFromBrowseTree();

                    // For devices that support browsing, we *may* have an AvrcpPlayer with player
                    // metadata already. We could also be in the middle fetching it. If the player
                    // isn't there then we need to ensure that a default Addressed AvrcpPlayer is
                    // created to represent it. It can be updated if/when we do fetch the player.
                    if (!mAvailablePlayerList.contains(mAddressedPlayerId)) {
                        logD("Available player set does not contain the new Addressed Player");
                        AvrcpPlayer.Builder apb = new AvrcpPlayer.Builder();
                        apb.setDevice(mDevice);
                        apb.setPlayerId(mAddressedPlayerId);
                        apb.setSupportedFeature(AvrcpPlayer.FEATURE_PLAY);
                        apb.setSupportedFeature(AvrcpPlayer.FEATURE_PAUSE);
                        apb.setSupportedFeature(AvrcpPlayer.FEATURE_STOP);
                        apb.setSupportedFeature(AvrcpPlayer.FEATURE_FORWARD);
                        apb.setSupportedFeature(AvrcpPlayer.FEATURE_PREVIOUS);
                        mAvailablePlayerList.put(mAddressedPlayerId, apb.build());
                    }

                    // Set our new addressed player object from our set of available players that's
                    // guaranteed to have the addressed player now.
                    mAddressedPlayer = mAvailablePlayerList.get(mAddressedPlayerId);

                    AvrcpPlayer updatedPlayer = mAvailablePlayerList.get(mAddressedPlayerId);
                    if (updatedPlayer != null) {
                        mAddressedPlayer = updatedPlayer;
                        // If the new player supports the now playing feature then fetch it
                    // Fetch metadata including the now playing list if the new player supports the
                    // now playing feature
                    mService.getCurrentMetadataNative(Utils.getByteAddress(mDevice));
                    mService.getPlaybackStateNative(Utils.getByteAddress(mDevice));
                    if (mAddressedPlayer.supportsFeature(AvrcpPlayer.FEATURE_NOW_PLAYING)) {
                        sendMessage(MESSAGE_GET_FOLDER_ITEMS, mBrowseTree.mNowPlayingNode);
                    }
                        logD("AddressedPlayer = " + mAddressedPlayer.getName());
                    } else {
                        logD("Addressed player changed to unknown ID=" + mAddressedPlayerId);
                        mBrowseTree.mRootNode.setCached(false);
                        mBrowseTree.mRootNode.setExpectedChildren(255);
                        BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mRootNode);
                    }
                    removeUnusedArtworkFromBrowseTree();
                    logD("AddressedPlayer = " + mAddressedPlayer);
                    return true;

                case MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS:
@@ -822,13 +853,58 @@ class AvrcpControllerStateMachine extends StateMachine {
                    break;

                case MESSAGE_PROCESS_GET_PLAYER_ITEMS:
                    logD("Received new available player items");
                    BrowseTree.BrowseNode rootNode = mBrowseTree.mRootNode;

                    // The specification is not firm on what receiving available player changes
                    // means relative to the existing player IDs, the addressed player and any
                    // currently saved play status, track or now playing list metadata. We're going
                    // to assume nothing and act verbosely, as some devices are known to reuse
                    // Player IDs.
                    if (!rootNode.isCached()) {
                        List<AvrcpPlayer> playerList = (List<AvrcpPlayer>) msg.obj;

                        // Since players hold metadata, including cover art handles that point to
                        // stored images, be sure to save image UUIDs so we can see if we can
                        // remove them from storage after setting our new player object
                        ArrayList<String> coverArtUuids = new ArrayList<String>();
                        for (int i = 0; i < mAvailablePlayerList.size(); i++) {
                            AvrcpPlayer player = mAvailablePlayerList.valueAt(i);
                            AvrcpItem track = player.getCurrentTrack();
                            if (track != null && track.getCoverArtUuid() != null) {
                                coverArtUuids.add(track.getCoverArtUuid());
                            }
                        }

                        mAvailablePlayerList.clear();
                        for (AvrcpPlayer player : playerList) {
                            mAvailablePlayerList.put(player.getId(), player);
                        }

                        // If our new set of players contains our addressed player again then we
                        // will replace it and re-download metadata. If not, we'll re-use the old
                        // player to save the metadata queries.
                        if (!mAvailablePlayerList.contains(mAddressedPlayerId)) {
                            logD("Available player set doesn't contain the addressed player");
                            mAvailablePlayerList.put(mAddressedPlayerId, mAddressedPlayer);
                        } else {
                            logD("Update addressed player with new available player metadata");
                            mAddressedPlayer = mAvailablePlayerList.get(mAddressedPlayerId);
                            mService.getCurrentMetadataNative(Utils.getByteAddress(mDevice));
                            mService.getPlaybackStateNative(Utils.getByteAddress(mDevice));
                            mBrowseTree.mNowPlayingNode.setCached(false);
                            if (mAddressedPlayer.supportsFeature(AvrcpPlayer.FEATURE_NOW_PLAYING)) {
                                sendMessage(MESSAGE_GET_FOLDER_ITEMS, mBrowseTree.mNowPlayingNode);
                            }
                        }
                        logD("AddressedPlayer = " + mAddressedPlayer);

                        // Check old cover art UUIDs for deletion
                        for (String uuid : coverArtUuids) {
                            removeUnusedArtwork(uuid);
                        }

                        // Make sure our browse tree matches our received Available Player set only
                        rootNode.addChildren(playerList);
                        mBrowseTree.setCurrentBrowsedFolder(BrowseTree.ROOT);
                        rootNode.setExpectedChildren(playerList.size());
+203 −25

File changed.

Preview size limit exceeded, changes collapsed.