AVRCP Controller Shuffle/Repeat support

Add Custom actions to the Playback status for Shuffle and Repeat
capabilities when available from the remote device.

Bug: 72495707
Test: AvrcpControllerStateMachineTest#testShuffle
      AvrcpControllerStateMachineTest#testRepeat
	Change Shuffle and Repeat mode on connected phone via Headunit
Change-Id: Ia0a62d10032b6e918a38e66bd486a29199b772e2
(cherry picked from commit 1c862460a544108b8b12fb24827f9a717961d410)

Merged-In: Ia0a62d10032b6e918a38e66bd486a29199b772e2
Change-Id: I8669dbbe9552e5a53cb8c864c3e00c1c4df569ec
diff --git a/Android.mk b/Android.mk
index a34d2a3..1f73091 100644
--- a/Android.mk
+++ b/Android.mk
@@ -29,6 +29,7 @@
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
         androidx.core_core \
+        androidx.legacy_legacy-support-v4 \
         androidx.lifecycle_lifecycle-livedata \
         androidx.room_room-runtime \
 
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
index 64e63df..693b4a2 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
@@ -412,9 +412,10 @@
         if (stateMachine != null) {
             PlayerApplicationSettings supportedSettings =
                     PlayerApplicationSettings.makeSupportedSettings(playerAttribRsp);
+            stateMachine.sendMessage(
+                    AvrcpControllerStateMachine.MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS,
+                    supportedSettings);
         }
-        /* Do nothing */
-
     }
 
     private synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp,
@@ -426,10 +427,12 @@
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
 
-            PlayerApplicationSettings desiredSettings =
+            PlayerApplicationSettings currentSettings =
                     PlayerApplicationSettings.makeSettings(playerAttribRsp);
+            stateMachine.sendMessage(
+                    AvrcpControllerStateMachine.MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS,
+                    currentSettings);
         }
-        /* Do nothing */
     }
 
     // Browsing related JNI callbacks.
@@ -711,7 +714,7 @@
     /**
      * Send button press commands to addressed device
      *
-     * @param keyCode key code as defined in AVRCP specification
+     * @param keyCode  key code as defined in AVRCP specification
      * @param keyState 0 = key pressed, 1 = key released
      * @return command was sent
      */
@@ -720,7 +723,7 @@
     /**
      * Send group navigation commands
      *
-     * @param keyCode next/previous
+     * @param keyCode  next/previous
      * @param keyState state
      * @return command was sent
      */
@@ -741,7 +744,7 @@
      * Send response to set absolute volume
      *
      * @param absVol new volume
-     * @param label label
+     * @param label  label
      */
     public native void sendAbsVolRspNative(byte[] address, int absVol, int label);
 
@@ -749,8 +752,8 @@
      * Register for any volume level changes
      *
      * @param rspType type of response
-     * @param absVol current volume
-     * @param label label
+     * @param absVol  current volume
+     * @param label   label
      */
     public native void sendRegisterAbsVolRspNative(byte[] address, byte rspType, int absVol,
             int label);
@@ -764,7 +767,7 @@
      * Fetch the current now playing list
      *
      * @param start first index to retrieve
-     * @param end last index to retrieve
+     * @param end   last index to retrieve
      */
     public native void getNowPlayingListNative(byte[] address, int start, int end);
 
@@ -772,7 +775,7 @@
      * Fetch the current folder's listing
      *
      * @param start first index to retrieve
-     * @param end last index to retrieve
+     * @param end   last index to retrieve
      */
     public native void getFolderListNative(byte[] address, int start, int end);
 
@@ -780,7 +783,7 @@
      * Fetch the listing of players
      *
      * @param start first index to retrieve
-     * @param end last index to retrieve
+     * @param end   last index to retrieve
      */
     public native void getPlayerListNative(byte[] address, int start, int end);
 
@@ -788,15 +791,15 @@
      * Change the current browsed folder
      *
      * @param direction up/down
-     * @param uid folder unique id
+     * @param uid       folder unique id
      */
     public native void changeFolderPathNative(byte[] address, byte direction, long uid);
 
     /**
      * Play item with provided uid
      *
-     * @param scope scope of item to played
-     * @param uid song unique id
+     * @param scope      scope of item to played
+     * @param uid        song unique id
      * @param uidCounter counter
      */
     public native void playItemNative(byte[] address, byte scope, long uid, int uidCounter);
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
index 1d1a635..7a6c541 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
@@ -24,10 +24,10 @@
 import android.media.AudioManager;
 import android.media.MediaMetadata;
 import android.media.browse.MediaBrowser.MediaItem;
