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

Commit a85df05d authored by Sal Savage's avatar Sal Savage Committed by Automerger Merge Worker
Browse files

Merge changes I4b929fed,I0221ab61,If8d6a4e6,I9fa90826,I8eba8517 am: e7f8a673

Original change: https://android-review.googlesource.com/c/platform/packages/apps/Bluetooth/+/1712553

Change-Id: Id2eab27beae36412a9de84cdcfd6c990d08d2843
parents 883adcb7 e7f8a673
Loading
Loading
Loading
Loading
+35 −24
Original line number Diff line number Diff line
@@ -222,6 +222,30 @@ public class AvrcpControllerService extends ProfileService {
        return false;
    }

    private int toPlaybackStateFromJni(int fromJni) {
        int playbackState = PlaybackStateCompat.STATE_NONE;
        switch (fromJni) {
            case JNI_PLAY_STATUS_STOPPED:
                playbackState = PlaybackStateCompat.STATE_STOPPED;
                break;
            case JNI_PLAY_STATUS_PLAYING:
                playbackState = PlaybackStateCompat.STATE_PLAYING;
                break;
            case JNI_PLAY_STATUS_PAUSED:
                playbackState = PlaybackStateCompat.STATE_PAUSED;
                break;
            case JNI_PLAY_STATUS_FWD_SEEK:
                playbackState = PlaybackStateCompat.STATE_FAST_FORWARDING;
                break;
            case JNI_PLAY_STATUS_REV_SEEK:
                playbackState = PlaybackStateCompat.STATE_REWINDING;
                break;
            default:
                playbackState = PlaybackStateCompat.STATE_NONE;
        }
        return playbackState;
    }

    protected AvrcpControllerStateMachine newStateMachine(BluetoothDevice device) {
        return new AvrcpControllerStateMachine(device, this);
    }
@@ -530,31 +554,12 @@ public class AvrcpControllerService extends ProfileService {
        if (DBG) {
            Log.d(TAG, "onPlayStatusChanged " + playStatus);
        }
        int playbackState = PlaybackStateCompat.STATE_NONE;
        switch (playStatus) {
            case JNI_PLAY_STATUS_STOPPED:
                playbackState = PlaybackStateCompat.STATE_STOPPED;
                break;
            case JNI_PLAY_STATUS_PLAYING:
                playbackState = PlaybackStateCompat.STATE_PLAYING;
                break;
            case JNI_PLAY_STATUS_PAUSED:
                playbackState = PlaybackStateCompat.STATE_PAUSED;
                break;
            case JNI_PLAY_STATUS_FWD_SEEK:
                playbackState = PlaybackStateCompat.STATE_FAST_FORWARDING;
                break;
            case JNI_PLAY_STATUS_REV_SEEK:
                playbackState = PlaybackStateCompat.STATE_REWINDING;
                break;
            default:
                playbackState = PlaybackStateCompat.STATE_NONE;
        }
        BluetoothDevice device = mAdapter.getRemoteDevice(address);
        AvrcpControllerStateMachine stateMachine = getStateMachine(device);
        if (stateMachine != null) {
            stateMachine.sendMessage(
                    AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, playbackState);
                    AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
                    toPlaybackStateFromJni(playStatus));
        }
    }

