Merge "HFP: recover BT_SCO and set audio parameters while audio server restarted" into q-keystone-qcom-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ff6ab4f..bc10b4b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -76,9 +76,6 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
 
-    <!-- Allows access to read media audio -->
-    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
-
     <!-- Allows application to write to internal media storage -->
     <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
 
diff --git a/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java b/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java
index a104b89..f6d42ea 100644
--- a/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java
+++ b/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java
@@ -60,7 +60,7 @@
 
     void setCodecConfigPreference(BluetoothDevice device,
                                   BluetoothCodecStatus codecStatus,
-                                  BluetoothCodecConfig codecConfig) {
+                                  BluetoothCodecConfig newCodecConfig) {
         Objects.requireNonNull(codecStatus);
 
         // Check whether the codecConfig is selectable for this Bluetooth device.
@@ -69,34 +69,36 @@
                 codec.isMandatoryCodec())) {
             // Do not set codec preference to native if the selectableCodecs not contain mandatory
             // codec. The reason could be remote codec negotiation is not completed yet.
-            Log.w(TAG, "Cannot find mandatory codec in selectableCodecs.");
+            Log.w(TAG, "setCodecConfigPreference: must have mandatory codec before changing.");
             return;
         }
-        if (!isCodecConfigSelectable(codecConfig, selectableCodecs)) {
-            Log.w(TAG, "Codec is not selectable: " + codecConfig);
+        if (!codecStatus.isCodecConfigSelectable(newCodecConfig)) {
+            Log.w(TAG, "setCodecConfigPreference: invalid codec "
+                    + Objects.toString(newCodecConfig));
             return;
         }
 
         // Check whether the codecConfig would change current codec config.
-        int prioritizedCodecType = getPrioitizedCodecType(codecConfig, selectableCodecs);
+        int prioritizedCodecType = getPrioitizedCodecType(newCodecConfig, selectableCodecs);
         BluetoothCodecConfig currentCodecConfig = codecStatus.getCodecConfig();
         if (prioritizedCodecType == currentCodecConfig.getCodecType()
-                && (currentCodecConfig.getCodecType() != codecConfig.getCodecType()
-                || currentCodecConfig.sameAudioFeedingParameters(codecConfig))) {
+                && (prioritizedCodecType != newCodecConfig.getCodecType()
+                || (currentCodecConfig.similarCodecFeedingParameters(newCodecConfig)
+                && currentCodecConfig.sameCodecSpecificParameters(newCodecConfig)))) {
             // Same codec with same parameters, no need to send this request to native.
-            Log.i(TAG, "setCodecConfigPreference: codec not changed.");
+            Log.w(TAG, "setCodecConfigPreference: codec not changed.");
             return;
         }
 
         BluetoothCodecConfig[] codecConfigArray = new BluetoothCodecConfig[1];
-        codecConfigArray[0] = codecConfig;
+        codecConfigArray[0] = newCodecConfig;
         mA2dpNativeInterface.setCodecConfigPreference(device, codecConfigArray);
     }
 
     void enableOptionalCodecs(BluetoothDevice device, BluetoothCodecConfig currentCodecConfig) {
         if (currentCodecConfig != null && !currentCodecConfig.isMandatoryCodec()) {
-            Log.i(TAG, "enableOptionalCodecs: already using optional codec: "
-                    + currentCodecConfig.getCodecType());
+            Log.i(TAG, "enableOptionalCodecs: already using optional codec "
+                    + currentCodecConfig.getCodecName());
             return;
         }
 
@@ -118,7 +120,7 @@
 
     void disableOptionalCodecs(BluetoothDevice device, BluetoothCodecConfig currentCodecConfig) {
         if (currentCodecConfig != null && currentCodecConfig.isMandatoryCodec()) {
-            Log.i(TAG, "disableOptionalCodecs: already using mandatory codec");
+            Log.i(TAG, "disableOptionalCodecs: already using mandatory codec.");
             return;
         }
 
@@ -153,20 +155,6 @@
         return prioritizedCodecConfig.getCodecType();
     }
 
-    // Check whether the codecConfig is selectable
-    private static boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig,
-            BluetoothCodecConfig[] selectableCodecs) {
-        for (BluetoothCodecConfig config : selectableCodecs) {
-            if (codecConfig.getCodecType() == config.getCodecType()
-                    && (codecConfig.getSampleRate() & config.getSampleRate()) != 0
-                    && (codecConfig.getBitsPerSample() & config.getBitsPerSample()) != 0
-                    && (codecConfig.getChannelMode() & config.getChannelMode()) != 0) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     // Assign the A2DP Source codec config priorities
     private BluetoothCodecConfig[] assignCodecConfigPriorities() {
         Resources resources = mContext.getResources();
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index 8104526..9866312 100644
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -873,18 +873,6 @@
             // change, so the Audio Service can reset accordingly the audio
             // feeding parameters in the Audio HAL to the Bluetooth stack.
 
-            // Split A2dp will be enabled by default
-            boolean isSplitA2dpEnabled = true;
-            AdapterService adapterService = AdapterService.getAdapterService();
-
-            if (adapterService != null){
-                isSplitA2dpEnabled = adapterService.isSplitA2dpEnabled();
-                Log.v(TAG,"isSplitA2dpEnabled: " + isSplitA2dpEnabled);
-            } else {
-                Log.e(TAG,"adapterService is null");
-            }
-
-
             if (wasMuted) {
                mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
                                           AudioManager.ADJUST_UNMUTE,
@@ -1101,7 +1089,7 @@
             device = mActiveDevice;
         }
         if (device == null) {
-            Log.e(TAG, "Cannot set codec config preference: no active A2DP device");
+            Log.e(TAG, "setCodecConfigPreference: Invalid device");
             return;
         }
 
@@ -1127,14 +1115,13 @@
             }
         }
 
-        if (getSupportsOptionalCodecs(device) != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) {
-            Log.e(TAG, "Cannot set codec config preference: not supported");
+        if (codecConfig == null) {
+            Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
             return;
         }
-
         BluetoothCodecStatus codecStatus = getCodecStatus(device);
         if (codecStatus == null) {
-            Log.e(TAG, "Codec status is null on " + device);
+            Log.e(TAG, "setCodecConfigPreference: Codec status is null");
             return;
         }
         if (mAdapterService.isTwsPlusDevice(device) && ( cs4 == 0 ||
@@ -1161,16 +1148,16 @@
             device = mActiveDevice;
         }
         if (device == null) {
-            Log.e(TAG, "Cannot enable optional codecs: no active A2DP device");
+            Log.e(TAG, "enableOptionalCodecs: Invalid device");
             return;
         }
         if (getSupportsOptionalCodecs(device) != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) {
-            Log.e(TAG, "Cannot enable optional codecs: not supported");
+            Log.e(TAG, "enableOptionalCodecs: No optional codecs");
             return;
         }
         BluetoothCodecStatus codecStatus = getCodecStatus(device);
         if (codecStatus == null) {
-            Log.e(TAG, "Cannot enable optional codecs: codec status is null");
+            Log.e(TAG, "enableOptionalCodecs: Codec status is null");
             return;
         }
         mA2dpCodecConfig.enableOptionalCodecs(device, codecStatus.getCodecConfig());
@@ -1192,16 +1179,16 @@
             device = mActiveDevice;
         }
         if (device == null) {
-            Log.e(TAG, "Cannot disable optional codecs: no active A2DP device");
+            Log.e(TAG, "disableOptionalCodecs: Invalid device");
             return;
         }
         if (getSupportsOptionalCodecs(device) != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) {
-            Log.e(TAG, "Cannot disable optional codecs: not supported");
+            Log.e(TAG, "disableOptionalCodecs: No optional codecs");
             return;
         }
         BluetoothCodecStatus codecStatus = getCodecStatus(device);
         if (codecStatus == null) {
-            Log.e(TAG, "Cannot disable optional codecs: codec status is null");
+            Log.e(TAG, "disableOptionalCodecs: Codec status is null");
             return;
         }
         mA2dpCodecConfig.disableOptionalCodecs(device, codecStatus.getCodecConfig());
@@ -1329,10 +1316,13 @@
         // Inform the Audio Service about the codec configuration change,
         // so the Audio Service can reset accordingly the audio feeding
         // parameters in the Audio HAL to the Bluetooth stack.
+        int rememberedVolume = -1;
         if (isActiveDevice(device) && !sameAudioFeedingParameters) {
+            if (mAvrcp_ext != null)
+                rememberedVolume = mAvrcp_ext.getVolume(device);
             mAudioManager.handleBluetoothA2dpActiveDeviceChange(device,
                     BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
-                    true, -1);
+                    true, rememberedVolume);
         }
     }
     void updateTwsChannelMode(int state, BluetoothDevice device) {
diff --git a/src/com/android/bluetooth/btservice/AdapterProperties.java b/src/com/android/bluetooth/btservice/AdapterProperties.java
index c3c57ff..beff7c8 100644
--- a/src/com/android/bluetooth/btservice/AdapterProperties.java
+++ b/src/com/android/bluetooth/btservice/AdapterProperties.java
@@ -524,6 +524,12 @@
     }
 
     /**
+     * @param set the maximum number of connected audio devices
+     */
+    void setMaxConnectedAudioDevices(int maxConnectedAudioDevices) {
+         mMaxConnectedAudioDevices = maxConnectedAudioDevices;
+    }
+    /**
      * @return the maximum number of connected audio devices
      */
     int getMaxConnectedAudioDevices() {
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index de1e64c..277eb8c 100644
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -678,6 +678,12 @@
             debugLog("stateChangeCallback: disableNative() completed");
             mAdapterStateMachine.sendMessage(AdapterState.STACK_DISABLED);
         } else if (status == AbstractionLayer.BT_STATE_ON) {
+            String BT_SOC = getSocName();
+
+            if (BT_SOC.equals("pronto")) {
+                Log.i(TAG, "setting max audio connection to 2");
+                mAdapterProperties.setMaxConnectedAudioDevices(2);
+            }
             mAdapterStateMachine.sendMessage(AdapterState.BLE_STARTED);
         } else {
             Log.e(TAG, "Incorrect status " + status + " in stateChangeCallback");
diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java
index afbd7e4..0ca6734 100644
--- a/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -1274,6 +1274,21 @@
                         + " is not active, use active device " + mActiveDevice + " instead");
                 device = mActiveDevice;
             }
