Merge "Snap for 7925884 from 96fdc09881dba687586ca457d13729ed9e53b022 to sdk-release" into sdk-release
diff --git a/jni/com_android_bluetooth_btservice_AdapterService.cpp b/jni/com_android_bluetooth_btservice_AdapterService.cpp
index 40be390..bbf46bb 100644
--- a/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -67,6 +67,7 @@
static jmethodID method_pinRequestCallback;
static jmethodID method_sspRequestCallback;
static jmethodID method_bondStateChangeCallback;
+static jmethodID method_addressConsolidateCallback;
static jmethodID method_aclStateChangeCallback;
static jmethodID method_discoveryStateChangeCallback;
static jmethodID method_linkQualityReportCallback;
@@ -302,6 +303,34 @@
(jint)fail_reason);
}
+static void address_consolidate_callback(RawAddress* main_bd_addr,
+ RawAddress* secondary_bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+
+ ScopedLocalRef<jbyteArray> main_addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!main_addr.get()) {
+ ALOGE("Address allocation failed in %s", __func__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(main_addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)main_bd_addr);
+
+ ScopedLocalRef<jbyteArray> secondary_addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!secondary_addr.get()) {
+ ALOGE("Address allocation failed in %s", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(secondary_addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)secondary_bd_addr);
+
+ sCallbackEnv->CallVoidMethod(sJniCallbacksObj,
+ method_addressConsolidateCallback,
+ main_addr.get(), secondary_addr.get());
+}
+
static void acl_state_changed_callback(bt_status_t status, RawAddress* bd_addr,
bt_acl_state_t state,
int transport_link_type,
@@ -629,15 +658,23 @@
p_energy_info->idle_time, p_energy_info->energy_used, array.get());
}
-static bt_callbacks_t sBluetoothCallbacks = {
- sizeof(sBluetoothCallbacks), adapter_state_change_callback,
- adapter_properties_callback, remote_device_properties_callback,
- device_found_callback, discovery_state_changed_callback,
- pin_request_callback, ssp_request_callback,
- bond_state_changed_callback, acl_state_changed_callback,
- callback_thread_event, dut_mode_recv_callback,
- le_test_mode_recv_callback, energy_info_recv_callback,
- link_quality_report_callback, generate_local_oob_data_callback};
+static bt_callbacks_t sBluetoothCallbacks = {sizeof(sBluetoothCallbacks),
+ adapter_state_change_callback,
+ adapter_properties_callback,
+ remote_device_properties_callback,
+ device_found_callback,
+ discovery_state_changed_callback,
+ pin_request_callback,
+ ssp_request_callback,
+ bond_state_changed_callback,
+ address_consolidate_callback,
+ acl_state_changed_callback,
+ callback_thread_event,
+ dut_mode_recv_callback,
+ le_test_mode_recv_callback,
+ energy_info_recv_callback,
+ link_quality_report_callback,
+ generate_local_oob_data_callback};
// The callback to call when the wake alarm fires.
static alarm_cb sAlarmCallback;
@@ -847,6 +884,9 @@
method_bondStateChangeCallback =
env->GetMethodID(jniCallbackClass, "bondStateChangeCallback", "(I[BII)V");
+ method_addressConsolidateCallback = env->GetMethodID(
+ jniCallbackClass, "addressConsolidateCallback", "([B[B)V");
+
method_aclStateChangeCallback =
env->GetMethodID(jniCallbackClass, "aclStateChangeCallback", "(I[BIII)V");
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index 542509e..557f3b2 100644
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -37,6 +37,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
+import android.media.BtProfileConnectionInfo;
import android.os.HandlerThread;
import android.util.Log;
@@ -462,13 +463,10 @@
// device, the user has explicitly switched the output to the local device and music
// should continue playing. Otherwise, the remote device has been indeed disconnected
// and audio should be suspended before switching the output to the local device.
- boolean suppressNoisyIntent = !forceStopPlayingAudio
- && (getConnectionState(previousActiveDevice)
- == BluetoothProfile.STATE_CONNECTED);
- Log.i(TAG, "removeActiveDevice: suppressNoisyIntent=" + suppressNoisyIntent);
- mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
- BluetoothProfile.A2DP, suppressNoisyIntent, -1);
+ boolean stopAudio = forceStopPlayingAudio || (getConnectionState(previousActiveDevice)
+ != BluetoothProfile.STATE_CONNECTED);
+ mAudioManager.handleBluetoothActiveDeviceChanged(null, previousActiveDevice,
+ BtProfileConnectionInfo.a2dpInfo(!stopAudio, -1));
synchronized (mStateMachines) {
// Make sure the Active device in native layer is set to null and audio is off
@@ -552,13 +550,6 @@
// This needs to happen before we inform the audio manager that the device
// disconnected. Please see comment in updateAndBroadcastActiveDevice() for why.
updateAndBroadcastActiveDevice(device);
- // Make sure the Audio Manager knows the previous Active device is disconnected,
- // and the new Active device is connected.
- if (previousActiveDevice != null) {
- mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
- BluetoothProfile.A2DP, true, -1);
- }
BluetoothDevice newActiveDevice = null;
synchronized (mStateMachines) {
@@ -583,13 +574,13 @@
rememberedVolume = mFactory.getAvrcpTargetService()
.getRememberedVolumeForDevice(newActiveDevice);
}
- mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- newActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
- true, rememberedVolume);
- // Inform the Audio Service about the codec configuration
+ // Make sure the Audio Manager knows the previous Active device is disconnected,
+ // and the new Active device is connected.
+ // And 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.
- mAudioManager.handleBluetoothA2dpDeviceConfigChange(newActiveDevice);
+ mAudioManager.handleBluetoothActiveDeviceChanged(newActiveDevice, previousActiveDevice,
+ BtProfileConnectionInfo.a2dpInfo(true, rememberedVolume));
}
return true;
}
@@ -972,8 +963,13 @@
// Inform the Audio Service about the codec configuration change,
// so the Audio Service can reset accordingly the audio feeding
// parameters in the Audio HAL to the Bluetooth stack.
- if (isActiveDevice(device) && !sameAudioFeedingParameters) {
- mAudioManager.handleBluetoothA2dpDeviceConfigChange(device);
+ // Until we are able to detect from device_port_proxy if the config has changed or not,
+ // the Bluetooth stack can only disable the audio session and need to ask audioManager to
+ // restart the session even if feeding parameter are the same. (sameAudioFeedingParameters
+ // is left unused until there)
+ if (isActiveDevice(device)) {
+ mAudioManager.handleBluetoothActiveDeviceChanged(device, device,
+ BtProfileConnectionInfo.a2dpInfo(false, -1));
}
}
diff --git a/src/com/android/bluetooth/btservice/JniCallbacks.java b/src/com/android/bluetooth/btservice/JniCallbacks.java
index f00353a..03d9264 100644
--- a/src/com/android/bluetooth/btservice/JniCallbacks.java
+++ b/src/com/android/bluetooth/btservice/JniCallbacks.java
@@ -67,6 +67,10 @@
mBondStateMachine.bondStateChangeCallback(status, address, newState, hciReason);
}
+ void addressConsolidateCallback(byte[] mainAddress, byte[] secondaryAddress) {
+ mRemoteDevices.addressConsolidateCallback(mainAddress, secondaryAddress);
+ }
+
void aclStateChangeCallback(int status, byte[] address, int newState,
int transportLinkType, int hciReason) {
mRemoteDevices.aclStateChangeCallback(status, address, newState,
diff --git a/src/com/android/bluetooth/btservice/RemoteDevices.java b/src/com/android/bluetooth/btservice/RemoteDevices.java
index 2f0a8d4..e6812ee 100644
--- a/src/com/android/bluetooth/btservice/RemoteDevices.java
+++ b/src/com/android/bluetooth/btservice/RemoteDevices.java
@@ -667,6 +667,19 @@
}
}
+ void addressConsolidateCallback(byte[] mainAddress, byte[] secondaryAddress) {
+ BluetoothDevice device = getDevice(mainAddress);
+ if (device == null) {
+ errorLog("addressConsolidateCallback: device is NULL, address="
+ + Utils.getAddressStringFromByte(mainAddress) + ", secondaryAddress="
+ + Utils.getAddressStringFromByte(secondaryAddress));
+ return;
+ }
+ Log.d(TAG, "addressConsolidateCallback device: " + device + ", secondaryAddress:"
+ + Utils.getAddressStringFromByte(secondaryAddress));
+ // TODO
+ }
+
void aclStateChangeCallback(int status, byte[] address, int newState,
int transportLinkType, int hciReason) {
BluetoothDevice device = getDevice(address);
diff --git a/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java b/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
index 7fe1345..3b8a9bb 100644
--- a/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
+++ b/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
@@ -599,7 +599,9 @@
for (Map.Entry<Executor, IBluetoothCsipSetCoordinatorCallback> entry :
mCallbacks.get(uuid).entrySet()) {
- Log.e(TAG, " executing " + uuid + " " + entry.getKey());
+ if (DBG) {
+ Log.d(TAG, " executing " + uuid + " " + entry.getKey());
+ }
try {
executeCallback(entry.getKey(), entry.getValue(), device, groupId);
} catch (RemoteException e) {
diff --git a/src/com/android/bluetooth/hearingaid/HearingAidService.java b/src/com/android/bluetooth/hearingaid/HearingAidService.java
index 2ab14cb..6199ff1 100644
--- a/src/com/android/bluetooth/hearingaid/HearingAidService.java
+++ b/src/com/android/bluetooth/hearingaid/HearingAidService.java
@@ -30,6 +30,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
+import android.media.BtProfileConnectionInfo;
import android.os.HandlerThread;
import android.os.ParcelUuid;
import android.util.Log;
@@ -694,30 +695,15 @@
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
- if (device == null) {
- if (DBG) {
- Log.d(TAG, "Set Hearing Aid audio to disconnected");
- }
- boolean suppressNoisyIntent =
- (getConnectionState(mPreviousAudioDevice) == BluetoothProfile.STATE_CONNECTED);
- mAudioManager.setBluetoothHearingAidDeviceConnectionState(
- mPreviousAudioDevice, BluetoothProfile.STATE_DISCONNECTED,
- suppressNoisyIntent, 0);
- mPreviousAudioDevice = null;
- } else {
- if (DBG) {
- Log.d(TAG, "Set Hearing Aid audio to connected");
- }
- if (mPreviousAudioDevice != null) {
- mAudioManager.setBluetoothHearingAidDeviceConnectionState(
- mPreviousAudioDevice, BluetoothProfile.STATE_DISCONNECTED,
- true, 0);
- }
- mAudioManager.setBluetoothHearingAidDeviceConnectionState(
- device, BluetoothProfile.STATE_CONNECTED,
- true, 0);
- mPreviousAudioDevice = device;
+ boolean stopAudio = device == null
+ && (getConnectionState(mPreviousAudioDevice) != BluetoothProfile.STATE_CONNECTED);
+ if (DBG) {
+ Log.d(TAG, "Hearing Aid audio: " + mPreviousAudioDevice + " -> " + device
+ + ". Stop audio: " + stopAudio);
}
+ mAudioManager.handleBluetoothActiveDeviceChanged(device, mPreviousAudioDevice,
+ BtProfileConnectionInfo.hearingAidInfo(!stopAudio));
+ mPreviousAudioDevice = device;
}
// Remove state machine if the bonding for a device is removed
diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index bbc7878..369b5d5 100644
--- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -860,6 +860,11 @@
break;
}
case DEVICE_STATE_CHANGED:
+ if (mDeviceSilenced) {
+ stateLogW("DEVICE_STATE_CHANGED: " + mDevice
+ + " is silenced, skip notify state changed.");
+ break;
+ }
mNativeInterface.notifyDeviceStatus(mDevice, (HeadsetDeviceState) message.obj);
break;
case SEND_CCLC_RESPONSE:
diff --git a/src/com/android/bluetooth/le_audio/LeAudioService.java b/src/com/android/bluetooth/le_audio/LeAudioService.java
index da5828e..77bc559 100644
--- a/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -23,16 +23,12 @@
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
-import android.bluetooth.BluetoothVolumeControl;
-import android.bluetooth.IBluetoothCsipSetCoordinator;
-import android.bluetooth.IBluetoothCsipSetCoordinatorCallback;
import android.bluetooth.IBluetoothLeAudio;
-import android.content.AttributionSource;
import android.bluetooth.IBluetoothVolumeControl;
+import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -40,6 +36,7 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.media.AudioManager;
+import android.media.BtProfileConnectionInfo;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.ParcelUuid;
@@ -51,7 +48,6 @@
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ServiceFactory;
import com.android.bluetooth.btservice.storage.DatabaseManager;
-import com.android.bluetooth.csip.CsipSetCoordinatorService;
import com.android.bluetooth.mcp.McpService;
import com.android.bluetooth.vc.VolumeControlService;
import com.android.internal.annotations.VisibleForTesting;
@@ -93,8 +89,8 @@
private AdapterService mAdapterService;
private DatabaseManager mDatabaseManager;
private HandlerThread mStateMachinesThread;
- private BluetoothDevice mPreviousAudioOutDevice;
- private BluetoothDevice mPreviousAudioInDevice;
+ private BluetoothDevice mActiveAudioOutDevice;
+ private BluetoothDevice mActiveAudioInDevice;
ServiceFactory mServiceFactory = new ServiceFactory();
LeAudioNativeInterface mLeAudioNativeInterface;
@@ -116,7 +112,6 @@
private final Map<BluetoothDevice, LeAudioStateMachine> mStateMachines = new HashMap<>();
private final Map<BluetoothDevice, Integer> mDeviceGroupIdMap = new ConcurrentHashMap<>();
- private final Map<BluetoothDevice, Integer> mSetMemberAvailable = new ConcurrentHashMap<>();
private int mActiveDeviceGroupId = LE_AUDIO_GROUP_ID_INVALID;
private final int mContextSupportingInputAudio =
BluetoothLeAudio.CONTEXT_TYPE_COMMUNICATION |
@@ -137,50 +132,6 @@
private BroadcastReceiver mBondStateChangedReceiver;
private BroadcastReceiver mConnectionStateChangedReceiver;
- class MyCsipSetCoordinatorCallbacks extends IBluetoothCsipSetCoordinatorCallback.Stub {
- @Override
- public void onCsisSetMemberAvailable(BluetoothDevice device, int groupId) {
- synchronized (LeAudioService.this) {
- LeAudioService.this.setMemberAvailable(device, groupId);
- }
- }
- };
-
- private volatile MyCsipSetCoordinatorCallbacks mCsipSetCoordinatorCallback =
- new MyCsipSetCoordinatorCallbacks();
- private volatile IBluetoothCsipSetCoordinator mCsipSetCoordinatorProxy;
- private final ServiceConnection mCsipSetCoordinatorProxyConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) {
- Log.d(TAG, "CsisClientProxy connected");
- }
- synchronized (LeAudioService.this) {
- mCsipSetCoordinatorProxy = IBluetoothCsipSetCoordinator.Stub.asInterface(service);
- CsipSetCoordinatorService mCsipSetCoordinatorService =
- CsipSetCoordinatorService.getCsipSetCoordinatorService();
- if (mCsipSetCoordinatorService == null) {
- Log.e(TAG, "CsisClientService is null on LeAudioService starts");
- return;
- }
- mCsipSetCoordinatorService.registerCsisMemberObserver(
- LeAudioService.this.getMainExecutor(),
- BluetoothUuid.CAP,
- mCsipSetCoordinatorCallback);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) {
- Log.d(TAG, "CsisClientProxy disconnected");
- }
- synchronized (LeAudioService.this) {
- mCsipSetCoordinatorProxy = null;
- }
- }
- };
-
private volatile IBluetoothVolumeControl mVolumeControlProxy;
VolumeControlService mVolumeControlService = null;
private final ServiceConnection mVolumeControlProxyConnection = new ServiceConnection() {
@@ -245,7 +196,6 @@
mStateMachinesThread.start();
mDeviceGroupIdMap.clear();
- mSetMemberAvailable.clear();
mGroupDescriptors.clear();
// Setup broadcast receivers
@@ -258,8 +208,6 @@
mConnectionStateChangedReceiver = new ConnectionStateChangedReceiver();
registerReceiver(mConnectionStateChangedReceiver, filter);
- /* Bind Csis Service */
- bindCsisClientService();
/* Bind Volume control service */
bindVolumeControlService();
@@ -317,7 +265,6 @@
}
mDeviceGroupIdMap.clear();
- mSetMemberAvailable.clear();
mGroupDescriptors.clear();
if (mStateMachinesThread != null) {
@@ -329,7 +276,6 @@
mAdapterService = null;
mAudioManager = null;
- unbindCsisClientService();
unbindVolumeControlService();
return true;
}
@@ -382,30 +328,6 @@
}
}
- private void bindCsisClientService() {
- synchronized (mCsipSetCoordinatorProxyConnection) {
- Intent intent = new Intent(IBluetoothCsipSetCoordinator.class.getName());
- ComponentName comp = intent.resolveSystemService(getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !bindService(intent, mCsipSetCoordinatorProxyConnection, 0)) {
- Log.wtf(TAG, "Could not bind to IBluetoothCsisClient Service with " +
- intent);
- }
- }
- }
- private void unbindCsisClientService() {
- synchronized (mCsipSetCoordinatorProxyConnection) {
- if (mCsipSetCoordinatorProxy != null) {
- if (DBG) {
- Log.d(TAG, "Unbinding CsisClientProxyConnection");
- }
- mCsipSetCoordinatorProxy = null;
- // Synchronization should make sure unbind can be successful
- unbindService(mCsipSetCoordinatorProxyConnection);
- }
- }
- }
-
public boolean connect(BluetoothDevice device) {
if (DBG) {
Log.d(TAG, "connect(): " + device);
@@ -651,9 +573,9 @@
return null;
}
- private void updateActiveInDevice(BluetoothDevice device, Integer groupId, Integer oldActiveContexts,
- Integer newActiveContexts) {
-
+ private boolean updateActiveInDevice(BluetoothDevice device, Integer groupId,
+ Integer oldActiveContexts,
+ Integer newActiveContexts) {
Integer oldSupportedAudioDirections =
getAudioDirectionsFromActiveContextsMap(oldActiveContexts);
Integer newSupportedAudioDirections =
@@ -664,51 +586,47 @@
boolean newSupportedByDeviceInput = (newSupportedAudioDirections
& AUDIO_DIRECTION_INPUT_BIT) != 0;
- if (device != null && mPreviousAudioInDevice != null) {
- int previousGroupId = getGroupId(mPreviousAudioInDevice);
+ if (device != null && mActiveAudioInDevice != null) {
+ int previousGroupId = getGroupId(mActiveAudioInDevice);
if (previousGroupId == groupId) {
/* This is thes same group as aleady notified to the system.
* Therefore do not change the device we have connected to the group,
* unless, previous one is disconnected now
*/
- if (mPreviousAudioInDevice.isConnected())
- device = mPreviousAudioInDevice;
+ if (mActiveAudioInDevice.isConnected()) {
+ device = mActiveAudioInDevice;
+ }
}
}
- /* Disconnect input:
- * - If active input device changed (to none or any)
- * - If device stops supporting input
+ BluetoothDevice previousInDevice = mActiveAudioInDevice;
+
+ /*
+ * Do not update input if neither previous nor current device support input
*/
- boolean inActiveDeviceReplace = (device != mPreviousAudioInDevice);
- if (inActiveDeviceReplace && (mPreviousAudioInDevice != null)) {
- mAudioManager.setBluetoothLeAudioInDeviceConnectionState(
- mPreviousAudioInDevice, BluetoothProfile.STATE_DISCONNECTED);
+ if (!oldSupportedByDeviceInput && !newSupportedByDeviceInput) {
+ Log.d(TAG, "updateActiveInDevice: Device does not support input.");
+ return false;
}
- mPreviousAudioInDevice = device;
-
- if (device == null) {
- Log.d(TAG, " device is null.");
- return;
- }
-
- if (inActiveDeviceReplace == false ||
- (oldSupportedByDeviceInput == newSupportedByDeviceInput)) {
- Log.d(TAG, " Nothing to do.");
- return;
- }
-
- /* Connect input:
- * - If active input device changed
- * - If device starts support input
+ /*
+ * Update input if:
+ * - Device changed
+ * OR
+ * - Device stops / starts supporting input
*/
- mAudioManager.setBluetoothLeAudioInDeviceConnectionState(
- device, BluetoothProfile.STATE_CONNECTED);
-
+ if (!Objects.equals(device, previousInDevice)
+ || (oldSupportedByDeviceInput != newSupportedByDeviceInput)) {
+ mActiveAudioInDevice = newSupportedByDeviceInput ? device : null;
+ mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioInDevice, previousInDevice,
+ BtProfileConnectionInfo.leAudio(false, false));
+ return true;
+ }
+ Log.d(TAG, "updateActiveInDevice: Nothing to do.");
+ return false;
}
- private void updateActiveOutDevice(BluetoothDevice device, Integer groupId,
+ private boolean updateActiveOutDevice(BluetoothDevice device, Integer groupId,
Integer oldActiveContexts,
Integer newActiveContexts) {
Integer oldSupportedAudioDirections =
@@ -721,52 +639,46 @@
boolean newSupportedByDeviceOutput = (newSupportedAudioDirections
& AUDIO_DIRECTION_OUTPUT_BIT) != 0;
-
- if (device != null && mPreviousAudioOutDevice != null) {
- int previousGroupId = getGroupId(mPreviousAudioOutDevice);
+ if (device != null && mActiveAudioOutDevice != null) {
+ int previousGroupId = getGroupId(mActiveAudioOutDevice);
if (previousGroupId == groupId) {
/* This is the same group as already notified to the system.
* Therefore do not change the device we have connected to the group,
* unless, previous one is disconnected now
*/
- if (mPreviousAudioOutDevice.isConnected())
- device = mPreviousAudioOutDevice;
+ if (mActiveAudioOutDevice.isConnected()) {
+ device = mActiveAudioOutDevice;
+ }
}
}
- /* Disconnect output:
- * - If active output device changed (to none or any)
- * - If device stops supporting output
+ BluetoothDevice previousOutDevice = mActiveAudioOutDevice;
+
+ /*
+ * Do not update output if neither previous nor current device support output
*/
- boolean outActiveDeviceReplace = (device != mPreviousAudioOutDevice);
- if (outActiveDeviceReplace && (mPreviousAudioOutDevice != null)) {
- boolean suppressNoisyIntent =
- (getConnectionState(mPreviousAudioOutDevice) ==
- BluetoothProfile.STATE_CONNECTED);
- mAudioManager.setBluetoothLeAudioOutDeviceConnectionState(
- mPreviousAudioOutDevice, BluetoothProfile.STATE_DISCONNECTED,
- suppressNoisyIntent);
+ if (!oldSupportedByDeviceOutput && !newSupportedByDeviceOutput) {
+ Log.d(TAG, "updateActiveOutDevice: Device does not support output.");
+ return false;
}
- mPreviousAudioOutDevice = device;
-
- if (device == null) {
- Log.d(TAG, " device is null.");
- return;
- }
-
- if (outActiveDeviceReplace == false ||
- (oldSupportedByDeviceOutput == newSupportedByDeviceOutput)) {
- Log.d(TAG, " Nothing to do.");
- return;
- }
-
- /* Connect output:
- * - If active output device changed
- * - If device starts support output
+ /*
+ * Update output if:
+ * - Device changed
+ * OR
+ * - Device stops / starts supporting output
*/
- mAudioManager.setBluetoothLeAudioOutDeviceConnectionState(
- device, BluetoothProfile.STATE_CONNECTED, true);
+ if (!Objects.equals(device, previousOutDevice)
+ || (oldSupportedByDeviceOutput != newSupportedByDeviceOutput)) {
+ mActiveAudioOutDevice = newSupportedByDeviceOutput ? device : null;
+ final boolean suppressNoisyIntent = (mActiveAudioOutDevice != null)
+ || (getConnectionState(previousOutDevice) == BluetoothProfile.STATE_CONNECTED);
+ mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioOutDevice,
+ previousOutDevice, BtProfileConnectionInfo.leAudio(suppressNoisyIntent, true));
+ return true;
+ }
+ Log.d(TAG, "updateActiveOutDevice: Nothing to do.");
+ return false;
}
/**
@@ -782,14 +694,18 @@
if (isActive)
device = getFirstDeviceFromGroup(groupId);
- updateActiveOutDevice(device, groupId, oldActiveContexts, newActiveContexts);
- updateActiveInDevice(device, groupId, oldActiveContexts, newActiveContexts);
+ boolean outReplaced =
+ updateActiveOutDevice(device, groupId, oldActiveContexts, newActiveContexts);
+ boolean inReplaced =
+ updateActiveInDevice(device, groupId, oldActiveContexts, newActiveContexts);
- Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mPreviousAudioOutDevice);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- sendBroadcast(intent, BLUETOOTH_CONNECT);
+ if (outReplaced || inReplaced) {
+ Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mActiveAudioOutDevice);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ sendBroadcast(intent, BLUETOOTH_CONNECT);
+ }
}
/**
@@ -1097,11 +1013,6 @@
return;
}
- /* Remove bonded set member from outstanding list */
- if (mSetMemberAvailable.containsKey(device)) {
- mSetMemberAvailable.remove(device);
- }
-
int groupId = getGroupId(device);
if (groupId != LE_AUDIO_GROUP_ID_INVALID) {
/* In case device is still in the group, let's remove it */
@@ -1239,22 +1150,6 @@
}
}
- synchronized void setMemberAvailable(BluetoothDevice device, int groupId) {
- if (device == null) {
- Log.e(TAG, "unexpected invocation. device=" + device);
- return;
- }
-
- if (mSetMemberAvailable.containsKey(device)) {
- if (DBG) {
- Log.d(TAG, "Device " + device + " is already notified- drop it");
- }
- return;
- }
-
- mSetMemberAvailable.put(device, groupId);
- }
-
/**
* Check whether can connect to a peer device.
* The check considers a number of factors during the evaluation.
diff --git a/src/com/android/bluetooth/mcp/MediaControlProfile.java b/src/com/android/bluetooth/mcp/MediaControlProfile.java
index 724b969..fb72a36 100644
--- a/src/com/android/bluetooth/mcp/MediaControlProfile.java
+++ b/src/com/android/bluetooth/mcp/MediaControlProfile.java
@@ -192,7 +192,7 @@
}
}
- private void removePendingStateRequests(Set<PlayerStateField> fields) {
+ private synchronized void removePendingStateRequests(Set<PlayerStateField> fields) {
if (mPendingStateRequest == null) return;
for (PlayerStateField field : fields) {
@@ -263,7 +263,9 @@
@Override
public void onPlayerStateRequest(PlayerStateField[] stateFields) {
- mPendingStateRequest = Stream.of(stateFields).collect(Collectors.toList());
+ synchronized (this) {
+ mPendingStateRequest = Stream.of(stateFields).collect(Collectors.toList());
+ }
processPendingPlayerStateRequest();
}
@@ -513,70 +515,76 @@
Map<PlayerStateField, Object> handled_request_map = new HashMap<>();
- if (mPendingStateRequest == null) return;
-
- // Notice: If we are unable to provide the requested field it will stay queued until we are
- // able to provide it.
- for (PlayerStateField settings_field : mPendingStateRequest) {
- switch (settings_field) {
- case PLAYBACK_STATE:
- if (mCurrentData.state != null) {
- handled_request_map.put(settings_field,
- playerState2McsState(mCurrentData.state.getState()));
- }
- break;
- case TRACK_DURATION:
- handled_request_map.put(settings_field, getCurrentTrackDuration());
- break;
- case PLAYBACK_SPEED:
- if (mCurrentData.state != null) {
- handled_request_map.put(
- settings_field, mCurrentData.state.getPlaybackSpeed());
- }
- break;
- case SEEKING_SPEED:
- float seeking_speed = 1.0f;
- if (mCurrentData.state != null) {
- if ((mCurrentData.state.getState() == PlaybackState.STATE_FAST_FORWARDING)
- || (mCurrentData.state.getState()
- == PlaybackState.STATE_REWINDING)) {
- seeking_speed = mCurrentData.state.getPlaybackSpeed();
+ synchronized (this) {
+ if (mPendingStateRequest == null) return;
+ // Notice: If we are unable to provide the requested field it will stay queued until we
+ // are able to provide it.
+ for (PlayerStateField settings_field : mPendingStateRequest) {
+ switch (settings_field) {
+ case PLAYBACK_STATE:
+ if (mCurrentData.state != null) {
+ handled_request_map.put(settings_field,
+ playerState2McsState(mCurrentData.state.getState()));
}
- }
+ break;
+ case TRACK_DURATION:
+ handled_request_map.put(settings_field, getCurrentTrackDuration());
+ break;
+ case PLAYBACK_SPEED:
+ if (mCurrentData.state != null) {
+ handled_request_map.put(
+ settings_field, mCurrentData.state.getPlaybackSpeed());
+ }
+ break;
+ case SEEKING_SPEED:
+ float seeking_speed = 1.0f;
+ if (mCurrentData.state != null) {
+ if ((mCurrentData.state.getState()
+ == PlaybackState.STATE_FAST_FORWARDING)
+ || (mCurrentData.state.getState()
+ == PlaybackState.STATE_REWINDING)) {
+ seeking_speed = mCurrentData.state.getPlaybackSpeed();
+ }
+ }
- handled_request_map.put(settings_field, seeking_speed);
- break;
- case PLAYING_ORDER:
- handled_request_map.put(settings_field, getCurrentPlayerPlayingOrder());
- break;
- case TRACK_POSITION:
- if (mCurrentData.state != null) {
- handled_request_map.put(
- settings_field, getDriftCorrectedTrackPosition(mCurrentData.state));
- }
- break;
- case PLAYER_NAME:
- String player_name = getCurrentPlayerName();
- if (player_name != null) handled_request_map.put(settings_field, player_name);
- break;
- case ICON_URL:
- // Not implemented
- break;
- case ICON_OBJ_ID:
- // TODO: Implement once we have Object Transfer Service
- break;
- case PLAYING_ORDER_SUPPORTED:
- Integer playing_order = getSupportedPlayingOrder();
- if (playing_order != null) {
- handled_request_map.put(settings_field, playing_order.intValue());
- }
- break;
- case OPCODES_SUPPORTED:
- if (mCurrentData.state != null) {
- handled_request_map.put(settings_field,
- playerActions2McsSupportedOpcodes(mCurrentData.state.getActions()));
- }
- break;
+ handled_request_map.put(settings_field, seeking_speed);
+ break;
+ case PLAYING_ORDER:
+ handled_request_map.put(settings_field, getCurrentPlayerPlayingOrder());
+ break;
+ case TRACK_POSITION:
+ if (mCurrentData.state != null) {
+ handled_request_map.put(
+ settings_field, getDriftCorrectedTrackPosition(
+ mCurrentData.state));
+ }
+ break;
+ case PLAYER_NAME:
+ String player_name = getCurrentPlayerName();
+ if (player_name != null) {
+ handled_request_map.put(settings_field, player_name);
+ }
+ break;
+ case ICON_URL:
+ // Not implemented
+ break;
+ case ICON_OBJ_ID:
+ // TODO: Implement once we have Object Transfer Service
+ break;
+ case PLAYING_ORDER_SUPPORTED:
+ Integer playing_order = getSupportedPlayingOrder();
+ if (playing_order != null) {
+ handled_request_map.put(settings_field, playing_order.intValue());
+ }
+ break;
+ case OPCODES_SUPPORTED:
+ if (mCurrentData.state != null) {
+ handled_request_map.put(settings_field,
+ playerActions2McsSupportedOpcodes(
+ mCurrentData.state.getActions()));
+ }
+ break;
+ }
}
}
@@ -588,10 +596,12 @@
}
if (DBG) {
- if (mPendingStateRequest != null && !mPendingStateRequest.isEmpty()) {
- Log.w(TAG, "MCS service state fields left unhandled: ");
- for (PlayerStateField item : mPendingStateRequest) {
- Log.w(TAG, " > " + item);
+ synchronized (this) {
+ if (mPendingStateRequest != null && !mPendingStateRequest.isEmpty()) {
+ Log.w(TAG, "MCS service state fields left unhandled: ");
+ for (PlayerStateField item : mPendingStateRequest) {
+ Log.w(TAG, " > " + item);
+ }
}
}
}
diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapUtils.java b/src/com/android/bluetooth/pbap/BluetoothPbapUtils.java
index 7691c3f..df78600 100644
--- a/src/com/android/bluetooth/pbap/BluetoothPbapUtils.java
+++ b/src/com/android/bluetooth/pbap/BluetoothPbapUtils.java
@@ -49,7 +49,18 @@
private static final String TAG = "BluetoothPbapUtils";
private static final boolean V = BluetoothPbapService.VERBOSE;
+ // Filter constants from Bluetooth PBAP specification
private static final int FILTER_PHOTO = 3;
+ private static final int FILTER_BDAY = 4;
+ private static final int FILTER_ADDRESS = 5;
+ private static final int FILTER_LABEL = 6;
+ private static final int FILTER_EMAIL = 8;
+ private static final int FILTER_MAILER = 9;
+ private static final int FILTER_ORG = 16;
+ private static final int FILTER_NOTE = 17;
+ private static final int FILTER_SOUND = 19;
+ private static final int FILTER_URL = 20;
+ private static final int FILTER_NICKNAME = 23;
private static final long QUERY_CONTACT_RETRY_INTERVAL = 4000;
@@ -122,6 +133,40 @@
}
vType |= VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT;
}
+ if (hasFilter(filter)) {
+ if (!isFilterBitSet(filter, FILTER_ADDRESS) && !isFilterBitSet(filter, FILTER_LABEL)) {
+ Log.i(TAG, "Excluding addresses from VCardComposer...");
+ vType |= VCardConfig.FLAG_REFRAIN_ADDRESS_EXPORT;
+ }
+ if (!isFilterBitSet(filter, FILTER_EMAIL) && !isFilterBitSet(filter, FILTER_MAILER)) {
+ Log.i(TAG, "Excluding email addresses from VCardComposer...");
+ vType |= VCardConfig.FLAG_REFRAIN_EMAIL_EXPORT;
+ }
+ if (!isFilterBitSet(filter, FILTER_ORG)) {
+ Log.i(TAG, "Excluding organization from VCardComposer...");
+ vType |= VCardConfig.FLAG_REFRAIN_ORGANIZATION_EXPORT;
+ }
+ if (!isFilterBitSet(filter, FILTER_URL)) {
+ Log.i(TAG, "Excluding URLS from VCardComposer...");
+ vType |= VCardConfig.FLAG_REFRAIN_WEBSITES_EXPORT;
+ }
+ if (!isFilterBitSet(filter, FILTER_NOTE)) {
+ Log.i(TAG, "Excluding notes from VCardComposer...");
+ vType |= VCardConfig.FLAG_REFRAIN_NOTES_EXPORT;
+ }
+ if (!isFilterBitSet(filter, FILTER_NICKNAME)) {
+ Log.i(TAG, "Excluding nickname from VCardComposer...");
+ vType |= VCardConfig.FLAG_REFRAIN_NICKNAME_EXPORT;
+ }
+ if (!isFilterBitSet(filter, FILTER_SOUND)) {
+ Log.i(TAG, "Excluding phonetic name from VCardComposer...");
+ vType |= VCardConfig.FLAG_REFRAIN_PHONETIC_NAME_EXPORT;
+ }
+ if (!isFilterBitSet(filter, FILTER_BDAY)) {
+ Log.i(TAG, "Excluding birthday from VCardComposer...");
+ vType |= VCardConfig.FLAG_REFRAIN_EVENTS_EXPORT;
+ }
+ }
return new VCardComposer(ctx, vType, true);
}
diff --git a/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java b/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
index dab3099..b2996f9 100644
--- a/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
+++ b/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
@@ -28,6 +28,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
+import android.media.BtProfileConnectionInfo;
import android.os.Looper;
import android.os.ParcelUuid;
@@ -521,9 +522,8 @@
Assert.assertTrue(mService.getConnectedDevices().contains(mRightDevice));
// Verify the audio is routed to Hearing Aid Profile
- verify(mAudioManager).setBluetoothHearingAidDeviceConnectionState(
- any(BluetoothDevice.class), eq(BluetoothProfile.STATE_CONNECTED),
- eq(true), eq(0));
+ verify(mAudioManager).handleBluetoothActiveDeviceChanged(
+ any(BluetoothDevice.class), eq(null), any(BtProfileConnectionInfo.class));
// Send a disconnect request
Assert.assertTrue("Disconnect failed", mService.disconnect(mLeftDevice));
@@ -570,9 +570,8 @@
Assert.assertFalse(mService.getConnectedDevices().contains(mRightDevice));
// Verify the audio is not routed to Hearing Aid Profile
- verify(mAudioManager).setBluetoothHearingAidDeviceConnectionState(
- any(BluetoothDevice.class), eq(BluetoothProfile.STATE_DISCONNECTED),
- eq(false), eq(0));
+ verify(mAudioManager).handleBluetoothActiveDeviceChanged(
+ eq(null), any(BluetoothDevice.class), any(BtProfileConnectionInfo.class));
}
/**
diff --git a/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java b/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
index 43ddfcc..db66f00 100644
--- a/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
+++ b/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
@@ -47,11 +47,13 @@
import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;
+import com.android.bluetooth.R;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import org.junit.After;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -98,6 +100,9 @@
@Before
public void setUp() throws Exception {
mTargetContext = InstrumentationRegistry.getTargetContext();
+ Assume.assumeTrue("Ignore test when LeAudioService is not enabled",
+ mTargetContext.getResources().getBoolean(R.bool.profile_supported_le_audio));
+
// Set up mocks and test assets
MockitoAnnotations.initMocks(this);
@@ -149,6 +154,10 @@
@After
public void tearDown() throws Exception {
+ if (!mTargetContext.getResources().getBoolean(R.bool.profile_supported_le_audio)) {
+ return;
+ }
+
mBondedDevices.clear();
mGroupIntentQueue.clear();
stopService();