@@ -698,10 +703,16 @@ public class AvrcpControllerService extends ProfileService {
                            + transportFlags + " play status " + playStatus + " player type "
                            + playerType);
        }

        BluetoothDevice device = mAdapter.getRemoteDevice(address);
        AvrcpPlayer player = new AvrcpPlayer(device, id, name, transportFlags, playStatus,
                playerType);
        return player;
        AvrcpPlayer.Builder apb = new AvrcpPlayer.Builder();
        apb.setDevice(device);
        apb.setPlayerId(id);
        apb.setPlayerType(playerType);
        apb.setSupportedFeatures(transportFlags);
        apb.setName(name);
        apb.setPlayStatus(toPlaybackStateFromJni(playStatus));
        return apb.build();
    }

    private void handleChangeFolderRsp(byte[] address, int count) {
+127 −18
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.support.v4.media.MediaBrowserCompat.MediaItem;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
@@ -125,9 +126,11 @@ class AvrcpControllerStateMachine extends StateMachine {
    boolean mRemoteControlConnected = false;
    boolean mBrowsingConnected = false;
    final BrowseTree mBrowseTree;
    private AvrcpPlayer mAddressedPlayer = new AvrcpPlayer();
    private int mAddressedPlayerId = -1;
    private SparseArray<AvrcpPlayer> mAvailablePlayerList = new SparseArray<AvrcpPlayer>();

    private AvrcpPlayer mAddressedPlayer;
    private int mAddressedPlayerId;
    private SparseArray<AvrcpPlayer> mAvailablePlayerList;

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

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

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

        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);
        mAddressedPlayer = apb.build();
        mAvailablePlayerList.put(mAddressedPlayerId, mAddressedPlayer);

        mBrowseTree = new BrowseTree(mDevice);
        mDisconnected = new Disconnected();
        mConnecting = new Connecting();
@@ -215,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.
     *
@@ -226,6 +253,22 @@ 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() + "): ");
        for (int i = 0; i < mAvailablePlayerList.size(); i++) {
            AvrcpPlayer player = mAvailablePlayerList.valueAt(i);
            boolean isAddressed = (player.getId() == mAddressedPlayerId);
            ProfileService.println(sb, "\t" + (isAddressed ? "(Addressed) " : "") + player);
        }

        List<MediaItem> queue = null;
        if (mBrowseTree.mNowPlayingNode != null) {
            queue = mBrowseTree.mNowPlayingNode.getContents();
        }
        ProfileService.println(sb, "Queue (" + (queue == null ? 0 : queue.size()) + "): " + queue);
    }

    @VisibleForTesting
@@ -253,6 +296,7 @@ class AvrcpControllerStateMachine extends StateMachine {

    synchronized void onBrowsingConnected() {
        mBrowsingConnected = true;
        requestContents(mBrowseTree.mRootNode);
    }

    synchronized void onBrowsingDisconnected() {
@@ -262,8 +306,10 @@ class AvrcpControllerStateMachine extends StateMachine {
        String previousTrackUuid = previousTrack != null ? previousTrack.getCoverArtUuid() : null;
        mAddressedPlayer.updateCurrentTrack(null);
        mBrowseTree.mNowPlayingNode.setCached(false);
        mBrowseTree.mRootNode.setCached(false);
        if (isActive()) {
            BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode);
            BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mRootNode);
        }
        removeUnusedArtwork(previousTrackUuid);
        removeUnusedArtworkFromBrowseTree();
@@ -531,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
@@ -541,22 +589,37 @@ class AvrcpControllerStateMachine extends StateMachine {
                    if (isActive()) {
                        BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode);
                    }
                    removeUnusedArtworkFromBrowseTree();

                    AvrcpPlayer updatedPlayer = mAvailablePlayerList.get(mAddressedPlayerId);
                    if (updatedPlayer != null) {
                        mAddressedPlayer = updatedPlayer;
                        // If the new player supports the now playing feature then fetch it
                    // 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);

                    // 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:
@@ -688,6 +751,7 @@ class AvrcpControllerStateMachine extends StateMachine {
            mBrowseTree.mRootNode.setExpectedChildren(255);
            BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mRootNode);
            removeUnusedArtworkFromBrowseTree();
            requestContents(mBrowseTree.mRootNode);
        }
    }