+            if (mAdapterService.isTwsPlusDevice(device) &&
+                    !isAudioConnected(device)) {
+                BluetoothDevice peerDevice = getTwsPlusConnectedPeer(device);
+                if (peerDevice != null && isAudioConnected(peerDevice)) {
+                    Log.w(TAG, "startVoiceRecognition: requested TWS+ device " + device
+                            + " is not audio connected, use TWS+ peer device " + peerDevice
+                            + " instead");
+                    device = peerDevice;
+                } else {
+                    Log.w(TAG, "stopVoiceRecognition: both earbuds are not audio connected, resume A2DP");
+                    mVoiceRecognitionStarted = false;
+                    mHfpA2dpSyncInterface.releaseA2DP(null);
+                    return false;
+                }
+            }
             final HeadsetStateMachine stateMachine = mStateMachines.get(device);
             if (stateMachine == null) {
                 Log.w(TAG, "stopVoiceRecognition: " + device + " is never connected");
@@ -1291,7 +1306,13 @@
             }
             mVoiceRecognitionStarted = false;
             stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP, device);
-            stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
+
+            if (isAudioOn()) {
+                stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device);
+            } else {
+                Log.w(TAG, "SCO is not connected and VR stopped, resuming A2DP");
+                stateMachine.sendMessage(HeadsetStateMachine.RESUME_A2DP);
+            }
         }
         return true;
     }
