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

Commit 8d0ee001 authored by Joseph Pirozzo's avatar Joseph Pirozzo Committed by Gerrit Code Review
Browse files

Merge "AVRCP Controller Player Capabilities"

parents 4a9e96fc 09f6f932
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ static jmethodID method_createFromNativePlayerItem;
static jmethodID method_handleChangeFolderRsp;
static jmethodID method_handleSetBrowsedPlayerRsp;
static jmethodID method_handleSetAddressedPlayerRsp;
static jmethodID method_handleAddressedPlayerChanged;

static jclass class_MediaBrowser_MediaItem;
static jclass class_AvrcpPlayer;
@@ -579,6 +580,17 @@ static void btavrcp_set_addressed_player_callback(const RawAddress& bd_addr,
      sCallbacksObj, method_handleSetAddressedPlayerRsp, (jint)status);
}

static void btavrcp_addressed_player_changed_callback(const RawAddress& bd_addr,
                                                      uint16_t id) {
  ALOGI("%s status %d", __func__, id);

  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;

  sCallbackEnv->CallVoidMethod(sCallbacksObj,
                               method_handleAddressedPlayerChanged, (jint)id);
}

static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = {
    sizeof(sBluetoothAvrcpCallbacks),
    btavrcp_passthrough_response_callback,
@@ -596,7 +608,8 @@ static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = {
    btavrcp_get_folder_items_callback,
    btavrcp_change_path_callback,
    btavrcp_set_browsed_player_callback,
    btavrcp_set_addressed_player_callback};
    btavrcp_set_addressed_player_callback,
    btavrcp_addressed_player_changed_callback};

static void classInitNative(JNIEnv* env, jclass clazz) {
  method_handlePassthroughRsp =
@@ -658,6 +671,9 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
      env->GetMethodID(clazz, "handleSetBrowsedPlayerRsp", "(II)V");
  method_handleSetAddressedPlayerRsp =
      env->GetMethodID(clazz, "handleSetAddressedPlayerRsp", "(I)V");
  method_handleAddressedPlayerChanged =
      env->GetMethodID(clazz, "handleAddressedPlayerChanged", "(I)V");

  ALOGI("%s: succeeds", __func__);
}

