summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl2
-rw-r--r--android/app/src/com/android/bluetooth/a2dp/A2dpService.java6
-rw-r--r--android/app/src/com/android/bluetooth/bass_client/BassClientService.java39
-rw-r--r--android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java1
-rw-r--r--android/app/src/com/android/bluetooth/btservice/RemoteDevices.java324
-rw-r--r--android/app/src/com/android/bluetooth/gatt/AdvertiseBinder.kt63
-rw-r--r--android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java248
-rw-r--r--android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java51
-rw-r--r--android/app/src/com/android/bluetooth/gatt/GattService.java11
-rw-r--r--android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java9
-rw-r--r--android/app/src/com/android/bluetooth/le_scan/ScanController.java15
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java46
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java6
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java9
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java41
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java22
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanControllerTest.java49
-rw-r--r--android/pandora/server/src/A2dp.kt9
-rw-r--r--flags/Android.bp1
-rw-r--r--flags/BUILD.gn1
-rw-r--r--flags/bta_dm.aconfig7
-rw-r--r--flags/gap.aconfig10
-rw-r--r--flags/hfp.aconfig10
-rw-r--r--flags/le_scanning.aconfig12
-rw-r--r--framework/api/system-current.txt2
-rw-r--r--framework/java/android/bluetooth/BluetoothA2dp.java15
-rw-r--r--framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt45
-rw-r--r--offload/hci/data.rs2
-rw-r--r--system/bta/dm/bta_dm_disc.cc5
-rw-r--r--system/bta/hh/bta_hh_headtracker.cc5
-rw-r--r--system/bta/le_audio/state_machine.cc2
-rw-r--r--system/bta/test/bta_disc_test.cc56
-rw-r--r--system/btif/include/btif_dm.h1
-rw-r--r--system/btif/include/btif_storage.h1
-rw-r--r--system/btif/src/btif_core.cc21
-rw-r--r--system/btif/src/btif_dm.cc155
-rw-r--r--system/btif/src/btif_storage.cc74
-rw-r--r--system/btif/src/btif_util.cc1
-rw-r--r--system/btif/test/btif_core_test.cc1
-rw-r--r--system/gd/hci/le_advertising_manager.h4
-rw-r--r--system/gd/storage/config_keys.h1
-rw-r--r--system/include/hardware/bluetooth.h10
-rw-r--r--system/test/headless/property.cc4
-rw-r--r--system/test/headless/property.h1
44 files changed, 883 insertions, 515 deletions
diff --git a/android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl b/android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl
index bcfc95585d..fa5d1362ac 100644
--- a/android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl
+++ b/android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl
@@ -53,7 +53,7 @@ interface IBluetoothA2dp {
boolean isA2dpPlaying(in BluetoothDevice device, in AttributionSource attributionSource);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)")
List<BluetoothCodecType> getSupportedCodecTypes();
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true)")
BluetoothCodecStatus getCodecStatus(in BluetoothDevice device, in AttributionSource attributionSource);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true)")
oneway void setCodecConfigPreference(in BluetoothDevice device, in BluetoothCodecConfig codecConfig, in AttributionSource attributionSource);
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
index 136a0d4621..100076127b 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -50,7 +50,6 @@ import android.media.BluetoothProfileConnectionInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Looper;
import android.sysprop.BluetoothProperties;
import android.util.Log;
@@ -1515,12 +1514,14 @@ public class A2dpService extends ProfileService {
@Override
public BluetoothCodecStatus getCodecStatus(
BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
A2dpService service = getServiceAndEnforceConnect(source);
if (service == null) {
return null;
}
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ Utils.enforceCdmAssociationIfNotBluetoothPrivileged(
+ service, service.mCompanionDeviceManager, source, device);
return service.getCodecStatus(device);
}
@@ -1530,6 +1531,7 @@ public class A2dpService extends ProfileService {
BluetoothDevice device,
BluetoothCodecConfig codecConfig,
AttributionSource source) {
+ requireNonNull(device);
A2dpService service = getServiceAndEnforceConnect(source);
if (service == null) {
return;
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
index 9b24ff60cc..0daef0175a 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -2499,6 +2499,28 @@ public class BassClientService extends ProfileService {
BluetoothDevice srcDevice = getDeviceForSyncHandle(syncHandle);
mSyncHandleToDeviceMap.remove(syncHandle);
int broadcastId = getBroadcastIdForSyncHandle(syncHandle);
+ if (leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator()) {
+ synchronized (mPendingSourcesToAdd) {
+ Iterator<AddSourceData> iterator = mPendingSourcesToAdd.iterator();
+ while (iterator.hasNext()) {
+ AddSourceData pendingSourcesToAdd = iterator.next();
+ if (pendingSourcesToAdd.mSourceMetadata.getBroadcastId() == broadcastId) {
+ iterator.remove();
+ }
+ }
+ }
+ synchronized (mSinksWaitingForPast) {
+ Iterator<Map.Entry<BluetoothDevice, Pair<Integer, Integer>>> iterator =
+ mSinksWaitingForPast.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<BluetoothDevice, Pair<Integer, Integer>> entry = iterator.next();
+ int broadcastIdForPast = entry.getValue().first;
+ if (broadcastId == broadcastIdForPast) {
+ iterator.remove();
+ }
+ }
+ }
+ }
mSyncHandleToBroadcastIdMap.remove(syncHandle);
if (srcDevice != null) {
mPeriodicAdvertisementResultMap.get(srcDevice).remove(broadcastId);
@@ -4054,7 +4076,10 @@ public class BassClientService extends ProfileService {
mUnicastSourceStreamStatus = Optional.of(status);
if (status == STATUS_LOCAL_STREAM_REQUESTED) {
- if (areReceiversReceivingOnlyExternalBroadcast(getConnectedDevices())) {
+ if ((leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator()
+ && hasPrimaryDeviceManagedExternalBroadcast())
+ || (!leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator()
+ && areReceiversReceivingOnlyExternalBroadcast(getConnectedDevices()))) {
if (leaudioBroadcastAssistantPeripheralEntrustment()) {
cacheSuspendingSources(BassConstants.INVALID_BROADCAST_ID);
List<Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice>> sourcesToStop =
@@ -4067,11 +4092,13 @@ public class BassClientService extends ProfileService {
suspendAllReceiversSourceSynchronization();
}
}
- for (Map.Entry<Integer, PauseType> entry : mPausedBroadcastIds.entrySet()) {
- Integer broadcastId = entry.getKey();
- PauseType pauseType = entry.getValue();
- if (pauseType != PauseType.HOST_INTENTIONAL) {
- suspendReceiversSourceSynchronization(broadcastId);
+ if (!leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator()) {
+ for (Map.Entry<Integer, PauseType> entry : mPausedBroadcastIds.entrySet()) {
+ Integer broadcastId = entry.getKey();
+ PauseType pauseType = entry.getValue();
+ if (pauseType != PauseType.HOST_INTENTIONAL) {
+ suspendReceiversSourceSynchronization(broadcastId);
+ }
}
}
} else if (status == STATUS_LOCAL_STREAM_SUSPENDED) {
diff --git a/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java b/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java
index 981b2db0c7..9ebe10d181 100644
--- a/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java
+++ b/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java
@@ -50,6 +50,7 @@ public final class AbstractionLayer {
static final int BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID = 0X16;
static final int BT_PROPERTY_REMOTE_MODEL_NUM = 0x17;
static final int BT_PROPERTY_LPP_OFFLOAD_FEATURES = 0x1B;
+ static final int BT_PROPERTY_UUIDS_LE = 0x1C;
public static final int BT_DEVICE_TYPE_BREDR = 0x01;
public static final int BT_DEVICE_TYPE_BLE = 0x02;
diff --git a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
index f0bad17e64..f297d905ac 100644
--- a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
+++ b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
@@ -345,7 +345,8 @@ public class RemoteDevices {
private String mModelName;
@VisibleForTesting int mBondState;
@VisibleForTesting int mDeviceType;
- @VisibleForTesting ParcelUuid[] mUuids;
+ @VisibleForTesting ParcelUuid[] mUuidsBrEdr;
+ @VisibleForTesting ParcelUuid[] mUuidsLe;
private BluetoothSinkAudioPolicy mAudioPolicy;
DeviceProperties() {
@@ -506,20 +507,71 @@ public class RemoteDevices {
}
/**
- * @return the mUuids
+ * @return the UUIDs on LE and Classic transport
*/
ParcelUuid[] getUuids() {
synchronized (mObject) {
- return mUuids;
+ /* When we bond dual mode device, and discover LE and Classic services, stack would
+ * return LE and Classic UUIDs separately, but Java apps expect them merged.
+ */
+ int combinedUuidsLength =
+ (mUuidsBrEdr != null ? mUuidsBrEdr.length : 0)
+ + (mUuidsLe != null ? mUuidsLe.length : 0);
+ if (!Flags.separateServiceStorage() || combinedUuidsLength == 0) {
+ return mUuidsBrEdr;
+ }
+
+ java.util.LinkedHashSet<ParcelUuid> result =
+ new java.util.LinkedHashSet<ParcelUuid>();
+ if (mUuidsBrEdr != null) {
+ for (ParcelUuid uuid : mUuidsBrEdr) {
+ result.add(uuid);
+ }
+ }
+
+ if (mUuidsLe != null) {
+ for (ParcelUuid uuid : mUuidsLe) {
+ result.add(uuid);
+ }
+ }
+
+ return result.toArray(new ParcelUuid[combinedUuidsLength]);
+ }
+ }
+
+ /**
+ * @return just classic transport UUIDS
+ */
+ ParcelUuid[] getUuidsBrEdr() {
+ synchronized (mObject) {
+ return mUuidsBrEdr;
+ }
+ }
+
+ /**
+ * @param uuids the mUuidsBrEdr to set
+ */
+ void setUuidsBrEdr(ParcelUuid[] uuids) {
+ synchronized (mObject) {
+ this.mUuidsBrEdr = uuids;
+ }
+ }
+
+ /**
+ * @return the mUuidsLe
+ */
+ ParcelUuid[] getUuidsLe() {
+ synchronized (mObject) {
+ return mUuidsLe;
}
}
/**
- * @param uuids the mUuids to set
+ * @param uuids the mUuidsLe to set
*/
- void setUuids(ParcelUuid[] uuids) {
+ void setUuidsLe(ParcelUuid[] uuids) {
synchronized (mObject) {
- this.mUuids = uuids;
+ this.mUuidsLe = uuids;
}
}
@@ -636,7 +688,8 @@ public class RemoteDevices {
cachedBluetoothDevice issued a connect using the local cached copy of uuids,
without waiting for the ACTION_UUID intent.
This was resulting in multiple calls to connect().*/
- mUuids = null;
+ mUuidsBrEdr = null;
+ mUuidsLe = null;
mAlias = null;
}
}
@@ -988,147 +1041,168 @@ public class RemoteDevices {
return;
}
+ boolean uuids_updated = false;
+
for (int j = 0; j < types.length; j++) {
type = types[j];
val = values[j];
- if (val.length > 0) {
- synchronized (mObject) {
- debugLog("Update property, device=" + bdDevice + ", type: " + type);
- switch (type) {
- case AbstractionLayer.BT_PROPERTY_BDNAME:
- final String newName = new String(val);
- if (newName.equals(deviceProperties.getName())) {
- debugLog("Skip name update for " + bdDevice);
- break;
- }
- deviceProperties.setName(newName);
- List<String> wordBreakdownList =
- MetricsLogger.getInstance().getWordBreakdownList(newName);
- if (SdkLevel.isAtLeastU()) {
- MetricsLogger.getInstance()
- .uploadRestrictedBluetothDeviceName(wordBreakdownList);
- }
- intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
- intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProperties.getName());
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mAdapterService.sendBroadcast(
- intent,
- BLUETOOTH_CONNECT,
- Utils.getTempBroadcastOptions().toBundle());
- debugLog("Remote device name is: " + deviceProperties.getName());
- break;
- case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
- deviceProperties.setAlias(bdDevice, new String(val));
- debugLog("Remote device alias is: " + deviceProperties.getAlias());
- break;
- case AbstractionLayer.BT_PROPERTY_BDADDR:
- deviceProperties.setAddress(val);
- debugLog(
- "Remote Address is:"
- + Utils.getRedactedAddressStringFromByte(val));
+ if (val.length == 0) {
+ continue;
+ }
+
+ synchronized (mObject) {
+ debugLog("Update property, device=" + bdDevice + ", type: " + type);
+ switch (type) {
+ case AbstractionLayer.BT_PROPERTY_BDNAME:
+ final String newName = new String(val);
+ if (newName.equals(deviceProperties.getName())) {
+ debugLog("Skip name update for " + bdDevice);
break;
- case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
- final int newBluetoothClass = Utils.byteArrayToInt(val);
- if (newBluetoothClass == deviceProperties.getBluetoothClass()) {
- debugLog(
- "Skip class update, device="
- + bdDevice
- + ", cod=0x"
- + Integer.toHexString(newBluetoothClass));
- break;
- }
- deviceProperties.setBluetoothClass(newBluetoothClass);
- intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
- intent.putExtra(
- BluetoothDevice.EXTRA_CLASS,
- new BluetoothClass(deviceProperties.getBluetoothClass()));
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mAdapterService.sendBroadcast(
- intent,
- BLUETOOTH_CONNECT,
- Utils.getTempBroadcastOptions().toBundle());
+ }
+ deviceProperties.setName(newName);
+ List<String> wordBreakdownList =
+ MetricsLogger.getInstance().getWordBreakdownList(newName);
+ if (SdkLevel.isAtLeastU()) {
+ MetricsLogger.getInstance()
+ .uploadRestrictedBluetothDeviceName(wordBreakdownList);
+ }
+ intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
+ intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProperties.getName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mAdapterService.sendBroadcast(
+ intent,
+ BLUETOOTH_CONNECT,
+ Utils.getTempBroadcastOptions().toBundle());
+ debugLog("Remote device name is: " + deviceProperties.getName());
+ break;
+ case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+ deviceProperties.setAlias(bdDevice, new String(val));
+ debugLog("Remote device alias is: " + deviceProperties.getAlias());
+ break;
+ case AbstractionLayer.BT_PROPERTY_BDADDR:
+ deviceProperties.setAddress(val);
+ debugLog(
+ "Remote Address is:" + Utils.getRedactedAddressStringFromByte(val));
+ break;
+ case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
+ final int newBluetoothClass = Utils.byteArrayToInt(val);
+ if (newBluetoothClass == deviceProperties.getBluetoothClass()) {
debugLog(
- "Remote class update, device="
+ "Skip class update, device="
+ bdDevice
+ ", cod=0x"
+ Integer.toHexString(newBluetoothClass));
break;
- case AbstractionLayer.BT_PROPERTY_UUIDS:
+ }
+ deviceProperties.setBluetoothClass(newBluetoothClass);
+ intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
+ intent.putExtra(
+ BluetoothDevice.EXTRA_CLASS,
+ new BluetoothClass(deviceProperties.getBluetoothClass()));
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mAdapterService.sendBroadcast(
+ intent,
+ BLUETOOTH_CONNECT,
+ Utils.getTempBroadcastOptions().toBundle());
+ debugLog(
+ "Remote class update, device="
+ + bdDevice
+ + ", cod=0x"
+ + Integer.toHexString(newBluetoothClass));
+ break;
+ case AbstractionLayer.BT_PROPERTY_UUIDS:
+ case AbstractionLayer.BT_PROPERTY_UUIDS_LE:
+ if (type == AbstractionLayer.BT_PROPERTY_UUIDS) {
final ParcelUuid[] newUuids = Utils.byteArrayToUuid(val);
- if (areUuidsEqual(newUuids, deviceProperties.getUuids())) {
+ if (areUuidsEqual(newUuids, deviceProperties.getUuidsBrEdr())) {
// SDP Skip adding UUIDs to property cache if equal
debugLog("Skip uuids update for " + bdDevice.getAddress());
MetricsLogger.getInstance()
.cacheCount(BluetoothProtoEnums.SDP_UUIDS_EQUAL_SKIP, 1);
break;
}
- deviceProperties.setUuids(newUuids);
- if (mAdapterService.getState() == BluetoothAdapter.STATE_ON) {
- // SDP Adding UUIDs to property cache and sending intent
- MetricsLogger.getInstance()
- .cacheCount(
- BluetoothProtoEnums.SDP_ADD_UUID_WITH_INTENT, 1);
- mAdapterService.deviceUuidUpdated(bdDevice);
- sendUuidIntent(bdDevice, deviceProperties, true);
- } else if (mAdapterService.getState()
- == BluetoothAdapter.STATE_BLE_ON) {
- // SDP Adding UUIDs to property cache but with no intent
- MetricsLogger.getInstance()
- .cacheCount(
- BluetoothProtoEnums.SDP_ADD_UUID_WITH_NO_INTENT, 1);
- mAdapterService.deviceUuidUpdated(bdDevice);
- } else {
- // SDP Silently dropping UUIDs and with no intent
+ deviceProperties.setUuidsBrEdr(newUuids);
+ } else if (type == AbstractionLayer.BT_PROPERTY_UUIDS_LE) {
+ final ParcelUuid[] newUuidsLe = Utils.byteArrayToUuid(val);
+ if (areUuidsEqual(newUuidsLe, deviceProperties.getUuidsLe())) {
+ // SDP Skip adding UUIDs to property cache if equal
+ debugLog("Skip LE uuids update for " + bdDevice.getAddress());
MetricsLogger.getInstance()
- .cacheCount(BluetoothProtoEnums.SDP_DROP_UUID, 1);
- }
- break;
- case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
- if (deviceProperties.isConsolidated()) {
+ .cacheCount(BluetoothProtoEnums.SDP_UUIDS_EQUAL_SKIP, 1);
break;
}
- // The device type from hal layer, defined in bluetooth.h,
- // matches the type defined in BluetoothDevice.java
- deviceProperties.setDeviceType(Utils.byteArrayToInt(val));
- break;
- case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
- // RSSI from hal is in one byte
- deviceProperties.setRssi(val[0]);
- break;
- case AbstractionLayer.BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER:
- deviceProperties.setIsCoordinatedSetMember(val[0] != 0);
- break;
- case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_CAPABILITY:
- deviceProperties.setAshaCapability(val[0]);
- break;
- case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID:
- deviceProperties.setAshaTruncatedHiSyncId(val[0]);
- break;
- case AbstractionLayer.BT_PROPERTY_REMOTE_MODEL_NUM:
- final String modelName = new String(val);
- debugLog("Remote device model name: " + modelName);
- deviceProperties.setModelName(modelName);
- BluetoothStatsLog.write(
- BluetoothStatsLog.BLUETOOTH_DEVICE_INFO_REPORTED,
- mAdapterService.obfuscateAddress(bdDevice),
- BluetoothProtoEnums.DEVICE_INFO_INTERNAL,
- LOG_SOURCE_DIS,
- null,
- modelName,
- null,
- null,
- mAdapterService.getMetricId(bdDevice),
- bdDevice.getAddressType(),
- 0,
- 0,
- 0);
+ deviceProperties.setUuidsLe(newUuidsLe);
+ }
+ uuids_updated = true;
+ break;
+ case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
+ if (deviceProperties.isConsolidated()) {
break;
- }
+ }
+ // The device type from hal layer, defined in bluetooth.h,
+ // matches the type defined in BluetoothDevice.java
+ deviceProperties.setDeviceType(Utils.byteArrayToInt(val));
+ break;
+ case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
+ // RSSI from hal is in one byte
+ deviceProperties.setRssi(val[0]);
+ break;
+ case AbstractionLayer.BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER:
+ deviceProperties.setIsCoordinatedSetMember(val[0] != 0);
+ break;
+ case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_CAPABILITY:
+ deviceProperties.setAshaCapability(val[0]);
+ break;
+ case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID:
+ deviceProperties.setAshaTruncatedHiSyncId(val[0]);
+ break;
+ case AbstractionLayer.BT_PROPERTY_REMOTE_MODEL_NUM:
+ final String modelName = new String(val);
+ debugLog("Remote device model name: " + modelName);
+ deviceProperties.setModelName(modelName);
+ BluetoothStatsLog.write(
+ BluetoothStatsLog.BLUETOOTH_DEVICE_INFO_REPORTED,
+ mAdapterService.obfuscateAddress(bdDevice),
+ BluetoothProtoEnums.DEVICE_INFO_INTERNAL,
+ LOG_SOURCE_DIS,
+ null,
+ modelName,
+ null,
+ null,
+ mAdapterService.getMetricId(bdDevice),
+ bdDevice.getAddressType(),
+ 0,
+ 0,
+ 0);
+ break;
}
}
}
+
+ if (!uuids_updated) {
+ return;
+ }
+
+ /* uuids_updated == true
+ * We might have received LE and BREDR UUIDS separately, ensure that UUID intent is sent
+ * just once */
+
+ if (mAdapterService.getState() == BluetoothAdapter.STATE_ON) {
+ // SDP Adding UUIDs to property cache and sending intent
+ MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_ADD_UUID_WITH_INTENT, 1);
+ mAdapterService.deviceUuidUpdated(bdDevice);
+ sendUuidIntent(bdDevice, deviceProperties, true);
+ } else if (mAdapterService.getState() == BluetoothAdapter.STATE_BLE_ON) {
+ // SDP Adding UUIDs to property cache but with no intent
+ MetricsLogger.getInstance()
+ .cacheCount(BluetoothProtoEnums.SDP_ADD_UUID_WITH_NO_INTENT, 1);
+ mAdapterService.deviceUuidUpdated(bdDevice);
+ } else {
+ // SDP Silently dropping UUIDs and with no intent
+ MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_DROP_UUID, 1);
+ }
}
void deviceFoundCallback(byte[] address) {
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseBinder.kt b/android/app/src/com/android/bluetooth/gatt/AdvertiseBinder.kt
index 92aaf31101..66415cd82f 100644
--- a/android/app/src/com/android/bluetooth/gatt/AdvertiseBinder.kt
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseBinder.kt
@@ -66,28 +66,31 @@ class AdvertiseBinder(
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null)
}
- getManager(source)
- ?.startAdvertisingSet(
- parameters,
- advertiseData,
- scanResponse,
- periodicParameters,
- periodicData,
- duration,
- maxExtAdvEvents,
- serverIf,
- callback,
- source,
- )
+ getManager(source)?.let {
+ it.doOnAdvertiseThread {
+ it.startAdvertisingSet(
+ parameters,
+ advertiseData,
+ scanResponse,
+ periodicParameters,
+ periodicData,
+ duration,
+ maxExtAdvEvents,
+ serverIf,
+ callback,
+ source,
+ )
+ }
+ }
}
override fun stopAdvertisingSet(callback: IAdvertisingSetCallback, source: AttributionSource) {
- getManager(source)?.stopAdvertisingSet(callback)
+ getManager(source)?.let { it.doOnAdvertiseThread { it.stopAdvertisingSet(callback) } }
}
override fun getOwnAddress(advertiserId: Int, source: AttributionSource) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null)
- getManager(source)?.getOwnAddress(advertiserId)
+ getManager(source)?.let { it.doOnAdvertiseThread { it.getOwnAddress(advertiserId) } }
}
override fun enableAdvertisingSet(
@@ -97,7 +100,11 @@ class AdvertiseBinder(
maxExtAdvEvents: Int,
source: AttributionSource,
) {
- getManager(source)?.enableAdvertisingSet(advertiserId, enable, duration, maxExtAdvEvents)
+ getManager(source)?.let {
+ it.doOnAdvertiseThread {
+ it.enableAdvertisingSet(advertiserId, enable, duration, maxExtAdvEvents)
+ }
+ }
}
override fun setAdvertisingData(
@@ -105,7 +112,9 @@ class AdvertiseBinder(
data: AdvertiseData?,
source: AttributionSource,
) {
- getManager(source)?.setAdvertisingData(advertiserId, data)
+ getManager(source)?.let {
+ it.doOnAdvertiseThread { it.setAdvertisingData(advertiserId, data) }
+ }
}
override fun setScanResponseData(
@@ -113,7 +122,9 @@ class AdvertiseBinder(
data: AdvertiseData?,
source: AttributionSource,
) {
- getManager(source)?.setScanResponseData(advertiserId, data)
+ getManager(source)?.let {
+ it.doOnAdvertiseThread { it.setScanResponseData(advertiserId, data) }
+ }
}
override fun setAdvertisingParameters(
@@ -127,7 +138,9 @@ class AdvertiseBinder(
) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null)
}
- getManager(source)?.setAdvertisingParameters(advertiserId, parameters)
+ getManager(source)?.let {
+ it.doOnAdvertiseThread { it.setAdvertisingParameters(advertiserId, parameters) }
+ }
}
override fun setPeriodicAdvertisingParameters(
@@ -135,7 +148,9 @@ class AdvertiseBinder(
parameters: PeriodicAdvertisingParameters?,
source: AttributionSource,
) {
- getManager(source)?.setPeriodicAdvertisingParameters(advertiserId, parameters)
+ getManager(source)?.let {
+ it.doOnAdvertiseThread { it.setPeriodicAdvertisingParameters(advertiserId, parameters) }
+ }
}
override fun setPeriodicAdvertisingData(
@@ -143,7 +158,9 @@ class AdvertiseBinder(
data: AdvertiseData?,
source: AttributionSource,
) {
- getManager(source)?.setPeriodicAdvertisingData(advertiserId, data)
+ getManager(source)?.let {
+ it.doOnAdvertiseThread { it.setPeriodicAdvertisingData(advertiserId, data) }
+ }
}
override fun setPeriodicAdvertisingEnable(
@@ -151,6 +168,8 @@ class AdvertiseBinder(
enable: Boolean,
source: AttributionSource,
) {
- getManager(source)?.setPeriodicAdvertisingEnable(advertiserId, enable)
+ getManager(source)?.let {
+ it.doOnAdvertiseThread { it.setPeriodicAdvertisingEnable(advertiserId, enable) }
+ }
}
}
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
index 7e89f99055..6ed719cedc 100644
--- a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
@@ -24,45 +24,52 @@ import android.bluetooth.le.PeriodicAdvertisingParameters;
import android.content.AttributionSource;
import android.os.Binder;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.IInterface;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
+import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
-import com.android.internal.annotations.GuardedBy;
+import com.android.bluetooth.flags.Flags;
import com.android.internal.annotations.VisibleForTesting;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
-/**
- * Manages Bluetooth LE advertising operations and interacts with bluedroid stack. TODO: add tests.
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+/** Manages Bluetooth LE advertising operations. */
public class AdvertiseManager {
private static final String TAG = GattServiceConfig.TAG_PREFIX + "AdvertiseManager";
- private final GattService mService;
+ private static final long RUN_SYNC_WAIT_TIME_MS = 2000L;
+
+ private final AdapterService mService;
private final AdvertiseManagerNativeInterface mNativeInterface;
private final AdvertiseBinder mAdvertiseBinder;
private final AdvertiserMap mAdvertiserMap;
- @GuardedBy("itself")
private final Map<IBinder, AdvertiserInfo> mAdvertisers = new HashMap<>();
- private Handler mHandler;
- static int sTempRegistrationId = -1;
+ private final Handler mHandler;
+ private volatile boolean mIsAvailable = true;
+ @VisibleForTesting int mTempRegistrationId = -1;
- AdvertiseManager(GattService service) {
- this(service, AdvertiseManagerNativeInterface.getInstance(), new AdvertiserMap());
+ AdvertiseManager(AdapterService service, Looper advertiseLooper) {
+ this(
+ service,
+ advertiseLooper,
+ AdvertiseManagerNativeInterface.getInstance(),
+ new AdvertiserMap());
}
@VisibleForTesting
AdvertiseManager(
- GattService service,
+ AdapterService service,
+ Looper advertiseLooper,
AdvertiseManagerNativeInterface nativeInterface,
AdvertiserMap advertiserMap) {
Log.d(TAG, "advertise manager created");
@@ -70,42 +77,26 @@ public class AdvertiseManager {
mNativeInterface = nativeInterface;
mAdvertiserMap = advertiserMap;
- // Start a HandlerThread that handles advertising operations
mNativeInterface.init(this);
- HandlerThread thread = new HandlerThread("BluetoothAdvertiseManager");
- thread.start();
- mHandler = new Handler(thread.getLooper());
+ mHandler = new Handler(advertiseLooper);
mAdvertiseBinder = new AdvertiseBinder(service, this);
}
- // TODO(b/327849650): We shouldn't need this, it should be safe to do in the cleanup method. But
- // it would be a logic change.
- void clear() {
- mAdvertiserMap.clear();
- }
-
void cleanup() {
Log.d(TAG, "cleanup()");
- mAdvertiseBinder.cleanup();
- mNativeInterface.cleanup();
- synchronized (mAdvertisers) {
- mAdvertisers.clear();
- }
- sTempRegistrationId = -1;
-
- if (mHandler != null) {
- // Shut down the thread
- mHandler.removeCallbacksAndMessages(null);
- Looper looper = mHandler.getLooper();
- if (looper != null) {
- looper.quit();
- }
- mHandler = null;
- }
+ mIsAvailable = false;
+ mHandler.removeCallbacksAndMessages(null);
+ forceRunSyncOnAdvertiseThread(
+ () -> {
+ mAdvertiserMap.clear();
+ mAdvertiseBinder.cleanup();
+ mNativeInterface.cleanup();
+ mAdvertisers.clear();
+ });
}
void dump(StringBuilder sb) {
- mAdvertiserMap.dump(sb);
+ forceRunSyncOnAdvertiseThread(() -> mAdvertiserMap.dump(sb));
}
AdvertiseBinder getBinder() {
@@ -129,8 +120,12 @@ public class AdvertiseManager {
}
}
+ private interface CallbackWrapper {
+ void call() throws RemoteException;
+ }
+
IBinder toBinder(IAdvertisingSetCallback e) {
- return ((IInterface) e).asBinder();
+ return e.asBinder();
}
class AdvertisingSetDeathRecipient implements IBinder.DeathRecipient {
@@ -145,25 +140,22 @@ public class AdvertiseManager {
@Override
public void binderDied() {
Log.d(TAG, "Binder is dead - unregistering advertising set (" + mPackageName + ")!");
- stopAdvertisingSet(callback);
+ doOnAdvertiseThread(() -> stopAdvertisingSet(callback));
}
}
- Map.Entry<IBinder, AdvertiserInfo> findAdvertiser(int advertiserId) {
+ private Map.Entry<IBinder, AdvertiserInfo> findAdvertiser(int advertiserId) {
Map.Entry<IBinder, AdvertiserInfo> entry = null;
- synchronized (mAdvertisers) {
- for (Map.Entry<IBinder, AdvertiserInfo> e : mAdvertisers.entrySet()) {
- if (e.getValue().id == advertiserId) {
- entry = e;
- break;
- }
+ for (Map.Entry<IBinder, AdvertiserInfo> e : mAdvertisers.entrySet()) {
+ if (e.getValue().id == advertiserId) {
+ entry = e;
+ break;
}
}
return entry;
}
- void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status)
- throws Exception {
+ void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status) {
Log.d(
TAG,
"onAdvertisingSetStarted() - regId="
@@ -172,6 +164,7 @@ public class AdvertiseManager {
+ advertiserId
+ ", status="
+ status);
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(regId);
@@ -191,26 +184,24 @@ public class AdvertiseManager {
} else {
IBinder binder = entry.getKey();
binder.unlinkToDeath(entry.getValue().deathRecipient, 0);
- synchronized (mAdvertisers) {
- mAdvertisers.remove(binder);
- }
+ mAdvertisers.remove(binder);
AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(regId);
if (stats != null) {
- int instanceCount;
- synchronized (mAdvertisers) {
- instanceCount = mAdvertisers.size();
- }
- stats.recordAdvertiseStop(instanceCount);
+ stats.recordAdvertiseStop(mAdvertisers.size());
stats.recordAdvertiseErrorCount(status);
}
mAdvertiserMap.removeAppAdvertiseStats(regId);
}
- callback.onAdvertisingSetStarted(mAdvertiseBinder, advertiserId, txPower, status);
+ sendToCallback(
+ advertiserId,
+ () ->
+ callback.onAdvertisingSetStarted(
+ mAdvertiseBinder, advertiserId, txPower, status));
}
- void onAdvertisingEnabled(int advertiserId, boolean enable, int status) throws Exception {
+ void onAdvertisingEnabled(int advertiserId, boolean enable, int status) {
Log.d(
TAG,
"onAdvertisingSetEnabled() - advertiserId="
@@ -219,6 +210,7 @@ public class AdvertiseManager {
+ enable
+ ", status="
+ status);
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
@@ -230,16 +222,13 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onAdvertisingEnabled(advertiserId, enable, status);
+ sendToCallback(
+ advertiserId, () -> callback.onAdvertisingEnabled(advertiserId, enable, status));
if (!enable && status != 0) {
AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(advertiserId);
if (stats != null) {
- int instanceCount;
- synchronized (mAdvertisers) {
- instanceCount = mAdvertisers.size();
- }
- stats.recordAdvertiseStop(instanceCount);
+ stats.recordAdvertiseStop(mAdvertisers.size());
}
}
}
@@ -255,6 +244,7 @@ public class AdvertiseManager {
int serverIf,
IAdvertisingSetCallback callback,
AttributionSource attrSource) {
+ checkThread();
// If we are using an isolated server, force usage of an NRPA
if (serverIf != 0
&& parameters.getOwnAddressType()
@@ -289,7 +279,7 @@ public class AdvertiseManager {
throw new IllegalArgumentException("Can't link to advertiser's death");
}
- String deviceName = AdapterService.getAdapterService().getName();
+ String deviceName = mService.getName();
try {
byte[] advDataBytes = AdvertiseHelper.advertiseDataToBytes(advertiseData, deviceName);
byte[] scanResponseBytes =
@@ -297,10 +287,8 @@ public class AdvertiseManager {
byte[] periodicDataBytes =
AdvertiseHelper.advertiseDataToBytes(periodicData, deviceName);
- int cbId = --sTempRegistrationId;
- synchronized (mAdvertisers) {
- mAdvertisers.put(binder, new AdvertiserInfo(cbId, deathRecipient, callback));
- }
+ int cbId = --mTempRegistrationId;
+ mAdvertisers.put(binder, new AdvertiserInfo(cbId, deathRecipient, callback));
Log.d(TAG, "startAdvertisingSet() - reg_id=" + cbId + ", callback: " + binder);
@@ -340,9 +328,9 @@ public class AdvertiseManager {
}
}
- void onOwnAddressRead(int advertiserId, int addressType, String address)
- throws RemoteException {
+ void onOwnAddressRead(int advertiserId, int addressType, String address) {
Log.d(TAG, "onOwnAddressRead() advertiserId=" + advertiserId);
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
@@ -351,10 +339,12 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onOwnAddressRead(advertiserId, addressType, address);
+ sendToCallback(
+ advertiserId, () -> callback.onOwnAddressRead(advertiserId, addressType, address));
}
void getOwnAddress(int advertiserId) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "getOwnAddress() - bad advertiserId " + advertiserId);
@@ -364,13 +354,11 @@ public class AdvertiseManager {
}
void stopAdvertisingSet(IAdvertisingSetCallback callback) {
+ checkThread();
IBinder binder = toBinder(callback);
Log.d(TAG, "stopAdvertisingSet() " + binder);
- AdvertiserInfo adv;
- synchronized (mAdvertisers) {
- adv = mAdvertisers.remove(binder);
- }
+ AdvertiserInfo adv = mAdvertisers.remove(binder);
if (adv == null) {
Log.e(TAG, "stopAdvertisingSet() - no client found for callback");
return;
@@ -397,6 +385,7 @@ public class AdvertiseManager {
}
void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "enableAdvertisingSet() - bad advertiserId " + advertiserId);
@@ -408,12 +397,13 @@ public class AdvertiseManager {
}
void setAdvertisingData(int advertiserId, AdvertiseData data) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "setAdvertisingData() - bad advertiserId " + advertiserId);
return;
}
- String deviceName = AdapterService.getAdapterService().getName();
+ String deviceName = mService.getName();
try {
mNativeInterface.setAdvertisingData(
advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName));
@@ -430,12 +420,13 @@ public class AdvertiseManager {
}
void setScanResponseData(int advertiserId, AdvertiseData data) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "setScanResponseData() - bad advertiserId " + advertiserId);
return;
}
- String deviceName = AdapterService.getAdapterService().getName();
+ String deviceName = mService.getName();
try {
mNativeInterface.setScanResponseData(
advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName));
@@ -452,6 +443,7 @@ public class AdvertiseManager {
}
void setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "setAdvertisingParameters() - bad advertiserId " + advertiserId);
@@ -464,6 +456,7 @@ public class AdvertiseManager {
void setPeriodicAdvertisingParameters(
int advertiserId, PeriodicAdvertisingParameters parameters) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "setPeriodicAdvertisingParameters() - bad advertiserId " + advertiserId);
@@ -475,12 +468,13 @@ public class AdvertiseManager {
}
void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "setPeriodicAdvertisingData() - bad advertiserId " + advertiserId);
return;
}
- String deviceName = AdapterService.getAdapterService().getName();
+ String deviceName = mService.getName();
try {
mNativeInterface.setPeriodicAdvertisingData(
advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName));
@@ -497,6 +491,7 @@ public class AdvertiseManager {
}
void setPeriodicAdvertisingEnable(int advertiserId, boolean enable) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "setPeriodicAdvertisingEnable() - bad advertiserId " + advertiserId);
@@ -505,7 +500,8 @@ public class AdvertiseManager {
mNativeInterface.setPeriodicAdvertisingEnable(advertiserId, enable);
}
- void onAdvertisingDataSet(int advertiserId, int status) throws Exception {
+ void onAdvertisingDataSet(int advertiserId, int status) {
+ checkThread();
Log.d(TAG, "onAdvertisingDataSet() advertiserId=" + advertiserId + ", status=" + status);
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
@@ -515,10 +511,11 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onAdvertisingDataSet(advertiserId, status);
+ sendToCallback(advertiserId, () -> callback.onAdvertisingDataSet(advertiserId, status));
}
- void onScanResponseDataSet(int advertiserId, int status) throws Exception {
+ void onScanResponseDataSet(int advertiserId, int status) {
+ checkThread();
Log.d(TAG, "onScanResponseDataSet() advertiserId=" + advertiserId + ", status=" + status);
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
@@ -528,11 +525,10 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onScanResponseDataSet(advertiserId, status);
+ sendToCallback(advertiserId, () -> callback.onScanResponseDataSet(advertiserId, status));
}
- void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status)
- throws Exception {
+ void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) {
Log.d(
TAG,
"onAdvertisingParametersUpdated() advertiserId="
@@ -541,6 +537,7 @@ public class AdvertiseManager {
+ txPower
+ ", status="
+ status);
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
@@ -549,16 +546,19 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onAdvertisingParametersUpdated(advertiserId, txPower, status);
+ sendToCallback(
+ advertiserId,
+ () -> callback.onAdvertisingParametersUpdated(advertiserId, txPower, status));
}
- void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) throws Exception {
+ void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) {
Log.d(
TAG,
"onPeriodicAdvertisingParametersUpdated() advertiserId="
+ advertiserId
+ ", status="
+ status);
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
@@ -569,16 +569,19 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onPeriodicAdvertisingParametersUpdated(advertiserId, status);
+ sendToCallback(
+ advertiserId,
+ () -> callback.onPeriodicAdvertisingParametersUpdated(advertiserId, status));
}
- void onPeriodicAdvertisingDataSet(int advertiserId, int status) throws Exception {
+ void onPeriodicAdvertisingDataSet(int advertiserId, int status) {
Log.d(
TAG,
"onPeriodicAdvertisingDataSet() advertiserId="
+ advertiserId
+ ", status="
+ status);
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
@@ -587,11 +590,11 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onPeriodicAdvertisingDataSet(advertiserId, status);
+ sendToCallback(
+ advertiserId, () -> callback.onPeriodicAdvertisingDataSet(advertiserId, status));
}
- void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status)
- throws Exception {
+ void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) {
Log.d(
TAG,
"onPeriodicAdvertisingEnabled() advertiserId="
@@ -604,9 +607,12 @@ public class AdvertiseManager {
Log.i(TAG, "onAdvertisingSetEnable() - bad advertiserId " + advertiserId);
return;
}
+ checkThread();
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onPeriodicAdvertisingEnabled(advertiserId, enable, status);
+ sendToCallback(
+ advertiserId,
+ () -> callback.onPeriodicAdvertisingEnabled(advertiserId, enable, status));
AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(advertiserId);
if (stats != null) {
@@ -614,4 +620,52 @@ public class AdvertiseManager {
}
}
+ void doOnAdvertiseThread(Runnable r) {
+ if (mIsAvailable) {
+ if (Flags.advertiseThread()) {
+ mHandler.post(
+ () -> {
+ if (mIsAvailable) {
+ r.run();
+ }
+ });
+ } else {
+ r.run();
+ }
+ }
+ }
+
+ private void forceRunSyncOnAdvertiseThread(Runnable r) {
+ if (!Flags.advertiseThread()) {
+ r.run();
+ return;
+ }
+ final CompletableFuture<Void> future = new CompletableFuture<>();
+ mHandler.postAtFrontOfQueue(
+ () -> {
+ r.run();
+ future.complete(null);
+ });
+ try {
+ future.get(RUN_SYNC_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException | TimeoutException | ExecutionException e) {
+ Log.w(TAG, "Unable to complete sync task: " + e);
+ }
+ }
+
+ private void checkThread() {
+ if (Flags.advertiseThread()
+ && !mHandler.getLooper().isCurrentThread()
+ && !Utils.isInstrumentationTestMode()) {
+ throw new IllegalStateException("Not on advertise thread");
+ }
+ }
+
+ private void sendToCallback(int advertiserId, CallbackWrapper wrapper) {
+ try {
+ wrapper.call();
+ } catch (RemoteException e) {
+ Log.i(TAG, "RemoteException in callback for advertiserId: " + advertiserId);
+ }
+ }
}
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java
index 215c709970..6cdd47f97e 100644
--- a/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java
@@ -19,11 +19,12 @@ package com.android.bluetooth.gatt;
import android.bluetooth.le.AdvertisingSetParameters;
import android.bluetooth.le.PeriodicAdvertisingParameters;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
/** Native interface for AdvertiseManager */
-@VisibleForTesting
+@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public class AdvertiseManagerNativeInterface {
private static final String TAG = AdvertiseManagerNativeInterface.class.getSimpleName();
@@ -121,43 +122,47 @@ public class AdvertiseManagerNativeInterface {
setPeriodicAdvertisingEnableNative(advertiserId, enable);
}
- void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status)
- throws Exception {
- mManager.onAdvertisingSetStarted(regId, advertiserId, txPower, status);
+ void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onAdvertisingSetStarted(regId, advertiserId, txPower, status));
}
- void onOwnAddressRead(int advertiserId, int addressType, String address) throws Exception {
- mManager.onOwnAddressRead(advertiserId, addressType, address);
+ void onOwnAddressRead(int advertiserId, int addressType, String address) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onOwnAddressRead(advertiserId, addressType, address));
}
- void onAdvertisingEnabled(int advertiserId, boolean enable, int status) throws Exception {
- mManager.onAdvertisingEnabled(advertiserId, enable, status);
+ void onAdvertisingEnabled(int advertiserId, boolean enable, int status) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onAdvertisingEnabled(advertiserId, enable, status));
}
- void onAdvertisingDataSet(int advertiserId, int status) throws Exception {
- mManager.onAdvertisingDataSet(advertiserId, status);
+ void onAdvertisingDataSet(int advertiserId, int status) {
+ mManager.doOnAdvertiseThread(() -> mManager.onAdvertisingDataSet(advertiserId, status));
}
- void onScanResponseDataSet(int advertiserId, int status) throws Exception {
- mManager.onScanResponseDataSet(advertiserId, status);
+ void onScanResponseDataSet(int advertiserId, int status) {
+ mManager.doOnAdvertiseThread(() -> mManager.onScanResponseDataSet(advertiserId, status));
}
- void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status)
- throws Exception {
- mManager.onAdvertisingParametersUpdated(advertiserId, txPower, status);
+ void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onAdvertisingParametersUpdated(advertiserId, txPower, status));
}
- void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) throws Exception {
- mManager.onPeriodicAdvertisingParametersUpdated(advertiserId, status);
+ void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onPeriodicAdvertisingParametersUpdated(advertiserId, status));
}
- void onPeriodicAdvertisingDataSet(int advertiserId, int status) throws Exception {
- mManager.onPeriodicAdvertisingDataSet(advertiserId, status);
+ void onPeriodicAdvertisingDataSet(int advertiserId, int status) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onPeriodicAdvertisingDataSet(advertiserId, status));
}
- void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status)
- throws Exception {
- mManager.onPeriodicAdvertisingEnabled(advertiserId, enable, status);
+ void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onPeriodicAdvertisingEnabled(advertiserId, enable, status));
}
private native void initializeNative();
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index cc2e759f61..e9563f8908 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -51,6 +51,7 @@ import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Build;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -156,6 +157,7 @@ public class GattService extends ProfileService {
private final DistanceMeasurementManager mDistanceMeasurementManager;
private final ActivityManager mActivityManager;
private final PackageManager mPackageManager;
+ private final HandlerThread mHandlerThread;
public GattService(AdapterService adapterService) {
super(requireNonNull(adapterService));
@@ -169,7 +171,12 @@ public class GattService extends ProfileService {
mNativeInterface = GattObjectsFactory.getInstance().getNativeInterface();
mNativeInterface.init(this);
- mAdvertiseManager = new AdvertiseManager(this);
+
+ // Create a thread to handle LE operations
+ mHandlerThread = new HandlerThread("Bluetooth LE");
+ mHandlerThread.start();
+
+ mAdvertiseManager = new AdvertiseManager(mAdapterService, mHandlerThread.getLooper());
if (!Flags.scanManagerRefactor()) {
mScanController = new ScanController(adapterService);
@@ -209,7 +216,6 @@ public class GattService extends ProfileService {
if (mScanController != null) {
mScanController.stop();
}
- mAdvertiseManager.clear();
mClientMap.clear();
mRestrictedHandles.clear();
mServerMap.clear();
@@ -224,6 +230,7 @@ public class GattService extends ProfileService {
mNativeInterface.cleanup();
mAdvertiseManager.cleanup();
mDistanceMeasurementManager.cleanup();
+ mHandlerThread.quit();
}
/** This is only used when Flags.scanManagerRefactor() is true. */
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index fd56faa613..fb0ed9d2f0 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -55,7 +55,6 @@ import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.MetricsLogger;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
-import com.android.bluetooth.flags.Flags;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -1122,12 +1121,6 @@ class HeadsetStateMachine extends StateMachine {
}
}
break;
- case INTENT_SCO_VOLUME_CHANGED:
- if (Flags.hfpAllowVolumeChangeWithoutSco()) {
- // when flag is removed, remove INTENT_SCO_VOLUME_CHANGED case in AudioOn
- processIntentScoVolume((Intent) message.obj, mDevice);
- }
- break;
case INTENT_CONNECTION_ACCESS_REPLY:
handleAccessPermissionResult((Intent) message.obj);
break;
@@ -1630,8 +1623,6 @@ class HeadsetStateMachine extends StateMachine {
break;
}
case INTENT_SCO_VOLUME_CHANGED:
- // TODO: b/362313390 Remove this case once the fix is in place because this
- // message will be handled by the ConnectedBase state.
processIntentScoVolume((Intent) message.obj, mDevice);
break;
case STACK_EVENT:
diff --git a/android/app/src/com/android/bluetooth/le_scan/ScanController.java b/android/app/src/com/android/bluetooth/le_scan/ScanController.java
index 22aecf7c6d..7f39fdaaf1 100644
--- a/android/app/src/com/android/bluetooth/le_scan/ScanController.java
+++ b/android/app/src/com/android/bluetooth/le_scan/ScanController.java
@@ -759,14 +759,15 @@ public class ScanController {
}
}
}
- if (permittedResults.isEmpty()) {
- return;
- }
}
if (client.hasDisavowedLocation) {
permittedResults.removeIf(mLocationDenylistPredicate);
}
+ if (permittedResults.isEmpty()) {
+ mScanManager.callbackDone(scannerId, status);
+ return;
+ }
if (app.mCallback != null) {
app.mCallback.onBatchScanResults(permittedResults);
@@ -792,6 +793,9 @@ public class ScanController {
@SuppressWarnings("NonApiType")
private void sendBatchScanResults(
ScannerMap.ScannerApp app, ScanClient client, ArrayList<ScanResult> results) {
+ if (results.isEmpty()) {
+ return;
+ }
try {
if (app.mCallback != null) {
if (mScanManager.isAutoBatchScanClientEnabled(client)) {
@@ -835,14 +839,11 @@ public class ScanController {
}
}
}
- if (permittedResults.isEmpty()) {
- return;
- }
}
if (client.filters == null || client.filters.isEmpty()) {
sendBatchScanResults(app, client, permittedResults);
- // TODO: Question to reviewer: Shouldn't there be a return here?
+ return;
}
// Reconstruct the scan results.
ArrayList<ScanResult> results = new ArrayList<ScanResult>();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
index cc3304c21d..c1c7734f2e 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
@@ -6360,7 +6360,8 @@ public class BassClientServiceTest {
@Test
@EnableFlags({
Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
- Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE
+ Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE,
+ Flags.FLAG_LEAUDIO_MONITOR_UNICAST_SOURCE_WHEN_MANAGED_BY_BROADCAST_DELEGATOR
})
public void sinkUnintentional_handleUnicastSourceStreamStatusChange_withoutScanning() {
sinkUnintentionalWithoutScanning();
@@ -6369,7 +6370,6 @@ public class BassClientServiceTest {
mBassClientService.handleUnicastSourceStreamStatusChange(
0 /* STATUS_LOCAL_STREAM_REQUESTED */);
verifyStopBigMonitoringWithUnsync();
- verifyRemoveMessageAndInjectSourceRemoval();
checkNoResumeSynchronizationByBig();
/* Unicast finished streaming */
@@ -6382,7 +6382,8 @@ public class BassClientServiceTest {
@Test
@EnableFlags({
Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
- Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE
+ Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE,
+ Flags.FLAG_LEAUDIO_MONITOR_UNICAST_SOURCE_WHEN_MANAGED_BY_BROADCAST_DELEGATOR
})
public void sinkUnintentional_handleUnicastSourceStreamStatusChange_duringScanning() {
sinkUnintentionalDuringScanning();
@@ -6391,7 +6392,6 @@ public class BassClientServiceTest {
mBassClientService.handleUnicastSourceStreamStatusChange(
0 /* STATUS_LOCAL_STREAM_REQUESTED */);
verifyStopBigMonitoringWithoutUnsync();
- verifyRemoveMessageAndInjectSourceRemoval();
checkNoResumeSynchronizationByBig();
/* Unicast finished streaming */
@@ -6654,6 +6654,44 @@ public class BassClientServiceTest {
@Test
@EnableFlags({
Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
+ Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE,
+ Flags.FLAG_LEAUDIO_BROADCAST_ASSISTANT_PERIPHERAL_ENTRUSTMENT,
+ Flags.FLAG_LEAUDIO_MONITOR_UNICAST_SOURCE_WHEN_MANAGED_BY_BROADCAST_DELEGATOR
+ })
+ public void hostIntentional_handleUnicastSourceStreamStatusChange_beforeResumeCompleted() {
+ prepareSynchronizedPairAndStopSearching();
+
+ /* Unicast would like to stream */
+ mBassClientService.handleUnicastSourceStreamStatusChange(
+ 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
+ checkNoSinkPause();
+
+ /* Unicast finished streaming */
+ mBassClientService.handleUnicastSourceStreamStatusChange(
+ 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
+ mInOrderMethodProxy
+ .verify(mMethodProxy)
+ .periodicAdvertisingManagerRegisterSync(
+ any(), any(), anyInt(), anyInt(), any(), any());
+
+ /* Unicast would like to stream again before previous resume was complete*/
+ mBassClientService.handleUnicastSourceStreamStatusChange(
+ 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
+
+ /* Unicast finished streaming */
+ mBassClientService.handleUnicastSourceStreamStatusChange(
+ 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
+ mInOrderMethodProxy
+ .verify(mMethodProxy)
+ .periodicAdvertisingManagerRegisterSync(
+ any(), any(), anyInt(), anyInt(), any(), any());
+ onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE); // In case of add source to inactive
+ verifyAllGroupMembersGettingUpdateOrAddSource(createBroadcastMetadata(TEST_BROADCAST_ID));
+ }
+
+ @Test
+ @EnableFlags({
+ Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE
})
public void hostIntentional_handleUnicastSourceStreamStatusChangeNoContext_withoutScanning() {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java
index cc40c6af72..e274ac6eed 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java
@@ -218,7 +218,7 @@ public class BondStateMachineTest {
RemoteDevices.DeviceProperties testDeviceProperties =
mRemoteDevices.addDeviceProperties(TEST_BT_ADDR_BYTES);
- testDeviceProperties.mUuids = TEST_UUIDS;
+ testDeviceProperties.mUuidsBrEdr = TEST_UUIDS;
BluetoothDevice testDevice = testDeviceProperties.getDevice();
assertThat(testDevice).isNotNull();
@@ -228,7 +228,7 @@ public class BondStateMachineTest {
bondingMsg.arg2 = AbstractionLayer.BT_STATUS_RMT_DEV_DOWN;
mBondStateMachine.sendMessage(bondingMsg);
- pendingDeviceProperties.mUuids = TEST_UUIDS;
+ pendingDeviceProperties.mUuidsBrEdr = TEST_UUIDS;
Message uuidUpdateMsg = mBondStateMachine.obtainMessage(BondStateMachine.UUID_UPDATE);
uuidUpdateMsg.obj = pendingDevice;
@@ -634,7 +634,7 @@ public class BondStateMachineTest {
}
if (uuids != null) {
// Add dummy UUID for the device.
- mDeviceProperties.mUuids = TEST_UUIDS;
+ mDeviceProperties.mUuidsBrEdr = TEST_UUIDS;
}
testSendIntentCase(
oldState,
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java
index 3a0abb2870..1eca1e2483 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java
@@ -16,6 +16,8 @@
package com.android.bluetooth.gatt;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -54,6 +56,13 @@ public class AdvertiseBinderTest {
@Before
public void setUp() {
+ doAnswer(
+ invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ })
+ .when(mAdvertiseManager)
+ .doOnAdvertiseThread(any());
mBinder = new AdvertiseBinder(mAdapterService, mAdvertiseManager);
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java
index 6621a43331..4cd5423ce9 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java
@@ -27,15 +27,16 @@ import android.bluetooth.le.AdvertisingSetParameters;
import android.bluetooth.le.IAdvertisingSetCallback;
import android.bluetooth.le.PeriodicAdvertisingParameters;
import android.os.IBinder;
+import android.os.test.TestLooper;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.flags.Flags;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -44,17 +45,21 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+import java.util.List;
+
/** Test cases for {@link AdvertiseManager}. */
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
public class AdvertiseManagerTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+ @Rule public final SetFlagsRule mSetFlagsRule;
@Mock private AdapterService mAdapterService;
- @Mock private GattService mService;
-
@Mock private AdvertiserMap mAdvertiserMap;
@Mock private AdvertiseManagerNativeInterface mNativeInterface;
@@ -66,10 +71,23 @@ public class AdvertiseManagerTest {
private AdvertiseManager mAdvertiseManager;
private int mAdvertiserId;
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(Flags.FLAG_ADVERTISE_THREAD);
+ }
+
+ public AdvertiseManagerTest(FlagsParameterization flags) {
+ mSetFlagsRule = new SetFlagsRule(flags);
+ }
+
@Before
public void setUp() throws Exception {
- TestUtils.setAdapterService(mAdapterService);
- mAdvertiseManager = new AdvertiseManager(mService, mNativeInterface, mAdvertiserMap);
+ mAdvertiseManager =
+ new AdvertiseManager(
+ mAdapterService,
+ new TestLooper().getLooper(),
+ mNativeInterface,
+ mAdvertiserMap);
AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build();
AdvertiseData advertiseData = new AdvertiseData.Builder().build();
@@ -95,12 +113,7 @@ public class AdvertiseManagerTest {
mCallback,
InstrumentationRegistry.getTargetContext().getAttributionSource());
- mAdvertiserId = AdvertiseManager.sTempRegistrationId;
- }
-
- @After
- public void tearDown() throws Exception {
- TestUtils.clearAdapterService(mAdapterService);
+ mAdvertiserId = mAdvertiseManager.mTempRegistrationId;
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
index e594c924a8..0b42a50d92 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
@@ -1793,28 +1793,6 @@ public class HeadsetStateMachineTest {
}
@Test
- @EnableFlags({Flags.FLAG_HFP_ALLOW_VOLUME_CHANGE_WITHOUT_SCO})
- public void testVolumeChangeEvent_fromIntentWhenConnected() {
- setUpConnectedState();
- int originalVolume = mHeadsetStateMachine.mSpeakerVolume;
- mHeadsetStateMachine.mSpeakerVolume = 0;
- int vol = 10;
-
- // Send INTENT_SCO_VOLUME_CHANGED message
- Intent volumeChange = new Intent(AudioManager.ACTION_VOLUME_CHANGED);
- volumeChange.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, vol);
-
- mHeadsetStateMachine.sendMessage(
- HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED, volumeChange);
- TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
-
- // verify volume processed
- verify(mNativeInterface).setVolume(mTestDevice, HeadsetHalConstants.VOLUME_TYPE_SPK, vol);
-
- mHeadsetStateMachine.mSpeakerVolume = originalVolume;
- }
-
- @Test
public void testVolumeChangeEvent_fromIntentWhenAudioOn() {
setUpAudioOnState();
int originalVolume = mHeadsetStateMachine.mSpeakerVolume;
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanControllerTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanControllerTest.java
index 2b19c1aa0b..ea051bc528 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanControllerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanControllerTest.java
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothAdapter;
@@ -47,7 +48,6 @@ import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
@@ -55,6 +55,9 @@ import com.android.bluetooth.btservice.CompanionManager;
import com.android.bluetooth.gatt.GattNativeInterface;
import com.android.bluetooth.gatt.GattObjectsFactory;
+import com.google.testing.junit.testparameterinjector.TestParameter;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -73,7 +76,7 @@ import java.util.Set;
/** Test cases for {@link ScanController}. */
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(TestParameterInjector.class)
public class ScanControllerTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -187,7 +190,8 @@ public class ScanControllerTest {
}
@Test
- public void onBatchScanReportsInternal_deliverBatchScan() throws RemoteException {
+ public void onBatchScanReportsInternal_deliverBatchScan_full(
+ @TestParameter boolean expectResults) throws RemoteException {
int status = 1;
int scannerId = 2;
int reportType = ScanManager.SCAN_RESULT_TYPE_FULL;
@@ -200,28 +204,59 @@ public class ScanControllerTest {
Set<ScanClient> scanClientSet = new HashSet<>();
ScanClient scanClient = new ScanClient(scannerId);
scanClient.associatedDevices = new ArrayList<>();
- scanClient.associatedDevices.add("02:00:00:00:00:00");
scanClient.scannerId = scannerId;
+ if (expectResults) {
+ scanClient.hasScanWithoutLocationPermission = true;
+ }
scanClientSet.add(scanClient);
doReturn(scanClientSet).when(mScanManager).getFullBatchScanQueue();
doReturn(mApp).when(mScannerMap).getById(scanClient.scannerId);
+ IScannerCallback callback = mock(IScannerCallback.class);
+ mApp.mCallback = callback;
mScanController.onBatchScanReportsInternal(
status, scannerId, reportType, numRecords, recordData);
verify(mScanManager).callbackDone(scannerId, status);
+ if (expectResults) {
+ verify(callback).onBatchScanResults(any());
+ } else {
+ verify(callback, never()).onBatchScanResults(any());
+ }
+ }
- reportType = ScanManager.SCAN_RESULT_TYPE_TRUNCATED;
- recordData =
+ @Test
+ public void onBatchScanReportsInternal_deliverBatchScan_truncated(
+ @TestParameter boolean expectResults) throws RemoteException {
+ int status = 1;
+ int scannerId = 2;
+ int reportType = ScanManager.SCAN_RESULT_TYPE_TRUNCATED;
+ int numRecords = 1;
+ byte[] recordData =
new byte[] {
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x04, 0x02, 0x02, 0x00, 0x00, 0x02
};
+
+ Set<ScanClient> scanClientSet = new HashSet<>();
+ ScanClient scanClient = new ScanClient(scannerId);
+ scanClient.associatedDevices = new ArrayList<>();
+ if (expectResults) {
+ scanClient.associatedDevices.add("02:00:00:00:00:00");
+ }
+ scanClient.scannerId = scannerId;
+ scanClientSet.add(scanClient);
doReturn(scanClientSet).when(mScanManager).getBatchScanQueue();
+ doReturn(mApp).when(mScannerMap).getById(scanClient.scannerId);
IScannerCallback callback = mock(IScannerCallback.class);
mApp.mCallback = callback;
mScanController.onBatchScanReportsInternal(
status, scannerId, reportType, numRecords, recordData);
- verify(callback).onBatchScanResults(any());
+ verify(mScanManager).callbackDone(scannerId, status);
+ if (expectResults) {
+ verify(callback).onBatchScanResults(any());
+ } else {
+ verify(callback, never()).onBatchScanResults(any());
+ }
}
@Test
diff --git a/android/pandora/server/src/A2dp.kt b/android/pandora/server/src/A2dp.kt
index 6708dd1c46..0b5a0d6640 100644
--- a/android/pandora/server/src/A2dp.kt
+++ b/android/pandora/server/src/A2dp.kt
@@ -41,7 +41,6 @@ import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filter
@@ -110,10 +109,6 @@ class A2dp(val context: Context) : A2DPImplBase(), Closeable {
}
}
- // TODO: b/234891800, AVDTP start request sometimes never sent if playback starts too
- // early.
- delay(2000L)
-
val source =
Source.newBuilder().setCookie(ByteString.copyFrom(device.getAddress(), "UTF-8"))
OpenSourceResponse.newBuilder().setSource(source).build()
@@ -147,10 +142,6 @@ class A2dp(val context: Context) : A2DPImplBase(), Closeable {
}
}
- // TODO: b/234891800, AVDTP start request sometimes never sent if playback starts too
- // early.
- delay(2000L)
-
val source =
Source.newBuilder().setCookie(ByteString.copyFrom(device.getAddress(), "UTF-8"))
WaitSourceResponse.newBuilder().setSource(source).build()
diff --git a/flags/Android.bp b/flags/Android.bp
index 8a1dc66552..80a57891a5 100644
--- a/flags/Android.bp
+++ b/flags/Android.bp
@@ -31,6 +31,7 @@ aconfig_declarations {
"hid.aconfig",
"l2cap.aconfig",
"le_advertising.aconfig",
+ "le_scanning.aconfig",
"leaudio.aconfig",
"mapclient.aconfig",
"mcp.aconfig",
diff --git a/flags/BUILD.gn b/flags/BUILD.gn
index 9f96d85933..da5aa8621b 100644
--- a/flags/BUILD.gn
+++ b/flags/BUILD.gn
@@ -24,6 +24,7 @@ aconfig("bluetooth_flags_c_lib") {
"hid.aconfig",
"l2cap.aconfig",
"le_advertising.aconfig",
+ "le_scanning.aconfig",
"leaudio.aconfig",
"mapclient.aconfig",
"mcp.aconfig",
diff --git a/flags/bta_dm.aconfig b/flags/bta_dm.aconfig
index d91f3168b7..287a0f0187 100644
--- a/flags/bta_dm.aconfig
+++ b/flags/bta_dm.aconfig
@@ -9,13 +9,6 @@ flag {
}
flag {
- name: "bta_dm_discover_both"
- namespace: "bluetooth"
- description: "perform both LE and Classic service discovery simulteanously on capable devices"
- bug: "339217881"
-}
-
-flag {
name: "cancel_open_discovery_client"
namespace: "bluetooth"
description: "Cancel connection from discovery client correctly"
diff --git a/flags/gap.aconfig b/flags/gap.aconfig
index 8fe10911d5..5da5144aa1 100644
--- a/flags/gap.aconfig
+++ b/flags/gap.aconfig
@@ -281,3 +281,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "batch_scan_optimization"
+ namespace: "bluetooth"
+ description: "Optimized batch scan for less wakeups"
+ bug: "392132489"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/flags/hfp.aconfig b/flags/hfp.aconfig
index 7b81b483b4..026c3b22b3 100644
--- a/flags/hfp.aconfig
+++ b/flags/hfp.aconfig
@@ -100,16 +100,6 @@ flag {
}
flag {
- name: "hfp_allow_volume_change_without_sco"
- namespace: "bluetooth"
- description: "Allow Audio Fwk to change SCO volume when HFP profile is connected and SCO not connected"
- bug: "362313390"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "choose_wrong_hfp_codec_in_specific_config"
namespace: "bluetooth"
description: "Flag to fix codec selection in nego when the peer device only support NB and SWB."
diff --git a/flags/le_scanning.aconfig b/flags/le_scanning.aconfig
new file mode 100644
index 0000000000..0b4985e45e
--- /dev/null
+++ b/flags/le_scanning.aconfig
@@ -0,0 +1,12 @@
+package: "com.android.bluetooth.flags"
+container: "com.android.bt"
+
+flag {
+ name: "scan_results_in_main_thread"
+ namespace: "bluetooth"
+ description: "Use main thread for handling scan results"
+ bug: "392693506"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 5a506f21fb..60657262e3 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -53,7 +53,7 @@ package android.bluetooth {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void disableOptionalCodecs(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void enableOptionalCodecs(@NonNull android.bluetooth.BluetoothDevice);
method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BufferConstraints getBufferConstraints();
- method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothCodecStatus getCodecStatus(@NonNull android.bluetooth.BluetoothDevice);
+ method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true) public android.bluetooth.BluetoothCodecStatus getCodecStatus(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getDynamicBufferSupport();
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int isOptionalCodecsEnabled(@NonNull android.bluetooth.BluetoothDevice);
diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java
index 5bcd0789ab..54f2c702aa 100644
--- a/framework/java/android/bluetooth/BluetoothA2dp.java
+++ b/framework/java/android/bluetooth/BluetoothA2dp.java
@@ -724,20 +724,23 @@ public final class BluetoothA2dp implements BluetoothProfile {
/**
* Gets the current codec status (configuration and capability).
*
+ * <p>This method requires the calling app to have the {@link
+ * android.Manifest.permission#BLUETOOTH_CONNECT} permission. Additionally, an app must either
+ * have the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} or be associated with the
+ * Companion Device manager (see {@link android.companion.CompanionDeviceManager#associate(
+ * AssociationRequest, android.companion.CompanionDeviceManager.Callback, Handler)})
+ *
* @param device the remote Bluetooth device.
* @return the current codec status
* @hide
*/
@SystemApi
- @Nullable
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
@RequiresPermission(
- allOf = {
- BLUETOOTH_CONNECT,
- BLUETOOTH_PRIVILEGED,
- })
- public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
+ allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED},
+ conditional = true)
+ public @Nullable BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
verifyDeviceNotNull(device, "getCodecStatus");
final IBluetoothA2dp service = getService();
diff --git a/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt b/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt
index e658e8c645..ea41c55ee0 100644
--- a/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt
+++ b/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt
@@ -226,6 +226,51 @@ public class DckL2capTest() : Closeable {
Log.d(TAG, "testReceive: done")
}
+ @Test
+ @VirtualOnly
+ fun testReadReturnOnRemoteSocketDisconnect() {
+ Log.d(TAG, "testReadReturnonSocketDisconnect: Connect L2CAP")
+ var bluetoothSocket: BluetoothSocket?
+ val l2capServer = bluetoothAdapter.listenUsingInsecureL2capChannel()
+ val socketFlow = flow { emit(l2capServer.accept()) }
+ val connectResponse = createAndConnectL2capChannelWithBumble(l2capServer.psm)
+ runBlocking {
+ bluetoothSocket = socketFlow.first()
+ assertThat(connectResponse.hasChannel()).isTrue()
+ }
+
+ val inputStream = bluetoothSocket!!.inputStream
+
+ // block on read() on server thread
+ val readThread = Thread {
+ Log.d(TAG, "testReadReturnOnRemoteSocketDisconnect: Receive data on Android")
+ val ret = inputStream.read()
+ Log.d(TAG, "testReadReturnOnRemoteSocketDisconnect: read returns : " + ret)
+ Log.d(
+ TAG,
+ "testReadReturnOnRemoteSocketDisconnect: isConnected() : " +
+ bluetoothSocket!!.isConnected(),
+ )
+ assertThat(ret).isEqualTo(-1)
+ assertThat(bluetoothSocket!!.isConnected()).isFalse()
+ }
+ readThread.start()
+ // check that socket is still connected
+ assertThat(bluetoothSocket!!.isConnected()).isTrue()
+
+ // read() would be blocking till underlying l2cap is disconnected
+ Thread.sleep(1000 * 10)
+ Log.d(TAG, "testReadReturnOnRemoteSocketDisconnect: disconnect after 10 secs")
+ val disconnectRequest =
+ DisconnectRequest.newBuilder().setChannel(connectResponse.channel).build()
+ val disconnectResponse = mBumble.l2capBlocking().disconnect(disconnectRequest)
+ assertThat(disconnectResponse.hasSuccess()).isTrue()
+ inputStream.close()
+ bluetoothSocket?.close()
+ l2capServer.close()
+ Log.d(TAG, "testReadReturnOnRemoteSocketDisconnect: done")
+ }
+
private fun createAndConnectL2capChannelWithBumble(psm: Int): ConnectResponse {
Log.d(TAG, "createAndConnectL2capChannelWithBumble")
val remoteDevice =
diff --git a/offload/hci/data.rs b/offload/hci/data.rs
index 0e20e82029..bb4a452a59 100644
--- a/offload/hci/data.rs
+++ b/offload/hci/data.rs
@@ -120,7 +120,7 @@ impl<'a> IsoData<'a> {
}
}
-impl<'a> Write for IsoData<'a> {
+impl Write for IsoData<'_> {
fn write(&self, w: &mut Writer) {
let (pb_flag, hdr) = match self.sdu_fragment {
IsoSduFragment::First { ref hdr, is_last: false } => (0b00, Some(hdr)),
diff --git a/system/bta/dm/bta_dm_disc.cc b/system/bta/dm/bta_dm_disc.cc
index e9c6ba5e77..c3977454f5 100644
--- a/system/bta/dm/bta_dm_disc.cc
+++ b/system/bta/dm/bta_dm_disc.cc
@@ -511,7 +511,7 @@ static void bta_dm_gatt_disc_complete(tCONN_ID conn_id, tGATT_STATUS status) {
log::verbose("conn_id = {}, status = {}, sdp_pending = {}, le_pending = {}", conn_id, status,
sdp_pending, le_pending);
- if (com::android::bluetooth::flags::bta_dm_discover_both() && sdp_pending && !le_pending) {
+ if (sdp_pending && !le_pending) {
/* LE Service discovery finished, and services were reported, but SDP is not
* finished yet. gatt_close_timer closed the connection, and we received
* this callback because of disconnection */
@@ -784,8 +784,7 @@ static void bta_dm_disc_sm_execute(tBTA_DM_DISC_EVT event, std::unique_ptr<tBTA_
"bad message type: {}", msg->index());
auto req = std::get<tBTA_DM_API_DISCOVER>(*msg);
- if (com::android::bluetooth::flags::bta_dm_discover_both() &&
- is_same_device(req.bd_addr, bta_dm_discovery_cb.peer_bdaddr)) {
+ if (is_same_device(req.bd_addr, bta_dm_discovery_cb.peer_bdaddr)) {
bta_dm_discover_services(std::get<tBTA_DM_API_DISCOVER>(*msg));
} else {
bta_dm_queue_disc(std::get<tBTA_DM_API_DISCOVER>(*msg));
diff --git a/system/bta/hh/bta_hh_headtracker.cc b/system/bta/hh/bta_hh_headtracker.cc
index c8b3e20f05..0282cb59d0 100644
--- a/system/bta/hh/bta_hh_headtracker.cc
+++ b/system/bta/hh/bta_hh_headtracker.cc
@@ -140,7 +140,10 @@ void bta_hh_headtracker_parse_service(tBTA_HH_DEV_CB* p_dev_cb, const gatt::Serv
bool bta_hh_headtracker_supported(tBTA_HH_DEV_CB* p_dev_cb) {
if (p_dev_cb->hid_srvc.headtracker_support == BTA_HH_UNKNOWN) {
bluetooth::Uuid remote_uuids[BT_MAX_NUM_UUIDS] = {};
- bt_property_t remote_properties = {BT_PROPERTY_UUIDS, sizeof(remote_uuids), &remote_uuids};
+ bt_property_t remote_properties = {com::android::bluetooth::flags::separate_service_storage()
+ ? BT_PROPERTY_UUIDS_LE
+ : BT_PROPERTY_UUIDS,
+ sizeof(remote_uuids), &remote_uuids};
const RawAddress& bd_addr = p_dev_cb->link_spec.addrt.bda;
p_dev_cb->hid_srvc.headtracker_support = BTA_HH_UNAVAILABLE;
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index 8d8ca50ab0..cc0a79690c 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -1084,7 +1084,7 @@ public:
/* Note, that this type is actually LONG WRITE.
* Meaning all the Prepare Writes plus Execute is handled in the stack
*/
- write_type = GATT_WRITE_PREPARE;
+ write_type = GATT_WRITE;
}
BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ctp_hdls_.val_hdl,
diff --git a/system/bta/test/bta_disc_test.cc b/system/bta/test/bta_disc_test.cc
index 58421f2962..3a1dbc9882 100644
--- a/system/bta/test/bta_disc_test.cc
+++ b/system/bta/test/bta_disc_test.cc
@@ -219,61 +219,7 @@ int gatt_service_cb_both_call_cnt = 0;
/* This test exercises the usual service discovery flow when bonding to
* dual-mode, CTKD capable device on LE transport.
*/
-TEST_F_WITH_FLAGS(BtaInitializedTest, bta_dm_disc_both_transports_flag_disabled,
- REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(TEST_BT, bta_dm_discover_both))) {
- bta_dm_disc_start(true);
-
- std::promise<void> gatt_triggered;
- int gatt_call_cnt = 0;
- base::RepeatingCallback<void(const RawAddress&)> gatt_performer =
- base::BindLambdaForTesting([&](const RawAddress& /*bd_addr*/) {
- gatt_call_cnt++;
- gatt_triggered.set_value();
- });
- bta_dm_disc_override_gatt_performer_for_testing(gatt_performer);
-
- int sdp_call_cnt = 0;
- base::RepeatingCallback<void(tBTA_DM_SDP_STATE*)> sdp_performer =
- base::BindLambdaForTesting([&](tBTA_DM_SDP_STATE* /*sdp_state*/) { sdp_call_cnt++; });
- bta_dm_disc_override_sdp_performer_for_testing(sdp_performer);
-
- gatt_service_cb_both_call_cnt = 0;
- service_cb_both_call_cnt = 0;
-
- bta_dm_disc_start_service_discovery(
- {[](RawAddress, std::vector<bluetooth::Uuid>&, bool) {}, nullptr,
- [](RawAddress /*addr*/, const std::vector<bluetooth::Uuid>&, tBTA_STATUS) {
- service_cb_both_call_cnt++;
- }},
- kRawAddress, BT_TRANSPORT_BR_EDR);
- EXPECT_EQ(sdp_call_cnt, 1);
-
- bta_dm_disc_start_service_discovery(
- {[](RawAddress, std::vector<bluetooth::Uuid>&, bool) { gatt_service_cb_both_call_cnt++; },
- nullptr, [](RawAddress /*addr*/, const std::vector<bluetooth::Uuid>&, tBTA_STATUS) {}},
- kRawAddress, BT_TRANSPORT_LE);
-
- // GATT discovery is queued, until SDP finishes
- EXPECT_EQ(gatt_call_cnt, 0);
-
- bta_dm_sdp_finished(kRawAddress, BTA_SUCCESS, {}, {});
- EXPECT_EQ(service_cb_both_call_cnt, 1);
-
- // SDP finished, wait until GATT is triggered.
- EXPECT_EQ(std::future_status::ready,
- gatt_triggered.get_future().wait_for(std::chrono::seconds(1)));
- bta_dm_gatt_finished(kRawAddress, BTA_SUCCESS);
- EXPECT_EQ(gatt_service_cb_both_call_cnt, 1);
-
- bta_dm_disc_override_sdp_performer_for_testing({});
- bta_dm_disc_override_gatt_performer_for_testing({});
-}
-
-/* This test exercises the usual service discovery flow when bonding to
- * dual-mode, CTKD capable device on LE transport.
- */
-TEST_F_WITH_FLAGS(BtaInitializedTest, bta_dm_disc_both_transports_flag_enabled,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, bta_dm_discover_both))) {
+TEST_F(BtaInitializedTest, bta_dm_disc_both_transports) {
bta_dm_disc_start(true);
int gatt_call_cnt = 0;
diff --git a/system/btif/include/btif_dm.h b/system/btif/include/btif_dm.h
index b165baa441..9069c97b76 100644
--- a/system/btif/include/btif_dm.h
+++ b/system/btif/include/btif_dm.h
@@ -151,6 +151,7 @@ void btif_dm_get_ble_local_keys(tBTA_DM_BLE_LOCAL_KEY_MASK* p_key_mask, Octet16*
tBTA_BLE_LOCAL_ID_KEYS* p_id_keys);
void btif_update_remote_properties(const RawAddress& bd_addr, BD_NAME bd_name, DEV_CLASS dev_class,
tBT_DEVICE_TYPE dev_type);
+bool btif_is_interesting_le_service(const bluetooth::Uuid& uuid);
bool check_cod_hid(const RawAddress& bd_addr);
bool check_cod_hid_major(const RawAddress& bd_addr, uint32_t cod);
diff --git a/system/btif/include/btif_storage.h b/system/btif/include/btif_storage.h
index 0c1043faae..01432bade6 100644
--- a/system/btif/include/btif_storage.h
+++ b/system/btif/include/btif_storage.h
@@ -446,6 +446,7 @@ bt_status_t btif_storage_set_hid_connection_policy(const tAclLinkSpec& link_spec
bt_status_t btif_storage_get_hid_connection_policy(const tAclLinkSpec& link_spec,
bool* reconnect_allowed);
+void btif_storage_migrate_services();
/******************************************************************************
* Exported for unit tests
*****************************************************************************/
diff --git a/system/btif/src/btif_core.cc b/system/btif/src/btif_core.cc
index f0d06373b6..8e481daed0 100644
--- a/system/btif/src/btif_core.cc
+++ b/system/btif/src/btif_core.cc
@@ -134,7 +134,12 @@ int btif_is_enabled(void) {
return (!btif_is_dut_mode()) && (stack_manager_get_interface()->get_stack_is_running());
}
-void btif_init_ok() { btif_dm_load_ble_local_keys(); }
+void btif_init_ok() {
+ btif_dm_load_ble_local_keys();
+ if (com::android::bluetooth::flags::separate_service_storage()) {
+ btif_storage_migrate_services();
+ }
+}
/*******************************************************************************
*
@@ -290,7 +295,7 @@ void btif_dut_mode_send(uint16_t opcode, uint8_t* buf, uint8_t len) {
****************************************************************************/
static bt_status_t btif_in_get_adapter_properties(void) {
- const static uint32_t NUM_ADAPTER_PROPERTIES = 5;
+ static const uint32_t NUM_ADAPTER_PROPERTIES = 5;
bt_property_t properties[NUM_ADAPTER_PROPERTIES];
uint32_t num_props = 0;
@@ -340,12 +345,13 @@ static bt_status_t btif_in_get_adapter_properties(void) {
}
static bt_status_t btif_in_get_remote_device_properties(RawAddress* bd_addr) {
- bt_property_t remote_properties[8];
+ bt_property_t remote_properties[9];
uint32_t num_props = 0;
bt_bdname_t name, alias;
uint32_t cod, devtype;
Uuid remote_uuids[BT_MAX_NUM_UUIDS];
+ Uuid remote_uuids_le[BT_MAX_NUM_UUIDS];
memset(remote_properties, 0, sizeof(remote_properties));
BTIF_STORAGE_FILL_PROPERTY(&remote_properties[num_props], BT_PROPERTY_BDNAME, sizeof(name),
@@ -369,10 +375,17 @@ static bt_status_t btif_in_get_remote_device_properties(RawAddress* bd_addr) {
num_props++;
BTIF_STORAGE_FILL_PROPERTY(&remote_properties[num_props], BT_PROPERTY_UUIDS, sizeof(remote_uuids),
- remote_uuids);
+ &remote_uuids);
btif_storage_get_remote_device_property(bd_addr, &remote_properties[num_props]);
num_props++;
+ if (com::android::bluetooth::flags::separate_service_storage()) {
+ BTIF_STORAGE_FILL_PROPERTY(&remote_properties[num_props], BT_PROPERTY_UUIDS_LE,
+ sizeof(remote_uuids_le), &remote_uuids_le);
+ btif_storage_get_remote_device_property(bd_addr, &remote_properties[num_props]);
+ num_props++;
+ }
+
GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb(
BT_STATUS_SUCCESS, *bd_addr, num_props, remote_properties);
diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc
index a023c22970..6f83aee781 100644
--- a/system/btif/src/btif_dm.cc
+++ b/system/btif/src/btif_dm.cc
@@ -1517,15 +1517,16 @@ static void btif_dm_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH*
}
/* Returns true if |uuid| should be passed as device property */
-static bool btif_is_interesting_le_service(bluetooth::Uuid uuid) {
+bool btif_is_interesting_le_service(const bluetooth::Uuid& uuid) {
return uuid.As16Bit() == UUID_SERVCLASS_LE_HID || uuid == UUID_HEARING_AID || uuid == UUID_VC ||
uuid == UUID_CSIS || uuid == UUID_LE_AUDIO || uuid == UUID_LE_MIDI || uuid == UUID_HAS ||
uuid == UUID_BASS || uuid == UUID_BATTERY || uuid == ANDROID_HEADTRACKER_SERVICE_UUID;
}
-static bt_status_t btif_get_existing_uuids(RawAddress* bd_addr, Uuid* existing_uuids) {
+static bt_status_t btif_get_existing_uuids(RawAddress* bd_addr, Uuid* existing_uuids,
+ bt_property_type_t property_type = BT_PROPERTY_UUIDS) {
bt_property_t tmp_prop;
- BTIF_STORAGE_FILL_PROPERTY(&tmp_prop, BT_PROPERTY_UUIDS, sizeof(*existing_uuids), existing_uuids);
+ BTIF_STORAGE_FILL_PROPERTY(&tmp_prop, property_type, sizeof(*existing_uuids), existing_uuids);
return btif_storage_get_remote_device_property(bd_addr, &tmp_prop);
}
@@ -1537,9 +1538,10 @@ static bool btif_is_gatt_service_discovery_post_pairing(const RawAddress bd_addr
(pairing_cb.gatt_over_le == btif_dm_pairing_cb_t::ServiceDiscoveryState::SCHEDULED);
}
-static void btif_merge_existing_uuids(RawAddress& addr, std::set<Uuid>* uuids) {
+static void btif_merge_existing_uuids(RawAddress& addr, std::set<Uuid>* uuids,
+ bt_property_type_t property_type = BT_PROPERTY_UUIDS) {
Uuid existing_uuids[BT_MAX_NUM_UUIDS] = {};
- bt_status_t lookup_result = btif_get_existing_uuids(&addr, existing_uuids);
+ bt_status_t lookup_result = btif_get_existing_uuids(&addr, existing_uuids, property_type);
if (lookup_result == BT_STATUS_FAIL) {
return;
@@ -1550,18 +1552,14 @@ static void btif_merge_existing_uuids(RawAddress& addr, std::set<Uuid>* uuids) {
if (btif_should_ignore_uuid(uuid)) {
continue;
}
- if (btif_is_interesting_le_service(uuid)) {
- log::info("interesting le service {} insert", uuid.ToString());
- uuids->insert(uuid);
- }
+
+ uuids->insert(uuid);
}
}
static void btif_on_service_discovery_results(RawAddress bd_addr,
const std::vector<bluetooth::Uuid>& uuids_param,
tBTA_STATUS result) {
- bt_property_t prop;
- std::vector<uint8_t> property_value;
std::set<Uuid> uuids;
bool a2dp_sink_capable = false;
@@ -1589,8 +1587,12 @@ static void btif_on_service_discovery_results(RawAddress bd_addr,
pairing_cb.sdp_over_classic = btif_dm_pairing_cb_t::ServiceDiscoveryState::FINISHED;
}
- prop.type = BT_PROPERTY_UUIDS;
- prop.len = 0;
+ std::vector<uint8_t> bredr_property_value;
+ std::vector<uint8_t> le_property_value;
+ bt_property_t uuid_props[2] = {};
+ bt_property_t& bredr_prop = uuid_props[0];
+ bt_property_t& le_prop = uuid_props[1];
+
if ((result == BTA_SUCCESS) && !uuids_param.empty()) {
log::info("New UUIDs for {}:", bd_addr);
for (const auto& uuid : uuids_param) {
@@ -1610,13 +1612,35 @@ static void btif_on_service_discovery_results(RawAddress bd_addr,
for (auto& uuid : uuids) {
auto uuid_128bit = uuid.To128BitBE();
- property_value.insert(property_value.end(), uuid_128bit.begin(), uuid_128bit.end());
+ bredr_property_value.insert(bredr_property_value.end(), uuid_128bit.begin(),
+ uuid_128bit.end());
if (uuid == UUID_A2DP_SINK) {
a2dp_sink_capable = true;
}
}
- prop.val = (void*)property_value.data();
- prop.len = Uuid::kNumBytes128 * uuids.size();
+
+ bredr_prop = {BT_PROPERTY_UUIDS, static_cast<int>(Uuid::kNumBytes128 * uuids.size()),
+ (void*)bredr_property_value.data()};
+
+ if (com::android::bluetooth::flags::separate_service_storage()) {
+ bt_status_t ret = btif_storage_set_remote_device_property(&bd_addr, &bredr_prop);
+ ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote classic services failed", ret);
+
+ std::set<Uuid> le_uuids;
+ if (results_for_bonding_device) {
+ btif_merge_existing_uuids(pairing_cb.static_bdaddr, &le_uuids, BT_PROPERTY_UUIDS_LE);
+ btif_merge_existing_uuids(pairing_cb.bd_addr, &le_uuids, BT_PROPERTY_UUIDS_LE);
+ } else {
+ btif_merge_existing_uuids(bd_addr, &le_uuids, BT_PROPERTY_UUIDS_LE);
+ }
+
+ for (auto& uuid : le_uuids) {
+ auto uuid_128bit = uuid.To128BitBE();
+ le_property_value.insert(le_property_value.end(), uuid_128bit.begin(), uuid_128bit.end());
+ }
+ le_prop = {BT_PROPERTY_UUIDS_LE, static_cast<int>(Uuid::kNumBytes128 * le_uuids.size()),
+ (void*)le_property_value.data()};
+ }
}
bool skip_reporting_wait_for_le = false;
@@ -1649,17 +1673,18 @@ static void btif_on_service_discovery_results(RawAddress bd_addr,
log::info("SDP failed, send {} EIR UUIDs to unblock bonding {}", num_eir_uuids, bd_addr);
for (auto eir_uuid : uuids_iter->second) {
auto uuid_128bit = eir_uuid.To128BitBE();
- property_value.insert(property_value.end(), uuid_128bit.begin(), uuid_128bit.end());
+ bredr_property_value.insert(bredr_property_value.end(), uuid_128bit.begin(),
+ uuid_128bit.end());
}
eir_uuids_cache.erase(uuids_iter);
}
if (num_eir_uuids > 0) {
- prop.val = (void*)property_value.data();
- prop.len = num_eir_uuids * Uuid::kNumBytes128;
+ bredr_prop.val = (void*)bredr_property_value.data();
+ bredr_prop.len = num_eir_uuids * Uuid::kNumBytes128;
} else {
log::warn("SDP failed and we have no EIR UUIDs to report either");
- prop.val = &uuid;
- prop.len = Uuid::kNumBytes128;
+ bredr_prop.val = &uuid;
+ bredr_prop.len = Uuid::kNumBytes128;
}
}
@@ -1677,9 +1702,10 @@ static void btif_on_service_discovery_results(RawAddress bd_addr,
uuids_param.size(), num_eir_uuids));
if (!uuids_param.empty() || num_eir_uuids != 0) {
- /* Also write this to the NVRAM */
- const bt_status_t ret = btif_storage_set_remote_device_property(&bd_addr, &prop);
- ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed", ret);
+ if (!com::android::bluetooth::flags::separate_service_storage()) {
+ const bt_status_t ret = btif_storage_set_remote_device_property(&bd_addr, &bredr_prop);
+ ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed", ret);
+ }
if (skip_reporting_wait_for_le) {
log::info(
@@ -1694,16 +1720,13 @@ static void btif_on_service_discovery_results(RawAddress bd_addr,
}
/* Send the event to the BTIF */
- GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb(BT_STATUS_SUCCESS, bd_addr,
- 1, &prop);
+ GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb(
+ BT_STATUS_SUCCESS, bd_addr, ARRAY_SIZE(uuid_props), uuid_props);
}
}
static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid>& services,
bool is_transport_le) {
- std::vector<bt_property_t> prop;
- std::vector<uint8_t> property_value;
- std::set<Uuid> uuids;
RawAddress static_addr_copy = pairing_cb.static_bdaddr;
bool lea_supported = is_le_audio_capable_during_service_discovery(bd_addr);
@@ -1739,6 +1762,7 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid
BTM_LogHistory(kBtmLogTag, bd_addr, "Discovered GATT services using SDP transport");
}
+ std::set<Uuid> uuids;
for (Uuid uuid : services) {
if (btif_is_interesting_le_service(uuid)) {
if (btif_should_ignore_uuid(uuid)) {
@@ -1767,46 +1791,28 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid
log::info("Will return Classic SDP results, if done, to unblock bonding");
}
- Uuid existing_uuids[BT_MAX_NUM_UUIDS] = {};
-
- // Look up UUIDs using pseudo address (either RPA or static address)
- bt_status_t existing_lookup_result = btif_get_existing_uuids(&bd_addr, existing_uuids);
-
- if (existing_lookup_result != BT_STATUS_FAIL) {
- log::info("Got some existing UUIDs by address {}", bd_addr);
-
- for (int i = 0; i < BT_MAX_NUM_UUIDS; i++) {
- Uuid uuid = existing_uuids[i];
- if (uuid.IsEmpty()) {
- continue;
- }
- uuids.insert(uuid);
+ if (!com::android::bluetooth::flags::separate_service_storage()) {
+ // Look up UUIDs using pseudo address (either RPA or static address)
+ btif_merge_existing_uuids(bd_addr, &uuids);
+ if (bd_addr != static_addr_copy) {
+ // Look up UUID using static address, if different than sudo address
+ btif_merge_existing_uuids(static_addr_copy, &uuids);
}
}
- if (bd_addr != static_addr_copy) {
- // Look up UUID using static address, if different than sudo address
- existing_lookup_result = btif_get_existing_uuids(&static_addr_copy, existing_uuids);
- if (existing_lookup_result != BT_STATUS_FAIL) {
- log::info("Got some existing UUIDs by static address {}", static_addr_copy);
- for (int i = 0; i < BT_MAX_NUM_UUIDS; i++) {
- Uuid uuid = existing_uuids[i];
- if (uuid.IsEmpty()) {
- continue;
- }
- uuids.insert(uuid);
- }
- }
- }
+ std::vector<bt_property_t> prop;
+ std::vector<uint8_t> property_value;
for (auto& uuid : uuids) {
auto uuid_128bit = uuid.To128BitBE();
property_value.insert(property_value.end(), uuid_128bit.begin(), uuid_128bit.end());
}
- prop.push_back(bt_property_t{BT_PROPERTY_UUIDS,
- static_cast<int>(Uuid::kNumBytes128 * uuids.size()),
- (void*)property_value.data()});
+ prop.push_back(bt_property_t{
+ (com::android::bluetooth::flags::separate_service_storage() && is_transport_le)
+ ? BT_PROPERTY_UUIDS_LE
+ : BT_PROPERTY_UUIDS,
+ static_cast<int>(Uuid::kNumBytes128 * uuids.size()), (void*)property_value.data()});
/* Also write this to the NVRAM */
bt_status_t ret = btif_storage_set_remote_device_property(&bd_addr, &prop[0]);
@@ -1817,8 +1823,7 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid
* send them with rest of SDP results in on_service_discovery_results */
return;
} else {
- if (pairing_cb.sdp_over_classic == btif_dm_pairing_cb_t::ServiceDiscoveryState::SCHEDULED &&
- com::android::bluetooth::flags::bta_dm_discover_both()) {
+ if (pairing_cb.sdp_over_classic == btif_dm_pairing_cb_t::ServiceDiscoveryState::SCHEDULED) {
/* Don't report services yet, they will be reported together once SDP
* finishes. */
log::info("will report services later, with SDP results {}", bd_addr);
@@ -1826,6 +1831,32 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid
}
}
+ if (!com::android::bluetooth::flags::separate_service_storage()) {
+ /* Send the event to the BTIF */
+ GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb(BT_STATUS_SUCCESS, bd_addr,
+ prop.size(), prop.data());
+ return;
+ }
+
+ std::set<Uuid> bredr_uuids;
+ // Look up UUIDs using pseudo address (either RPA or static address)
+ btif_merge_existing_uuids(bd_addr, &bredr_uuids);
+ if (bd_addr != static_addr_copy) {
+ // Look up UUID using static address, if different than sudo address
+ btif_merge_existing_uuids(static_addr_copy, &bredr_uuids);
+ }
+
+ std::vector<uint8_t> bredr_property_value;
+
+ for (auto& uuid : bredr_uuids) {
+ auto uuid_128bit = uuid.To128BitBE();
+ bredr_property_value.insert(bredr_property_value.end(), uuid_128bit.begin(), uuid_128bit.end());
+ }
+
+ prop.push_back(bt_property_t{BT_PROPERTY_UUIDS,
+ static_cast<int>(Uuid::kNumBytes128 * bredr_uuids.size()),
+ (void*)bredr_property_value.data()});
+
/* Send the event to the BTIF */
GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb(BT_STATUS_SUCCESS, bd_addr,
prop.size(), prop.data());
diff --git a/system/btif/src/btif_storage.cc b/system/btif/src/btif_storage.cc
index e51756483c..4704362f2c 100644
--- a/system/btif/src/btif_storage.cc
+++ b/system/btif/src/btif_storage.cc
@@ -47,6 +47,7 @@
#include "btif/include/btif_api.h"
#include "btif/include/btif_config.h"
+#include "btif/include/btif_dm.h"
#include "btif/include/btif_util.h"
#include "btif/include/core_callbacks.h"
#include "btif/include/stack_manager_t.h"
@@ -179,15 +180,18 @@ static bool prop2cfg(const RawAddress* remote_bd_addr, bt_property_t* prop) {
case BT_PROPERTY_TYPE_OF_DEVICE:
btif_config_set_int(bdstr, BTIF_STORAGE_KEY_DEV_TYPE, *reinterpret_cast<int*>(prop->val));
break;
- case BT_PROPERTY_UUIDS: {
+ case BT_PROPERTY_UUIDS:
+ case BT_PROPERTY_UUIDS_LE: {
std::string val;
size_t cnt = (prop->len) / sizeof(Uuid);
for (size_t i = 0; i < cnt; i++) {
val += (reinterpret_cast<Uuid*>(prop->val) + i)->ToString() + " ";
}
- btif_config_set_str(bdstr, BTIF_STORAGE_KEY_REMOTE_SERVICE, val);
- break;
- }
+ std::string key = (prop->type == BT_PROPERTY_UUIDS_LE) ? BTIF_STORAGE_KEY_REMOTE_SERVICE_LE
+ : BTIF_STORAGE_KEY_REMOTE_SERVICE;
+ btif_config_set_str(bdstr, key, val);
+ } break;
+
case BT_PROPERTY_REMOTE_VERSION_INFO: {
bt_remote_version_t* info = reinterpret_cast<bt_remote_version_t*>(prop->val);
@@ -300,10 +304,15 @@ static bool cfg2prop(const RawAddress* remote_bd_addr, bt_property_t* prop) {
reinterpret_cast<int*>(prop->val));
}
break;
- case BT_PROPERTY_UUIDS: {
+ case BT_PROPERTY_UUIDS:
+ case BT_PROPERTY_UUIDS_LE: {
char value[1280];
int size = sizeof(value);
- if (btif_config_get_str(bdstr, BTIF_STORAGE_KEY_REMOTE_SERVICE, value, &size)) {
+
+ std::string key = (prop->type == BT_PROPERTY_UUIDS_LE) ? BTIF_STORAGE_KEY_REMOTE_SERVICE_LE
+ : BTIF_STORAGE_KEY_REMOTE_SERVICE;
+
+ if (btif_config_get_str(bdstr, key, value, &size)) {
Uuid* p_uuid = reinterpret_cast<Uuid*>(prop->val);
size_t num_uuids = btif_split_uuids_string(value, p_uuid, BT_MAX_NUM_UUIDS);
prop->len = num_uuids * sizeof(Uuid);
@@ -938,13 +947,14 @@ bt_status_t btif_storage_load_bonded_devices(void) {
uint32_t i = 0;
bt_property_t adapter_props[6];
uint32_t num_props = 0;
- bt_property_t remote_properties[10];
+ bt_property_t remote_properties[11];
RawAddress addr;
bt_bdname_t name, alias, model_name;
bt_scan_mode_t mode;
uint32_t disc_timeout;
Uuid local_uuids[BT_MAX_NUM_UUIDS];
Uuid remote_uuids[BT_MAX_NUM_UUIDS];
+ Uuid remote_uuids_le[BT_MAX_NUM_UUIDS];
bt_status_t status;
remove_devices_with_sample_ltk();
@@ -1026,10 +1036,16 @@ bt_status_t btif_storage_load_bonded_devices(void) {
sizeof(devtype), &remote_properties[num_props]);
num_props++;
- btif_storage_get_remote_prop(p_remote_addr, BT_PROPERTY_UUIDS, remote_uuids,
+ btif_storage_get_remote_prop(p_remote_addr, BT_PROPERTY_UUIDS, &remote_uuids,
sizeof(remote_uuids), &remote_properties[num_props]);
num_props++;
+ if (com::android::bluetooth::flags::separate_service_storage()) {
+ btif_storage_get_remote_prop(p_remote_addr, BT_PROPERTY_UUIDS_LE, &remote_uuids_le,
+ sizeof(remote_uuids_le), &remote_properties[num_props]);
+ num_props++;
+ }
+
// Floss needs appearance for metrics purposes
uint16_t appearance = 0;
if (btif_storage_get_remote_prop(p_remote_addr, BT_PROPERTY_APPEARANCE, &appearance,
@@ -1438,6 +1454,48 @@ void btif_storage_remove_gatt_cl_db_hash(const RawAddress& bd_addr) {
bd_addr));
}
+// TODO(b/369381361) Remove this function after all devices are migrated
+void btif_storage_migrate_services() {
+ for (const auto& mac_address : btif_config_get_paired_devices()) {
+ auto addr_str = mac_address.ToString();
+
+ int device_type = BT_DEVICE_TYPE_UNKNOWN;
+ btif_config_get_int(addr_str, BTIF_STORAGE_KEY_DEV_TYPE, &device_type);
+
+ if ((device_type == BT_DEVICE_TYPE_BREDR) ||
+ btif_config_exist(addr_str, BTIF_STORAGE_KEY_REMOTE_SERVICE_LE)) {
+ /* Classic only, or already migrated entries don't need migration */
+ continue;
+ }
+
+ bt_property_t remote_uuids_prop;
+ Uuid remote_uuids[BT_MAX_NUM_UUIDS];
+ BTIF_STORAGE_FILL_PROPERTY(&remote_uuids_prop, BT_PROPERTY_UUIDS, sizeof(remote_uuids),
+ remote_uuids);
+ btif_storage_get_remote_device_property(&mac_address, &remote_uuids_prop);
+
+ log::info("Will migrate Services => ServicesLe for {}", mac_address.ToStringForLogging());
+
+ std::vector<uint8_t> property_value;
+ for (auto& uuid : remote_uuids) {
+ if (!btif_is_interesting_le_service(uuid)) {
+ continue;
+ }
+
+ log::info("interesting LE service: {}", uuid);
+ auto uuid_128bit = uuid.To128BitBE();
+ property_value.insert(property_value.end(), uuid_128bit.begin(), uuid_128bit.end());
+ }
+
+ bt_property_t le_uuids_prop{BT_PROPERTY_UUIDS_LE, static_cast<int>(property_value.size()),
+ (void*)property_value.data()};
+
+ /* Write LE services to storage */
+ btif_storage_set_remote_device_property(&mac_address, &le_uuids_prop);
+ log::info("Migration finished for {}", mac_address.ToStringForLogging());
+ }
+}
+
void btif_debug_linkkey_type_dump(int fd) {
dprintf(fd, "\nLink Key Types:\n");
for (const auto& bd_addr : btif_config_get_paired_devices()) {
diff --git a/system/btif/src/btif_util.cc b/system/btif/src/btif_util.cc
index 85c0069eaa..c6cf544d4a 100644
--- a/system/btif/src/btif_util.cc
+++ b/system/btif/src/btif_util.cc
@@ -129,6 +129,7 @@ std::string dump_property_type(bt_property_type_t type) {
CASE_RETURN_STRING(BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT);
CASE_RETURN_STRING(BT_PROPERTY_ADAPTER_BONDED_DEVICES);
CASE_RETURN_STRING(BT_PROPERTY_REMOTE_FRIENDLY_NAME);
+ CASE_RETURN_STRING(BT_PROPERTY_UUIDS_LE);
default:
RETURN_UNKNOWN_TYPE_STRING(bt_property_type_t, type);
}
diff --git a/system/btif/test/btif_core_test.cc b/system/btif/test/btif_core_test.cc
index 66881a92d8..1bffc9ce3a 100644
--- a/system/btif/test/btif_core_test.cc
+++ b/system/btif/test/btif_core_test.cc
@@ -320,6 +320,7 @@ TEST_F(BtifUtilsTest, dump_property_type) {
"BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT"),
std::make_pair(BT_PROPERTY_ADAPTER_BONDED_DEVICES, "BT_PROPERTY_ADAPTER_BONDED_DEVICES"),
std::make_pair(BT_PROPERTY_REMOTE_FRIENDLY_NAME, "BT_PROPERTY_REMOTE_FRIENDLY_NAME"),
+ std::make_pair(BT_PROPERTY_UUIDS_LE, "BT_PROPERTY_UUIDS_LE"),
};
for (const auto& type : types) {
EXPECT_TRUE(dump_property_type(type.first).starts_with(type.second));
diff --git a/system/gd/hci/le_advertising_manager.h b/system/gd/hci/le_advertising_manager.h
index 55245a5cfa..fe614861e2 100644
--- a/system/gd/hci/le_advertising_manager.h
+++ b/system/gd/hci/le_advertising_manager.h
@@ -47,8 +47,8 @@ class AdvertisingConfig {
public:
std::vector<GapData> advertisement;
std::vector<GapData> scan_response;
- uint16_t interval_min;
- uint16_t interval_max;
+ uint32_t interval_min;
+ uint32_t interval_max;
AdvertisingType advertising_type;
AdvertiserAddressType requested_advertiser_address_type;
PeerAddressType peer_address_type;
diff --git a/system/gd/storage/config_keys.h b/system/gd/storage/config_keys.h
index 4629a494ba..d3659b6a96 100644
--- a/system/gd/storage/config_keys.h
+++ b/system/gd/storage/config_keys.h
@@ -111,6 +111,7 @@
#define BTIF_STORAGE_KEY_PIN_LENGTH "PinLength"
#define BTIF_STORAGE_KEY_PRODUCT_ID "ProductId"
#define BTIF_STORAGE_KEY_REMOTE_SERVICE "Service"
+#define BTIF_STORAGE_KEY_REMOTE_SERVICE_LE "ServiceLe"
#define BTIF_STORAGE_KEY_REMOTE_VER_MFCT "Manufacturer"
#define BTIF_STORAGE_KEY_REMOTE_VER_SUBVER "LmpSubVer"
#define BTIF_STORAGE_KEY_REMOTE_VER_VER "LmpVer"
diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h
index a7a3bb0aa2..fa15353d9f 100644
--- a/system/include/hardware/bluetooth.h
+++ b/system/include/hardware/bluetooth.h
@@ -299,7 +299,7 @@ typedef enum {
*/
BT_PROPERTY_TYPE_OF_DEVICE,
/**
- * Description - Bluetooth Service Record
+ * Description - Bluetooth Service Record, UUIDs on BREDR transport
* Access mode - Only GET.
* Data type - bt_service_record_t
*/
@@ -427,6 +427,14 @@ typedef enum {
*/
BT_PROPERTY_LPP_OFFLOAD_FEATURES,
+ /**
+ * Description - Bluetooth Service 128-bit UUIDs on LE transport
+ * Access mode - Only GET.
+ * Data type - Array of bluetooth::Uuid (Array size inferred from property
+ * length).
+ */
+ BT_PROPERTY_UUIDS_LE,
+
BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP = 0xFF,
} bt_property_type_t;
diff --git a/system/test/headless/property.cc b/system/test/headless/property.cc
index fe6e21f1ed..2b5df50ff2 100644
--- a/system/test/headless/property.cc
+++ b/system/test/headless/property.cc
@@ -99,6 +99,10 @@ std::map<::bt_property_type_t,
return new headless::property::void_t(data, len,
BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP);
}},
+ {BT_PROPERTY_UUIDS_LE,
+ [](const uint8_t* data, const size_t len) -> headless::bt_property_t* {
+ return new headless::property::uuid_t(data, len);
+ }},
};
} // namespace
diff --git a/system/test/headless/property.h b/system/test/headless/property.h
index 6af17a244f..1113495f24 100644
--- a/system/test/headless/property.h
+++ b/system/test/headless/property.h
@@ -52,6 +52,7 @@ inline std::string bt_property_type_text(const ::bt_property_type_t type) {
CASE_RETURN_TEXT(BT_PROPERTY_REMOTE_MODEL_NUM);
CASE_RETURN_TEXT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP);
CASE_RETURN_TEXT(BT_PROPERTY_REMOTE_ADDR_TYPE);
+ CASE_RETURN_TEXT(BT_PROPERTY_UUIDS_LE);
CASE_RETURN_TEXT(BT_PROPERTY_RESERVED_0x14);
default:
RETURN_UNKNOWN_TYPE_STRING(::bt_property_type_t, type);