@@ -1937,9 +1958,14 @@
                 mVoiceRecognitionTimeoutEvent = null;
             }
             if (mVoiceRecognitionStarted) {
-                if (!disconnectAudio()) {
-                    Log.w(TAG, "stopVoiceRecognitionByHeadset: failed to disconnect audio from "
+                if (isAudioOn()) {
+                    if (!disconnectAudio()) {
+                        Log.w(TAG, "stopVoiceRecognitionByHeadset: failed to disconnect audio from "
                             + fromDevice);
+                    }
+                } else {
+                    Log.w(TAG, "stopVoiceRecognitionByHeadset: No SCO connected, resume A2DP");
+                    mHfpA2dpSyncInterface.releaseA2DP(null);
                 }
                 mVoiceRecognitionStarted = false;
             }
diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index ec8e1ab..37c5c22 100644
--- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -41,6 +41,7 @@
 import android.net.NetworkInfo;
 import android.net.Network;
 import android.util.StatsLog;
+import android.os.Build;
 
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.ProfileService;
@@ -883,11 +884,13 @@
                     stateLogW("Disconnected");
                     processWBSEvent(HeadsetHalConstants.BTHF_WBS_NO);
                     stateLogD(" retryConnectCount = " + retryConnectCount);
-                    if(retryConnectCount == 1) {
-                        Log.d(TAG," retry once more ");
+                    if(retryConnectCount == 1 && !hasDeferredMessages(DISCONNECT)) {
+                        Log.d(TAG,"No deferred Disconnect, retry once more ");
                         sendMessageDelayed(CONNECT, mDevice, RETRY_CONNECT_TIME_SEC);
-                    } else if (retryConnectCount >= MAX_RETRY_CONNECT_COUNT) {
+                    } else if (retryConnectCount >= MAX_RETRY_CONNECT_COUNT ||
+                            hasDeferredMessages(DISCONNECT)) {
                         // we already tried twice.
+                        Log.d(TAG,"Already tried twice or has deferred Disconnect");
                         retryConnectCount = 0;
                     }
                     transitionTo(mDisconnected);
@@ -2635,6 +2638,10 @@
             processAtCpbr(atCommand.substring(5), commandType, device);
         } else if (atCommand.startsWith("+CSQ")) {
             mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
+        } else if (atCommand.equals("+CGMI")) {
+            mNativeInterface.atResponseString(device, "+CGMI: \"" + Build.MANUFACTURER + "\"");
+        } else if (atCommand.equals("+CGMM")) {
+            mNativeInterface.atResponseString(device, "+CGMM: " + Build.MODEL);
         } else {
             processVendorSpecificAt(atCommand, device);
         }
diff --git a/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java b/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
index c121cf7..a97a5b0 100644
--- a/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
+++ b/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
@@ -23,11 +23,14 @@
 import android.bluetooth.BluetoothCodecStatus;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
+import android.content.res.Resources;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.bluetooth.R;
+
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -40,75 +43,126 @@
 @RunWith(AndroidJUnit4.class)
 public class A2dpCodecConfigTest {
     private Context mTargetContext;
-    private A2dpCodecConfig mA2dpCodecConfig;
-    private BluetoothAdapter mAdapter;
-    private BluetoothCodecConfig mCodecConfigSbc;
-    private BluetoothCodecConfig mCodecConfigAac;
-    private BluetoothCodecConfig mCodecConfigAptx;
-    private BluetoothCodecConfig mCodecConfigAptxHd;
-    private BluetoothCodecConfig mCodecConfigLdac;
     private BluetoothDevice mTestDevice;
+    private A2dpCodecConfig mA2dpCodecConfig;
 
+    @Mock private Context mMockContext;
+    @Mock private Resources mMockResources;
     @Mock private A2dpNativeInterface mA2dpNativeInterface;
 
-    static final int SBC_PRIORITY_DEFAULT = 1001;
-    static final int AAC_PRIORITY_DEFAULT = 2001;
-    static final int APTX_PRIORITY_DEFAULT = 3001;
-    static final int APTX_HD_PRIORITY_DEFAULT = 4001;
-    static final int LDAC_PRIORITY_DEFAULT = 5001;
-    static final int PRIORITY_HIGH = 1000000;
+    private static final int[] sOptionalCodecTypes = new int[] {
+            BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+            BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+            BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+            BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC
+    };
 
+    // Not use the default value to make sure it reads from config
+    private static final int SBC_PRIORITY_DEFAULT = 1001;
+    private static final int AAC_PRIORITY_DEFAULT = 3001;
+    private static final int APTX_PRIORITY_DEFAULT = 5001;
+    private static final int APTX_HD_PRIORITY_DEFAULT = 7001;
+    private static final int LDAC_PRIORITY_DEFAULT = 9001;
+    private static final int PRIORITY_HIGH = 1000000;
+
+    private static final BluetoothCodecConfig[] sCodecCapabilities = new BluetoothCodecConfig[] {
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                     SBC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_MONO
+                                     | BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     0, 0, 0, 0),       // Codec-specific fields
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                                     AAC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     0, 0, 0, 0),       // Codec-specific fields
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+                                     APTX_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100
+                                     | BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     0, 0, 0, 0),       // Codec-specific fields
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+                                     APTX_HD_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100
+                                     | BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     0, 0, 0, 0),       // Codec-specific fields
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                                     LDAC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100
+                                     | BluetoothCodecConfig.SAMPLE_RATE_48000
+                                     | BluetoothCodecConfig.SAMPLE_RATE_88200
+                                     | BluetoothCodecConfig.SAMPLE_RATE_96000,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16
+                                     | BluetoothCodecConfig.BITS_PER_SAMPLE_24
+                                     | BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     0, 0, 0, 0)        // Codec-specific fields
+    };
+
+    private static final BluetoothCodecConfig[] sDefaultCodecConfigs = new BluetoothCodecConfig[] {
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                     SBC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     0, 0, 0, 0),       // Codec-specific fields
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                                     AAC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_44100,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     0, 0, 0, 0),       // Codec-specific fields
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+                                     APTX_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     0, 0, 0, 0),       // Codec-specific fields
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+                                     APTX_HD_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_48000,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     0, 0, 0, 0),       // Codec-specific fields
+            new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                                     LDAC_PRIORITY_DEFAULT,
+                                     BluetoothCodecConfig.SAMPLE_RATE_96000,
+                                     BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                                     BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                                     0, 0, 0, 0)        // Codec-specific fields
+    };
 
     @Before
     public void setUp() throws Exception {
-        mTargetContext = InstrumentationRegistry.getTargetContext();
         // Set up mocks and test assets
         MockitoAnnotations.initMocks(this);
+        mTargetContext = InstrumentationRegistry.getTargetContext();
 
-        mA2dpCodecConfig = new A2dpCodecConfig(mTargetContext, mA2dpNativeInterface);
+        when(mMockContext.getResources()).thenReturn(mMockResources);
+        when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_sbc))
+                .thenReturn(SBC_PRIORITY_DEFAULT);
+        when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_aac))
+                .thenReturn(AAC_PRIORITY_DEFAULT);
+        when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_aptx))
+                .thenReturn(APTX_PRIORITY_DEFAULT);
+        when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_aptx_hd))
+                .thenReturn(APTX_HD_PRIORITY_DEFAULT);
+        when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_ldac))
+                .thenReturn(LDAC_PRIORITY_DEFAULT);
+
+        mA2dpCodecConfig = new A2dpCodecConfig(mMockContext, mA2dpNativeInterface);
         mTestDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:01:02:03:04:05");
 
         doReturn(true).when(mA2dpNativeInterface).setCodecConfigPreference(
                 any(BluetoothDevice.class),
                 any(BluetoothCodecConfig[].class));