-import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.os.Message;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -42,6 +42,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+
 /**
  * Provides Bluetooth AVRCP Controller State Machine responsible for all remote control connections
  * and interactions with a remote controlable device.
@@ -76,11 +77,15 @@
     static final int MESSAGE_PROCESS_SET_ADDRESSED_PLAYER = 214;
     static final int MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED = 215;
     static final int MESSAGE_PROCESS_NOW_PLAYING_CONTENTS_CHANGED = 216;
+    static final int MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS = 217;
+    static final int MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS = 218;
 
     //300->399 Events for Browsing
     static final int MESSAGE_GET_FOLDER_ITEMS = 300;
     static final int MESSAGE_PLAY_ITEM = 301;
     static final int MSG_AVRCP_PASSTHRU = 302;
+    static final int MSG_AVRCP_SET_SHUFFLE = 303;
+    static final int MSG_AVRCP_SET_REPEAT = 304;
 
     static final int MESSAGE_INTERNAL_ABS_VOL_TIMEOUT = 404;
 
@@ -215,12 +220,12 @@
 
     synchronized void onBrowsingDisconnected() {
         if (!mBrowsingConnected) return;
-        mAddressedPlayer.setPlayStatus(PlaybackState.STATE_ERROR);
+        mAddressedPlayer.setPlayStatus(PlaybackStateCompat.STATE_ERROR);
         mAddressedPlayer.updateCurrentTrack(null);
         mBrowseTree.mNowPlayingNode.setCached(false);
         BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode);
-        PlaybackState.Builder pbb = new PlaybackState.Builder();
-        pbb.setState(PlaybackState.STATE_ERROR, PlaybackState.PLAYBACK_POSITION_UNKNOWN,
+        PlaybackStateCompat.Builder pbb = new PlaybackStateCompat.Builder();
+        pbb.setState(PlaybackStateCompat.STATE_ERROR, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN,
                 1.0f).setActions(0);
         pbb.setErrorMessage(mService.getString(R.string.bluetooth_disconnected));
         BluetoothMediaBrowserService.notifyChanged(pbb.build());
@@ -322,6 +327,14 @@
                     passThru(msg.arg1);
                     return true;
 
+                case MSG_AVRCP_SET_REPEAT:
+                    setRepeat(msg.arg1);
+                    return true;
+
+                case MSG_AVRCP_SET_SHUFFLE:
+                    setShuffle(msg.arg1);
+                    return true;
+
                 case MESSAGE_PROCESS_TRACK_CHANGED:
                     mAddressedPlayer.updateCurrentTrack((MediaMetadata) msg.obj);
                     BluetoothMediaBrowserService.trackChanged((MediaMetadata) msg.obj);
@@ -331,7 +344,7 @@
                     mAddressedPlayer.setPlayStatus(msg.arg1);
                     BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState());
                     if (mAddressedPlayer.getPlaybackState().getState()
-                            == PlaybackState.STATE_PLAYING
+                            == PlaybackStateCompat.STATE_PLAYING
                             && A2dpSinkService.getFocusState() == AudioManager.AUDIOFOCUS_NONE
                             && !shouldRequestFocus()) {
                         sendMessage(MSG_AVRCP_PASSTHRU,
@@ -362,6 +375,18 @@
                     }
                     return true;
 
+                case MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS:
+                    mAddressedPlayer.setSupportedPlayerApplicationSettings(
+                            (PlayerApplicationSettings) msg.obj);
+                    BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState());
+                    return true;
+
+                case MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS:
+                    mAddressedPlayer.setCurrentPlayerApplicationSettings(
+                            (PlayerApplicationSettings) msg.obj);
+                    BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState());
+                    return true;
+
                 case DISCONNECT:
                     transitionTo(mDisconnecting);
                     return true;
@@ -419,6 +444,20 @@
             return (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_REWIND)
                     || (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_FF);
         }
+
+        private void setRepeat(int repeatMode) {
+            mService.setPlayerApplicationSettingValuesNative(mDeviceAddress, (byte) 1,
+                    new byte[]{PlayerApplicationSettings.REPEAT_STATUS}, new byte[]{
+                            PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal(
+                                    PlayerApplicationSettings.REPEAT_STATUS, repeatMode)});
+        }
+
+        private void setShuffle(int shuffleMode) {
+            mService.setPlayerApplicationSettingValuesNative(mDeviceAddress, (byte) 1,
+                    new byte[]{PlayerApplicationSettings.SHUFFLE_STATUS}, new byte[]{
+                            PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal(
+                                    PlayerApplicationSettings.SHUFFLE_STATUS, shuffleMode)});
+        }
     }
 
     // Handle the get folder listing action
@@ -538,7 +577,7 @@
                 case MESSAGE_GET_FOLDER_ITEMS:
                     if (!mBrowseNode.equals(msg.obj)) {
                         if (shouldAbort(mBrowseNode.getScope(),
-                                 ((BrowseTree.BrowseNode) msg.obj).getScope())) {
+                                ((BrowseTree.BrowseNode) msg.obj).getScope())) {
                             mAbort = true;
                         }
                         deferMessage(msg);
@@ -564,7 +603,7 @@
 
                 default:
                     logD(STATE_TAG + " deferring message " + msg.what
-                                + " to connected!");
+                            + " to connected!");
                     deferMessage(msg);
             }
             return true;
@@ -575,8 +614,8 @@
          * necessary.
          *
          * @return true:  a new folder in the same scope
-         *                a new player while fetching contents of a folder
-         *         false: other cases, specifically Now Playing while fetching a folder
+         * a new player while fetching contents of a folder
+         * false: other cases, specifically Now Playing while fetching a folder
          */
         private boolean shouldAbort(int currentScope, int fetchScope) {
             if ((currentScope == fetchScope)
@@ -697,7 +736,7 @@
         mService.sendAbsVolRspNative(mDeviceAddress, absVol, label);
     }
 
-    MediaSession.Callback mSessionCallbacks = new MediaSession.Callback() {
+    MediaSessionCompat.Callback mSessionCallbacks = new MediaSessionCompat.Callback() {
         @Override
         public void onPlay() {
             logD("onPlay");
@@ -770,6 +809,19 @@
             BrowseTree.BrowseNode node = mBrowseTree.findBrowseNodeByID(mediaId);
             sendMessage(MESSAGE_PLAY_ITEM, node);
         }
+
+        @Override
+        public void onSetRepeatMode(int repeatMode) {
+            logD("onSetRepeatMode");
+            sendMessage(MSG_AVRCP_SET_REPEAT, repeatMode);
+        }
+
+        @Override
+        public void onSetShuffleMode(int shuffleMode) {
+            logD("onSetShuffleMode");
+            sendMessage(MSG_AVRCP_SET_SHUFFLE, shuffleMode);
+
+        }
     };
 
     protected void broadcastConnectionStateChanged(int currentState) {
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
index bed38d9..2ef3c14 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
@@ -17,8 +17,9 @@
 package com.android.bluetooth.avrcpcontroller;
 
 import android.media.MediaMetadata;
-import android.media.session.PlaybackState;
 import android.os.SystemClock;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
 import android.util.Log;
 
 import java.util.Arrays;
@@ -41,27 +42,31 @@
     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 mPlayStatus = PlaybackStateCompat.STATE_NONE;
+    private long mPlayTime = PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN;
     private long mPlayTimeUpdate = 0;
     private float mPlaySpeed = 1;
     private int mId;
     private String mName = "";
     private int mPlayerType;
-    private byte[] mPlayerFeatures;
+    private byte[] mPlayerFeatures = new byte[16];
     private long mAvailableActions;
     private MediaMetadata mCurrentTrack;
-    private PlaybackState mPlaybackState;
+    private PlaybackStateCompat mPlaybackStateCompat;
+    private PlayerApplicationSettings mSupportedPlayerApplicationSettings =
+            new PlayerApplicationSettings();
+    private PlayerApplicationSettings mCurrentPlayerApplicationSettings;
 
     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
-                | PlaybackState.ACTION_STOP;
-        PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder()
+        mAvailableActions = PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY
+                | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
+                | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
+                | PlaybackStateCompat.ACTION_STOP;
+        PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
                 .setActions(mAvailableActions);
-        mPlaybackState = playbackStateBuilder.build();
+        mPlaybackStateCompat = playbackStateBuilder.build();
     }
 
     AvrcpPlayer(int id, String name, byte[] playerFeatures, int playStatus, int playerType) {
@@ -70,10 +75,10 @@
         mPlayStatus = playStatus;
         mPlayerType = playerType;
         mPlayerFeatures = Arrays.copyOf(playerFeatures, playerFeatures.length);
-        updateAvailableActions();
-        PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder()
+        PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
                 .setActions(mAvailableActions);
-        mPlaybackState = playbackStateBuilder.build();
+        mPlaybackStateCompat = playbackStateBuilder.build();
+        updateAvailableActions();
     }
 
     public int getId() {
@@ -87,7 +92,8 @@
     public void setPlayTime(int playTime) {
         mPlayTime = playTime;
         mPlayTimeUpdate = SystemClock.elapsedRealtime();
-        mPlaybackState = new PlaybackState.Builder(mPlaybackState).setState(mPlayStatus, mPlayTime,
+        mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState(
+                mPlayStatus, mPlayTime,
                 mPlaySpeed).build();
     }
 
@@ -97,30 +103,48 @@
 
     public void setPlayStatus(int playStatus) {
         mPlayTime += mPlaySpeed * (SystemClock.elapsedRealtime()
-                - mPlaybackState.getLastPositionUpdateTime());
+                - mPlaybackStateCompat.getLastPositionUpdateTime());
         mPlayStatus = playStatus;
         switch (mPlayStatus) {
-            case PlaybackState.STATE_STOPPED:
+            case PlaybackStateCompat.STATE_STOPPED:
                 mPlaySpeed = 0;
                 break;
-            case PlaybackState.STATE_PLAYING:
+            case PlaybackStateCompat.STATE_PLAYING:
                 mPlaySpeed = 1;
                 break;
-            case PlaybackState.STATE_PAUSED:
+            case PlaybackStateCompat.STATE_PAUSED:
                 mPlaySpeed = 0;
                 break;
-            case PlaybackState.STATE_FAST_FORWARDING:
+            case PlaybackStateCompat.STATE_FAST_FORWARDING:
                 mPlaySpeed = 3;
                 break;
-            case PlaybackState.STATE_REWINDING:
+            case PlaybackStateCompat.STATE_REWINDING:
                 mPlaySpeed = -3;
                 break;
         }
 
-        mPlaybackState = new PlaybackState.Builder(mPlaybackState).setState(mPlayStatus, mPlayTime,
+        mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState(
+                mPlayStatus, mPlayTime,
                 mPlaySpeed).build();
     }
 
+    public void setSupportedPlayerApplicationSettings(
+            PlayerApplicationSettings playerApplicationSettings) {
+        mSupportedPlayerApplicationSettings = playerApplicationSettings;
+        updateAvailableActions();
+    }
+
+    public void setCurrentPlayerApplicationSettings(
+            PlayerApplicationSettings playerApplicationSettings) {
+        Log.d(TAG, "Settings changed");
+        mCurrentPlayerApplicationSettings = playerApplicationSettings;
+        MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
+        session.setRepeatMode(mCurrentPlayerApplicationSettings.getSetting(
+                PlayerApplicationSettings.REPEAT_STATUS));
+        session.setShuffleMode(mCurrentPlayerApplicationSettings.getSetting(
+                PlayerApplicationSettings.SHUFFLE_STATUS));
+    }
+
     public int getPlayStatus() {
         return mPlayStatus;
     }
@@ -131,17 +155,22 @@
         return (mPlayerFeatures[byteNumber] & bitMask) == bitMask;
     }
 
-    public PlaybackState getPlaybackState() {
+    public boolean supportsSetting(int settingType, int settingValue) {
+        return mSupportedPlayerApplicationSettings.supportsSetting(settingType, settingValue);
+    }
+
+    public PlaybackStateCompat getPlaybackState() {
         if (DBG) {
             Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime);
         }
-        return mPlaybackState;
+        return mPlaybackStateCompat;
     }
 
     public synchronized void updateCurrentTrack(MediaMetadata update) {
         if (update != null) {
             long trackNumber = update.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER);
-            mPlaybackState = new PlaybackState.Builder(mPlaybackState).setActiveQueueItemId(
+            mPlaybackStateCompat = new PlaybackStateCompat.Builder(
+                    mPlaybackStateCompat).setActiveQueueItemId(
                     trackNumber - 1).build();
         }
         mCurrentTrack = update;
@@ -153,26 +182,37 @@
 
     private void updateAvailableActions() {
         if (supportsFeature(FEATURE_PLAY)) {
-            mAvailableActions = mAvailableActions | PlaybackState.ACTION_PLAY;
+            mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PLAY;
         }
         if (supportsFeature(FEATURE_STOP)) {
-            mAvailableActions = mAvailableActions | PlaybackState.ACTION_STOP;
+            mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_STOP;
         }
         if (supportsFeature(FEATURE_PAUSE)) {
-            mAvailableActions = mAvailableActions | PlaybackState.ACTION_PAUSE;
+            mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PAUSE;
         }
         if (supportsFeature(FEATURE_REWIND)) {
-            mAvailableActions = mAvailableActions | PlaybackState.ACTION_REWIND;
+            mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_REWIND;
         }
         if (supportsFeature(FEATURE_FAST_FORWARD)) {
-            mAvailableActions = mAvailableActions | PlaybackState.ACTION_FAST_FORWARD;
+            mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_FAST_FORWARD;
         }
         if (supportsFeature(FEATURE_FORWARD)) {
-            mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_NEXT;
+            mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
         }
         if (supportsFeature(FEATURE_PREVIOUS)) {
-            mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_PREVIOUS;
+            mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
         }
+        if (mSupportedPlayerApplicationSettings.supportsSetting(
+                PlayerApplicationSettings.REPEAT_STATUS)) {
+            mAvailableActions |= PlaybackStateCompat.ACTION_SET_REPEAT_MODE;
+        }
+        if (mSupportedPlayerApplicationSettings.supportsSetting(
+                PlayerApplicationSettings.SHUFFLE_STATUS)) {
+            mAvailableActions |= PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE;
+        }
+        mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat)
+                .setActions(mAvailableActions).build();
+
         if (DBG) Log.d(TAG, "Supported Actions = " + mAvailableActions);
     }
 }
diff --git a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
index 3504cd4..774f953 100644
--- a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
+++ b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
@@ -18,13 +18,17 @@
 
 import android.media.MediaMetadata;
 import android.media.browse.MediaBrowser.MediaItem;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
 import android.os.Bundle;
-import android.service.media.MediaBrowserService;
+import android.support.v4.media.MediaBrowserCompat;
+import android.support.v4.media.MediaDescriptionCompat;
+import android.support.v4.media.MediaMetadataCompat;
+import android.support.v4.media.session.MediaControllerCompat;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
 import android.util.Log;
 
+import androidx.media.MediaBrowserServiceCompat;
+
 import com.android.bluetooth.R;
 
 import java.util.ArrayList;
@@ -37,43 +41,44 @@
  * The applications are expected to use MediaBrowser (see API) and all the music
  * browsing/playback/metadata can be controlled via MediaBrowser and MediaController.
  *
- * The current behavior of MediaSession exposed by this service is as follows:
- * 1. MediaSession is active (i.e. SystemUI and other overview UIs can see updates) when device is
- * connected and first starts playing. Before it starts playing we do not active the session.
+ * The current behavior of MediaSessionCompat exposed by this service is as follows:
+ * 1. MediaSessionCompat is active (i.e. SystemUI and other overview UIs can see updates) when
+ * device is connected and first starts playing. Before it starts playing we do not activate the
+ * session.
  * 1.1 The session is active throughout the duration of connection.
  * 2. The session is de-activated when the device disconnects. It will be connected again when (1)
  * happens.
  */
-public class BluetoothMediaBrowserService extends MediaBrowserService {
+public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
     private static final String TAG = "BluetoothMediaBrowserService";
     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static BluetoothMediaBrowserService sBluetoothMediaBrowserService;
 
-    private MediaSession mSession;
+    private MediaSessionCompat mSession;
 
     // Browsing related structures.
-    private List<MediaSession.QueueItem> mMediaQueue = new ArrayList<>();
+    private List<MediaSessionCompat.QueueItem> mMediaQueue = new ArrayList<>();
 
     /**
-     * Initialize this BluetoothMediaBrowserService, creating our MediaSession, MediaPlayer and
-     * MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService.
+     * Initialize this BluetoothMediaBrowserService, creating our MediaSessionCompat, MediaPlayer
+     * and MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService.
      */
     @Override
     public void onCreate() {
         if (DBG) Log.d(TAG, "onCreate");
         super.onCreate();
 
-        // Create and configure the MediaSession
-        mSession = new MediaSession(this, TAG);
+        // Create and configure the MediaSessionCompat
+        mSession = new MediaSessionCompat(this, TAG);
         setSessionToken(mSession.getSessionToken());
-        mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS
-                | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+        mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
+                | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
         mSession.setQueueTitle(getString(R.string.bluetooth_a2dp_sink_queue_name));
         mSession.setQueue(mMediaQueue);
-        PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder();
-        playbackStateBuilder.setState(PlaybackState.STATE_ERROR,
-                PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0);
+        PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder();
+        playbackStateBuilder.setState(PlaybackStateCompat.STATE_ERROR,
+                PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0);
         playbackStateBuilder.setErrorMessage(getString(R.string.bluetooth_disconnected));
         mSession.setPlaybackState(playbackStateBuilder.build());
         sBluetoothMediaBrowserService = this;
@@ -91,9 +96,10 @@
 
     @Override
     public synchronized void onLoadChildren(final String parentMediaId,
-            final Result<List<MediaItem>> result) {
+            final Result<List<MediaBrowserCompat.MediaItem>> result) {
         if (DBG) Log.d(TAG, "onLoadChildren parentMediaId=" + parentMediaId);
-        List<MediaItem> contents = getContents(parentMediaId);
+        List<MediaBrowserCompat.MediaItem> contents =
+                MediaBrowserCompat.MediaItem.fromMediaItemList(getContents(parentMediaId));
         if (contents == null) {
             result.detach();
         } else {
@@ -112,7 +118,8 @@
         mMediaQueue.clear();
         if (songList != null) {
             for (MediaItem song : songList) {
-                mMediaQueue.add(new MediaSession.QueueItem(song.getDescription(),
+                mMediaQueue.add(new MediaSessionCompat.QueueItem(
+                        MediaDescriptionCompat.fromMediaDescription(song.getDescription()),
                         mMediaQueue.size()));
             }
         }
@@ -129,7 +136,7 @@
         }
     }
 
-    static synchronized void addressedPlayerChanged(MediaSession.Callback callback) {
+    static synchronized void addressedPlayerChanged(MediaSessionCompat.Callback callback) {
         if (sBluetoothMediaBrowserService != null) {
             sBluetoothMediaBrowserService.mSession.setCallback(callback);
         } else {
@@ -139,13 +146,14 @@
 
     static synchronized void trackChanged(MediaMetadata mediaMetadata) {
         if (sBluetoothMediaBrowserService != null) {
-            sBluetoothMediaBrowserService.mSession.setMetadata(mediaMetadata);
+            sBluetoothMediaBrowserService.mSession.setMetadata(
+                    MediaMetadataCompat.fromMediaMetadata(mediaMetadata));
         } else {
             Log.w(TAG, "trackChanged Unavailable");
         }
     }
 
-    static synchronized void notifyChanged(PlaybackState playbackState) {
+    static synchronized void notifyChanged(PlaybackStateCompat playbackState) {
         Log.d(TAG, "notifyChanged PlaybackState" + playbackState);
         if (sBluetoothMediaBrowserService != null) {
             sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState);
@@ -179,7 +187,7 @@
     /**
      * Get object for controlling playback
      */
-    public static synchronized MediaController.TransportControls getTransportControls() {
+    public static synchronized MediaControllerCompat.TransportControls getTransportControls() {
         if (sBluetoothMediaBrowserService != null) {
             return sBluetoothMediaBrowserService.mSession.getController().getTransportControls();
         } else {
@@ -198,4 +206,16 @@
             Log.w(TAG, "setActive Unavailable");
         }
     }
+
+    /**
+     * Get Media session for updating state
+     */
+    public static synchronized MediaSessionCompat getSession() {
+        if (sBluetoothMediaBrowserService != null) {
+            return sBluetoothMediaBrowserService.mSession;
+        } else {
+            Log.w(TAG, "getSession Unavailable");
+            return null;
+        }
+    }
 }
diff --git a/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java b/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java
index c34a2d7..362548e 100644
--- a/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java
+++ b/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java
@@ -16,12 +16,11 @@
 
 package com.android.bluetooth.avrcpcontroller;
 
-import android.bluetooth.BluetoothAvrcpPlayerSettings;
+import android.support.v4.media.session.PlaybackStateCompat;
 import android.util.Log;
+import android.util.SparseArray;
 
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
 
 /*
  * Contains information Player Application Setting extended from BluetootAvrcpPlayerSettings
@@ -32,10 +31,10 @@
     /*
      * Values for SetPlayerApplicationSettings from AVRCP Spec V1.6 Appendix F.
      */
-    private static final byte JNI_ATTRIB_EQUALIZER_STATUS = 0x01;
-    private static final byte JNI_ATTRIB_REPEAT_STATUS = 0x02;
-    private static final byte JNI_ATTRIB_SHUFFLE_STATUS = 0x03;
-    private static final byte JNI_ATTRIB_SCAN_STATUS = 0x04;
+    static final byte EQUALIZER_STATUS = 0x01;
+    static final byte REPEAT_STATUS = 0x02;
+    static final byte SHUFFLE_STATUS = 0x03;
+    static final byte SCAN_STATUS = 0x04;
 
     private static final byte JNI_EQUALIZER_STATUS_OFF = 0x01;
     private static final byte JNI_EQUALIZER_STATUS_ON = 0x02;
@@ -55,18 +54,17 @@
 
     private static final byte JNI_STATUS_INVALID = -1;
 
-
     /*
      * Hash map of current settings.
      */
-    private Map<Integer, Integer> mSettings = new HashMap<Integer, Integer>();
+    private SparseArray<Integer> mSettings = new SparseArray<>();
 
     /*
      * Hash map of supported values, a setting should be supported by the remote in order to enable
      * in mSettings.
      */
-    private Map<Integer, ArrayList<Integer>> mSupportedValues =
-            new HashMap<Integer, ArrayList<Integer>>();
+    private SparseArray<ArrayList<Integer>> mSupportedValues =
+            new SparseArray<ArrayList<Integer>>();
 
     /* Convert from JNI array to Java classes. */
     static PlayerApplicationSettings makeSupportedSettings(byte[] btAvrcpAttributeList) {
@@ -82,8 +80,7 @@
                     supportedValues.add(
                             mapAttribIdValtoAvrcpPlayerSetting(attrId, btAvrcpAttributeList[i++]));
                 }
-                newObj.mSupportedValues.put(mapBTAttribIdToAvrcpPlayerSettings(attrId),
-                        supportedValues);
+                newObj.mSupportedValues.put(attrId, supportedValues);
             }
         } catch (ArrayIndexOutOfBoundsException exception) {
             Log.e(TAG, "makeSupportedSettings attributeList index error.");
@@ -91,25 +88,13 @@
         return newObj;
     }
 
-    public BluetoothAvrcpPlayerSettings getAvrcpSettings() {
-        int supportedSettings = 0;
-        for (Integer setting : mSettings.keySet()) {
-            supportedSettings |= setting;
-        }
-        BluetoothAvrcpPlayerSettings result = new BluetoothAvrcpPlayerSettings(supportedSettings);
-        for (Integer setting : mSettings.keySet()) {
-            result.addSettingValue(setting, mSettings.get(setting));
-        }
-        return result;
-    }
-
     static PlayerApplicationSettings makeSettings(byte[] btAvrcpAttributeList) {
         PlayerApplicationSettings newObj = new PlayerApplicationSettings();
         try {
             for (int i = 0; i < btAvrcpAttributeList.length; ) {
                 byte attrId = btAvrcpAttributeList[i++];
 
-                newObj.mSettings.put(mapBTAttribIdToAvrcpPlayerSettings(attrId),
+                newObj.mSettings.put(attrId,
                         mapAttribIdValtoAvrcpPlayerSetting(attrId, btAvrcpAttributeList[i++]));
             }
         } catch (ArrayIndexOutOfBoundsException exception) {
@@ -123,177 +108,69 @@
         mSupportedValues = updates.mSupportedValues;
     }
 
-    public void setValues(BluetoothAvrcpPlayerSettings updates) {
-        int supportedSettings = updates.getSettings();
-        for (int i = 1; i <= BluetoothAvrcpPlayerSettings.SETTING_SCAN; i++) {
-            if ((i & supportedSettings) > 0) {
-                mSettings.put(i, updates.getSettingValue(i));
-            }
-        }
+    public boolean supportsSetting(int settingType, int settingValue) {
+        if (null == mSupportedValues.get(settingType)) return false;
+        return mSupportedValues.valueAt(settingType).contains(settingValue);
     }
 
-    /*
-     * Check through all settings to ensure that they are all available to be set and then check
-     * that the desired value is in fact supported by our remote player.
-     */
-    public boolean supportsSettings(BluetoothAvrcpPlayerSettings settingsToCheck) {
-        int settingSubset = settingsToCheck.getSettings();
-        int supportedSettings = 0;
-        for (Integer setting : mSupportedValues.keySet()) {
-            supportedSettings |= setting;
-        }
-        try {
-            if ((supportedSettings & settingSubset) == settingSubset) {
-                for (Integer settingId : mSettings.keySet()) {
-                    // The setting is in both settings to check and supported settings but the
-                    // value is not supported.
-                    if ((settingId & settingSubset) == settingId && (!mSupportedValues.get(
-                            settingId).contains(settingsToCheck.getSettingValue(settingId)))) {
-                        return false;
-                    }
-                }
-                return true;
-            }
-        } catch (NullPointerException e) {
-            Log.e(TAG,
-                    "supportsSettings received a supported setting that has no supported values.");
-        }
-        return false;
+    public boolean supportsSetting(int settingType) {
+        return (null != mSupportedValues.get(settingType));
     }
 
-    // Convert currently desired settings into an attribute array to pass to the native layer to
-    // enable them.
-    public ArrayList<Byte> getNativeSettings() {
-        int i = 0;
-        ArrayList<Byte> attribArray = new ArrayList<Byte>();
-        for (Integer settingId : mSettings.keySet()) {
-            switch (settingId) {
-                case BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER:
-                    attribArray.add(JNI_ATTRIB_EQUALIZER_STATUS);
-                    attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId,
-                            mSettings.get(settingId)));
-                    break;
-                case BluetoothAvrcpPlayerSettings.SETTING_REPEAT:
-                    attribArray.add(JNI_ATTRIB_REPEAT_STATUS);
-                    attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId,
-                            mSettings.get(settingId)));
-                    break;
-                case BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE:
-                    attribArray.add(JNI_ATTRIB_SHUFFLE_STATUS);
-                    attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId,
-                            mSettings.get(settingId)));
-                    break;
-                case BluetoothAvrcpPlayerSettings.SETTING_SCAN:
-                    attribArray.add(JNI_ATTRIB_SCAN_STATUS);
-                    attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId,
-                            mSettings.get(settingId)));
-                    break;
-                default:
-                    Log.w(TAG, "Unknown setting found in getNativeSettings: " + settingId);
-            }
-        }
-        return attribArray;
+    public int getSetting(int settingType) {
+        if (null == mSettings.get(settingType)) return -1;
+        return mSettings.get(settingType);
     }
 
     // Convert a native Attribute Id/Value pair into the AVRCP equivalent value.
     private static int mapAttribIdValtoAvrcpPlayerSetting(byte attribId, byte attribVal) {
-        if (attribId == JNI_ATTRIB_EQUALIZER_STATUS) {
-            switch (attribVal) {
-                case JNI_EQUALIZER_STATUS_OFF:
-                    return BluetoothAvrcpPlayerSettings.STATE_OFF;
-                case JNI_EQUALIZER_STATUS_ON:
-                    return BluetoothAvrcpPlayerSettings.STATE_ON;
-            }
-        } else if (attribId == JNI_ATTRIB_REPEAT_STATUS) {
+        if (attribId == REPEAT_STATUS) {
             switch (attribVal) {
                 case JNI_REPEAT_STATUS_ALL_TRACK_REPEAT:
-                    return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK;
+                    return PlaybackStateCompat.REPEAT_MODE_ALL;
                 case JNI_REPEAT_STATUS_GROUP_REPEAT:
-                    return BluetoothAvrcpPlayerSettings.STATE_GROUP;
+                    return PlaybackStateCompat.REPEAT_MODE_GROUP;
                 case JNI_REPEAT_STATUS_OFF:
-                    return BluetoothAvrcpPlayerSettings.STATE_OFF;
+                    return PlaybackStateCompat.REPEAT_MODE_NONE;
                 case JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT:
-                    return BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK;
+                    return PlaybackStateCompat.REPEAT_MODE_ONE;
             }
-        } else if (attribId == JNI_ATTRIB_SCAN_STATUS) {
-            switch (attribVal) {
-                case JNI_SCAN_STATUS_ALL_TRACK_SCAN:
-                    return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK;
-                case JNI_SCAN_STATUS_GROUP_SCAN:
-                    return BluetoothAvrcpPlayerSettings.STATE_GROUP;
-                case JNI_SCAN_STATUS_OFF:
-                    return BluetoothAvrcpPlayerSettings.STATE_OFF;
-            }
-        } else if (attribId == JNI_ATTRIB_SHUFFLE_STATUS) {
+        } else if (attribId == SHUFFLE_STATUS) {
             switch (attribVal) {
                 case JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE:
-                    return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK;
+                    return PlaybackStateCompat.SHUFFLE_MODE_ALL;
                 case JNI_SHUFFLE_STATUS_GROUP_SHUFFLE:
-                    return BluetoothAvrcpPlayerSettings.STATE_GROUP;
+                    return PlaybackStateCompat.SHUFFLE_MODE_GROUP;
                 case JNI_SHUFFLE_STATUS_OFF:
-                    return BluetoothAvrcpPlayerSettings.STATE_OFF;
-            }
-        }
-        return BluetoothAvrcpPlayerSettings.STATE_INVALID;
-    }
-
-    // Convert an AVRCP Setting/Value pair into the native equivalent value;
-    private static byte mapAvrcpPlayerSettingstoBTattribVal(int mSetting, int mSettingVal) {
-        if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER) {
-            switch (mSettingVal) {
-                case BluetoothAvrcpPlayerSettings.STATE_OFF:
-                    return JNI_EQUALIZER_STATUS_OFF;
-                case BluetoothAvrcpPlayerSettings.STATE_ON:
-                    return JNI_EQUALIZER_STATUS_ON;
-            }
-        } else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_REPEAT) {
-            switch (mSettingVal) {
-                case BluetoothAvrcpPlayerSettings.STATE_OFF:
-                    return JNI_REPEAT_STATUS_OFF;
-                case BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK:
-                    return JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT;
-                case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
-                    return JNI_REPEAT_STATUS_ALL_TRACK_REPEAT;
-                case BluetoothAvrcpPlayerSettings.STATE_GROUP:
-                    return JNI_REPEAT_STATUS_GROUP_REPEAT;
-            }
-        } else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE) {
-            switch (mSettingVal) {
-                case BluetoothAvrcpPlayerSettings.STATE_OFF:
-                    return JNI_SHUFFLE_STATUS_OFF;
-                case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
-                    return JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE;
-                case BluetoothAvrcpPlayerSettings.STATE_GROUP:
-                    return JNI_SHUFFLE_STATUS_GROUP_SHUFFLE;
-            }
-        } else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_SCAN) {
-            switch (mSettingVal) {
-                case BluetoothAvrcpPlayerSettings.STATE_OFF:
-                    return JNI_SCAN_STATUS_OFF;
-                case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
-                    return JNI_SCAN_STATUS_ALL_TRACK_SCAN;
-                case BluetoothAvrcpPlayerSettings.STATE_GROUP:
-                    return JNI_SCAN_STATUS_GROUP_SCAN;
+                    return PlaybackStateCompat.SHUFFLE_MODE_NONE;
             }
         }
         return JNI_STATUS_INVALID;
     }
 
-    // convert a native Attribute Id into the AVRCP Setting equivalent value;
-    private static int mapBTAttribIdToAvrcpPlayerSettings(byte attribId) {
-        switch (attribId) {
-            case JNI_ATTRIB_EQUALIZER_STATUS:
-                return BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER;
-            case JNI_ATTRIB_REPEAT_STATUS:
-                return BluetoothAvrcpPlayerSettings.SETTING_REPEAT;
-            case JNI_ATTRIB_SHUFFLE_STATUS:
-                return BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE;
-            case JNI_ATTRIB_SCAN_STATUS:
-                return BluetoothAvrcpPlayerSettings.SETTING_SCAN;
-            default:
-                return BluetoothAvrcpPlayerSettings.STATE_INVALID;
+    // Convert an AVRCP Setting/Value pair into the native equivalent value;
+    static byte mapAvrcpPlayerSettingstoBTattribVal(int mSetting, int mSettingVal) {
+        if (mSetting == REPEAT_STATUS) {
+            switch (mSettingVal) {
+                case PlaybackStateCompat.REPEAT_MODE_NONE:
+                    return JNI_REPEAT_STATUS_OFF;
+                case PlaybackStateCompat.REPEAT_MODE_ONE:
+                    return JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT;
+                case PlaybackStateCompat.REPEAT_MODE_ALL:
+                    return JNI_REPEAT_STATUS_ALL_TRACK_REPEAT;
+                case PlaybackStateCompat.REPEAT_MODE_GROUP:
+                    return JNI_REPEAT_STATUS_GROUP_REPEAT;
+            }
+        } else if (mSetting == SHUFFLE_STATUS) {
+            switch (mSettingVal) {
+                case PlaybackStateCompat.SHUFFLE_MODE_NONE:
+                    return JNI_SHUFFLE_STATUS_OFF;
+                case PlaybackStateCompat.SHUFFLE_MODE_ALL:
+                    return JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE;
+                case PlaybackStateCompat.SHUFFLE_MODE_GROUP:
+                    return JNI_SHUFFLE_STATUS_GROUP_SHUFFLE;
+            }
         }
+        return JNI_STATUS_INVALID;
     }
-
 }
-
diff --git a/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java b/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
index b1d1743..0ebb31e 100644
--- a/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
+++ b/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
@@ -23,8 +23,8 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.Intent;
-import android.media.session.MediaController;
 import android.os.Looper;
+import android.support.v4.media.session.MediaControllerCompat;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
@@ -213,7 +213,7 @@
     @Test
     public void testPlay() throws Exception {
         setUpConnectedState(true, true);
-        MediaController.TransportControls transportControls =
+        MediaControllerCompat.TransportControls transportControls =
                 BluetoothMediaBrowserService.getTransportControls();
 
         //Play
@@ -232,7 +232,7 @@
     @Test
     public void testPause() throws Exception {
         setUpConnectedState(true, true);
-        MediaController.TransportControls transportControls =
+        MediaControllerCompat.TransportControls transportControls =
                 BluetoothMediaBrowserService.getTransportControls();
 
         //Pause
@@ -251,7 +251,7 @@
     @Test
     public void testStop() throws Exception {
         setUpConnectedState(true, true);
-        MediaController.TransportControls transportControls =
+        MediaControllerCompat.TransportControls transportControls =
                 BluetoothMediaBrowserService.getTransportControls();
 
         //Stop
@@ -270,7 +270,7 @@
     @Test
     public void testNext() throws Exception {
         setUpConnectedState(true, true);
-        MediaController.TransportControls transportControls =
+        MediaControllerCompat.TransportControls transportControls =
                 BluetoothMediaBrowserService.getTransportControls();
 
         //Next
@@ -290,7 +290,7 @@
     @Test
     public void testPrevious() throws Exception {
         setUpConnectedState(true, true);
-        MediaController.TransportControls transportControls =
+        MediaControllerCompat.TransportControls transportControls =
                 BluetoothMediaBrowserService.getTransportControls();
 
         //Previous
@@ -310,7 +310,7 @@
     @Test
     public void testFastForward() throws Exception {
         setUpConnectedState(true, true);
-        MediaController.TransportControls transportControls =
+        MediaControllerCompat.TransportControls transportControls =
                 BluetoothMediaBrowserService.getTransportControls();
 
         //FastForward
@@ -331,7 +331,7 @@
     @Test
     public void testRewind() throws Exception {
         setUpConnectedState(true, true);
-        MediaController.TransportControls transportControls =
+        MediaControllerCompat.TransportControls transportControls =
                 BluetoothMediaBrowserService.getTransportControls();
 
         //Rewind
@@ -347,6 +347,44 @@
     }
 
     /**
+     * Test media browser shuffle command
+     */
+    @Test
+    public void testShuffle() throws Exception {
+        byte[] shuffleSetting = new byte[]{3};
+        byte[] shuffleMode = new byte[]{2};
+
+        setUpConnectedState(true, true);
+        MediaControllerCompat.TransportControls transportControls =
+                BluetoothMediaBrowserService.getTransportControls();
+
+        //Shuffle
+        transportControls.setShuffleMode(1);
+        verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .setPlayerApplicationSettingValuesNative(
+                eq(mTestAddress), eq((byte) 1), eq(shuffleSetting), eq(shuffleMode));
+    }
+
+    /**
+     * Test media browser repeat command
+     */
+    @Test
+    public void testRepeat() throws Exception {
+        byte[] repeatSetting = new byte[]{2};
+        byte[] repeatMode = new byte[]{3};
+
+        setUpConnectedState(true, true);
+        MediaControllerCompat.TransportControls transportControls =
+                BluetoothMediaBrowserService.getTransportControls();
+
+        //Shuffle
+        transportControls.setRepeatMode(2);
+        verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .setPlayerApplicationSettingValuesNative(
+                eq(mTestAddress), eq((byte) 1), eq(repeatSetting), eq(repeatMode));
+    }
+
+    /**
      * Test media browsing
      * Verify that a browse tree is created with the proper root
      * Verify that a player can be fetched and added to the browse tree
@@ -475,7 +513,7 @@
         BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID());
         mAvrcpStateMachine.requestContents(results);
 
-        MediaController.TransportControls transportControls =
+        MediaControllerCompat.TransportControls transportControls =
                 BluetoothMediaBrowserService.getTransportControls();
         transportControls.play();
         verify(mAvrcpControllerService,