Merge "BT: Set AutoConnection for TWS+ pair devices" 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 b11ae58..1cc276f 100644
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -75,6 +75,7 @@
private final Object mBtTwsLock = new Object();
private final Object mBtAvrcpLock = new Object();
private final Object mActiveDeviceLock = new Object();
+ private final Object mVariableLock = new Object();
@VisibleForTesting
A2dpNativeInterface mA2dpNativeInterface;
@@ -129,12 +130,18 @@
switch (msg.what) {
case SET_EBMONO_CFG:
Log.d(TAG, "setparameters to Mono");
- mAudioManager.setParameters("TwsChannelConfig=mono");
+ synchronized (mVariableLock) {
+ if(mAudioManager != null)
+ mAudioManager.setParameters("TwsChannelConfig=mono");
+ }
mTwsPlusChannelMode = "mono";
break;
case SET_EBDUALMONO_CFG:
Log.d(TAG, "setparameters to Dual-Mono");
- mAudioManager.setParameters("TwsChannelConfig=dual-mono");
+ synchronized (mVariableLock) {
+ if(mAudioManager != null)
+ mAudioManager.setParameters("TwsChannelConfig=dual-mono");
+ }
mTwsPlusChannelMode = "dual-mono";
break;
default:
@@ -164,73 +171,74 @@
// Step 1: Get AdapterService, A2dpNativeInterface, AudioManager.
// None of them can be null.
- mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
+ synchronized (mVariableLock) {
+ mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
"AdapterService cannot be null when A2dpService starts");
- mA2dpNativeInterface = Objects.requireNonNull(A2dpNativeInterface.getInstance(),
+ mA2dpNativeInterface = Objects.requireNonNull(A2dpNativeInterface.getInstance(),
"A2dpNativeInterface cannot be null when A2dpService starts");
- mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- Objects.requireNonNull(mAudioManager,
+ mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ Objects.requireNonNull(mAudioManager,
"AudioManager cannot be null when A2dpService starts");
- // Step 2: Get maximum number of connected audio devices
- mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices();
- mSetMaxConnectedAudioDevices = mMaxConnectedAudioDevices;
- Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices);
- if (mAdapterService.isVendorIntfEnabled()) {
- String twsPlusEnabled = SystemProperties.get("persist.vendor.btstack.enable.twsplus");
- if (!twsPlusEnabled.isEmpty() && "true".equals(twsPlusEnabled)) {
- mIsTwsPlusEnabled = true;
- }
- Log.i(TAG, "mMaxConnectedAudioDevices: " + mMaxConnectedAudioDevices);
- String twsShoEnabled = SystemProperties.get("persist.vendor.btstack.enable.twsplussho");
- if (!twsShoEnabled.isEmpty() && "true".equals(twsShoEnabled) &&
- (mIsTwsPlusEnabled == true) && mMaxConnectedAudioDevices <= 2) {
- mMaxConnectedAudioDevices = 3;
- Log.i(TAG, "TWS+ SHO enabled mMaxConnectedAudioDevices changed to: " + mMaxConnectedAudioDevices);
- } else if (mIsTwsPlusEnabled && mMaxConnectedAudioDevices < 2) {
- mMaxConnectedAudioDevices = 2;
- Log.i(TAG, "TWS+ enabled mMaxConnectedAudioDevices changed to: " + mMaxConnectedAudioDevices);
- }
+ // Step 2: Get maximum number of connected audio devices
+ mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices();
mSetMaxConnectedAudioDevices = mMaxConnectedAudioDevices;
- String twsPlusMonoEnabled = SystemProperties.get("persist.vendor.btstack.twsplus.monosupport");
- if (!twsPlusMonoEnabled.isEmpty() && "true".equals(twsPlusMonoEnabled)) {
- mIsTwsPlusMonoSupported = true;
+ Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices);
+ if (mAdapterService != null && mAdapterService.isVendorIntfEnabled()) {
+ String twsPlusEnabled = SystemProperties.get("persist.vendor.btstack.enable.twsplus");
+ if (!twsPlusEnabled.isEmpty() && "true".equals(twsPlusEnabled)) {
+ mIsTwsPlusEnabled = true;
+ }
+ Log.i(TAG, "mMaxConnectedAudioDevices: " + mMaxConnectedAudioDevices);
+ String twsShoEnabled = SystemProperties.get("persist.vendor.btstack.enable.twsplussho");
+ if (!twsShoEnabled.isEmpty() && "true".equals(twsShoEnabled) &&
+ (mIsTwsPlusEnabled == true) && mMaxConnectedAudioDevices <= 2) {
+ mMaxConnectedAudioDevices = 3;
+ Log.i(TAG, "TWS+ SHO enabled mMaxConnectedAudioDevices changed to: " + mMaxConnectedAudioDevices);
+ } else if (mIsTwsPlusEnabled && mMaxConnectedAudioDevices < 2) {
+ mMaxConnectedAudioDevices = 2;
+ Log.i(TAG, "TWS+ enabled mMaxConnectedAudioDevices changed to: " + mMaxConnectedAudioDevices);
+ }
+ mSetMaxConnectedAudioDevices = mMaxConnectedAudioDevices;
+ String twsPlusMonoEnabled = SystemProperties.get("persist.vendor.btstack.twsplus.monosupport");
+ if (!twsPlusMonoEnabled.isEmpty() && "true".equals(twsPlusMonoEnabled)) {
+ mIsTwsPlusMonoSupported = true;
+ }
+ String TwsPlusChannelMode = SystemProperties.get("persist.vendor.btstack.twsplus.defaultchannelmode");
+ if (!TwsPlusChannelMode.isEmpty() && "mono".equals(TwsPlusChannelMode)) {
+ mTwsPlusChannelMode = "mono";
+ }
+ Log.d(TAG, "Default TwsPlus ChannelMode: " + mTwsPlusChannelMode);
}
- String TwsPlusChannelMode = SystemProperties.get("persist.vendor.btstack.twsplus.defaultchannelmode");
- if (!TwsPlusChannelMode.isEmpty() && "mono".equals(TwsPlusChannelMode)) {
- mTwsPlusChannelMode = "mono";
- }
- Log.d(TAG, "Default TwsPlus ChannelMode: " + mTwsPlusChannelMode);
- }
- // Step 3: Setup AVRCP
- if(mAdapterService.isVendorIntfEnabled())
- mAvrcp_ext = Avrcp_ext.make(this, this, mMaxConnectedAudioDevices);
- else
- mAvrcp = Avrcp.make(this);
+ // Step 3: Setup AVRCP
+ if(mAdapterService != null && mAdapterService.isVendorIntfEnabled())
+ mAvrcp_ext = Avrcp_ext.make(this, this, mMaxConnectedAudioDevices);
+ else
+ mAvrcp = Avrcp.make(this);
- // Step 4: Start handler thread for state machines
- mStateMachines.clear();
- mStateMachinesThread = new HandlerThread("A2dpService.StateMachines");
- mStateMachinesThread.start();
+ // Step 4: Start handler thread for state machines
+ mStateMachines.clear();
+ mStateMachinesThread = new HandlerThread("A2dpService.StateMachines");
+ mStateMachinesThread.start();
- // Step 5: Setup codec config
- mA2dpCodecConfig = new A2dpCodecConfig(this, mA2dpNativeInterface);
+ // Step 5: Setup codec config
+ mA2dpCodecConfig = new A2dpCodecConfig(this, mA2dpNativeInterface);
- // Step 6: Initialize native interface
- List<BluetoothCodecConfig> mCodecConfigOffload;
- mCodecConfigOffload = mAudioManager.getHwOffloadEncodingFormatsSupportedForA2DP();
- BluetoothCodecConfig[] OffloadCodecConfig = new BluetoothCodecConfig[mCodecConfigOffload.size()];
- OffloadCodecConfig = mCodecConfigOffload.toArray(OffloadCodecConfig);
- mA2dpNativeInterface.init(mMaxConnectedAudioDevices,
+ // Step 6: Initialize native interface
+ List<BluetoothCodecConfig> mCodecConfigOffload;
+ mCodecConfigOffload = mAudioManager.getHwOffloadEncodingFormatsSupportedForA2DP();
+ BluetoothCodecConfig[] OffloadCodecConfig = new BluetoothCodecConfig[mCodecConfigOffload.size()];
+ OffloadCodecConfig = mCodecConfigOffload.toArray(OffloadCodecConfig);
+ mA2dpNativeInterface.init(mMaxConnectedAudioDevices,
mA2dpCodecConfig.codecConfigPriorities(),OffloadCodecConfig);
- // Step 7: Check if A2DP is in offload mode
- mA2dpOffloadEnabled = mAdapterService.isA2dpOffloadEnabled();
- if (DBG) {
- Log.d(TAG, "A2DP offload flag set to " + mA2dpOffloadEnabled);
- }
-
+ // Step 7: Check if A2DP is in offload mode
+ mA2dpOffloadEnabled = mAdapterService.isA2dpOffloadEnabled();
+ if (DBG) {
+ Log.d(TAG, "A2DP offload flag set to " + mA2dpOffloadEnabled);
+ }
+ }
// Step 8: Setup broadcast receivers
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
@@ -260,7 +268,6 @@
// Step 9: Clear active device and stop playing audio
removeActiveDevice(true);
-
// Step 8: Mark service as stopped
setA2dpService(null);
@@ -269,11 +276,11 @@
mConnectionStateChangedReceiver = null;
unregisterReceiver(mBondStateChangedReceiver);
mBondStateChangedReceiver = null;
-
// Step 6: Cleanup native interface
- mA2dpNativeInterface.cleanup();
- mA2dpNativeInterface = null;
-
+ synchronized (mVariableLock) {
+ if (mA2dpNativeInterface != null)
+ mA2dpNativeInterface.cleanup();
+ }
// Step 5: Clear codec config
mA2dpCodecConfig = null;
@@ -303,21 +310,22 @@
}
// Step 2: Reset maximum number of connected audio devices
- if (mAdapterService.isVendorIntfEnabled()) {
- if (mIsTwsPlusEnabled) {
- mMaxConnectedAudioDevices = 2;
+ synchronized (mVariableLock) {
+ if (mAdapterService != null && mAdapterService.isVendorIntfEnabled()) {
+ if (mIsTwsPlusEnabled) {
+ mMaxConnectedAudioDevices = 2;
+ } else {
+ mMaxConnectedAudioDevices = 1;
+ }
} else {
- mMaxConnectedAudioDevices = 1;
- }
- } else {
- mMaxConnectedAudioDevices = 1;
+ mMaxConnectedAudioDevices = 1;
+ }
+ mSetMaxConnectedAudioDevices = 1;
+ // Step 1: Clear AdapterService, A2dpNativeInterface, AudioManager
+ mAudioManager = null;
+ mA2dpNativeInterface = null;
+ mAdapterService = null;
}
- mSetMaxConnectedAudioDevices = 1;
-
- // Step 1: Clear AdapterService, A2dpNativeInterface, AudioManager
- mAudioManager = null;
- mA2dpNativeInterface = null;
- mAdapterService = null;
return true;
}
@@ -356,10 +364,14 @@
Log.e(TAG, "Cannot connect to " + device + " : PRIORITY_OFF");
return false;
}
- if (!BluetoothUuid.isUuidPresent(mAdapterService.getRemoteUuids(device),
- BluetoothUuid.AudioSink)) {
- Log.e(TAG, "Cannot connect to " + device + " : Remote does not have A2DP Sink UUID");
- return false;
+ synchronized (mVariableLock) {
+ if (mAdapterService == null)
+ return false;
+ if (!BluetoothUuid.isUuidPresent(mAdapterService.getRemoteUuids(device),
+ BluetoothUuid.AudioSink)) {
+ Log.e(TAG, "Cannot connect to " + device + " : Remote does not have A2DP Sink UUID");
+ return false;
+ }
}
synchronized (mBtA2dpLock) {
@@ -503,15 +515,17 @@
int tws_device = 0;
// Count devices that are in the process of connecting or already connected
synchronized (mBtA2dpLock) {
- for (A2dpStateMachine sm : mStateMachines.values()) {
+ for (A2dpStateMachine sm : mStateMachines.values()) {
switch (sm.getConnectionState()) {
case BluetoothProfile.STATE_CONNECTING:
case BluetoothProfile.STATE_CONNECTED:
if (Objects.equals(device, sm.getDevice())) {
return true; // Already connected or accounted for
}
- if (mAdapterService.isTwsPlusDevice(sm.getDevice()))
- tws_device++;
+ synchronized (mVariableLock) {
+ if (mAdapterService != null && mAdapterService.isTwsPlusDevice(sm.getDevice()))
+ tws_device++;
+ }
connected++;
break;
default:
@@ -521,11 +535,13 @@
}
Log.d(TAG,"connectionAllowedCheckMaxDevices connected = " + connected +
"tws connected = " + tws_device);
- if (mAdapterService.isVendorIntfEnabled() &&
- ((tws_device > 0) || mAdapterService.isTwsPlusDevice(device) ||
- ((tws_device > 0) && connected == mMaxConnectedAudioDevices &&
- !mAdapterService.isTwsPlusDevice(device)))) {
- return isConnectionAllowed(device, tws_device, connected);
+ synchronized (mVariableLock) {
+ if (mAdapterService != null && mAdapterService.isVendorIntfEnabled() &&
+ ((tws_device > 0) || mAdapterService.isTwsPlusDevice(device) ||
+ ((tws_device > 0) && connected == mMaxConnectedAudioDevices &&
+ !mAdapterService.isTwsPlusDevice(device)))) {
+ return isConnectionAllowed(device, tws_device, connected);
+ }
}
if (mSetMaxConnectedAudioDevices == 1 &&
connected == mSetMaxConnectedAudioDevices) {
@@ -548,9 +564,13 @@
public boolean okToConnect(BluetoothDevice device, boolean isOutgoingRequest) {
Log.i(TAG, "okToConnect: device " + device + " isOutgoingRequest: " + isOutgoingRequest);
// Check if this is an incoming connection in Quiet mode.
- if (mAdapterService.isQuietModeEnabled() && !isOutgoingRequest) {
- Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled");
- return false;
+ synchronized (mVariableLock) {
+ if (mAdapterService == null)
+ return false;
+ if (mAdapterService.isQuietModeEnabled() && !isOutgoingRequest) {
+ Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled");
+ return false;
+ }
}
// Check if too many devices
if (!connectionAllowedCheckMaxDevices(device)) {
@@ -562,7 +582,11 @@
// Check priority and accept or reject the connection.
// Note: Logic can be simplified, but keeping it this way for readability
int priority = getPriority(device);
- int bondState = mAdapterService.getBondState(device);
+ int bondState = BluetoothDevice.BOND_NONE;
+ synchronized (mVariableLock) {
+ if (mAdapterService != null)
+ bondState = mAdapterService.getBondState(device);
+ }
// If priority is undefined, it is likely that service discovery has not completed and peer
// initiated the connection. Allow this connection only if the device is bonded or bonding
boolean serviceDiscoveryPending = (priority == BluetoothProfile.PRIORITY_UNDEFINED)
@@ -589,15 +613,21 @@
if (states == null) {
return devices;
}
- final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
+ BluetoothDevice [] bondedDevices = null;
+ synchronized (mVariableLock) {
+ if (mAdapterService != null)
+ bondedDevices = mAdapterService.getBondedDevices();
+ }
if (bondedDevices == null) {
return devices;
}
synchronized (mStateMachines) {
for (BluetoothDevice device : bondedDevices) {
- if (!BluetoothUuid.isUuidPresent(mAdapterService.getRemoteUuids(device),
+ synchronized (mVariableLock) {
+ if (mAdapterService != null && !BluetoothUuid.isUuidPresent(mAdapterService.getRemoteUuids(device),
BluetoothUuid.AudioSink)) {
- continue;
+ continue;
+ }
}
int connectionState = BluetoothProfile.STATE_DISCONNECTED;
A2dpStateMachine sm = mStateMachines.get(device);
@@ -681,16 +711,20 @@
isBAActive = (mBatService != null) && (mBatService.isBATActive());
Log.d(TAG," removeActiveDevice: BA active " + isBAActive);
// If BA streaming is ongoing, we don't want to pause music player
- if(!isBAActive) {
- mAudioManager.handleBluetoothA2dpActiveDeviceChange(
- previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
- BluetoothProfile.A2DP, suppressNoisyIntent, -1);
- }
+ synchronized (mVariableLock ) {
+ if(!isBAActive && mAudioManager != null) {
+ mAudioManager.handleBluetoothA2dpActiveDeviceChange(
+ previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.A2DP, suppressNoisyIntent, -1);
+ }
+ }
}
// Make sure the Active device in native layer is set to null and audio is off
- if (!mA2dpNativeInterface.setActiveDevice(null)) {
- Log.w(TAG, "setActiveDevice(null): Cannot remove active device in native "
- + "layer");
+ synchronized (mVariableLock ) {
+ if (mA2dpNativeInterface != null && !mA2dpNativeInterface.setActiveDevice(null)) {
+ Log.w(TAG, "setActiveDevice(null): Cannot remove active device in native "
+ + "layer");
+ }
}
}
@@ -712,9 +746,12 @@
// Set the device as the active device if currently no active device.
setActiveDevice(device);
}
- if (!mA2dpNativeInterface.setSilenceDevice(device, silence)) {
- Log.e(TAG, "Cannot set " + device + " silence mode " + silence + " in native layer");
- return false;
+ synchronized (mVariableLock) {
+ if (mA2dpNativeInterface != null &&
+ !mA2dpNativeInterface.setSilenceDevice(device, silence)) {
+ Log.e(TAG, "Cannot set " + device + " silence mode " + silence + " in native layer");
+ return false;
+ }
}
return true;
}
@@ -794,12 +831,15 @@
+ "device is not connected");
return false;
}
- if (mActiveDevice != null && mAdapterService.isTwsPlusDevice(device) &&
- mAdapterService.isTwsPlusDevice(mActiveDevice) &&
- !Objects.equals(device, mActiveDevice) &&
- getConnectionState(mActiveDevice) == BluetoothProfile.STATE_CONNECTED) {
- Log.d(TAG,"Ignore setActiveDevice request");
- return false;
+ synchronized (mVariableLock) {
+ if (mActiveDevice != null && mAdapterService != null &&
+ mAdapterService.isTwsPlusDevice(device) &&
+ mAdapterService.isTwsPlusDevice(mActiveDevice) &&
+ !Objects.equals(device, mActiveDevice) &&
+ getConnectionState(mActiveDevice) == BluetoothProfile.STATE_CONNECTED) {
+ Log.d(TAG,"Ignore setActiveDevice request");
+ return false;
+ }
}
codecStatus = sm.getCodecStatus();
@@ -816,47 +856,43 @@
Log.w(TAG, "setActiveDevice coming out of mutex lock");
}
- if (!mA2dpNativeInterface.setActiveDevice(device)) {
- Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native layer");
- return false;
+ synchronized (mVariableLock) {
+ if (mA2dpNativeInterface != null && !mA2dpNativeInterface.setActiveDevice(device)) {
+ Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native layer");
+ return false;
+ }
}
+
updateAndBroadcastActiveDevice(device);
Log.d(TAG, "setActiveDevice(" + device + "): completed");
if (deviceChanged) {
if(mAvrcp_ext != null)
mAvrcp_ext.setActiveDevice(device);
- if (mAdapterService.isTwsPlusDevice(device) &&
- (previousActiveDevice != null && mAdapterService.isTwsPlusDevice(previousActiveDevice))) {
- Log.d(TAG,"TWS+ active device disconnected, setting other pair as active");
- tws_switch = true;
+ synchronized (mVariableLock) {
+ if (mAdapterService != null && mAdapterService.isTwsPlusDevice(device) &&
+ (previousActiveDevice != null && mAdapterService.isTwsPlusDevice(previousActiveDevice))) {
+ Log.d(TAG,"TWS+ active device disconnected, setting other pair as active");
+ tws_switch = true;
+ }
}
// Send an intent with the active device codec config
if (codecStatus != null) {
broadcastCodecConfig(mActiveDevice, codecStatus);
}
int rememberedVolume = -1;
- if (mFactory.getAvrcpTargetService() != null) {
- rememberedVolume = mFactory.getAvrcpTargetService()
- .getRememberedVolumeForDevice(mActiveDevice);
- } else if (mAdapterService.isVendorIntfEnabled()) {
- rememberedVolume = mAvrcp_ext.getVolume(device);
- Log.d(TAG,"volume = " + rememberedVolume);
- }
- // Make sure the Audio Manager knows the previous Active device is disconnected,
- // and the new Active device is connected.
- // Also, mute and unmute the output during the switch to avoid audio glitches.
- boolean wasMuted = false;
- if (previousActiveDevice != null && !tws_switch) {
- if (!mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC)) {
- mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
- AudioManager.ADJUST_MUTE,
- mAudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
- wasMuted = true;
+ synchronized (mVariableLock) {
+ if (mFactory.getAvrcpTargetService() != null) {
+ rememberedVolume = mFactory.getAvrcpTargetService()
+ .getRememberedVolumeForDevice(mActiveDevice);
+ } else if (mAdapterService != null && mAdapterService.isVendorIntfEnabled()) {
+ rememberedVolume = mAvrcp_ext.getVolume(device);
+ Log.d(TAG,"volume = " + rememberedVolume);
}
- }
+ // Make sure the Audio Manager knows the previous Active device is disconnected,
+ // and the new Active device is connected.
- if (!isBAActive) {
+ if (!isBAActive && mAudioManager != null) {
// Make sure the Audio Manager knows the previous
// Active device is disconnected, and the new Active
// device is connected.
@@ -864,19 +900,25 @@
// new active device so that Audio Service
// can reset accordingly the audio feeding parameters
// in the Audio HAL to the Bluetooth stack.
- mAudioManager.handleBluetoothA2dpActiveDeviceChange(
- mActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
- true, rememberedVolume);
- }
+ mAudioManager.handleBluetoothA2dpActiveDeviceChange(
+ mActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
+ true, rememberedVolume);
+ }
// 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.
- if (wasMuted) {
- mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
- AudioManager.ADJUST_UNMUTE,
- mAudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
+ // 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 (mAvrcp_ext != null && !tws_switch) {
mAvrcp_ext.setAbsVolumeFlag(device);
@@ -909,15 +951,23 @@
if (DBG) {
Log.d(TAG, "Saved priority " + device + " = " + priority);
}
- mAdapterService.getDatabase()
+
+ synchronized (mVariableLock) {
+ if(mAdapterService != null)
+ mAdapterService.getDatabase()
.setProfilePriority(device, BluetoothProfile.A2DP, priority);
+ }
return true;
}
public int getPriority(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
- return mAdapterService.getDatabase()
- .getProfilePriority(device, BluetoothProfile.A2DP);
+ synchronized (mVariableLock) {
+ if(mAdapterService != null)
+ return mAdapterService.getDatabase()
+ .getProfilePriority(device, BluetoothProfile.A2DP);
+ }
+ return BluetoothProfile.PRIORITY_UNDEFINED;
}
/* Absolute volume implementation */
@@ -1089,7 +1139,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;
}
@@ -1115,20 +1165,21 @@
}
}
- 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 ||
- codecConfig.getCodecType() != BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE )) {
- Log.w(TAG, "Block un-supportive codec on TWS+ device: " + device);
- return;
+ synchronized (mVariableLock) {
+ if (mAdapterService != null && mAdapterService.isTwsPlusDevice(device) && ( cs4 == 0 ||
+ codecConfig.getCodecType() != BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE )) {
+ Log.w(TAG, "Block un-supportive codec on TWS+ device: " + device);
+ return;
+ }
}
mA2dpCodecConfig.setCodecConfigPreference(device, codecStatus, codecConfig);
}
@@ -1149,16 +1200,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());
@@ -1180,16 +1231,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());
@@ -1197,19 +1248,30 @@
public int getSupportsOptionalCodecs(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
- return mAdapterService.getDatabase().getA2dpSupportsOptionalCodecs(device);
+ synchronized (mVariableLock) {
+ if(mAdapterService != null)
+ return mAdapterService.getDatabase().getA2dpSupportsOptionalCodecs(device);
+ }
+ return BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED;
}
public void setSupportsOptionalCodecs(BluetoothDevice device, boolean doesSupport) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
int value = doesSupport ? BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED
: BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED;
- mAdapterService.getDatabase().setA2dpSupportsOptionalCodecs(device, value);
+ synchronized (mVariableLock) {
+ if(mAdapterService != null)
+ mAdapterService.getDatabase().setA2dpSupportsOptionalCodecs(device, value);
+ }
}
public int getOptionalCodecsEnabled(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
- return mAdapterService.getDatabase().getA2dpOptionalCodecsEnabled(device);
+ synchronized (mVariableLock) {
+ if(mAdapterService != null)
+ return mAdapterService.getDatabase().getA2dpOptionalCodecsEnabled(device);
+ }
+ return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN;
}
public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
@@ -1220,7 +1282,10 @@
Log.w(TAG, "Unexpected value passed to setOptionalCodecsEnabled:" + value);
return;
}
- mAdapterService.getDatabase().setA2dpOptionalCodecsEnabled(device, value);
+ synchronized (mVariableLock) {
+ if(mAdapterService != null)
+ mAdapterService.getDatabase().setA2dpOptionalCodecsEnabled(device, value);
+ }
}
// Handle messages from native (JNI) to Java
@@ -1235,40 +1300,46 @@
switch (stackEvent.valueInt) {
case A2dpStackEvent.CONNECTION_STATE_CONNECTED:
case A2dpStackEvent.CONNECTION_STATE_CONNECTING:
- // Create a new state machine only when connecting to a device
- if (mAdapterService.isVendorIntfEnabled())
- mA2dpStackEvent = stackEvent.valueInt;
- if (mAdapterService.isTwsPlusDevice(device)) {
- sm = getOrCreateStateMachine(device);
- break;
- }
- if (!connectionAllowedCheckMaxDevices(device)) {
- Log.e(TAG, "Cannot connect to " + device
- + " : too many connected devices");
- mA2dpNativeInterface.disconnectA2dp(device);
- return;
+ synchronized (mVariableLock) {
+ // Create a new state machine only when connecting to a device
+ if (mAdapterService != null && mAdapterService.isVendorIntfEnabled())
+ mA2dpStackEvent = stackEvent.valueInt;
+ if (mAdapterService != null && mAdapterService.isTwsPlusDevice(device)) {
+ sm = getOrCreateStateMachine(device);
+ break;
+ }
+ if (!connectionAllowedCheckMaxDevices(device) && mA2dpNativeInterface != null) {
+ Log.e(TAG, "Cannot connect to " + device
+ + " : too many connected devices");
+ mA2dpNativeInterface.disconnectA2dp(device);
+ return;
+ }
}
sm = getOrCreateStateMachine(device);
break;
default:
- if (mAdapterService.isVendorIntfEnabled() &&
- mA2dpStackEvent == A2dpStackEvent.CONNECTION_STATE_CONNECTED ||
- mA2dpStackEvent == A2dpStackEvent.CONNECTION_STATE_CONNECTING) {
- Log.d(TAG,"Reset local stack event value");
- mA2dpStackEvent = EVENT_TYPE_NONE;
+ synchronized (mVariableLock) {
+ if (mAdapterService!= null && mAdapterService.isVendorIntfEnabled() &&
+ mA2dpStackEvent == A2dpStackEvent.CONNECTION_STATE_CONNECTED ||
+ mA2dpStackEvent == A2dpStackEvent.CONNECTION_STATE_CONNECTING) {
+ Log.d(TAG,"Reset local stack event value");
+ mA2dpStackEvent = EVENT_TYPE_NONE;
+ }
}
break;
}
}
} else {
- if (mAdapterService.isVendorIntfEnabled()) {
- switch (sm.getConnectionState()) {
- case BluetoothProfile.STATE_DISCONNECTED:
- mA2dpStackEvent = stackEvent.valueInt;
- break;
- default:
- mA2dpStackEvent = EVENT_TYPE_NONE;
- break;
+ synchronized (mVariableLock) {
+ if (mAdapterService != null && mAdapterService.isVendorIntfEnabled()) {
+ switch (sm.getConnectionState()) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ mA2dpStackEvent = stackEvent.valueInt;
+ break;
+ default:
+ mA2dpStackEvent = EVENT_TYPE_NONE;
+ break;
+ }
}
}
}
@@ -1296,20 +1367,24 @@
// Log codec config and capability metrics
BluetoothCodecConfig codecConfig = codecStatus.getCodecConfig();
- StatsLog.write(StatsLog.BLUETOOTH_A2DP_CODEC_CONFIG_CHANGED,
- mAdapterService.obfuscateAddress(device), codecConfig.getCodecType(),
- codecConfig.getCodecPriority(), codecConfig.getSampleRate(),
- codecConfig.getBitsPerSample(), codecConfig.getChannelMode(),
- codecConfig.getCodecSpecific1(), codecConfig.getCodecSpecific2(),
- codecConfig.getCodecSpecific3(), codecConfig.getCodecSpecific4());
- BluetoothCodecConfig[] codecCapabilities = codecStatus.getCodecsSelectableCapabilities();
- for (BluetoothCodecConfig codecCapability : codecCapabilities) {
- StatsLog.write(StatsLog.BLUETOOTH_A2DP_CODEC_CAPABILITY_CHANGED,
- mAdapterService.obfuscateAddress(device), codecCapability.getCodecType(),
- codecCapability.getCodecPriority(), codecCapability.getSampleRate(),
- codecCapability.getBitsPerSample(), codecCapability.getChannelMode(),
+ synchronized (mVariableLock) {
+ if(mAdapterService != null) {
+ StatsLog.write(StatsLog.BLUETOOTH_A2DP_CODEC_CONFIG_CHANGED,
+ mAdapterService.obfuscateAddress(device), codecConfig.getCodecType(),
+ codecConfig.getCodecPriority(), codecConfig.getSampleRate(),
+ codecConfig.getBitsPerSample(), codecConfig.getChannelMode(),
codecConfig.getCodecSpecific1(), codecConfig.getCodecSpecific2(),
codecConfig.getCodecSpecific3(), codecConfig.getCodecSpecific4());
+ BluetoothCodecConfig[] codecCapabilities = codecStatus.getCodecsSelectableCapabilities();
+ for (BluetoothCodecConfig codecCapability : codecCapabilities) {
+ StatsLog.write(StatsLog.BLUETOOTH_A2DP_CODEC_CAPABILITY_CHANGED,
+ mAdapterService.obfuscateAddress(device), codecCapability.getCodecType(),
+ codecCapability.getCodecPriority(), codecCapability.getSampleRate(),
+ codecCapability.getBitsPerSample(), codecCapability.getChannelMode(),
+ codecConfig.getCodecSpecific1(), codecConfig.getCodecSpecific2(),
+ codecConfig.getCodecSpecific3(), codecConfig.getCodecSpecific4());
+ }
+ }
}
broadcastCodecConfig(device, codecStatus);
@@ -1317,15 +1392,27 @@
// 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) {
- mAudioManager.handleBluetoothA2dpActiveDeviceChange(device,
- BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
- true, -1);
+ if (mAvrcp_ext != null)
+ rememberedVolume = mAvrcp_ext.getVolume(device);
+ synchronized (mVariableLock) {
+ if (mAudioManager != null) {
+ mAudioManager.handleBluetoothA2dpActiveDeviceChange(device,
+ BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
+ true, rememberedVolume);
+ }
+ }
}
}
+
void updateTwsChannelMode(int state, BluetoothDevice device) {
if (mIsTwsPlusMonoSupported) {
- BluetoothDevice peerTwsDevice = mAdapterService.getTwsPlusPeerDevice(device);
+ BluetoothDevice peerTwsDevice = null;
+ synchronized (mVariableLock) {
+ if (mAdapterService != null)
+ peerTwsDevice = mAdapterService.getTwsPlusPeerDevice(device);
+ }
Log.d(TAG, "TwsChannelMode: " + mTwsPlusChannelMode);
synchronized(mBtTwsLock) {
if ("mono".equals(mTwsPlusChannelMode)) {
@@ -1336,7 +1423,10 @@
mHandler.sendMessageDelayed(msg, DualMonoCfg_Timeout);
} else if (state == BluetoothA2dp.STATE_PLAYING) {
Log.d(TAG, "setparameters to Mono");
- mAudioManager.setParameters("TwsChannelConfig=mono");
+ synchronized (mVariableLock) {
+ if (mAudioManager != null)
+ mAudioManager.setParameters("TwsChannelConfig=mono");
+ }
}
if ((state == BluetoothA2dp.STATE_NOT_PLAYING) &&
isA2dpPlaying(peerTwsDevice)) {
@@ -1362,7 +1452,10 @@
if ((state == BluetoothA2dp.STATE_NOT_PLAYING)
&& isA2dpPlaying(peerTwsDevice)) {
Log.d(TAG, "setparameters to Mono");
- mAudioManager.setParameters("TwsChannelConfig=mono");
+ synchronized (mVariableLock) {
+ if (mAudioManager != null)
+ mAudioManager.setParameters("TwsChannelConfig=mono");
+ }
mTwsPlusChannelMode = "mono";
}
}
@@ -1382,7 +1475,10 @@
if(isBAActive) {
return;
}
- mAudioManager.setParameters("reconfigA2dp=true");
+ synchronized (mVariableLock) {
+ if (mAudioManager != null)
+ mAudioManager.setParameters("reconfigA2dp=true");
+ }
}
@@ -1428,8 +1524,11 @@
mActiveDevice = device;
}
- StatsLog.write(StatsLog.BLUETOOTH_ACTIVE_DEVICE_CHANGED, BluetoothProfile.A2DP,
- mAdapterService.obfuscateAddress(device));
+ synchronized (mVariableLock) {
+ if (mAdapterService != null)
+ StatsLog.write(StatsLog.BLUETOOTH_ACTIVE_DEVICE_CHANGED, BluetoothProfile.A2DP,
+ mAdapterService.obfuscateAddress(device));
+ }
Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
@@ -1492,6 +1591,9 @@
if (mFactory.getAvrcpTargetService() != null) {
mFactory.getAvrcpTargetService().removeStoredVolumeForDevice(device);
}
+ if (mAvrcp_ext != null) {
+ mAvrcp_ext.removeVolumeForDevice(device);
+ }
removeStateMachine(device);
}
@@ -1547,7 +1649,6 @@
Log.i(TAG, "updateOptionalCodecsSupport: Mandatory codec is not selectable.");
return;
}
-
if (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN
|| supportsOptional != (previousSupport
== BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED)) {
@@ -1578,14 +1679,20 @@
if (toState == BluetoothProfile.STATE_CONNECTED) {
MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.A2DP);
}
+ int bondState = BluetoothDevice.BOND_NONE;
// Check if the device is disconnected - if unbond, remove the state machine
if (toState == BluetoothProfile.STATE_DISCONNECTED) {
- int bondState = mAdapterService.getBondState(device);
+ synchronized (mVariableLock) {
+ if(mAdapterService != null)
+ bondState = mAdapterService.getBondState(device);
+ }
if (bondState == BluetoothDevice.BOND_NONE) {
if (mFactory.getAvrcpTargetService() != null) {
mFactory.getAvrcpTargetService().removeStoredVolumeForDevice(device);
}
-
+ if (mAvrcp_ext != null) {
+ mAvrcp_ext.removeVolumeForDevice(device);
+ }
removeStateMachine(device);
}
}
diff --git a/src/com/android/bluetooth/btservice/AbstractionLayer.java b/src/com/android/bluetooth/btservice/AbstractionLayer.java
index 6ee0d6e..30493f1 100644
--- a/src/com/android/bluetooth/btservice/AbstractionLayer.java
+++ b/src/com/android/bluetooth/btservice/AbstractionLayer.java
@@ -126,6 +126,7 @@
public static final int PBAP = 2;
public static final int MAP = 3;
public static final int MAX_POW = 4;
+ public static final int HEARING_AID = 5;
// Profile features supported in profile_conf
public static final int PROFILE_VERSION =1;
@@ -134,9 +135,11 @@
public static final int USE_SIM_SUPPORT = 4;
public static final int MAP_EMAIL_SUPPORT = 5;
public static final int PBAP_0102_SUPPORT = 6;
- public static final int BR_MAX_POW_SUPPORT = 7;
- public static final int EDR_MAX_POW_SUPPORT = 8;
- public static final int BLE_MAX_POW_SUPPORT = 9;
+ public static final int MAP_0104_SUPPORT = 7;
+ public static final int BR_MAX_POW_SUPPORT = 8;
+ public static final int EDR_MAX_POW_SUPPORT = 9;
+ public static final int BLE_MAX_POW_SUPPORT = 10;
+ public static final int HEARING_AID_SUPPORT = 11;
static final int BT_VENDOR_PROPERTY_TWS_PLUS_DEVICE_TYPE = 0x01;
diff --git a/src/com/android/bluetooth/btservice/AdapterProperties.java b/src/com/android/bluetooth/btservice/AdapterProperties.java
index beff7c8..c7eb53c 100644
--- a/src/com/android/bluetooth/btservice/AdapterProperties.java
+++ b/src/com/android/bluetooth/btservice/AdapterProperties.java
@@ -86,7 +86,18 @@
private CopyOnWriteArrayList<BluetoothDevice> mBondedDevices =
new CopyOnWriteArrayList<BluetoothDevice>();
- private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting;
+ private final class ProfilesConnectionState {
+ public int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting;
+
+ ProfilesConnectionState(int connecting, int connected, int disconnecting) {
+ mProfilesConnecting = connecting;
+ mProfilesConnected = connected;
+ mProfilesDisconnecting = disconnecting;
+ }
+ }
+ private final HashMap<BluetoothDevice, ProfilesConnectionState> mDevicesConnectionState =
+ new HashMap<>();
+
private final HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState =
new HashMap<>();
@@ -218,6 +229,7 @@
public void init(RemoteDevices remoteDevices) {
mProfileConnectionState.clear();
+ mDevicesConnectionState.clear();
mRemoteDevices = remoteDevices;
// Get default max connected audio devices from config.xml in frameworks/base/core
@@ -274,6 +286,7 @@
}
mService = null;
mBondedDevices.clear();
+ mDevicesConnectionState.clear();
}
@Override
@@ -787,6 +800,10 @@
debugLog("Adding bonded device:" + device);
mBondedDevices.add(device);
}
+ if (!mDevicesConnectionState.containsKey(device)) {
+ debugLog("Adding connection state:" + device);
+ mDevicesConnectionState.put(device, new ProfilesConnectionState(0, 0, 0));
+ }
} else if (state == BluetoothDevice.BOND_NONE) {
// remove device from list
if (mBondedDevices.remove(device)) {
@@ -794,6 +811,10 @@
} else {
debugLog("Failed to remove device: " + device);
}
+ if (mDevicesConnectionState.containsKey(device)) {
+ debugLog("Removing connection state:" + device);
+ mDevicesConnectionState.remove(device);
+ }
}
} catch (Exception ee) {
Log.w(TAG, "onBondStateChanged: Exception ", ee);
@@ -868,7 +889,7 @@
try {
validateConnectionState =
- updateCountersAndCheckForConnectionStateChange(state, prevState);
+ updateCountersAndCheckForConnectionStateChange(device, state, prevState);
} catch (IllegalStateException ee) {
Log.w(TAG, "ADAPTER_CONNECTION_STATE_CHANGE: unexpected transition for profile="
+ profile + ", " + prevState + " -> " + state);
@@ -932,33 +953,45 @@
}
}
- private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
+ private boolean updateCountersAndCheckForConnectionStateChange(BluetoothDevice device,
+ int state, int prevState) {
+ if(!mDevicesConnectionState.containsKey(device)) {
+ Log.e(TAG, "Can't find device connection record, adding new one: " + device);
+ mDevicesConnectionState.put(device, new ProfilesConnectionState(0, 0, 0));
+ }
+ ProfilesConnectionState connstate = mDevicesConnectionState.get(device);
+
+ Log.e(TAG, "prevState=" + prevState + " -> State=" + state +
+ " mProfilesConnecting=" + connstate.mProfilesConnecting +
+ " mProfilesConnected=" + connstate.mProfilesConnected +
+ " mProfilesDisconnecting=" + connstate.mProfilesDisconnecting);
+
switch (prevState) {
case BluetoothProfile.STATE_CONNECTING:
- if (mProfilesConnecting > 0) {
- mProfilesConnecting--;
+ if (connstate.mProfilesConnecting > 0) {
+ connstate.mProfilesConnecting--;
} else {
- Log.e(TAG, "mProfilesConnecting " + mProfilesConnecting);
+ Log.e(TAG, "mProfilesConnecting " + connstate.mProfilesConnecting);
throw new IllegalStateException(
"Invalid state transition, " + prevState + " -> " + state);
}
break;
case BluetoothProfile.STATE_CONNECTED:
- if (mProfilesConnected > 0) {
- mProfilesConnected--;
+ if (connstate.mProfilesConnected > 0) {
+ connstate.mProfilesConnected--;
} else {
- Log.e(TAG, "mProfilesConnected " + mProfilesConnected);
+ Log.e(TAG, "mProfilesConnected " + connstate.mProfilesConnected);
throw new IllegalStateException(
"Invalid state transition, " + prevState + " -> " + state);
}
break;
case BluetoothProfile.STATE_DISCONNECTING:
- if (mProfilesDisconnecting > 0) {
- mProfilesDisconnecting--;
+ if (connstate.mProfilesDisconnecting > 0) {
+ connstate.mProfilesDisconnecting--;
} else {
- Log.e(TAG, "mProfilesDisconnecting " + mProfilesDisconnecting);
+ Log.e(TAG, "mProfilesDisconnecting " + connstate.mProfilesDisconnecting);
throw new IllegalStateException(
"Invalid state transition, " + prevState + " -> " + state);
}
@@ -967,19 +1000,19 @@
switch (state) {
case BluetoothProfile.STATE_CONNECTING:
- mProfilesConnecting++;
- return (mProfilesConnected == 0 && mProfilesConnecting == 1);
+ connstate.mProfilesConnecting++;
+ return (connstate.mProfilesConnected == 0 && connstate.mProfilesConnecting == 1);
case BluetoothProfile.STATE_CONNECTED:
- mProfilesConnected++;
- return (mProfilesConnected == 1);
+ connstate.mProfilesConnected++;
+ return (connstate.mProfilesConnected == 1);
case BluetoothProfile.STATE_DISCONNECTING:
- mProfilesDisconnecting++;
- return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
+ connstate.mProfilesDisconnecting++;
+ return (connstate.mProfilesConnected == 0 && connstate.mProfilesDisconnecting == 1);
case BluetoothProfile.STATE_DISCONNECTED:
- return (mProfilesConnected == 0 && mProfilesConnecting == 0);
+ return (connstate.mProfilesConnected == 0 && connstate.mProfilesConnecting == 0);
default:
return true;
@@ -1240,9 +1273,8 @@
// Reset adapter and profile connection states
setConnectionState(BluetoothAdapter.STATE_DISCONNECTED);
mProfileConnectionState.clear();
- mProfilesConnected = 0;
- mProfilesConnecting = 0;
- mProfilesDisconnecting = 0;
+ mDevicesConnectionState.clear();
+
// adapterPropertyChangedCallback has already been received. Set the scan mode.
setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);
// This keeps NV up-to date on first-boot after flash.
diff --git a/src/com/android/bluetooth/btservice/Config.java b/src/com/android/bluetooth/btservice/Config.java
index 0e170b1..0a91e55 100644
--- a/src/com/android/bluetooth/btservice/Config.java
+++ b/src/com/android/bluetooth/btservice/Config.java
@@ -121,10 +121,12 @@
for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) {
boolean supported = resources.getBoolean(config.mSupported);
- if (!supported && (config.mClass == HearingAidService.class) && FeatureFlagUtils
- .isEnabled(ctx, FeatureFlagUtils.HEARING_AID_SETTINGS)) {
- Log.v(TAG, "Feature Flag enables support for HearingAidService");
- supported = true;
+ if (config.mClass == HearingAidService.class &&
+ FeatureFlagUtils.isEnabled(ctx, FeatureFlagUtils.HEARING_AID_SETTINGS)) {
+ if (!isHearingAidSupported()) {
+ supported = false;
+ }
+ if (supported) Log.v(TAG, "HearingAidService is enabled");
}
if (supported && !isProfileDisabled(ctx, config.mMask)) {
@@ -201,4 +203,15 @@
// always return true for other profiles
return true;
}
+
+ private static boolean isHearingAidSupported() {
+ AdapterService adapterService = AdapterService.getAdapterService();
+ boolean isHearingAidSupported = false;
+ if (adapterService != null) {
+ isHearingAidSupported = adapterService.getProfileInfo(
+ AbstractionLayer.HEARING_AID, AbstractionLayer.HEARING_AID_SUPPORT);
+ Log.d(TAG, "isHearingAidSupported: " + isHearingAidSupported);
+ }
+ return isHearingAidSupported;
+ }
}
diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java
index c256d8d..fce6d43 100644
--- a/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -56,6 +56,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.concurrent.Executor;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
@@ -128,6 +129,8 @@
private boolean mIsTwsPlusEnabled = false;
private boolean mIsTwsPlusShoEnabled = false;
private vendorhfservice mVendorHf;
+ private Context mContext = null;
+ private AudioServerStateCallback mServerStateCallback = new AudioServerStateCallback();
@Override
public IProfileServiceBinder initBinder() {
@@ -226,6 +229,11 @@
mVendorHf.enableSwb(isSwbEnabled());
}
+ Log.d(TAG, "registering audio server state callback");
+ mContext = getApplicationContext();
+ Executor exec = mContext.getMainExecutor();
+ mSystemInterface.getAudioManager().setAudioServerStateCallback(exec, mServerStateCallback);
+
Log.i(TAG, " HeadsetService Started ");
return true;
}
@@ -424,6 +432,30 @@
}
}
+ private class AudioServerStateCallback extends AudioManager.AudioServerStateCallback {
+ @Override
+ public void onAudioServerDown() {
+ Log.d(TAG, "notifying onAudioServerDown");
+ }
+
+ @Override
+ public void onAudioServerUp() {
+ Log.d(TAG, "notifying onAudioServerUp");
+ if (isAudioOn()) {
+ Log.d(TAG, "onAudioServerUp: Audio is On, Notify HeadsetStateMachine");
+ synchronized (mStateMachines) {
+ for (HeadsetStateMachine stateMachine : mStateMachines.values()) {
+ if (stateMachine.getAudioState()
+ == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+ stateMachine.onAudioServerUp();
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
private final BroadcastReceiver mHeadsetReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -829,13 +861,14 @@
return null;
}
- private BluetoothDevice getConnectedTwspDevice() {
- List<BluetoothDevice> connDevices = getConnectedDevices();
+ private BluetoothDevice getConnectedOrConnectingTwspDevice() {
+ List<BluetoothDevice> connDevices =
+ getAllDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
int size = connDevices.size();
for(int i = 0; i < size; i++) {
BluetoothDevice ConnectedDevice = connDevices.get(i);
if (mAdapterService.isTwsPlusDevice(ConnectedDevice)) {
- logD("getConnectedTwspDevice: found" + ConnectedDevice);
+ logD("getConnectedorConnectingTwspDevice: found" + ConnectedDevice);
return ConnectedDevice;
}
}
@@ -873,12 +906,14 @@
if (connDevices.size() == 0) {
allowSecondHfConnection = true;
} else {
- BluetoothDevice connectedTwspDev = getConnectedTwspDevice();
- if (connectedTwspDev != null) {
+ BluetoothDevice connectedOrConnectingTwspDev =
+ getConnectedOrConnectingTwspDevice();
+ if (connectedOrConnectingTwspDev != null) {
// There is TWSP connected earbud
if (adapterService.isTwsPlusDevice(device)) {
if (adapterService.getTwsPlusPeerAddress
- (device).equals(connectedTwspDev.getAddress())) {
+ (device).equals(
+ connectedOrConnectingTwspDev.getAddress())) {
//Allow connection only if the outgoing
//is peer of TWS connected earbud
allowSecondHfConnection = true;
@@ -887,7 +922,8 @@
}
} else {
reservedSlotForTwspPeer = 0;
- if (getTwsPlusConnectedPeer(connectedTwspDev) == null) {
+ if (getTwsPlusConnectedPeer(
+ connectedOrConnectingTwspDev) == null) {
//Peer of Connected Tws+ device is not Connected
//yet, reserve one slot
reservedSlotForTwspPeer = 1;
@@ -933,8 +969,9 @@
"is"+ adapterService.isTwsPlusDevice(device));
Log.v(TAG, "TWS Peer Addr: " +
adapterService.getTwsPlusPeerAddress(device));
- if (connectedTwspDev != null) {
- Log.v(TAG, "Connected device" + connectedTwspDev.getAddress());
+ if (connectedOrConnectingTwspDev != null) {
+ Log.v(TAG, "Connected or Connecting device"
+ + connectedOrConnectingTwspDev.getAddress());
} else {
Log.v(TAG, "No Connected TWSP devices");
}
@@ -1251,7 +1288,9 @@
+ " instead");
device = peerDevice;
} else {
- Log.w(TAG, "stopVoiceRecognition: both earbuds are not audio connected");
+ Log.w(TAG, "stopVoiceRecognition: both earbuds are not audio connected, resume A2DP");
+ mVoiceRecognitionStarted = false;
+ mHfpA2dpSyncInterface.releaseA2DP(null);
return false;
}
}
@@ -1272,7 +1311,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;
}
@@ -1918,9 +1963,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 4e82d84..784dead 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;
@@ -1879,6 +1880,14 @@
return true;
}
+ public void onAudioServerUp() {
+ Log.i(TAG, "onAudioSeverUp: restore audio parameters");
+ mSystemInterface.getAudioManager().setBluetoothScoOn(false);
+ mSystemInterface.getAudioManager().setParameters("A2dpSuspended=true");
+ setAudioParameters();
+ mSystemInterface.getAudioManager().setBluetoothScoOn(true);
+ }
+
/*
* Put the AT command, company ID, arguments, and device in an Intent and broadcast it.
*/
@@ -2629,6 +2638,12 @@
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 + "\"");
+ mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0);
+ } else if (atCommand.equals("+CGMM")) {
+ mNativeInterface.atResponseString(device, "+CGMM: " + Build.MODEL);
+ mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0);
} else {
processVendorSpecificAt(atCommand, device);
}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppTransferActivity.java b/src/com/android/bluetooth/opp/BluetoothOppTransferActivity.java
index fc45d3f..e4d98e8 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppTransferActivity.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppTransferActivity.java
@@ -375,24 +375,13 @@
mTransInfo.mID);
} else if (mWhichDialog == DIALOG_SEND_COMPLETE_FAIL) {
// "try again"
-
// make current transfer "hidden"
BluetoothOppUtility.updateVisibilityToHidden(this, mUri);
// clear correspondent notification item
((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(
mTransInfo.mID);
-
- // retry the failed transfer
- Uri uri = BluetoothOppUtility.originalUri(Uri.parse(mTransInfo.mFileUri));
- BluetoothOppSendFileInfo sendFileInfo =
- BluetoothOppSendFileInfo.generateFileInfo(BluetoothOppTransferActivity
- .this, uri, mTransInfo.mFileType, false);
- uri = BluetoothOppUtility.generateUri(uri, sendFileInfo);
- BluetoothOppUtility.putSendFileInfo(uri, sendFileInfo);
- mTransInfo.mFileUri = uri.toString();
- BluetoothOppUtility.retryTransfer(this, mTransInfo);
-
+ retryFailedTrasfer();
BluetoothDevice remoteDevice = mAdapter.getRemoteDevice(mTransInfo.mDestAddr);
// Display toast message
@@ -502,4 +491,22 @@
.setText(getString(R.string.upload_fail_cancel));
}
}
+
+ // Retry the failed transfer in background thread
+ private void retryFailedTrasfer() {
+ new Thread() {
+ @Override
+ public void run() {
+ super.run();
+ Uri uri = BluetoothOppUtility.originalUri(Uri.parse(mTransInfo.mFileUri));
+ BluetoothOppSendFileInfo sendFileInfo =
+ BluetoothOppSendFileInfo.generateFileInfo(BluetoothOppTransferActivity
+ .this, uri, mTransInfo.mFileType, false);
+ uri = BluetoothOppUtility.generateUri(uri, sendFileInfo);
+ BluetoothOppUtility.putSendFileInfo(uri, sendFileInfo);
+ mTransInfo.mFileUri = uri.toString();
+ BluetoothOppUtility.retryTransfer(BluetoothOppTransferActivity.this, mTransInfo);
+ }
+ }.start();
+ }
}
diff --git a/src/com/android/bluetooth/sap/SapService.java b/src/com/android/bluetooth/sap/SapService.java
index 35716d8..9a9c0f8 100644
--- a/src/com/android/bluetooth/sap/SapService.java
+++ b/src/com/android/bluetooth/sap/SapService.java
@@ -165,6 +165,10 @@
} catch (IOException e) {
Log.e(TAG, "Error create RfcommServerSocket ", e);
initSocketOK = false;
+ } catch (SecurityException e) {
+ Log.e(TAG, "Error create RfcommServerSocket ", e);
+ initSocketOK = false;
+ break;
}
if (!initSocketOK) {
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);
}
}