-
-        // Create sample codec configs
-        mCodecConfigSbc = new BluetoothCodecConfig(
-            BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
-            SBC_PRIORITY_DEFAULT,
-            BluetoothCodecConfig.SAMPLE_RATE_44100,
-            BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-            BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-            0, 0, 0, 0);       // Codec-specific fields
-        mCodecConfigAac = new BluetoothCodecConfig(
-            BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
-            AAC_PRIORITY_DEFAULT,
-            BluetoothCodecConfig.SAMPLE_RATE_44100,
-            BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-            BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-            0, 0, 0, 0);       // Codec-specific fields
-        mCodecConfigAptx = new BluetoothCodecConfig(
-            BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
-            APTX_PRIORITY_DEFAULT,
-            BluetoothCodecConfig.SAMPLE_RATE_44100,
-            BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-            BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-            0, 0, 0, 0);       // Codec-specific fields
-        mCodecConfigAptxHd = new BluetoothCodecConfig(
-            BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
-            APTX_HD_PRIORITY_DEFAULT,
-            BluetoothCodecConfig.SAMPLE_RATE_44100,
-            BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-            BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-            0, 0, 0, 0);       // Codec-specific fields
-        mCodecConfigLdac = new BluetoothCodecConfig(
-            BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-            LDAC_PRIORITY_DEFAULT,
-            BluetoothCodecConfig.SAMPLE_RATE_44100,
-            BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-            BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-            0, 0, 0, 0);       // Codec-specific fields
-
     }
 
     @After
@@ -139,128 +193,383 @@
         }
     }
 
+    /**
+     * Test that we can fallback to default codec by lower the codec priority we changed before.
+     */
     @Test
     public void testSetCodecPreference_priorityHighToDefault() {
-        testSetCodecPreference_codecPriorityChangeCase(
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, SBC_PRIORITY_DEFAULT,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
                 true);
-        testSetCodecPreference_codecPriorityChangeCase(
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, AAC_PRIORITY_DEFAULT,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
                 true);
-        testSetCodecPreference_codecPriorityChangeCase(
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, APTX_PRIORITY_DEFAULT,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, PRIORITY_HIGH,
                 true);
-        testSetCodecPreference_codecPriorityChangeCase(
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, APTX_HD_PRIORITY_DEFAULT,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, PRIORITY_HIGH,
                 true);
-        testSetCodecPreference_codecPriorityChangeCase(
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
                 false);
     }
 
+    /**
+     * Test that we can change the default codec to another by raising the codec priority.
+     * LDAC is the default highest codec, so no need to test others.
+     */
     @Test
-    public void testSetCodecPreference_priorityDefaultToHigh() {
-        testSetCodecPreference_codecPriorityChangeCase(
+    public void testSetCodecPreference_priorityDefaultToRaiseHigh() {
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
                 true);
-        testSetCodecPreference_codecPriorityChangeCase(
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
                 true);
-        testSetCodecPreference_codecPriorityChangeCase(
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, PRIORITY_HIGH,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
                 true);
-        testSetCodecPreference_codecPriorityChangeCase(
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, PRIORITY_HIGH,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
                 true);
-        testSetCodecPreference_codecPriorityChangeCase(
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
                 false);
     }
 
     @Test
-    public void testSetCodecPreference_priorityHighToHigh() {
-        testSetCodecPreference_codecPriorityChangeCase(
+    public void testSetCodecPreference_prioritySbcHighToOthersHigh() {
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
                 false);
-        testSetCodecPreference_codecPriorityChangeCase(
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
                 true);
-        testSetCodecPreference_codecPriorityChangeCase(
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, PRIORITY_HIGH,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
                 true);
-        testSetCodecPreference_codecPriorityChangeCase(
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, PRIORITY_HIGH,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
                 true);
-        testSetCodecPreference_codecPriorityChangeCase(
+        testCodecPriorityChangeHelper(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
                 true);
     }
 
     @Test
-    public void testSetCodecPreference_parametersChange() {
-        int unSupportedParameter = 200;
+    public void testSetCodecPreference_priorityAacHighToOthersHigh() {
+        testCodecPriorityChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+                true);
+        testCodecPriorityChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+                false);
+        testCodecPriorityChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, PRIORITY_HIGH,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+                true);
+        testCodecPriorityChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, PRIORITY_HIGH,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+                true);
+        testCodecPriorityChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+                true);
+    }
 
-        testSetCodecPreference_parametersChangeCase(
+    @Test
+    public void testSetCodecPreference_parametersChangedInSameCodec() {
+        // The SBC default / preferred config is Stereo
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
                 BluetoothCodecConfig.SAMPLE_RATE_44100,
                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
                 false);
-        testSetCodecPreference_parametersChangeCase(
+        // SBC Mono is mandatory
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
                 BluetoothCodecConfig.SAMPLE_RATE_44100,
-                BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                BluetoothCodecConfig.CHANNEL_MODE_MONO,
                 true);
-        testSetCodecPreference_parametersChangeCase(
-                BluetoothCodecConfig.SAMPLE_RATE_44100,
-                unSupportedParameter,
+
+        // The LDAC default / preferred config within mDefaultCodecConfigs
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_96000,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
                 false);
 
-        testSetCodecPreference_parametersChangeCase(
-                BluetoothCodecConfig.SAMPLE_RATE_48000,
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_44100,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                true);
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_96000,
                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
                 true);
-        testSetCodecPreference_parametersChangeCase(
-                BluetoothCodecConfig.SAMPLE_RATE_48000,
-                BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_44100,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
                 true);
-        testSetCodecPreference_parametersChangeCase(
-                BluetoothCodecConfig.SAMPLE_RATE_48000,
-                unSupportedParameter,
+
+        // None for system default (Developer options)
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_NONE,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                false);
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_96000,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                false);
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_96000,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                BluetoothCodecConfig.CHANNEL_MODE_NONE,
                 false);
 
-        testSetCodecPreference_parametersChangeCase(
-                unSupportedParameter,
+        int unsupportedParameter = 0xc0;
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                unsupportedParameter,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                false);
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_96000,
+                unsupportedParameter,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                false);
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_96000,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                unsupportedParameter,
+                false);
+
+        int multipleSupportedParameters = 0x03;
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                multipleSupportedParameters,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                false);
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_96000,
+                multipleSupportedParameters,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                false);
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_96000,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                multipleSupportedParameters,
+                false);
+    }
+
+    @Test
+    public void testSetCodecPreference_parametersChangedToAnotherCodec() {
+        // different sample rate (44.1 kHz -> 96 kHz)
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                BluetoothCodecConfig.SAMPLE_RATE_96000,
                 BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                true);