@@ -789,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());
+3 −2
Original line number Diff line number Diff line
@@ -240,8 +240,9 @@ public class AvrcpItem {
    public String toString() {
        return "AvrcpItem{mUuid=" + mUuid + ", mUid=" + mUid + ", mItemType=" + mItemType
                + ", mType=" + mType + ", mDisplayableName=" + mDisplayableName
                + ", mTitle=" + mTitle + ", mPlayable=" + mPlayable + ", mBrowsable="
                + mBrowsable + ", mCoverArtHandle=" + getCoverArtHandle()
                + ", mTitle=" + mTitle + " mPlayingTime=" + mPlayingTime + " mTrack="
                + mTrackNumber + "/" + mTotalNumberOfTracks + ", mPlayable=" + mPlayable
                + ", mBrowsable=" + mBrowsable + ", mCoverArtHandle=" + getCoverArtHandle()
                + ", mImageUuid=" + mImageUuid + ", mImageUri" + mImageUri + "}";
    }

+155 −19
Original line number Diff line number Diff line
@@ -32,7 +32,17 @@ class AvrcpPlayer {
    private static final String TAG = "AvrcpPlayer";
    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);

    public static final int INVALID_ID = -1;
    public static final int DEFAULT_ID = -1;

    public static final int TYPE_UNKNOWN = -1;
    public static final int TYPE_AUDIO = 0;
    public static final int TYPE_VIDEO = 1;
    public static final int TYPE_BROADCASTING_AUDIO = 2;
    public static final int TYPE_BROADCASTING_VIDEO = 3;

    public static final int SUB_TYPE_UNKNOWN = -1;
    public static final int SUB_TYPE_AUDIO_BOOK = 0;
    public static final int SUB_TYPE_PODCAST = 1;

    public static final int FEATURE_PLAY = 40;
    public static final int FEATURE_STOP = 41;
@@ -60,31 +70,18 @@ class AvrcpPlayer {
            new PlayerApplicationSettings();
    private PlayerApplicationSettings mCurrentPlayerApplicationSettings;

    AvrcpPlayer() {
        mDevice = null;
        mId = INVALID_ID;
        //Set Default Actions in case Player data isn't available.
        mAvailableActions = PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY
                | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
                | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
                | PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_PREPARE;
        PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
                .setActions(mAvailableActions);
        mPlaybackStateCompat = playbackStateBuilder.build();
    }

    AvrcpPlayer(BluetoothDevice device, int id, String name, byte[] playerFeatures, int playStatus,
            int playerType) {
    private AvrcpPlayer(BluetoothDevice device, int id, int playerType, int playerSubType,
            String name, byte[] playerFeatures, int playStatus) {
        mDevice = device;
        mId = id;
        mName = name;
        mPlayStatus = playStatus;
        mPlayerType = playerType;
        mPlayerFeatures = Arrays.copyOf(playerFeatures, playerFeatures.length);
        PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
                .setActions(mAvailableActions);
        mPlaybackStateCompat = playbackStateBuilder.build();
        updateAvailableActions();
        setPlayStatus(playStatus);
    }

    public BluetoothDevice getDevice() {
@@ -112,8 +109,10 @@ class AvrcpPlayer {
    }

    public void setPlayStatus(int playStatus) {
        if (mPlayTime != PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN) {
            mPlayTime += mPlaySpeed * (SystemClock.elapsedRealtime()
                    - mPlaybackStateCompat.getLastPositionUpdateTime());
        }
        mPlayStatus = playStatus;
        switch (mPlayStatus) {
            case PlaybackStateCompat.STATE_STOPPED:
@@ -202,6 +201,7 @@ class AvrcpPlayer {
    }

    private void updateAvailableActions() {
        mAvailableActions = PlaybackStateCompat.ACTION_PREPARE;
        if (supportsFeature(FEATURE_PLAY)) {
            mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PLAY;
        }
@@ -236,4 +236,140 @@ class AvrcpPlayer {

        if (DBG) Log.d(TAG, "Supported Actions = " + mAvailableActions);
    }

    @Override
    public String toString() {
        return "<AvrcpPlayer id=" + mId + " name=" + mName + " track=" + mCurrentTrack
                + " playState=" + mPlaybackStateCompat + ">";
    }

    /**
     * A Builder object for an AvrcpPlayer
     */
    public static class Builder {
        private static final String TAG = "AvrcpPlayer.Builder";
        private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);

        private BluetoothDevice mDevice = null;
        private int mPlayerId = AvrcpPlayer.DEFAULT_ID;
        private int mPlayerType = AvrcpPlayer.TYPE_UNKNOWN;
        private int mPlayerSubType = AvrcpPlayer.SUB_TYPE_UNKNOWN;
        private String mPlayerName = null;
        private byte[] mSupportedFeatures = new byte[16];

        private int mPlayStatus = PlaybackStateCompat.STATE_NONE;
        private long mPlayTime = PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN;
        private float mPlaySpeed = 1;
        private long mPlayTimeUpdate = 0;

        private AvrcpItem mTrack = null;

        /**
         * Set the device that this Player came from
         *
         * @param device The BleutoothDevice representing the remote device
         * @return This object, so you can continue building
         */
        public Builder setDevice(BluetoothDevice device) {
            mDevice = device;
            return this;
        }

        /**
         * Set the Player ID for this Player
         *
         * @param playerId The ID for this player, defined in AVRCP 6.10.2.1
         * @return This object, so you can continue building
         */
        public Builder setPlayerId(int playerId) {
            mPlayerId = playerId;
            return this;
        }

        /**
         * Set the Player Type for this Player
         *
         * @param playerType The type for this player, defined in AVRCP 6.10.2.1
         * @return This object, so you can continue building
         */
        public Builder setPlayerType(int playerType) {
            mPlayerType = playerType;
            return this;
        }

        /**
         * Set the Player Sub-type for this Player
         *
         * @param playerSubType The sub-type for this player, defined in AVRCP 6.10.2.1
         * @return This object, so you can continue building
         */
        public Builder setPlayerSubType(int playerSubType) {
            mPlayerSubType = playerSubType;
            return this;
        }

        /**
         * Set the name for this Player. This is what users will see when browsing.
         *
         * @param name The name for this player, defined in AVRCP 6.10.2.1
         * @return This object, so you can continue building
         */
        public Builder setName(String name) {
            mPlayerName = name;
            return this;
        }

        /**
         * Set the entire set of supported features for this Player.
         *
         * @param features The feature set for this player, defined in AVRCP 6.10.2.1
         * @return This object, so you can continue building
         */
        public Builder setSupportedFeatures(byte[] supportedFeatures) {
            mSupportedFeatures = supportedFeatures;
            return this;
        }

        /**
         * Set a single features as supported for this Player.
         *
         * @param feature The feature for this player, defined in AVRCP 6.10.2.1
         * @return This object, so you can continue building
         */
        public Builder setSupportedFeature(int feature) {
            int byteNumber = feature / 8;
            byte bitMask = (byte) (1 << (feature % 8));
            mSupportedFeatures[byteNumber] = (byte) (mSupportedFeatures[byteNumber] | bitMask);
            return this;
        }

        /**
         * Set the initial play status of the Player.
         *
         * @param playStatus The play state for this player as a PlaybackStateCompat.STATE_* value
         * @return This object, so you can continue building
         */
        public Builder setPlayStatus(int playStatus) {
            mPlayStatus = playStatus;
            return this;
        }

        /**
         * Set the initial play status of the Player.
         *
         * @param track The initial track for this player
         * @return This object, so you can continue building
         */
        public Builder setCurrentTrack(AvrcpItem track) {
            mTrack = track;
            return this;
        }

        public AvrcpPlayer build() {
            AvrcpPlayer player = new AvrcpPlayer(mDevice, mPlayerId, mPlayerType, mPlayerSubType,
                    mPlayerName, mSupportedFeatures, mPlayStatus);
            player.updateCurrentTrack(mTrack);
            return player;
        }
    }
}
+240 −22

File changed.

Preview size limit exceeded, changes collapsed.