+19 −11
Original line number Diff line number Diff line
@@ -106,13 +106,11 @@ public class A2dpMediaBrowserService extends MediaBrowserService {
    private A2dpSinkService mA2dpSinkService = null;
    private Handler mAvrcpCommandQueue;
    private final Map<String, Result<List<MediaItem>>> mParentIdToRequestMap = new HashMap<>();
    private int mCurrentlyHeldKey = 0;

    // Browsing related structures.
    private List<MediaItem> mNowPlayingList = null;

    private long mTransportControlFlags = PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY
            | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS;

    private static final class AvrcpCommandQueueHandler extends Handler {
        WeakReference<A2dpMediaBrowserService> mInst;

@@ -408,10 +406,6 @@ public class A2dpMediaBrowserService extends MediaBrowserService {
        mA2dpSinkService = A2dpSinkService.getA2dpSinkService();

        PlaybackState playbackState = mAvrcpCtrlSrvc.getPlaybackState(mA2dpDevice);
        // Add actions required for playback and rebuild the object.
        PlaybackState.Builder pbb = new PlaybackState.Builder(playbackState);
        playbackState = pbb.setActions(mTransportControlFlags).build();

        MediaMetadata mediaMetadata = mAvrcpCtrlSrvc.getMetaData(mA2dpDevice);
        if (VDBG) {
            Log.d(TAG, "Media metadata " + mediaMetadata + " playback state " + playbackState);
@@ -435,7 +429,6 @@ public class A2dpMediaBrowserService extends MediaBrowserService {
        PlaybackState.Builder pbb = new PlaybackState.Builder();
        pbb = pbb.setState(PlaybackState.STATE_ERROR, PlaybackState.PLAYBACK_POSITION_UNKNOWN,
                PLAYBACK_SPEED)
                .setActions(mTransportControlFlags)
                .setErrorMessage(getString(R.string.bluetooth_disconnected));
        mSession.setPlaybackState(pbb.build());

@@ -470,8 +463,6 @@ public class A2dpMediaBrowserService extends MediaBrowserService {

        if (pb != null) {
            if (DBG) Log.d(TAG, "msgTrack() playbackstate " + pb);
            PlaybackState.Builder pbb = new PlaybackState.Builder(pb);
            pb = pbb.setActions(mTransportControlFlags).build();
            mSession.setPlaybackState(pb);

            // If we are now playing then we should start pushing updates via MediaSession so that
@@ -482,6 +473,11 @@ public class A2dpMediaBrowserService extends MediaBrowserService {
        }
    }

    private boolean isHoldableKey(int cmd) {
        return  (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_REWIND)
                || (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_FF);
    }

    private synchronized void msgPassThru(int cmd) {
        if (DBG) Log.d(TAG, "msgPassThru " + cmd);
        if (mA2dpDevice == null) {
@@ -489,13 +485,25 @@ public class A2dpMediaBrowserService extends MediaBrowserService {
            Log.w(TAG, "Already disconnected ignoring.");
            return;
        }
        // Some keys should be held until the next event.
        if (mCurrentlyHeldKey != 0) {
            mAvrcpCtrlSrvc.sendPassThroughCmd(mA2dpDevice, mCurrentlyHeldKey,
                    AvrcpControllerService.KEY_STATE_RELEASED);
            mCurrentlyHeldKey = 0;
        }

        // Send the pass through.
        mAvrcpCtrlSrvc.sendPassThroughCmd(mA2dpDevice, cmd,
                AvrcpControllerService.KEY_STATE_PRESSED);

        if (isHoldableKey(cmd)) {
            // Release cmd next time a command is sent.
            mCurrentlyHeldKey = cmd;
        } else {
            mAvrcpCtrlSrvc.sendPassThroughCmd(mA2dpDevice, cmd,
                AvrcpControllerService.KEY_STATE_RELEASED);
        }
    }

    private synchronized void msgGetPlayStatusNative() {
        if (DBG) Log.d(TAG, "msgGetPlayStatusNative");
+10 −1
Original line number Diff line number Diff line
@@ -1031,7 +1031,7 @@ public class AvrcpControllerService extends ProfileService {
                    "createFromNativePlayerItem name: " + name + " transportFlags " + transportFlags
                            + " play status " + playStatus + " player type " + playerType);
        }
        AvrcpPlayer player = new AvrcpPlayer(id, name, 0, playStatus, playerType);
        AvrcpPlayer player = new AvrcpPlayer(id, name, transportFlags, playStatus, playerType);
        return player;
    }

@@ -1063,6 +1063,15 @@ public class AvrcpControllerService extends ProfileService {
        mAvrcpCtSm.sendMessage(msg);
    }

    private void handleAddressedPlayerChanged(int id) {
        if (DBG) {
            Log.d(TAG, "handleAddressedPlayerChanged id: " + id);
        }
        Message msg = mAvrcpCtSm.obtainMessage(
                AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, id);
        mAvrcpCtSm.sendMessage(msg);
    }

    @Override
    public void dump(StringBuilder sb) {
        super.dump(sb);
+25 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.util.SparseArray;

import com.android.bluetooth.BluetoothMetricsProto;
import com.android.bluetooth.Utils;
@@ -73,6 +74,7 @@ class AvrcpControllerStateMachine extends StateMachine {
    static final int MESSAGE_PROCESS_FOLDER_PATH = 112;
    static final int MESSAGE_PROCESS_SET_BROWSED_PLAYER = 113;
    static final int MESSAGE_PROCESS_SET_ADDRESSED_PLAYER = 114;
    static final int MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED = 115;

    // commands from A2DP sink
    static final int MESSAGE_STOP_METADATA_BROADCASTS = 201;
@@ -136,6 +138,8 @@ class AvrcpControllerStateMachine extends StateMachine {
    // Only accessed from State Machine processMessage
    private int mVolumeChangedNotificationsToIgnore = 0;
    private int mPreviousPercentageVol = -1;
    private int mAddressedPlayerID = -1;
    private SparseArray<AvrcpPlayer> mAvailablePlayerList = new SparseArray<AvrcpPlayer>();

    // Depth from root of current browsing. This can be used to move to root directly.
    private int mBrowseDepth = 0;
@@ -458,6 +462,17 @@ class AvrcpControllerStateMachine extends StateMachine {
                        }
                        break;

                    case MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED:
                        mAddressedPlayerID = msg.arg1;
                        if (DBG) Log.d(TAG, "AddressedPlayer = " + mAddressedPlayerID);
                        AvrcpPlayer updatedPlayer = mAvailablePlayerList.get(mAddressedPlayerID);
                        if (updatedPlayer != null) {
                            mAddressedPlayer = updatedPlayer;
                            if (DBG) Log.d(TAG, "AddressedPlayer = " + mAddressedPlayer.getName());
                        }
                        sendMessage(MESSAGE_PROCESS_SET_ADDRESSED_PLAYER);
                        break;

                    default:
                        return false;
                }
@@ -729,12 +744,22 @@ class AvrcpControllerStateMachine extends StateMachine {
            switch (msg.what) {
                case MESSAGE_PROCESS_GET_PLAYER_ITEMS:
                    List<AvrcpPlayer> playerList = (List<AvrcpPlayer>) msg.obj;
                    mAvailablePlayerList.clear();
                    for (AvrcpPlayer player : playerList) {
                        mAvailablePlayerList.put(player.getId(), player);
                    }
                    mBrowseTree.refreshChildren(BrowseTree.ROOT, playerList);
                    ArrayList<MediaItem> mediaItemList = new ArrayList<>();
                    for (BrowseTree.BrowseNode c : mBrowseTree.findBrowseNodeByID(BrowseTree.ROOT)
                            .getChildren()) {
                        mediaItemList.add(c.getMediaItem());
                    }
                    if (DBG) Log.d(TAG, "AddressedPlayer List Updated");
                    AvrcpPlayer updatedPlayer = mAvailablePlayerList.get(mAddressedPlayerID);
                    if (updatedPlayer != null) {
                        mAddressedPlayer = updatedPlayer;
                        if (DBG) Log.d(TAG, "AddressedPlayer = " + mAddressedPlayer.getName());
                    }
                    broadcastFolderList(BrowseTree.ROOT, mediaItemList);
                    mBrowseTree.setCurrentBrowsedFolder(BrowseTree.ROOT);
                    transitionTo(mConnected);
+57 −2
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.bluetooth.avrcpcontroller;
import android.media.session.PlaybackState;
import android.util.Log;

import java.util.Arrays;

/*
 * Contains information about remote player
 */
@@ -28,21 +30,38 @@ class AvrcpPlayer {

    public static final int INVALID_ID = -1;

    public static final int FEATURE_PLAY = 40;
    public static final int FEATURE_STOP = 41;
    public static final int FEATURE_PAUSE = 42;
    public static final int FEATURE_REWIND = 44;
    public static final int FEATURE_FAST_FORWARD = 45;
    public static final int FEATURE_FORWARD = 47;
    public static final int FEATURE_PREVIOUS = 48;
    public static final int FEATURE_BROWSING = 59;

    private int mPlayStatus = PlaybackState.STATE_NONE;
    private long mPlayTime = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
    private int mId;
    private String mName = "";
    private int mPlayerType;
    private byte[] mPlayerFeatures;
    private long mAvailableActions;
    private TrackInfo mCurrentTrack = new TrackInfo();

    AvrcpPlayer() {
        mId = INVALID_ID;
        //Set Default Actions in case Player data isn't available.
        mAvailableActions = PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY
            | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS;
    }

    AvrcpPlayer(int id, String name, int transportFlags, int playStatus, int playerType) {
    AvrcpPlayer(int id, String name, byte[] playerFeatures, int playStatus, int playerType) {
        mId = id;
        mName = name;
        mPlayStatus = playStatus;
        mPlayerType = playerType;
        mPlayerFeatures = Arrays.copyOf(playerFeatures, playerFeatures.length);
        updateAvailableActions();
    }

    public int getId() {
@@ -65,6 +84,16 @@ class AvrcpPlayer {
        mPlayStatus = playStatus;
    }

    public int getPlayStatus() {
        return mPlayStatus;
    }

    public boolean supportsFeature(int featureId) {
        int byteNumber = featureId / 8;
        byte bitMask = (byte) (1 << (featureId % 8));
        return (mPlayerFeatures[byteNumber] & bitMask) == bitMask;
    }

    public PlaybackState getPlaybackState() {
        if (DBG) {
            Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime);
@@ -87,7 +116,8 @@ class AvrcpPlayer {
                speed = -3;
                break;
        }
        return new PlaybackState.Builder().setState(mPlayStatus, position, speed).build();
        return new PlaybackState.Builder().setState(mPlayStatus, position, speed)
            .setActions(mAvailableActions).build();
    }

    public synchronized void updateCurrentTrack(TrackInfo update) {
@@ -97,4 +127,29 @@ class AvrcpPlayer {
    public synchronized TrackInfo getCurrentTrack() {
        return mCurrentTrack;
    }

    private void updateAvailableActions() {
        if (supportsFeature(FEATURE_PLAY)) {
            mAvailableActions = mAvailableActions | PlaybackState.ACTION_PLAY;
        }
        if (supportsFeature(FEATURE_STOP)) {
            mAvailableActions = mAvailableActions | PlaybackState.ACTION_STOP;
        }
        if (supportsFeature(FEATURE_PAUSE)) {
            mAvailableActions = mAvailableActions | PlaybackState.ACTION_PAUSE;
        }
        if (supportsFeature(FEATURE_REWIND)) {
            mAvailableActions = mAvailableActions | PlaybackState.ACTION_REWIND;
        }
        if (supportsFeature(FEATURE_FAST_FORWARD)) {
            mAvailableActions = mAvailableActions | PlaybackState.ACTION_FAST_FORWARD;
        }
        if (supportsFeature(FEATURE_FORWARD)) {
            mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_NEXT;
        }
        if (supportsFeature(FEATURE_PREVIOUS)) {
            mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_PREVIOUS;
        }
        if (DBG) Log.d(TAG, "Supported Actions = " + mAvailableActions);
    }
}
Loading