+        // different bits per channel (16 bits -> 32 bits)
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                BluetoothCodecConfig.SAMPLE_RATE_44100,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                true);
+        // change all PCM parameters
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_44100,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                BluetoothCodecConfig.CHANNEL_MODE_MONO,
+                true);
+
+        // None for system default (Developer options)
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_NONE,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                true);
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_44100,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                true);
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_44100,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                BluetoothCodecConfig.CHANNEL_MODE_NONE,
+                true);
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                BluetoothCodecConfig.SAMPLE_RATE_NONE,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
+                BluetoothCodecConfig.CHANNEL_MODE_NONE,
+                true);
+
+        int unsupportedParameter = 0xc0;
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                unsupportedParameter,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
                 false);
-        testSetCodecPreference_parametersChangeCase(
-                unSupportedParameter,
-                BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                BluetoothCodecConfig.SAMPLE_RATE_44100,
+                unsupportedParameter,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
                 false);
-        testSetCodecPreference_parametersChangeCase(
-                unSupportedParameter,
-                unSupportedParameter,
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                BluetoothCodecConfig.SAMPLE_RATE_44100,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                unsupportedParameter,
+                false);
+
+        int multipleSupportedParameters = 0x03;
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                multipleSupportedParameters,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                false);
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                BluetoothCodecConfig.SAMPLE_RATE_44100,
+                multipleSupportedParameters,
+                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+                false);
+        testCodecParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+                BluetoothCodecConfig.SAMPLE_RATE_44100,
+                BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+                multipleSupportedParameters,
+                false);
+    }
+
+    @Test
+    public void testSetCodecPreference_ldacCodecSpecificFieldChanged() {
+        int ldacAudioQualityHigh = 1000;
+        int ldacAudioQualityABR = 1003;
+        int sbcCodecSpecificParameter = 0;
+        testCodecSpecificParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                ldacAudioQualityABR,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                ldacAudioQualityABR,
+                false);
+        testCodecSpecificParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                ldacAudioQualityHigh,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                ldacAudioQualityABR,
+                true);
+        testCodecSpecificParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                ldacAudioQualityABR,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                sbcCodecSpecificParameter,
+                true);
+
+        // Only LDAC will check the codec specific1 field, but not SBC
+        testCodecSpecificParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                sbcCodecSpecificParameter,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                ldacAudioQualityABR,
+                true);
+        testCodecSpecificParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                ldacAudioQualityABR,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+                ldacAudioQualityABR,
+                true);
+        testCodecSpecificParametersChangeHelper(
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                ldacAudioQualityHigh,
+                BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                sbcCodecSpecificParameter,
                 false);
     }
 
     @Test
     public void testDisableOptionalCodecs() {
-        int verifyCount = 0;
-        BluetoothCodecConfig[] codecConfigArray =
+        BluetoothCodecConfig[] codecConfigsArray =
                 new BluetoothCodecConfig[BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX];
-        codecConfigArray[0] = new BluetoothCodecConfig(
+        codecConfigsArray[0] = new BluetoothCodecConfig(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
                 BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
                 BluetoothCodecConfig.SAMPLE_RATE_NONE,
@@ -268,32 +577,32 @@
                 BluetoothCodecConfig.CHANNEL_MODE_NONE,
                 0, 0, 0, 0);       // Codec-specific fields
 
-        // Test don't invoke to native when current codec is SBC
-        mA2dpCodecConfig.disableOptionalCodecs(mTestDevice, mCodecConfigSbc);
-        verify(mA2dpNativeInterface, times(verifyCount))
-                .setCodecConfigPreference(mTestDevice, codecConfigArray);
+        // shouldn't invoke to native when current codec is SBC
+        mA2dpCodecConfig.disableOptionalCodecs(
+                mTestDevice,
+                getDefaultCodecConfigByType(
+                        BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                        BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT));
+        verify(mA2dpNativeInterface, times(0)).setCodecConfigPreference(mTestDevice,
+                                                                        codecConfigsArray);
 
-        // Test invoke to native when current codec is not SBC
-        mA2dpCodecConfig.disableOptionalCodecs(mTestDevice, mCodecConfigAac);
-        verify(mA2dpNativeInterface, times(++verifyCount))
-                .setCodecConfigPreference(mTestDevice, codecConfigArray);
-        mA2dpCodecConfig.disableOptionalCodecs(mTestDevice, mCodecConfigAptx);
-        verify(mA2dpNativeInterface, times(++verifyCount))
-                .setCodecConfigPreference(mTestDevice, codecConfigArray);
-        mA2dpCodecConfig.disableOptionalCodecs(mTestDevice, mCodecConfigAptxHd);
-        verify(mA2dpNativeInterface, times(++verifyCount))
-                .setCodecConfigPreference(mTestDevice, codecConfigArray);
-        mA2dpCodecConfig.disableOptionalCodecs(mTestDevice, mCodecConfigLdac);
-        verify(mA2dpNativeInterface, times(++verifyCount))
-                .setCodecConfigPreference(mTestDevice, codecConfigArray);
+        // should invoke to native when current codec is an optional codec
+        int invokedCounter = 0;
+        for (int codecType : sOptionalCodecTypes) {
+            mA2dpCodecConfig.disableOptionalCodecs(
+                    mTestDevice,
+                    getDefaultCodecConfigByType(codecType,
+                                                BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT));
+            verify(mA2dpNativeInterface, times(++invokedCounter))
+                    .setCodecConfigPreference(mTestDevice, codecConfigsArray);
+        }
     }
 
     @Test
     public void testEnableOptionalCodecs() {
-        int verifyCount = 0;
-        BluetoothCodecConfig[] codecConfigArray =
+        BluetoothCodecConfig[] codecConfigsArray =
                 new BluetoothCodecConfig[BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX];
-        codecConfigArray[0] = new BluetoothCodecConfig(
+        codecConfigsArray[0] = new BluetoothCodecConfig(
                 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
                 SBC_PRIORITY_DEFAULT,
                 BluetoothCodecConfig.SAMPLE_RATE_NONE,
@@ -301,115 +610,292 @@
                 BluetoothCodecConfig.CHANNEL_MODE_NONE,
                 0, 0, 0, 0);       // Codec-specific fields
 
-        // Test invoke to native when current codec is SBC
-        mA2dpCodecConfig.enableOptionalCodecs(mTestDevice, mCodecConfigSbc);
-        verify(mA2dpNativeInterface, times(++verifyCount))
-                .setCodecConfigPreference(mTestDevice, codecConfigArray);
+        // should invoke to native when current codec is SBC
+        mA2dpCodecConfig.enableOptionalCodecs(
+                mTestDevice,
+                getDefaultCodecConfigByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+                                            BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT));
+        verify(mA2dpNativeInterface, times(1))
+                .setCodecConfigPreference(mTestDevice, codecConfigsArray);
 
-        // Test don't invoke to native when current codec is not SBC
-        mA2dpCodecConfig.enableOptionalCodecs(mTestDevice, mCodecConfigAac);
-        verify(mA2dpNativeInterface, times(verifyCount))
-                .setCodecConfigPreference(mTestDevice, codecConfigArray);
-        mA2dpCodecConfig.enableOptionalCodecs(mTestDevice, mCodecConfigAptx);
-        verify(mA2dpNativeInterface, times(verifyCount))
-                .setCodecConfigPreference(mTestDevice, codecConfigArray);
-        mA2dpCodecConfig.enableOptionalCodecs(mTestDevice, mCodecConfigAptxHd);
-        verify(mA2dpNativeInterface, times(verifyCount))
-                .setCodecConfigPreference(mTestDevice, codecConfigArray);
-        mA2dpCodecConfig.enableOptionalCodecs(mTestDevice, mCodecConfigLdac);
-        verify(mA2dpNativeInterface, times(verifyCount))
-                .setCodecConfigPreference(mTestDevice, codecConfigArray);
+        // shouldn't invoke to native when current codec is already an optional
+        for (int codecType : sOptionalCodecTypes) {
+            mA2dpCodecConfig.enableOptionalCodecs(
+                    mTestDevice,
+                    getDefaultCodecConfigByType(codecType,
+                                                BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT));
+            verify(mA2dpNativeInterface, times(1))
+                    .setCodecConfigPreference(mTestDevice, codecConfigsArray);
+        }
     }
 
-    private void testSetCodecPreference_parametersChangeCase(int sampleRate, int bitPerSample,
-            boolean invokeNative) {
-        BluetoothCodecConfig[] invalidSelectableCodecs = new BluetoothCodecConfig[1];
-        invalidSelectableCodecs[0] = new BluetoothCodecConfig(
-                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                LDAC_PRIORITY_DEFAULT,
-                (BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000),
-                (BluetoothCodecConfig.BITS_PER_SAMPLE_16 | BluetoothCodecConfig.BITS_PER_SAMPLE_24),
-                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                0, 0, 0, 0);       // Codec-specific fields
+    private BluetoothCodecConfig getDefaultCodecConfigByType(int codecType, int codecPriority) {
+        for (BluetoothCodecConfig codecConfig : sDefaultCodecConfigs) {
+            if (codecConfig.getCodecType() != codecType) {
+                continue;
+            }
+            return new BluetoothCodecConfig(
+                    codecConfig.getCodecType(),
+                    (codecPriority != BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT
+                     ? codecPriority : codecConfig.getCodecPriority()),
+                    codecConfig.getSampleRate(), codecConfig.getBitsPerSample(),
+                    codecConfig.getChannelMode(), codecConfig.getCodecSpecific1(),
+                    codecConfig.getCodecSpecific2(), codecConfig.getCodecSpecific3(),
+                    codecConfig.getCodecSpecific4());
+        }
+        Assert.fail("getDefaultCodecConfigByType: No such codecType=" + codecType
+                + " in sDefaultCodecConfigs");
+        return null;
+    }
+
+    private BluetoothCodecConfig getCodecCapabilitiesByType(int codecType) {
+        for (BluetoothCodecConfig codecCapabilities : sCodecCapabilities) {
+            if (codecCapabilities.getCodecType() != codecType) {
+                continue;
+            }
+            return new BluetoothCodecConfig(
+                    codecCapabilities.getCodecType(), codecCapabilities.getCodecPriority(),
+                    codecCapabilities.getSampleRate(), codecCapabilities.getBitsPerSample(),
+                    codecCapabilities.getChannelMode(), codecCapabilities.getCodecSpecific1(),
+                    codecCapabilities.getCodecSpecific2(), codecCapabilities.getCodecSpecific3(),
+                    codecCapabilities.getCodecSpecific4());
+        }
+        Assert.fail("getCodecCapabilitiesByType: No such codecType=" + codecType
+                + " in sCodecCapabilities");
+        return null;
+    }
+
+    private void testCodecParametersChangeHelper(int newCodecType, int oldCodecType,
+            int sampleRate, int bitsPerSample, int channelMode, boolean invokeNative) {
+        BluetoothCodecConfig oldCodecConfig =
+                getDefaultCodecConfigByType(oldCodecType, PRIORITY_HIGH);
+        BluetoothCodecConfig[] newCodecConfigsArray = new BluetoothCodecConfig[] {
+                new BluetoothCodecConfig(newCodecType,
+                                         PRIORITY_HIGH,
+                                         sampleRate, bitsPerSample, channelMode,
+                                         0, 0, 0, 0)       // Codec-specific fields
+        };
+
+        // Test cases: 1. no mandatory; 2. mandatory + old + new; 3. all codecs
+        BluetoothCodecConfig[] minimumCodecsArray;
+        if (!oldCodecConfig.isMandatoryCodec() && !newCodecConfigsArray[0].isMandatoryCodec()) {
+            BluetoothCodecConfig[] optionalCodecsArray;
+            if (oldCodecType != newCodecType) {
+                optionalCodecsArray = new BluetoothCodecConfig[] {
+                        getCodecCapabilitiesByType(oldCodecType),
+                        getCodecCapabilitiesByType(newCodecType)
+                };
+                minimumCodecsArray = new BluetoothCodecConfig[] {
+                        getCodecCapabilitiesByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC),
+                        getCodecCapabilitiesByType(oldCodecType),
+                        getCodecCapabilitiesByType(newCodecType)
+                };
+            } else {
+                optionalCodecsArray = new BluetoothCodecConfig[]
+                        {getCodecCapabilitiesByType(oldCodecType)};
+                minimumCodecsArray = new BluetoothCodecConfig[] {
+                        getCodecCapabilitiesByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC),
+                        getCodecCapabilitiesByType(oldCodecType)
+                };
+            }
+            BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(oldCodecConfig,
+                                                                        sCodecCapabilities,
+                                                                        optionalCodecsArray);
+            mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+                                                      codecStatus,
+                                                      newCodecConfigsArray[0]);
+            // no mandatory codec in selectable, and should not apply
+            verify(mA2dpNativeInterface, times(0)).setCodecConfigPreference(mTestDevice,
+                                                                            newCodecConfigsArray);
 
 
-        BluetoothCodecConfig[] selectableCodecs = new BluetoothCodecConfig[2];
-        selectableCodecs[0] = mCodecConfigSbc;
-        selectableCodecs[1] = new BluetoothCodecConfig(
-                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                LDAC_PRIORITY_DEFAULT,
-                (BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000),
-                (BluetoothCodecConfig.BITS_PER_SAMPLE_16 | BluetoothCodecConfig.BITS_PER_SAMPLE_24),
-                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                0, 0, 0, 0);       // Codec-specific fields
+        } else {
+            if (oldCodecType != newCodecType) {
+                minimumCodecsArray = new BluetoothCodecConfig[] {
+                        getCodecCapabilitiesByType(oldCodecType),
+                        getCodecCapabilitiesByType(newCodecType)
+                };
+            } else {
+                minimumCodecsArray = new BluetoothCodecConfig[] {
+                        getCodecCapabilitiesByType(oldCodecType),
+                };
+            }
+        }
 
-        BluetoothCodecConfig[] codecConfigArray = new BluetoothCodecConfig[1];
-        codecConfigArray[0] = new BluetoothCodecConfig(
-                BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
-                LDAC_PRIORITY_DEFAULT,
-                sampleRate,
-                bitPerSample,
-                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                0, 0, 0, 0);       // Codec-specific fields
-
-        BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(mCodecConfigLdac,
-                                                                    invalidSelectableCodecs,
-                                                                    invalidSelectableCodecs);
-        mA2dpCodecConfig.setCodecConfigPreference(mTestDevice, codecStatus, codecConfigArray[0]);
-
-        codecStatus = new BluetoothCodecStatus(mCodecConfigLdac,
-                                               selectableCodecs,
-                                               selectableCodecs);
-
-        mA2dpCodecConfig.setCodecConfigPreference(mTestDevice, codecStatus, codecConfigArray[0]);
+        // 2. mandatory + old + new codecs only
+        BluetoothCodecStatus codecStatus =
+                new BluetoothCodecStatus(oldCodecConfig, sCodecCapabilities, minimumCodecsArray);
+        mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+                                                  codecStatus,
+                                                  newCodecConfigsArray[0]);
         verify(mA2dpNativeInterface, times(invokeNative ? 1 : 0))
-                .setCodecConfigPreference(mTestDevice, codecConfigArray);
+                .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
 
-    }
-
-    private void testSetCodecPreference_codecPriorityChangeCase(int newCodecType,
-            int newCodecPriority, int oldCodecType, int oldCodecPriority, boolean invokeNative) {
-        BluetoothCodecConfig[] selectableCodecs = new BluetoothCodecConfig[5];
-        selectableCodecs[0] = mCodecConfigSbc;
-        selectableCodecs[1] = mCodecConfigAac;
-        selectableCodecs[2] = mCodecConfigAptx;
-        selectableCodecs[3] = mCodecConfigAptxHd;
-        selectableCodecs[4] = mCodecConfigLdac;
-
-        BluetoothCodecConfig[] invalidSelectableCodecs = new BluetoothCodecConfig[4];
-        invalidSelectableCodecs[0] = mCodecConfigAac;
-        invalidSelectableCodecs[1] = mCodecConfigAptx;
-        invalidSelectableCodecs[2] = mCodecConfigAptxHd;
-        invalidSelectableCodecs[3] = mCodecConfigLdac;
-
-        BluetoothCodecConfig oldCodecConfig =  new BluetoothCodecConfig(
-                oldCodecType,
-                oldCodecPriority,
-                BluetoothCodecConfig.SAMPLE_RATE_44100,
-                BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-                BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-                0, 0, 0, 0);       // Codec-specific fields
-
-        BluetoothCodecConfig[] codecConfigArray = new BluetoothCodecConfig[1];
-        codecConfigArray[0] = new BluetoothCodecConfig(
-            newCodecType,
-            newCodecPriority,
-            BluetoothCodecConfig.SAMPLE_RATE_44100,
-            BluetoothCodecConfig.BITS_PER_SAMPLE_16,
-            BluetoothCodecConfig.CHANNEL_MODE_STEREO,
-            0, 0, 0, 0);       // Codec-specific fields
-
-        BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(oldCodecConfig,
-                                                                    invalidSelectableCodecs,
-                                                                    invalidSelectableCodecs);
-        mA2dpCodecConfig.setCodecConfigPreference(mTestDevice, codecStatus, codecConfigArray[0]);
-
+        // 3. all codecs were selectable
         codecStatus = new BluetoothCodecStatus(oldCodecConfig,
-                                               selectableCodecs,
-                                               selectableCodecs);
+                                               sCodecCapabilities,
+                                               sCodecCapabilities);
+        mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+                                                  codecStatus,
+                                                  newCodecConfigsArray[0]);
+        verify(mA2dpNativeInterface, times(invokeNative ? 2 : 0))
+                .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+    }
 
-        mA2dpCodecConfig.setCodecConfigPreference(mTestDevice, codecStatus, codecConfigArray[0]);
+    private void testCodecSpecificParametersChangeHelper(int newCodecType, int newCodecSpecific,
+            int oldCodecType, int oldCodecSpecific, boolean invokeNative) {
+        BluetoothCodecConfig codecDefaultTemp =
+                getDefaultCodecConfigByType(oldCodecType, PRIORITY_HIGH);
+        BluetoothCodecConfig oldCodecConfig =
+                new BluetoothCodecConfig(codecDefaultTemp.getCodecType(),
+                                         codecDefaultTemp.getCodecPriority(),
+                                         codecDefaultTemp.getSampleRate(),
+                                         codecDefaultTemp.getBitsPerSample(),
+                                         codecDefaultTemp.getChannelMode(),
+                                         oldCodecSpecific, 0, 0, 0);       // Codec-specific fields
+        codecDefaultTemp = getDefaultCodecConfigByType(newCodecType, PRIORITY_HIGH);
+        BluetoothCodecConfig[] newCodecConfigsArray = new BluetoothCodecConfig[] {
+                new BluetoothCodecConfig(codecDefaultTemp.getCodecType(),
+                                         codecDefaultTemp.getCodecPriority(),
+                                         codecDefaultTemp.getSampleRate(),
+                                         codecDefaultTemp.getBitsPerSample(),
+                                         codecDefaultTemp.getChannelMode(),
+                                         newCodecSpecific, 0, 0, 0)       // Codec-specific fields
+        };
+        BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(oldCodecConfig,
+                                                                    sCodecCapabilities,
+                                                                    sCodecCapabilities);
+        mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+                                                  codecStatus,
+                                                  newCodecConfigsArray[0]);
         verify(mA2dpNativeInterface, times(invokeNative ? 1 : 0))
-                .setCodecConfigPreference(mTestDevice, codecConfigArray);
+                .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+    }
+
+    private void testCodecPriorityChangeHelper(int newCodecType, int newCodecPriority,
+            int oldCodecType, int oldCodecPriority, boolean shouldApplyWhenAllSelectable) {
+
+        BluetoothCodecConfig[] newCodecConfigsArray =
+                new BluetoothCodecConfig[] {
+                        getDefaultCodecConfigByType(newCodecType, newCodecPriority)
+                };
+        BluetoothCodecConfig oldCodecConfig = getDefaultCodecConfigByType(oldCodecType,
+                                                                          oldCodecPriority);
+
+        // Test cases: 1. no mandatory; 2. no new codec; 3. mandatory + old + new; 4. all codecs
+        BluetoothCodecConfig[] minimumCodecsArray;
+        boolean isMinimumCodecsArraySelectable;
+        if (!oldCodecConfig.isMandatoryCodec()) {
+            if (oldCodecType == newCodecType || newCodecConfigsArray[0].isMandatoryCodec()) {
+                // selectable: {-mandatory, +oldCodec = newCodec}, or
+                // selectable: {-mandatory = newCodec, +oldCodec}. Not applied
+                BluetoothCodecConfig[] poorCodecsArray = new BluetoothCodecConfig[]
+                        {getCodecCapabilitiesByType(oldCodecType)};
+                BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(oldCodecConfig,
+                                                                            sCodecCapabilities,
+                                                                            poorCodecsArray);
+                mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+                                                          codecStatus,
+                                                          newCodecConfigsArray[0]);
+                verify(mA2dpNativeInterface, times(0))
+                        .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+
+                // selectable: {+mandatory, +oldCodec = newCodec}, or
+                // selectable: {+mandatory = newCodec, +oldCodec}.
+                minimumCodecsArray = new BluetoothCodecConfig[] {
+                        getCodecCapabilitiesByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC),
+                        getCodecCapabilitiesByType(oldCodecType)
+                };
+            } else {
+                // selectable: {-mandatory, +oldCodec, +newCodec}. Not applied
+                BluetoothCodecConfig[] poorCodecsArray = new BluetoothCodecConfig[] {
+                        getCodecCapabilitiesByType(oldCodecType),
+                        getCodecCapabilitiesByType(newCodecType)
+                };
+                BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(oldCodecConfig,
+                                                                            sCodecCapabilities,
+                                                                            poorCodecsArray);
+                mA2dpCodecConfig.setCodecConfigPreference(
+                        mTestDevice, codecStatus, newCodecConfigsArray[0]);
+                verify(mA2dpNativeInterface, times(0))
+                        .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+
+                // selectable: {+mandatory, +oldCodec, -newCodec}. Not applied
+                poorCodecsArray = new BluetoothCodecConfig[] {
+                        getCodecCapabilitiesByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC),
+                        getCodecCapabilitiesByType(oldCodecType)
+                };
+                codecStatus = new BluetoothCodecStatus(oldCodecConfig,
+                                                       sCodecCapabilities,
+                                                       poorCodecsArray);
+                mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+                                                          codecStatus,
+                                                          newCodecConfigsArray[0]);
+                verify(mA2dpNativeInterface, times(0))
+                        .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+
+                // selectable: {+mandatory, +oldCodec, +newCodec}.
+                minimumCodecsArray = new BluetoothCodecConfig[] {
+                      getCodecCapabilitiesByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC),
+                      getCodecCapabilitiesByType(oldCodecType),
+                      getCodecCapabilitiesByType(newCodecType)
+                };
+            }
+            // oldCodec priority should be reset to default, so compare with the default
+            if (newCodecConfigsArray[0].getCodecPriority()
+                    > getDefaultCodecConfigByType(
+                            oldCodecType,
+                            BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT).getCodecPriority()
+                    && oldCodecType != newCodecType) {
+                isMinimumCodecsArraySelectable = true;
+            } else {
+                // the old codec was still the highest priority after reset to default
+                isMinimumCodecsArraySelectable = false;
+            }
+        } else if (oldCodecType != newCodecType) {
+            // selectable: {+mandatory = oldCodec, -newCodec}. Not applied
+            BluetoothCodecConfig[] poorCodecsArray = new BluetoothCodecConfig[]
+                    {getCodecCapabilitiesByType(oldCodecType)};
+            BluetoothCodecStatus codecStatus =
+                    new BluetoothCodecStatus(oldCodecConfig, sCodecCapabilities, poorCodecsArray);
+            mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+                                                      codecStatus,
+                                                      newCodecConfigsArray[0]);
+            verify(mA2dpNativeInterface, times(0))
+                    .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+
+            // selectable: {+mandatory = oldCodec, +newCodec}.
+            minimumCodecsArray = new BluetoothCodecConfig[] {
+                    getCodecCapabilitiesByType(oldCodecType),
+                    getCodecCapabilitiesByType(newCodecType)
+            };
+            isMinimumCodecsArraySelectable = true;
+        } else {
+            // selectable: {mandatory = oldCodec = newCodec}.
+            minimumCodecsArray = new BluetoothCodecConfig[]
+                    {getCodecCapabilitiesByType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC)};
+            isMinimumCodecsArraySelectable = false;
+        }
+
+        // 3. mandatory + old + new codecs only
+        int invokedCounter = (isMinimumCodecsArraySelectable ? 1 : 0);
+        BluetoothCodecStatus codecStatus =
+                new BluetoothCodecStatus(oldCodecConfig, sCodecCapabilities, minimumCodecsArray);
+        mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+                                                  codecStatus,
+                                                  newCodecConfigsArray[0]);
+        verify(mA2dpNativeInterface, times(invokedCounter))
+                .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
+
+        // 4. all codecs were selectable
+        invokedCounter += (shouldApplyWhenAllSelectable ? 1 : 0);
+        codecStatus = new BluetoothCodecStatus(oldCodecConfig,
+                                               sCodecCapabilities,
+                                               sCodecCapabilities);
+        mA2dpCodecConfig.setCodecConfigPreference(mTestDevice,
+                                                  codecStatus,
+                                                  newCodecConfigsArray[0]);
+        verify(mA2dpNativeInterface, times(invokedCounter))
+                .setCodecConfigPreference(mTestDevice, newCodecConfigsArray);
     }
 }