Merge "gtbs: Add AttributionSource to the API"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index b396ce2..3f1b70e 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -7,11 +7,14 @@
[Builtin Hooks Options]
rustfmt = --config-path=rustfmt.toml
+# Only turn on clang-format check for C and C++ headers and sources
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp,hpp
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
-fw android/app/src/com/android/bluetooth/
android/app/lib/mapapi/com/android/bluetooth/mapapi/
android/app/tests/src/com/android/bluetooth/
+ framework/
aosp_first = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} ${PREUPLOAD_FILES}
yapf_hook = ./system/tools/scripts/yapf_checker.py
diff --git a/android/app/AndroidManifest.xml b/android/app/AndroidManifest.xml
index b84bdf9..aa40cab 100644
--- a/android/app/AndroidManifest.xml
+++ b/android/app/AndroidManifest.xml
@@ -422,6 +422,14 @@
<action android:name="android.bluetooth.IBluetoothHearingAid"/>
</intent-filter>
</service>
+ <service android:process="@string/process"
+ android:name=".hap.HapClientService"
+ android:enabled="@bool/profile_supported_hap_client"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothHapClient"/>
+ </intent-filter>
+ </service>
<service
android:process="@string/process"
android:name=".vc.VolumeControlService"
diff --git a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
index bbf46bb..5e3a297 100644
--- a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -1714,6 +1714,22 @@
return sBluetoothInterface->get_metric_id(addr_obj);
}
+static jboolean allowLowLatencyAudioNative(JNIEnv* env, jobject obj,
+ jboolean allowed,
+ jbyteArray address) {
+ ALOGV("%s", __func__);
+ if (!sBluetoothInterface) return false;
+ jbyte* addr = env->GetByteArrayElements(address, nullptr);
+ if (addr == nullptr) {
+ jniThrowIOException(env, EINVAL);
+ return false;
+ }
+ RawAddress addr_obj = {};
+ addr_obj.FromOctets((uint8_t*)addr);
+ sBluetoothInterface->allow_low_latency_audio(allowed, addr_obj);
+ return true;
+}
+
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"classInitNative", "()V", (void*)classInitNative},
@@ -1753,7 +1769,9 @@
{"createSocketChannelNative", "(ILjava/lang/String;[BIII)I",
(void*)createSocketChannelNative},
{"requestMaximumTxDataLengthNative", "([B)V",
- (void*)requestMaximumTxDataLengthNative}};
+ (void*)requestMaximumTxDataLengthNative},
+ {"allowLowLatencyAudioNative", "(Z[B)Z", (void*)allowLowLatencyAudioNative},
+};
int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) {
return jniRegisterNativeMethods(
diff --git a/android/app/jni/com_android_bluetooth_gatt.cpp b/android/app/jni/com_android_bluetooth_gatt.cpp
index fd933f4..9242de4 100644
--- a/android/app/jni/com_android_bluetooth_gatt.cpp
+++ b/android/app/jni/com_android_bluetooth_gatt.cpp
@@ -210,7 +210,8 @@
uint8_t secondary_phy, uint8_t advertising_sid,
int8_t tx_power, int8_t rssi,
uint16_t periodic_adv_int,
- std::vector<uint8_t> adv_data) {
+ std::vector<uint8_t> adv_data,
+ RawAddress* original_bda) {
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
@@ -221,10 +222,13 @@
sCallbackEnv->SetByteArrayRegion(jb.get(), 0, adv_data.size(),
(jbyte*)adv_data.data());
- sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanResult, event_type,
- addr_type, address.get(), primary_phy,
- secondary_phy, advertising_sid, tx_power, rssi,
- periodic_adv_int, jb.get());
+ ScopedLocalRef<jstring> original_address(
+ sCallbackEnv.get(), bdaddr2newjstr(sCallbackEnv.get(), original_bda));
+
+ sCallbackEnv->CallVoidMethod(
+ mCallbacksObj, method_onScanResult, event_type, addr_type, address.get(),
+ primary_phy, secondary_phy, advertising_sid, tx_power, rssi,
+ periodic_adv_int, jb.get(), original_address.get());
}
void btgattc_open_cb(int conn_id, int status, int clientIf,
@@ -916,10 +920,18 @@
sCallbackEnv->SetByteArrayRegion(jb.get(), 0, adv_data.size(),
(jbyte*)adv_data.data());
- sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanResult, event_type,
- addr_type, address.get(), primary_phy,
- secondary_phy, advertising_sid, tx_power, rssi,
- periodic_adv_int, jb.get());
+ // TODO(optedoblivion): Figure out original address for here, use empty
+ // for now
+
+ // length of data + '\0'
+ char empty_address[18] = "00:00:00:00:00:00";
+ ScopedLocalRef<jstring> fake_address(
+ sCallbackEnv.get(), sCallbackEnv->NewStringUTF(empty_address));
+
+ sCallbackEnv->CallVoidMethod(
+ mCallbacksObj, method_onScanResult, event_type, addr_type,
+ address.get(), primary_phy, secondary_phy, advertising_sid, tx_power,
+ rssi, periodic_adv_int, jb.get(), fake_address.get());
}
void OnTrackAdvFoundLost(AdvertisingTrackInfo track_info) {
@@ -994,8 +1006,9 @@
env->GetMethodID(clazz, "onClientRegistered", "(IIJJ)V");
method_onScannerRegistered =
env->GetMethodID(clazz, "onScannerRegistered", "(IIJJ)V");
- method_onScanResult = env->GetMethodID(clazz, "onScanResult",
- "(IILjava/lang/String;IIIIII[B)V");
+ method_onScanResult =
+ env->GetMethodID(clazz, "onScanResult",
+ "(IILjava/lang/String;IIIIII[BLjava/lang/String;)V");
method_onConnected =
env->GetMethodID(clazz, "onConnected", "(IIILjava/lang/String;)V");
method_onDisconnected =
diff --git a/android/app/res/values/config.xml b/android/app/res/values/config.xml
index a34113d..4653c91 100644
--- a/android/app/res/values/config.xml
+++ b/android/app/res/values/config.xml
@@ -37,6 +37,7 @@
<bool name="profile_supported_mcp_server">true</bool>
<bool name="profile_supported_csip_set_coordinator">true</bool>
<bool name="profile_supported_le_call_control">true</bool>
+ <bool name="profile_supported_hap_client">true</bool>
<!-- If true, we will require location to be enabled on the device to
fire Bluetooth LE scan result callbacks in addition to having one
@@ -106,6 +107,7 @@
<integer name="a2dp_source_codec_priority_aptx">3001</integer>
<integer name="a2dp_source_codec_priority_aptx_hd">4001</integer>
<integer name="a2dp_source_codec_priority_ldac">5001</integer>
+ <integer name="a2dp_source_codec_priority_lc3">6001</integer>
<!-- For enabling the AVRCP Target Cover Artowrk feature-->
<bool name="avrcp_target_enable_cover_art">true</bool>
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java b/android/app/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java
index c5c3e56..28c8523 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java
@@ -51,6 +51,8 @@
BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
private @CodecPriority int mA2dpSourceCodecPriorityLdac =
BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
+ private @CodecPriority int mA2dpSourceCodecPriorityLc3 =
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
private BluetoothCodecConfig[] mCodecConfigOffloading = new BluetoothCodecConfig[0];
@@ -231,6 +233,16 @@
mA2dpSourceCodecPriorityLdac = value;
}
+ try {
+ value = resources.getInteger(R.integer.a2dp_source_codec_priority_lc3);
+ } catch (NotFoundException e) {
+ value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
+ }
+ if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) && (value
+ < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) {
+ mA2dpSourceCodecPriorityLc3 = value;
+ }
+
BluetoothCodecConfig codecConfig;
BluetoothCodecConfig[] codecConfigArray =
new BluetoothCodecConfig[BluetoothCodecConfig.getMaxCodecType()];
@@ -259,6 +271,11 @@
.setCodecPriority(mA2dpSourceCodecPriorityLdac)
.build();
codecConfigArray[4] = codecConfig;
+ codecConfig = new BluetoothCodecConfig.Builder()
+ .setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3)
+ .setCodecPriority(mA2dpSourceCodecPriorityLc3)
+ .build();
+ codecConfigArray[5] = codecConfig;
return codecConfigArray;
}
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java
index e463b2d..2e4837e 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java
@@ -127,7 +127,9 @@
private int mDynamicAudioBufferSizeSupportedCodecsGroup2;
private boolean mIsLePeriodicAdvertisingSyncTransferSenderSupported;
+ private boolean mIsLePeriodicAdvertisingSyncTransferRecipientSupported;
private boolean mIsLeConnectedIsochronousStreamCentralSupported;
+ private boolean mIsLeIsochronousBroadcasterSupported;
private List<BufferConstraint> mBufferConstraintList;
@@ -514,6 +516,13 @@
}
/**
+ * @return the mIsLePeriodicAdvertisingSyncTransferRecipientSupported
+ */
+ boolean isLePeriodicAdvertisingSyncTransferRecipientSupported() {
+ return mIsLePeriodicAdvertisingSyncTransferRecipientSupported;
+ }
+
+ /**
* @return the mIsLeConnectedIsochronousStreamCentralSupported
*/
boolean isLeConnectedIsochronousStreamCentralSupported() {
@@ -521,6 +530,13 @@
}
/**
+ * @return the mIsLeIsochronousBroadcasterSupported
+ */
+ boolean isLeIsochronousBroadcasterSupported() {
+ return mIsLeIsochronousBroadcasterSupported;
+ }
+
+ /**
* @return the getLeMaximumAdvertisingDataLength
*/
int getLeMaximumAdvertisingDataLength() {
@@ -982,6 +998,8 @@
((0xFF & ((int) val[23])) << 8) + (0xFF & ((int) val[22]));
mIsLePeriodicAdvertisingSyncTransferSenderSupported = ((0xFF & ((int) val[24])) != 0);
mIsLeConnectedIsochronousStreamCentralSupported = ((0xFF & ((int) val[25])) != 0);
+ mIsLeIsochronousBroadcasterSupported = ((0xFF & ((int) val[26])) != 0);
+ mIsLePeriodicAdvertisingSyncTransferRecipientSupported = ((0xFF & ((int) val[27])) != 0);
Log.d(TAG, "BT_PROPERTY_LOCAL_LE_FEATURES: update from BT controller"
+ " mNumOfAdvertisementInstancesSupported = "
@@ -1005,7 +1023,11 @@
+ " mIsLePeriodicAdvertisingSyncTransferSenderSupported = "
+ mIsLePeriodicAdvertisingSyncTransferSenderSupported
+ " mIsLeConnectedIsochronousStreamCentralSupported = "
- + mIsLeConnectedIsochronousStreamCentralSupported);
+ + mIsLeConnectedIsochronousStreamCentralSupported
+ + " mIsLeIsochronousBroadcasterSupported = "
+ + mIsLeIsochronousBroadcasterSupported
+ + " mIsLePeriodicAdvertisingSyncTransferRecipientSupported = "
+ + mIsLePeriodicAdvertisingSyncTransferRecipientSupported);
//invalidateIsOffloadedFilteringSupportedCache();
}
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
index af56c3d..0780ee9 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -2457,13 +2457,27 @@
}
@Override
- public int isLePeriodicAdvertisingSyncTransferSenderSupported() {
+ public int isLeAudioBroadcastSourceSupported() {
AdapterService service = getService();
if (service == null) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
- if (service.mAdapterProperties.isLePeriodicAdvertisingSyncTransferSenderSupported()) {
+ if (service.isLeAudioBroadcastSourceSupported()) {
+ return BluetoothStatusCodes.SUCCESS;
+ }
+
+ return BluetoothStatusCodes.ERROR_FEATURE_NOT_SUPPORTED;
+ }
+
+ @Override
+ public int isLeAudioBroadcastAssistantSupported() {
+ AdapterService service = getService();
+ if (service == null) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+
+ if (service.isLeAudioBroadcastAssistantSupported()) {
return BluetoothStatusCodes.SUCCESS;
}
@@ -2633,6 +2647,16 @@
service.dump(fd, writer, args);
writer.close();
}
+
+ @Override
+ public boolean allowLowLatencyAudio(boolean allowed, BluetoothDevice device) {
+ AdapterService service = getService();
+ if (service == null) {
+ return false;
+ }
+ enforceBluetoothPrivilegedPermission(service);
+ return service.allowLowLatencyAudio(allowed, device);
+ }
}
// ----API Methods--------
@@ -3464,18 +3488,35 @@
return mAdapterProperties.isLePeriodicAdvertisingSupported();
}
+ /**
+ * Check if the LE audio broadcast source feature is supported.
+ *
+ * @return true, if the LE audio broadcast source is supported
+ */
+ public boolean isLeAudioBroadcastSourceSupported() {
+ //TODO: check the profile support status as well after we have the implementation
+ return mAdapterProperties.isLePeriodicAdvertisingSupported()
+ && mAdapterProperties.isLeExtendedAdvertisingSupported()
+ && mAdapterProperties.isLeIsochronousBroadcasterSupported();
+ }
+
+ /**
+ * Check if the LE audio broadcast assistant feature is supported.
+ *
+ * @return true, if the LE audio broadcast assistant is supported
+ */
+ public boolean isLeAudioBroadcastAssistantSupported() {
+ //TODO: check the profile support status as well after we have the implementation
+ return mAdapterProperties.isLePeriodicAdvertisingSupported()
+ && mAdapterProperties.isLeExtendedAdvertisingSupported()
+ && (mAdapterProperties.isLePeriodicAdvertisingSyncTransferSenderSupported()
+ || mAdapterProperties.isLePeriodicAdvertisingSyncTransferRecipientSupported());
+ }
+
public int getLeMaximumAdvertisingDataLength() {
return mAdapterProperties.getLeMaximumAdvertisingDataLength();
}
- public boolean isLePeriodicAdvertisingSyncTransferSenderSupported() {
- return mAdapterProperties.isLePeriodicAdvertisingSyncTransferSenderSupported();
- }
-
- public boolean isLeConnectedIsochronousStreamCentralSupported() {
- return mAdapterProperties.isLeConnectedIsochronousStreamCentralSupported();
- }
-
/**
* Get the maximum number of connected audio devices.
*
@@ -3740,6 +3781,11 @@
return getResources().getInteger(R.integer.config_bluetooth_operating_voltage_mv) / 1000.0;
}
+ @VisibleForTesting
+ protected RemoteDevices getRemoteDevices() {
+ return mRemoteDevices;
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
if (args.length == 0) {
@@ -4116,6 +4162,17 @@
return getMetricIdNative(Utils.getByteAddress(device));
}
+ /**
+ * Allow audio low latency
+ *
+ * @param allowed true if audio low latency is being allowed
+ * @param device device whose audio low latency will be allowed or disallowed
+ * @return boolean true if audio low latency is successfully allowed or disallowed
+ */
+ public boolean allowLowLatencyAudio(boolean allowed, BluetoothDevice device) {
+ return allowLowLatencyAudioNative(allowed, Utils.getByteAddress(device));
+ }
+
static native void classInitNative();
native boolean initNative(boolean startRestricted, boolean isCommonCriteriaMode,
@@ -4208,6 +4265,8 @@
/*package*/ native void requestMaximumTxDataLengthNative(byte[] address);
+ private native boolean allowLowLatencyAudioNative(boolean allowed, byte[] address);
+
// Returns if this is a mock object. This is currently used in testing so that we may not call
// System.exit() while finalizing the object. Otherwise GC of mock objects unfortunately ends up
// calling finalize() which in turn calls System.exit() and the process crashes.
diff --git a/android/app/src/com/android/bluetooth/btservice/Config.java b/android/app/src/com/android/bluetooth/btservice/Config.java
index 4c1eec3..abbb9f4 100644
--- a/android/app/src/com/android/bluetooth/btservice/Config.java
+++ b/android/app/src/com/android/bluetooth/btservice/Config.java
@@ -34,6 +34,7 @@
import com.android.bluetooth.avrcpcontroller.AvrcpControllerService;
import com.android.bluetooth.csip.CsipSetCoordinatorService;
import com.android.bluetooth.gatt.GattService;
+import com.android.bluetooth.hap.HapClientService;
import com.android.bluetooth.hearingaid.HearingAidService;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.hfpclient.HeadsetClientService;
@@ -132,6 +133,8 @@
new ProfileConfig(CsipSetCoordinatorService.class,
R.bool.profile_supported_csip_set_coordinator,
(1 << BluetoothProfile.CSIP_SET_COORDINATOR)),
+ new ProfileConfig(HapClientService.class, R.bool.profile_supported_hap_client,
+ (1 << BluetoothProfile.HAP_CLIENT)),
};
private static Class[] sSupportedProfiles = new Class[0];
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index 992c6af..1b9fcfb 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -111,6 +111,28 @@
private static final int TRUNCATED_RESULT_SIZE = 11;
private static final int TIME_STAMP_LENGTH = 2;
+ private enum MatchOrigin {
+ PSEUDO_ADDRESS,
+ ORIGINAL_ADDRESS
+ }
+
+ private static class MatchResult {
+ private final boolean mMatches;
+ private final MatchOrigin mOrigin;
+ private MatchResult(boolean matches, MatchOrigin origin) {
+ this.mMatches = matches;
+ this.mOrigin = origin;
+ }
+
+ public boolean getMatches() {
+ return mMatches;
+ }
+
+ public MatchOrigin getMatchOrigin() {
+ return mOrigin;
+ }
+ }
+
/**
* The default floor value for LE batch scan report delays greater than 0
*/
@@ -356,7 +378,8 @@
}
for (String test : TEST_MODE_BEACONS) {
onScanResultInternal(0x1b, 0x1, "DD:34:02:05:5C:4D", 1, 0, 0xff,
- 127, -54, 0x0, HexEncoding.decode(test));
+ 127, -54, 0x0, HexEncoding.decode(test),
+ "DD:34:02:05:5C:4E");
}
sendEmptyMessageDelayed(0, DateUtils.SECOND_IN_MILLIS);
}
@@ -1160,23 +1183,24 @@
void onScanResult(int eventType, int addressType, String address, int primaryPhy,
int secondaryPhy, int advertisingSid, int txPower, int rssi, int periodicAdvInt,
- byte[] advData) {
+ byte[] advData, String originalAddress) {
// When in testing mode, ignore all real-world events
if (isTestModeEnabled()) return;
onScanResultInternal(eventType, addressType, address, primaryPhy, secondaryPhy,
- advertisingSid, txPower, rssi, periodicAdvInt, advData);
+ advertisingSid, txPower, rssi, periodicAdvInt, advData, originalAddress);
}
void onScanResultInternal(int eventType, int addressType, String address, int primaryPhy,
int secondaryPhy, int advertisingSid, int txPower, int rssi, int periodicAdvInt,
- byte[] advData) {
+ byte[] advData, String originalAddress) {
if (VDBG) {
Log.d(TAG, "onScanResult() - eventType=0x" + Integer.toHexString(eventType)
+ ", addressType=" + addressType + ", address=" + address + ", primaryPhy="
+ primaryPhy + ", secondaryPhy=" + secondaryPhy + ", advertisingSid=0x"
+ Integer.toHexString(advertisingSid) + ", txPower=" + txPower + ", rssi="
- + rssi + ", periodicAdvInt=0x" + Integer.toHexString(periodicAdvInt));
+ + rssi + ", periodicAdvInt=0x" + Integer.toHexString(periodicAdvInt)
+ + ", originalAddress=" + originalAddress);
}
byte[] legacyAdvData = Arrays.copyOfRange(advData, 0, 62);
@@ -1184,6 +1208,7 @@
for (ScanClient client : mScanManager.getRegularScanQueue()) {
ScannerMap.App app = mScannerMap.getById(client.scannerId);
if (app == null) {
+ Log.i(TAG, "App is null; skip.");
continue;
}
@@ -1195,6 +1220,7 @@
if (settings.getLegacy()) {
if ((eventType & ET_LEGACY_MASK) == 0) {
// If this is legacy scan, but nonlegacy result - skip.
+ Log.i(TAG, "Legacy scan, non legacy result; skip.");
continue;
} else {
// Some apps are used to fixed-size advertise data.
@@ -1212,6 +1238,7 @@
if (client.hasDisavowedLocation) {
if (mLocationDenylistPredicate.test(result)) {
+ Log.i(TAG, "Skipping client for location deny list");
continue;
}
}
@@ -1232,11 +1259,22 @@
result = sanitized;
}
}
- if (!hasPermission || !matchesFilters(client, result)) {
+ MatchResult matchResult = matchesFilters(client, result, originalAddress);
+ if (!hasPermission || !matchResult.getMatches()) {
+ Log.i(TAG, "Skipping client: permission="
+ + hasPermission + " matches=" + matchResult.getMatches());
continue;
}
+ if (matchResult.getMatchOrigin() == MatchOrigin.ORIGINAL_ADDRESS) {
+ result = new ScanResult(BluetoothAdapter.getDefaultAdapter()
+ .getRemoteDevice(originalAddress), eventType, primaryPhy, secondaryPhy,
+ advertisingSid, txPower, rssi, periodicAdvInt, scanRecord,
+ SystemClock.elapsedRealtimeNanos());
+ }
+
if ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_ALL_MATCHES) == 0) {
+ Log.i(TAG, "Skipping client: CALLBACK_TYPE_ALL_MATCHES");
continue;
}
@@ -1335,16 +1373,28 @@
}
// Check if a scan record matches a specific filters.
- private boolean matchesFilters(ScanClient client, ScanResult scanResult) {
+ private MatchResult matchesFilters(ScanClient client, ScanResult scanResult) {
+ return matchesFilters(client, scanResult, null);
+ }
+
+ // Check if a scan record matches a specific filters or original address
+ private MatchResult matchesFilters(ScanClient client, ScanResult scanResult,
+ String originalAddress) {
if (client.filters == null || client.filters.isEmpty()) {
- return true;
+ // TODO: Do we really wanna return true here?
+ return new MatchResult(true, MatchOrigin.PSEUDO_ADDRESS);
}
for (ScanFilter filter : client.filters) {
+ // Need to check the filter matches, and the original address without changing the API
if (filter.matches(scanResult)) {
- return true;
+ return new MatchResult(true, MatchOrigin.PSEUDO_ADDRESS);
+ }
+ if (originalAddress != null
+ && originalAddress.equalsIgnoreCase(filter.getDeviceAddress())) {
+ return new MatchResult(true, MatchOrigin.ORIGINAL_ADDRESS);
}
}
- return false;
+ return new MatchResult(false, MatchOrigin.PSEUDO_ADDRESS);
}
void onClientRegistered(int status, int clientIf, long uuidLsb, long uuidMsb)
@@ -1956,7 +2006,7 @@
// Reconstruct the scan results.
ArrayList<ScanResult> results = new ArrayList<ScanResult>();
for (ScanResult scanResult : permittedResults) {
- if (matchesFilters(client, scanResult)) {
+ if (matchesFilters(client, scanResult).getMatches()) {
results.add(scanResult);
}
}
diff --git a/android/app/src/com/android/bluetooth/gatt/ScanFilterQueue.java b/android/app/src/com/android/bluetooth/gatt/ScanFilterQueue.java
index 4572c89..8231519 100644
--- a/android/app/src/com/android/bluetooth/gatt/ScanFilterQueue.java
+++ b/android/app/src/com/android/bluetooth/gatt/ScanFilterQueue.java
@@ -181,13 +181,13 @@
addName(filter.getDeviceName());
}
if (filter.getDeviceAddress() != null) {
- byte addressType = (byte) filter.getAddressType();
- // If addressType == iADDRESS_TYPE_PUBLIC (0) then this is the original
- // setDeviceAddress(address) API path which provided DEVICE_TYPE_ALL (2) which might map
- // to the stack value for address type of BTM_BLE_STATIC (2)
- // Additionally, we shouldn't confuse device type with address type.
- addDeviceAddress(filter.getDeviceAddress(),
- ((addressType == 0) ? DEVICE_TYPE_ALL : addressType), filter.getIrk());
+ /*
+ * Pass the addres type here. This address type will be used for the resolving address,
+ * however, the host stack will force the type to 0x02 for the APCF filter in
+ * btm_ble_adv_filter.cc#BTM_LE_PF_addr_filter(...)
+ */
+ addDeviceAddress(filter.getDeviceAddress(), (byte) filter.getAddressType(),
+ filter.getIrk());
}
if (filter.getServiceUuid() != null) {
if (filter.getServiceUuidMask() == null) {
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java
new file mode 100644
index 0000000..e6e5f0c
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.hap;
+
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.IBluetoothHapClient;
+import android.content.AttributionSource;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.SynchronousResultReceiver;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides Bluetooth Hearing Access profile, as a service.
+ * @hide
+ */
+public class HapClientService extends ProfileService {
+ private static final boolean DBG = true;
+ private static final String TAG = "HapClientService";
+
+ private static HapClientService sHapClient;
+
+ private static synchronized void setHapClient(HapClientService instance) {
+ if (DBG) {
+ Log.d(TAG, "setHapClient(): set to: " + instance);
+ }
+ sHapClient = instance;
+ }
+
+ /**
+ * Get the HapClientService instance
+ * @return HapClientService instance
+ */
+ public static synchronized HapClientService getHapClientService() {
+ if (sHapClient == null) {
+ Log.w(TAG, "getHapClientService(): service is NULL");
+ return null;
+ }
+
+ if (!sHapClient.isAvailable()) {
+ Log.w(TAG, "getHapClientService(): service is not available");
+ return null;
+ }
+ return sHapClient;
+ }
+
+ @Override
+ protected void create() {
+ if (DBG) {
+ Log.d(TAG, "create()");
+ }
+ }
+
+ @Override
+ protected void cleanup() {
+ if (DBG) {
+ Log.d(TAG, "cleanup()");
+ }
+ }
+
+ @Override
+ protected IProfileServiceBinder initBinder() {
+ return new BluetoothHapClientBinder(this);
+ }
+
+ @Override
+ protected boolean start() {
+ if (DBG) {
+ Log.d(TAG, "start()");
+ }
+
+ if (sHapClient != null) {
+ throw new IllegalStateException("start() called twice");
+ }
+
+ // Mark service as started
+ setHapClient(this);
+
+ return true;
+ }
+
+ @Override
+ protected boolean stop() {
+ if (DBG) {
+ Log.d(TAG, "stop()");
+ }
+ if (sHapClient == null) {
+ Log.w(TAG, "stop() called before start()");
+ return true;
+ }
+
+ // Marks service as stopped
+ setHapClient(null);
+
+ return true;
+ }
+
+ @Override
+ public void dump(StringBuilder sb) {
+ super.dump(sb);
+ }
+
+ /**
+ * Binder object: must be a static class or memory leak may occur
+ */
+ @VisibleForTesting
+ static class BluetoothHapClientBinder extends IBluetoothHapClient.Stub
+ implements IProfileServiceBinder {
+ private HapClientService mService;
+
+ BluetoothHapClientBinder(HapClientService svc) {
+ mService = svc;
+ }
+
+ private HapClientService getService(AttributionSource source) {
+ if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
+ || !Utils.checkServiceAvailable(mService, TAG)
+ || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
+ Log.w(TAG, "Hearing Access call not allowed for non-active user");
+ return null;
+ }
+
+ if (mService != null && mService.isAvailable()) {
+ return mService;
+ }
+ return null;
+ }
+
+ @Override
+ public void cleanup() {
+ mService = null;
+ }
+
+ @Override
+ public void connect(BluetoothDevice device, AttributionSource source,
+ SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void disconnect(BluetoothDevice device, AttributionSource source,
+ SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void getConnectedDevices(AttributionSource source,
+ SynchronousResultReceiver receiver) {
+ try {
+ List<BluetoothDevice> defaultValue = new ArrayList<>();
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void getDevicesMatchingConnectionStates(int[] states,
+ AttributionSource source, SynchronousResultReceiver receiver) {
+ try {
+ List<BluetoothDevice> defaultValue = new ArrayList<>();
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void getConnectionState(BluetoothDevice device, AttributionSource source,
+ SynchronousResultReceiver receiver) {
+ try {
+ int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy,
+ AttributionSource source, SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void getConnectionPolicy(BluetoothDevice device, AttributionSource source,
+ SynchronousResultReceiver receiver) {
+ try {
+ int defaultValue = BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void getActivePresetIndex(BluetoothDevice device, AttributionSource source,
+ SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void getHapGroup(BluetoothDevice device, AttributionSource source,
+ SynchronousResultReceiver receiver) {
+ try {
+ int defaultValue = BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void selectActivePreset(BluetoothDevice device, int presetIndex,
+ AttributionSource source, SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void groupSelectActivePreset(int groupId, int presetIndex,
+ AttributionSource source, SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void nextActivePreset(BluetoothDevice device, AttributionSource source,
+ SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void groupNextActivePreset(int groupId, AttributionSource source,
+ SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void previousActivePreset(BluetoothDevice device, AttributionSource source,
+ SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void groupPreviousActivePreset(int groupId, AttributionSource source, SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void getPresetInfo(BluetoothDevice device, int presetIndex,
+ AttributionSource source, SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void getAllPresetsInfo(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void getFeatures(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void setPresetName(BluetoothDevice device, int presetIndex, String name,
+ AttributionSource source, SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+ @Override
+ public void groupSetPresetName(int groupId, int presetIndex, String name,
+ AttributionSource source, SynchronousResultReceiver receiver) {
+ try {
+ boolean defaultValue = false;
+ receiver.send(defaultValue);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/pan/PanService.java b/android/app/src/com/android/bluetooth/pan/PanService.java
index 08d1a08..6ab5289 100644
--- a/android/app/src/com/android/bluetooth/pan/PanService.java
+++ b/android/app/src/com/android/bluetooth/pan/PanService.java
@@ -20,7 +20,6 @@
import static android.Manifest.permission.TETHER_PRIVILEGED;
import android.annotation.RequiresPermission;
-import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothPan.LocalPanRole;
@@ -28,19 +27,15 @@
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothPan;
import android.content.AttributionSource;
-import android.content.Context;
import android.content.Intent;
import android.content.res.Resources.NotFoundException;
-import android.net.ConnectivityManager;
-import android.net.InetAddresses;
-import android.net.InterfaceConfiguration;
-import android.net.LinkAddress;
+import android.net.ITetheredInterfaceCallback;
+import android.net.TetheringInterface;
import android.net.TetheringManager;
import android.os.Handler;
-import android.os.IBinder;
-import android.os.INetworkManagementService;
+import android.os.HandlerExecutor;
import android.os.Message;
-import android.os.ServiceManager;
+import android.os.RemoteException;
import android.os.UserManager;
import android.util.Log;
@@ -51,12 +46,13 @@
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
import com.android.modules.utils.SynchronousResultReceiver;
-import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -74,12 +70,13 @@
private static final int BLUETOOTH_PREFIX_LENGTH = 24;
private HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices;
- private ArrayList<String> mBluetoothIfaceAddresses;
private int mMaxPanDevices;
private String mPanIfName;
- private String mNapIfaceAddr;
+ private boolean mIsTethering = false;
private boolean mNativeAvailable;
+ private List<ITetheredInterfaceCallback> mBluetoothTetheringCallbacks;
+ private TetheringManager mTetheringManager;
private DatabaseManager mDatabaseManager;
@VisibleForTesting
UserManager mUserManager;
@@ -94,6 +91,24 @@
private AdapterService mAdapterService;
+ TetheringManager.TetheringEventCallback mTetheringCallback =
+ new TetheringManager.TetheringEventCallback() {
+ @Override
+ public void onError(TetheringInterface iface, int error) {
+ if (mIsTethering
+ && iface.getType() == TetheringManager.TETHERING_BLUETOOTH) {
+ // tethering is fail because of @TetheringIfaceError error.
+ Log.e(TAG, "Error setting up tether interface: " + error);
+ for (Map.Entry device : mPanDevices.entrySet()) {
+ disconnectPanNative(Utils.getByteAddress(
+ (BluetoothDevice) device.getKey()));
+ }
+ mPanDevices.clear();
+ mIsTethering = false;
+ }
+ }
+ };
+
static {
classInitNative();
}
@@ -129,8 +144,8 @@
mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(),
"DatabaseManager cannot be null when PanService starts");
+ mBluetoothTetheringCallbacks = new ArrayList<>();
mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>();
- mBluetoothIfaceAddresses = new ArrayList<String>();
try {
mMaxPanDevices = getResources().getInteger(
com.android.internal.R.integer.config_max_pan_devices);
@@ -142,6 +157,9 @@
mUserManager = getSystemService(UserManager.class);
+ mTetheringManager = getSystemService(TetheringManager.class);
+ mTetheringManager.registerTetheringEventCallback(
+ new HandlerExecutor(BackgroundThread.getHandler()), mTetheringCallback);
setPanService(this);
mStarted = true;
@@ -151,6 +169,7 @@
@Override
protected boolean stop() {
mAdapterService = null;
+ mTetheringManager.unregisterTetheringEventCallback(mTetheringCallback);
mHandler.removeCallbacksAndMessages(null);
return true;
}
@@ -337,7 +356,8 @@
}
@Override
- public void setBluetoothTethering(boolean value, AttributionSource source,
+ public void setBluetoothTethering(ITetheredInterfaceCallback callback,
+ boolean value, AttributionSource source,
SynchronousResultReceiver receiver) {
try {
PanService service = getService(source);
@@ -345,7 +365,7 @@
Log.d(TAG, "setBluetoothTethering: " + value
+ ", pkgName: " + source.getPackageName()
+ ", mTetherOn: " + service.mTetherOn);
- service.setBluetoothTethering(value, source.getPackageName(),
+ service.setBluetoothTethering(callback, value, source.getPackageName(),
source.getAttributionTag());
}
receiver.send(null);
@@ -426,8 +446,8 @@
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
android.Manifest.permission.TETHER_PRIVILEGED,
})
- void setBluetoothTethering(boolean value, final String pkgName,
- final String callingAttributionTag) {
+ void setBluetoothTethering(ITetheredInterfaceCallback callback, boolean value,
+ final String pkgName, final String callingAttributionTag) {
if (DBG) {
Log.d(TAG, "setBluetoothTethering: " + value + ", mTetherOn: " + mTetherOn);
}
@@ -440,6 +460,16 @@
if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING) && value) {
throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user.");
}
+ if (callback != null) {
+ if (value) {
+ mBluetoothTetheringCallbacks.add(callback);
+ } else {
+ mBluetoothTetheringCallbacks.remove(callback);
+ }
+ } else if (mBluetoothTetheringCallbacks.isEmpty()) {
+ Log.e(TAG, "setBluetoothTethering: " + value + ", Error: no callbacks registered.");
+ return;
+ }
if (mTetherOn != value) {
//drop any existing panu or pan-nap connection when changing the tethering state
mTetherOn = value;
@@ -635,22 +665,29 @@
return;
}
Log.d(TAG, "handlePanDeviceStateChange LOCAL_NAP_ROLE:REMOTE_PANU_ROLE");
- if (mNapIfaceAddr == null) {
- mNapIfaceAddr = startTethering(iface);
- if (mNapIfaceAddr == null) {
- Log.e(TAG, "Error seting up tether interface");
- mPanDevices.remove(device);
- disconnectPanNative(Utils.getByteAddress(device));
- return;
+ if (!mIsTethering) {
+ mIsTethering = true;
+ try {
+ for (ITetheredInterfaceCallback cb : mBluetoothTetheringCallbacks) {
+ cb.onAvailable(iface);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
} else if (state == BluetoothProfile.STATE_DISCONNECTED) {
mPanDevices.remove(device);
Log.i(TAG, "remote(PANU) is disconnected, Remaining connected PANU devices: "
+ mPanDevices.size());
- if (mNapIfaceAddr != null && mPanDevices.size() == 0) {
- stopTethering(iface);
- mNapIfaceAddr = null;
+ if (mIsTethering && mPanDevices.size() == 0) {
+ try {
+ for (ITetheredInterfaceCallback cb : mBluetoothTetheringCallbacks) {
+ cb.onUnavailable();
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mIsTethering = false;
}
}
} else if (mStarted) {
@@ -686,83 +723,6 @@
sendBroadcast(intent, BLUETOOTH_CONNECT);
}
- private String startTethering(String iface) {
- return configureBtIface(true, iface);
- }
-
- private String stopTethering(String iface) {
- return configureBtIface(false, iface);
- }
-
- private String configureBtIface(boolean enable, String iface) {
- Log.i(TAG, "configureBtIface: " + iface + " enable: " + enable);
-
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
- TetheringManager tm = getBaseContext().getSystemService(TetheringManager.class);
- String[] bluetoothRegexs = tm.getTetherableBluetoothRegexs();
-
- // bring toggle the interfaces
- String[] currentIfaces = new String[0];
- try {
- currentIfaces = service.listInterfaces();
- } catch (Exception e) {
- Log.e(TAG, "Error listing Interfaces :" + e);
- return null;
- }
-
- boolean found = false;
- for (String currIface : currentIfaces) {
- if (currIface.equals(iface)) {
- found = true;
- break;
- }
- }
-
- if (!found) {
- return null;
- }
-
- InterfaceConfiguration ifcg = null;
- String address = null;
- try {
- ifcg = service.getInterfaceConfig(iface);
- if (ifcg != null) {
- InetAddress addr = null;
- LinkAddress linkAddr = ifcg.getLinkAddress();
- if (linkAddr == null || (addr = linkAddr.getAddress()) == null || addr.equals(
- InetAddresses.parseNumericAddress("0.0.0.0")) || addr.equals(
- InetAddresses.parseNumericAddress("::0"))) {
- address = BLUETOOTH_IFACE_ADDR_START;
- addr = InetAddresses.parseNumericAddress(address);
- }
-
- ifcg.setLinkAddress(new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH));
- if (enable) {
- ifcg.setInterfaceUp();
- } else {
- ifcg.setInterfaceDown();
- }
- service.setInterfaceConfig(iface, ifcg);
-
- if (enable) {
- int tetherStatus = tm.tether(iface);
- if (tetherStatus != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
- Log.e(TAG, "Error tethering " + iface + " tetherStatus: " + tetherStatus);
- return null;
- }
- } else {
- int untetherStatus = tm.untether(iface);
- Log.i(TAG, "Untethered: " + iface + " untetherStatus: " + untetherStatus);
- }
- }
- } catch (Exception e) {
- Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
- return null;
- }
- return address;
- }
-
private List<BluetoothDevice> getConnectedPanDevices() {
List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsGatt.java b/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
index f78d8c0..5ad083e 100644
--- a/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
+++ b/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
@@ -359,6 +359,10 @@
return success;
}
+ public boolean setValueNoNotify(byte[] value) {
+ return super.setValue(value);
+ }
+
public boolean clearValue(boolean notify) {
boolean success = super.setValue(new byte[0]);
if (success && notify && isNotifiable()) {
@@ -413,7 +417,7 @@
value[1] = (byte) (callIndex);
value[2] = (byte) (requestResult);
- super.setValue(value);
+ super.setValueNoNotify(value);
// to avoid sending control point notification before write response
mHandler.post(() -> mNotifier.notify(device, this));
@@ -497,12 +501,24 @@
ByteArrayOutputStream stream = new ByteArrayOutputStream();
for (Map.Entry<Integer, TbsCall> entry : callsList.entrySet()) {
TbsCall call = entry.getValue();
- int listItemLength = Math.min(listItemLengthMax, 3 + call.getUri().getBytes().length);
+ if (call == null) {
+ Log.w(TAG, "setBearerListCurrentCalls: call is null");
+ continue;
+ }
+
+ int uri_len = 0;
+ if (call.getUri() != null) {
+ uri_len = call.getUri().getBytes().length;
+ }
+
+ int listItemLength = Math.min(listItemLengthMax, 3 + uri_len);
stream.write((byte) (listItemLength & 0xff));
stream.write((byte) (entry.getKey() & 0xff));
stream.write((byte) (call.getState() & 0xff));
stream.write((byte) (call.getFlags() & 0xff));
- stream.write(call.getUri().getBytes(), 0, listItemLength - 3);
+ if (uri_len > 0) {
+ stream.write(call.getUri().getBytes(), 0, listItemLength - 3);
+ }
}
return mBearerListCurrentCallsCharacteristic.setValue(stream.toByteArray());
@@ -580,9 +596,17 @@
if (DBG) {
Log.d(TAG, "setIncomingCall: callIndex=" + callIndex + " uri=" + uri);
}
- byte[] value = new byte[uri.length() + 1];
+ int uri_len = 0;
+ if (uri != null) {
+ uri_len = uri.length();
+ }
+
+ byte[] value = new byte[uri_len + 1];
value[0] = (byte) (callIndex & 0xff);
- System.arraycopy(uri.getBytes(), 0, value, 1, uri.length());
+
+ if (uri_len > 0) {
+ System.arraycopy(uri.getBytes(), 0, value, 1, uri_len);
+ }
return mIncomingCallCharacteristic.setValue(value);
}
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
index c3a07ba..a590a51 100644
--- a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
+++ b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
@@ -444,6 +444,11 @@
}
Integer callIndex = bearer.callIdIndexMap.remove(callId);
+ if (callIndex == null) {
+ Log.e(TAG, "callIndex: is null for callId" + callId);
+ return;
+ }
+
TbsCall tbsCall = mCurrentCallsList.remove(callIndex);
if (tbsCall == null) {
Log.e(TAG, "callRemoved: no such call");
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
index 89eead8..2ecb730 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
@@ -56,7 +56,8 @@
BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3
};
// Not use the default value to make sure it reads from config
@@ -65,6 +66,7 @@
private static final int APTX_PRIORITY_DEFAULT = 5001;
private static final int APTX_HD_PRIORITY_DEFAULT = 7001;
private static final int LDAC_PRIORITY_DEFAULT = 9001;
+ private static final int LC3_PRIORITY_DEFAULT = 11001;
private static final int PRIORITY_HIGH = 1000000;
private static final BluetoothCodecConfig[] sCodecCapabilities = new BluetoothCodecConfig[] {
@@ -105,6 +107,14 @@
| BluetoothCodecConfig.BITS_PER_SAMPLE_24
| BluetoothCodecConfig.BITS_PER_SAMPLE_32,
BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 0, 0, 0, 0), // Codec-specific fields
+ buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3,
+ LC3_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_44100
+ | BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_MONO
+ | BluetoothCodecConfig.CHANNEL_MODE_STEREO,
0, 0, 0, 0) // Codec-specific fields
};
@@ -138,6 +148,12 @@
BluetoothCodecConfig.SAMPLE_RATE_96000,
BluetoothCodecConfig.BITS_PER_SAMPLE_32,
BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ 0, 0, 0, 0), // Codec-specific fields
+ buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3,
+ LC3_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
0, 0, 0, 0) // Codec-specific fields
};
@@ -158,6 +174,8 @@
.thenReturn(APTX_HD_PRIORITY_DEFAULT);
when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_ldac))
.thenReturn(LDAC_PRIORITY_DEFAULT);
+ when(mMockResources.getInteger(R.integer.a2dp_source_codec_priority_lc3))
+ .thenReturn(LC3_PRIORITY_DEFAULT);
mA2dpCodecConfig = new A2dpCodecConfig(mMockContext, mA2dpNativeInterface);
mTestDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:01:02:03:04:05");
@@ -191,6 +209,9 @@
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
Assert.assertEquals(config.getCodecPriority(), LDAC_PRIORITY_DEFAULT);
break;
+ case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
+ Assert.assertEquals(config.getCodecPriority(), LC3_PRIORITY_DEFAULT);
+ break;
}
}
}
@@ -219,6 +240,10 @@
testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
+ true);
+ testCodecPriorityChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, PRIORITY_HIGH,
false);
}
@@ -230,23 +255,27 @@
public void testSetCodecPreference_priorityDefaultToRaiseHigh() {
testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
true);
testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
true);
testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, PRIORITY_HIGH,
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
true);
testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, PRIORITY_HIGH,
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
true);
testCodecPriorityChangeHelper(
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, LDAC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
+ true);
+ testCodecPriorityChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, PRIORITY_HIGH,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, LC3_PRIORITY_DEFAULT,
false);
}
@@ -272,6 +301,10 @@
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
true);
+ testCodecPriorityChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, PRIORITY_HIGH,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, PRIORITY_HIGH,
+ true);
}
@Test
@@ -296,6 +329,10 @@
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, PRIORITY_HIGH,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
true);
+ testCodecPriorityChangeHelper(
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3, PRIORITY_HIGH,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, PRIORITY_HIGH,
+ true);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
index 510a3fb..94c8ee1 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
@@ -81,6 +81,8 @@
@RunWith(AndroidJUnit4.class)
public class AdapterServiceTest {
private static final String TAG = AdapterServiceTest.class.getSimpleName();
+ private static final String TEST_BT_ADDR_1 = "00:11:22:33:44:55";
+ private static final String TEST_BT_ADDR_2 = "00:11:22:33:44:66";
private AdapterService mAdapterService;
private AdapterService.AdapterServiceBinder mServiceBinder;
@@ -754,6 +756,23 @@
obfuscatedAddress1));
}
+ @Test
+ public void testAddressConsolidation() {
+ // Create device properties
+ RemoteDevices remoteDevices = mAdapterService.getRemoteDevices();
+ remoteDevices.addDeviceProperties(Utils.getBytesFromAddress((TEST_BT_ADDR_1)));
+ String identityAddress = mAdapterService.getIdentityAddress(TEST_BT_ADDR_1);
+ Assert.assertEquals(identityAddress, TEST_BT_ADDR_1);
+
+ // Trigger address consolidate callback
+ remoteDevices.addressConsolidateCallback(Utils.getBytesFromAddress(TEST_BT_ADDR_1),
+ Utils.getBytesFromAddress(TEST_BT_ADDR_2));
+
+ // Verify we can get correct identity address
+ identityAddress = mAdapterService.getIdentityAddress(TEST_BT_ADDR_1);
+ Assert.assertEquals(identityAddress, TEST_BT_ADDR_2);
+ }
+
private static byte[] getMetricsSalt(HashMap<String, HashMap<String, String>> adapterConfig) {
HashMap<String, String> metricsSection = adapterConfig.get("Metrics");
if (metricsSection == null) {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java
index f2181a1..7985e44 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java
@@ -465,8 +465,8 @@
Assert.assertTrue(Arrays.equals(characteristic.getValue(),
new byte[] {(byte) (requestedOpcode & 0xff), (byte) (callIndex & 0xff),
(byte) (result & 0xff)}));
- verify(mMockGattServer).notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic),
- eq(false));
+ verify(mMockGattServer, after(2000)).notifyCharacteristicChanged(eq(mCurrentDevice),
+ eq(characteristic), eq(false));
reset(mMockGattServer);
callIndex = 0x02;
@@ -477,7 +477,8 @@
Assert.assertTrue(Arrays.equals(characteristic.getValue(),
new byte[] {(byte) (requestedOpcode & 0xff), (byte) (callIndex & 0xff),
(byte) (result & 0xff)}));
- verify(mMockGattServer, times(0)).notifyCharacteristicChanged(any(), any(), anyBoolean());
+ verify(mMockGattServer, after(2000).times(0)).notifyCharacteristicChanged(any(), any(),
+ anyBoolean());
}
@Test
diff --git a/framework/.clang-format b/framework/.clang-format
new file mode 100644
index 0000000..03af56d
--- /dev/null
+++ b/framework/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+
+AccessModifierOffset: -4
+AlignOperands: false
+AllowShortFunctionsOnASingleLine: Inline
+AlwaysBreakBeforeMultilineStrings: false
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ConstructorInitializerIndentWidth: 6
+ContinuationIndentWidth: 8
+IndentWidth: 4
+PenaltyBreakBeforeFirstCallParameter: 100000
+SpacesBeforeTrailingComments: 1
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java
index f94ee85..b70d268 100644
--- a/framework/java/android/bluetooth/BluetoothAdapter.java
+++ b/framework/java/android/bluetooth/BluetoothAdapter.java
@@ -2325,28 +2325,54 @@
}
/**
- * Returns {@link BluetoothStatusCodes#SUCCESS} if LE Periodic Advertising Sync Transfer Sender
- * feature is supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if the
- * feature is not supported or an error code
+ * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio broadcast source
+ * feature is supported, {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if the
+ * feature is not supported, or an error code.
*
- * @return whether the chipset supports the LE Periodic Advertising Sync Transfer Sender feature
+ * @return whether the LE audio broadcast source is supported
*/
@RequiresNoPermission
- public @LeFeatureReturnValues int isLePeriodicAdvertisingSyncTransferSenderSupported() {
- if (!getLeAccess()) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ public @LeFeatureReturnValues int isLeAudioBroadcastSourceSupported() {
+ if (!getLeAccess()) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ return mService.isLeAudioBroadcastSourceSupported();
}
- try {
- mServiceLock.readLock().lock();
- if (mService != null) {
- return mService.isLePeriodicAdvertisingSyncTransferSenderSupported();
- }
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- } finally {
- mServiceLock.readLock().unlock();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+
+ return BluetoothStatusCodes.ERROR_UNKNOWN;
+ }
+
+ /**
+ * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio broadcast assistant
+ * feature is supported, {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if the
+ * feature is not supported, or an error code.
+ *
+ * @return whether the LE audio broadcast assistent is supported
+ */
+ @RequiresNoPermission
+ public @LeFeatureReturnValues int isLeAudioBroadcastAssistantSupported() {
+ if (!getLeAccess()) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ return mService.isLeAudioBroadcastAssistantSupported();
}
- return BluetoothStatusCodes.ERROR_UNKNOWN;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return BluetoothStatusCodes.ERROR_UNKNOWN;
}
/**
diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java
index 9a4151a..2ee171b 100644
--- a/framework/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java
@@ -38,14 +38,10 @@
*/
public final class BluetoothCodecConfig implements Parcelable {
/** @hide */
- @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = {
- SOURCE_CODEC_TYPE_SBC,
- SOURCE_CODEC_TYPE_AAC,
- SOURCE_CODEC_TYPE_APTX,
- SOURCE_CODEC_TYPE_APTX_HD,
- SOURCE_CODEC_TYPE_LDAC,
- SOURCE_CODEC_TYPE_INVALID
- })
+ @IntDef(prefix = "SOURCE_CODEC_TYPE_",
+ value = {SOURCE_CODEC_TYPE_SBC, SOURCE_CODEC_TYPE_AAC, SOURCE_CODEC_TYPE_APTX,
+ SOURCE_CODEC_TYPE_APTX_HD, SOURCE_CODEC_TYPE_LDAC, SOURCE_CODEC_TYPE_LC3,
+ SOURCE_CODEC_TYPE_INVALID})
@Retention(RetentionPolicy.SOURCE)
public @interface SourceCodecType {}
@@ -76,6 +72,11 @@
public static final int SOURCE_CODEC_TYPE_LDAC = 4;
/**
+ * Source codec type LC3.
+ */
+ public static final int SOURCE_CODEC_TYPE_LC3 = 5;
+
+ /**
* Source codec type invalid. This is the default value used for codec
* type.
*/
@@ -85,7 +86,7 @@
* Represents the count of valid source codec types. Can be accessed via
* {@link #getMaxCodecType}.
*/
- private static final int SOURCE_CODEC_TYPE_MAX = 5;
+ private static final int SOURCE_CODEC_TYPE_MAX = 6;
/** @hide */
@IntDef(prefix = "CODEC_PRIORITY_", value = {
@@ -460,6 +461,8 @@
return "aptX HD";
case SOURCE_CODEC_TYPE_LDAC:
return "LDAC";
+ case SOURCE_CODEC_TYPE_LC3:
+ return "LC3";
case SOURCE_CODEC_TYPE_INVALID:
return "INVALID CODEC";
default:
@@ -664,9 +667,10 @@
switch (mCodecType) {
case SOURCE_CODEC_TYPE_AAC:
case SOURCE_CODEC_TYPE_LDAC:
- if (mCodecSpecific1 != other.mCodecSpecific1) {
- return false;
- }
+ case SOURCE_CODEC_TYPE_LC3:
+ if (mCodecSpecific1 != other.mCodecSpecific1) {
+ return false;
+ }
default:
return true;
}
diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java
index 1edf5cc..70971a0 100644
--- a/framework/java/android/bluetooth/BluetoothDevice.java
+++ b/framework/java/android/bluetooth/BluetoothDevice.java
@@ -2828,4 +2828,34 @@
public static @MetadataKey int getMaxMetadataKey() {
return METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD;
}
+
+ /**
+ * Enable or disable audio low latency for this {@link BluetoothDevice}.
+ *
+ * @param allowed true if low latency is allowed, false if low latency is disallowed.
+ * @return true if the value is successfully set,
+ * false if there is a error when setting the value.
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public boolean setLowLatencyAudioAllowed(boolean allowed) {
+ final IBluetooth service = sService;
+ Log.i(TAG, "Allowing bluetooth audio low latency: " + allowed);
+ if (service == null) {
+ Log.e(TAG, "Bluetooth is not enabled. Cannot allow low latency");
+ return false;
+ }
+ try {
+ service.allowLowLatencyAudio(allowed, this);
+ } catch (RemoteException e) {
+ Log.e(TAG, "allowLowLatencyAudio fail ", e);
+ e.rethrowFromSystemServer();
+ }
+ return true;
+ }
}
diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java
index 15db686..02df543 100644
--- a/framework/java/android/bluetooth/BluetoothLeAudio.java
+++ b/framework/java/android/bluetooth/BluetoothLeAudio.java
@@ -25,6 +25,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.AttributionSource;
@@ -243,6 +244,13 @@
public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
/**
+ * This represents an invalid audio location.
+ *
+ * @hide
+ */
+ public static final int AUDIO_LOCATION_INVALID = -1;
+
+ /**
* Contains group id.
* @hide
*/
@@ -723,6 +731,39 @@
}
/**
+ * Get the audio location for the device. The return value is a bit field. The bit definition
+ * is included in Bluetooth SIG Assigned Numbers - Generic Audio - Audio Location Definitions.
+ * ex. Front Left: 0x00000001
+ * Front Right: 0x00000002
+ * Front Left | Front Right: 0x00000003
+ *
+ * @param device the bluetooth device
+ * @return The bit field of audio location for the device, if bluetooth is off, return
+ * AUDIO_LOCATION_INVALID.
+ *
+ * @hide
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
+ @SystemApi
+ public int getAudioLocation(@NonNull BluetoothDevice device) {
+ if (VDBG) log("getAudioLocation()");
+ final IBluetoothLeAudio service = getService();
+ final int defaultLocation = AUDIO_LOCATION_INVALID;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ //TODO: add the implementation.
+ if (VDBG) log("getAudioLocation() from LE audio service");
+ }
+ return defaultLocation;
+ }
+
+ /**
* Set connection policy of the profile
*
* <p> The device should already be paired.
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
index b866cce..c6d161e 100644
--- a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
+++ b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
@@ -77,12 +77,28 @@
public static final int BASS_STATUS_NO_EMPTY_SLOT = 0x09;
public static final int BASS_STATUS_INVALID_GROUP_OP = 0x10;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_DISCONNECTING
+ })
+ public @interface ConnectionStateValues {}
+
+ /**
+ * Callback invoked when the connection state for an LE Audio Broadcast Sink changes
+ */
+ public void onConnectionStateChange(@ConnectionStateValues int prevState,
+ @ConnectionStateValues int newState) {}
+
/**
* Callback invoked when a new LE Audio Broadcast Source is found.
*
* @param result {@link ScanResult} scan result representing a Broadcast Source
*/
- public void onBluetoothLeBroadcastSourceFound(@NonNull ScanResult result) {}
+ public void onSourceFound(@NonNull ScanResult result) {}
/**
* Callback invoked when the Broadcast Assistant synchronizes with Periodic Advertisements (PAs)
@@ -90,7 +106,7 @@
*
* @param source the selected Broadcast Source
*/
- public void onBluetoothLeBroadcastSourceSelected(
+ public void onSourceSelected(
@NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {}
/**
@@ -99,7 +115,7 @@
*
* @param source the Broadcast Source with which synchronization was lost
*/
- public void onBluetoothLeBroadcastSourceLost(
+ public void onSourceLost(
@NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {}
/**
@@ -109,7 +125,7 @@
* @param sink Scan Delegator device on which a new Broadcast Source has been added
* @param source the added Broadcast Source
*/
- public void onBluetoothLeBroadcastSourceAdded(
+ public void onSourceAdded(
@NonNull BluetoothDevice sink,
@NonNull BluetoothLeBroadcastSourceInfo source,
@BassStatus int status) {}
@@ -121,7 +137,7 @@
* @param sink Scan Delegator device on which a Broadcast Source has been updated
* @param source the updated Broadcast Source
*/
- public void onBluetoothLeBroadcastSourceUpdated(
+ public void onSourceUpdated(
@NonNull BluetoothDevice sink,
@NonNull BluetoothLeBroadcastSourceInfo source,
@BassStatus int status) {}
@@ -133,7 +149,7 @@
* @param sink Scan Delegator device from which a Broadcast Source has been removed
* @param source the removed Broadcast Source
*/
- public void onBluetoothLeBroadcastSourceRemoved(
+ public void onSourceRemoved(
@NonNull BluetoothDevice sink,
@NonNull BluetoothLeBroadcastSourceInfo source,
@BassStatus int status) {}
diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java
index d4ad4ef..4641024 100644
--- a/framework/java/android/bluetooth/BluetoothPan.java
+++ b/framework/java/android/bluetooth/BluetoothPan.java
@@ -16,10 +16,12 @@
package android.bluetooth;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.bluetooth.BluetoothUtils.getSyncTimeout;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -30,6 +32,9 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
+import android.net.ITetheredInterfaceCallback;
+import android.net.TetheringManager.TetheredInterfaceCallback;
+import android.net.TetheringManager.TetheredInterfaceRequest;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
@@ -41,6 +46,8 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
/**
@@ -183,6 +190,49 @@
*/
public static final int PAN_OPERATION_SUCCESS = 1004;
+ /**
+ * Request class used by Tethering to notify that the interface is closed.
+ *
+ * @see #requestTetheredInterface
+ * @hide
+ */
+ public class BluetoothTetheredInterfaceRequest implements TetheredInterfaceRequest {
+ private IBluetoothPan mService;
+ private ITetheredInterfaceCallback mCb;
+
+ private BluetoothTetheredInterfaceRequest(@NonNull IBluetoothPan service,
+ @NonNull ITetheredInterfaceCallback cb) {
+ this.mService = service;
+ this.mCb = cb;
+ }
+
+ /**
+ * Called when the Tethering interface has been released.
+ */
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ })
+ @Override
+ public void release() {
+ if (mService == null || mCb == null) {
+ throw new IllegalStateException(
+ "The tethered interface has already been released.");
+ }
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ mService.setBluetoothTethering(mCb, false, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ } finally {
+ mService = null;
+ mCb = null;
+ }
+ }
+ }
+
private final Context mContext;
private final BluetoothAdapter mAdapter;
@@ -450,9 +500,12 @@
}
/**
- * Turns on/off bluetooth tethering
+ * Turns on/off bluetooth tethering.
*
* @param value is whether to enable or disable bluetooth tethering
+ *
+ * @deprecated Use {@link #requestTetheredInterface} with
+ * {@link TetheredInterfaceCallback} instead.
* @hide
*/
@SystemApi
@@ -462,6 +515,7 @@
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
android.Manifest.permission.TETHER_PRIVILEGED,
})
+ @Deprecated
public void setBluetoothTethering(boolean value) {
String pkgName = mContext.getOpPackageName();
if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
@@ -472,7 +526,7 @@
} else if (isEnabled()) {
try {
final SynchronousResultReceiver recv = new SynchronousResultReceiver();
- service.setBluetoothTethering(value, mAttributionSource, recv);
+ service.setBluetoothTethering(null, value, mAttributionSource, recv);
recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
} catch (RemoteException | TimeoutException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
@@ -481,6 +535,66 @@
}
/**
+ * Turns on Bluetooth tethering.
+ *
+ * <p>When one or more devices are connected, the PAN service will trigger
+ * {@link TetheredInterfaceCallback#onAvailable} to inform the caller that
+ * it is ready to tether. On the contrary, when all devices have been disconnected,
+ * the PAN service will trigger {@link TetheredInterfaceCallback#onUnavailable}.
+ * <p>To turn off Bluetooth tethering, the caller must use
+ * {@link TetheredInterfaceRequest#release} method.
+ *
+ * @param executor thread to execute callback methods
+ * @param callback is the tethering callback to indicate PAN service is ready
+ * or not to tether to one or more devices
+ *
+ * @return new instance of {@link TetheredInterfaceRequest} which can be
+ * used to turn off Bluetooth tethering or {@code null} if service
+ * is not enabled
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ })
+ @Nullable
+ public TetheredInterfaceRequest requestTetheredInterface(
+ @NonNull final Executor executor,
+ @NonNull final TetheredInterfaceCallback callback) {
+ Objects.requireNonNull(callback, "Callback must be non-null");
+ Objects.requireNonNull(executor, "Executor must be non-null");
+ final IBluetoothPan service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ final ITetheredInterfaceCallback cbInternal = new ITetheredInterfaceCallback.Stub() {
+ @Override
+ public void onAvailable(String iface) {
+ executor.execute(() -> callback.onAvailable(iface));
+ }
+
+ @Override
+ public void onUnavailable() {
+ executor.execute(() -> callback.onUnavailable());
+ }
+ };
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setBluetoothTethering(cbInternal, true, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ return new BluetoothTetheredInterfaceRequest(service, cbInternal);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
+ }
+ return null;
+ }
+
+ /**
* Determines whether tethering is enabled
*
* @return true if tethering is on, false if not or some error occurred
diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java
index d0f74e9..1fa2bcf 100644
--- a/framework/java/android/bluetooth/BluetoothProfile.java
+++ b/framework/java/android/bluetooth/BluetoothProfile.java
@@ -246,13 +246,19 @@
*/
int LE_CALL_CONTROL = 27;
+ /*
+ * Hearing Access Profile Client
+ *
+ */
+ int HAP_CLIENT = 28;
+
/**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- int MAX_PROFILE_ID = 27;
+ int MAX_PROFILE_ID = 28;
/**
* Default priority for devices that we try to auto-connect to and
@@ -452,6 +458,8 @@
return "HEARING_AID";
case LE_AUDIO:
return "LE_AUDIO";
+ case HAP_CLIENT:
+ return "HAP_CLIENT";
default:
return "UNKNOWN_PROFILE";
}
diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java
index 2a8ff51..5eee498 100644
--- a/framework/java/android/bluetooth/BluetoothUuid.java
+++ b/framework/java/android/bluetooth/BluetoothUuid.java
@@ -158,6 +158,12 @@
/** @hide */
@NonNull
@SystemApi
+ /* FIXME: Not known yet, using a placeholder instead. */
+ public static final ParcelUuid HAS =
+ ParcelUuid.fromString("EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid LE_AUDIO =
ParcelUuid.fromString("0000184E-0000-1000-8000-00805F9B34FB");
/** @hide */
diff --git a/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java b/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java
index bd55426..824ac53 100644
--- a/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java
+++ b/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java
@@ -27,56 +27,77 @@
* runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java
*/
public class BluetoothCodecConfigTest extends TestCase {
- private static final int[] kCodecTypeArray = new int[] {
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
- BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID,
- };
- private static final int[] kCodecPriorityArray = new int[] {
- BluetoothCodecConfig.CODEC_PRIORITY_DISABLED,
- BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
- BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
- };
- private static final int[] kSampleRateArray = new int[] {
- BluetoothCodecConfig.SAMPLE_RATE_NONE,
- BluetoothCodecConfig.SAMPLE_RATE_44100,
- BluetoothCodecConfig.SAMPLE_RATE_48000,
- BluetoothCodecConfig.SAMPLE_RATE_88200,
- BluetoothCodecConfig.SAMPLE_RATE_96000,
- BluetoothCodecConfig.SAMPLE_RATE_176400,
- BluetoothCodecConfig.SAMPLE_RATE_192000,
- };
- private static final int[] kBitsPerSampleArray = new int[] {
- BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
- BluetoothCodecConfig.BITS_PER_SAMPLE_16,
- BluetoothCodecConfig.BITS_PER_SAMPLE_24,
- BluetoothCodecConfig.BITS_PER_SAMPLE_32,
- };
- private static final int[] kChannelModeArray = new int[] {
- BluetoothCodecConfig.CHANNEL_MODE_NONE,
- BluetoothCodecConfig.CHANNEL_MODE_MONO,
- BluetoothCodecConfig.CHANNEL_MODE_STEREO,
- };
- private static final long[] kCodecSpecific1Array = new long[] { 1000, 1001, 1002, 1003, };
- private static final long[] kCodecSpecific2Array = new long[] { 2000, 2001, 2002, 2003, };
- private static final long[] kCodecSpecific3Array = new long[] { 3000, 3001, 3002, 3003, };
- private static final long[] kCodecSpecific4Array = new long[] { 4000, 4001, 4002, 4003, };
+ private static final int[] kCodecTypeArray = new int[] {
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3,
+ BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID,
+ };
+ private static final int[] kCodecPriorityArray = new int[] {
+ BluetoothCodecConfig.CODEC_PRIORITY_DISABLED,
+ BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
+ BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST,
+ };
+ private static final int[] kSampleRateArray = new int[] {
+ BluetoothCodecConfig.SAMPLE_RATE_NONE,
+ BluetoothCodecConfig.SAMPLE_RATE_44100,
+ BluetoothCodecConfig.SAMPLE_RATE_48000,
+ BluetoothCodecConfig.SAMPLE_RATE_88200,
+ BluetoothCodecConfig.SAMPLE_RATE_96000,
+ BluetoothCodecConfig.SAMPLE_RATE_176400,
+ BluetoothCodecConfig.SAMPLE_RATE_192000,
+ };
+ private static final int[] kBitsPerSampleArray = new int[] {
+ BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_16,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_24,
+ BluetoothCodecConfig.BITS_PER_SAMPLE_32,
+ };
+ private static final int[] kChannelModeArray = new int[] {
+ BluetoothCodecConfig.CHANNEL_MODE_NONE,
+ BluetoothCodecConfig.CHANNEL_MODE_MONO,
+ BluetoothCodecConfig.CHANNEL_MODE_STEREO,
+ };
+ private static final long[] kCodecSpecific1Array = new long[] {
+ 1000,
+ 1001,
+ 1002,
+ 1003,
+ };
+ private static final long[] kCodecSpecific2Array = new long[] {
+ 2000,
+ 2001,
+ 2002,
+ 2003,
+ };
+ private static final long[] kCodecSpecific3Array = new long[] {
+ 3000,
+ 3001,
+ 3002,
+ 3003,
+ };
+ private static final long[] kCodecSpecific4Array = new long[] {
+ 4000,
+ 4001,
+ 4002,
+ 4003,
+ };
- private static final int kTotalConfigs = kCodecTypeArray.length * kCodecPriorityArray.length *
- kSampleRateArray.length * kBitsPerSampleArray.length * kChannelModeArray.length *
- kCodecSpecific1Array.length * kCodecSpecific2Array.length * kCodecSpecific3Array.length *
- kCodecSpecific4Array.length;
+ private static final int kTotalConfigs = kCodecTypeArray.length * kCodecPriorityArray.length
+ * kSampleRateArray.length * kBitsPerSampleArray.length * kChannelModeArray.length
+ * kCodecSpecific1Array.length * kCodecSpecific2Array.length * kCodecSpecific3Array.length
+ * kCodecSpecific4Array.length;
- private int selectCodecType(int configId) {
- int left = kCodecTypeArray.length;
- int right = kTotalConfigs / left;
- int index = configId / right;
- index = index % kCodecTypeArray.length;
- return kCodecTypeArray[index];
- }
+ private int selectCodecType(int configId) {
+ int left = kCodecTypeArray.length;
+ int right = kTotalConfigs / left;
+ int index = configId / right;
+ index = index % kCodecTypeArray.length;
+ return kCodecTypeArray[index];
+ }
private int selectCodecPriority(int configId) {
int left = kCodecTypeArray.length * kCodecPriorityArray.length;
@@ -193,6 +214,9 @@
if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC) {
assertEquals("LDAC", bcc.getCodecName());
}
+ if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3) {
+ assertEquals("LC3", bcc.getCodecName());
+ }
if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
assertEquals("INVALID CODEC", bcc.getCodecName());
}
diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java
index 409025b..6b7a75a 100644
--- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java
@@ -23,6 +23,9 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
+import android.net.TetheringManager;
+import android.net.TetheringManager.TetheredInterfaceCallback;
+import android.net.TetheringManager.TetheredInterfaceRequest;
import android.os.Environment;
import android.util.Log;
@@ -407,6 +410,8 @@
private BluetoothPan mPan = null;
private BluetoothMapClient mMce = null;
private String mMsgHandle = null;
+ private TetheredInterfaceCallback mPanCallback = null;
+ private TetheredInterfaceRequest mBluetoothIfaceRequest;
/**
* Creates a utility instance for testing Bluetooth.
@@ -740,7 +745,17 @@
assertNotNull(mPan);
long start = System.currentTimeMillis();
- mPan.setBluetoothTethering(true);
+ mPanCallback = new TetheringManager.TetheredInterfaceCallback() {
+ @Override
+ public void onAvailable(String iface) {
+ }
+
+ @Override
+ public void onUnavailable() {
+ }
+ };
+ mBluetoothIfaceRequest = mPan.requestTetheredInterface(mContext.getMainExecutor(),
+ mPanCallback);
long stop = System.currentTimeMillis();
assertTrue(mPan.isTetheringOn());
@@ -758,7 +773,10 @@
assertNotNull(mPan);
long start = System.currentTimeMillis();
- mPan.setBluetoothTethering(false);
+ if (mBluetoothIfaceRequest != null) {
+ mBluetoothIfaceRequest.release();
+ mBluetoothIfaceRequest = null;
+ }
long stop = System.currentTimeMillis();
assertFalse(mPan.isTetheringOn());
diff --git a/service/.clang-format b/service/.clang-format
new file mode 100644
index 0000000..9f6d7a9
--- /dev/null
+++ b/service/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+
+AccessModifierOffset: -4
+AlignOperands: false
+AllowShortFunctionsOnASingleLine: Inline
+AlwaysBreakBeforeMultilineStrings: false
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ConstructorInitializerIndentWidth: 6
+ContinuationIndentWidth: 8
+IndentWidth: 4
+PenaltyBreakBeforeFirstCallParameter: 100000
+SpacesBeforeTrailingComments: 1#
diff --git a/system/binder/Android.bp b/system/binder/Android.bp
index 1391206..a5b5cda 100644
--- a/system/binder/Android.bp
+++ b/system/binder/Android.bp
@@ -24,6 +24,7 @@
"android/bluetooth/IBluetoothProfileServiceConnection.aidl",
"android/bluetooth/IBluetoothHeadset.aidl",
"android/bluetooth/IBluetoothHearingAid.aidl",
+ "android/bluetooth/IBluetoothHapClient.aidl",
"android/bluetooth/IBluetoothVolumeControl.aidl",
"android/bluetooth/IBluetoothHidHost.aidl",
"android/bluetooth/IBluetoothLeAudio.aidl",
diff --git a/system/binder/android/bluetooth/BluetoothHapPresetInfo.aidl b/system/binder/android/bluetooth/BluetoothHapPresetInfo.aidl
new file mode 100644
index 0000000..06f205a
--- /dev/null
+++ b/system/binder/android/bluetooth/BluetoothHapPresetInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+parcelable BluetoothHapPresetInfo;
diff --git a/system/binder/android/bluetooth/IBluetooth.aidl b/system/binder/android/bluetooth/IBluetooth.aidl
index b93cd62..97e2c59 100644
--- a/system/binder/android/bluetooth/IBluetooth.aidl
+++ b/system/binder/android/bluetooth/IBluetooth.aidl
@@ -198,7 +198,9 @@
@JavaPassthrough(annotation="@android.annotation.RequiresNoPermission")
int isLeAudioSupported();
@JavaPassthrough(annotation="@android.annotation.RequiresNoPermission")
- int isLePeriodicAdvertisingSyncTransferSenderSupported();
+ int isLeAudioBroadcastSourceSupported();
+ @JavaPassthrough(annotation="@android.annotation.RequiresNoPermission")
+ int isLeAudioBroadcastAssistantSupported();
@JavaPassthrough(annotation="@android.annotation.RequiresNoPermission")
int getLeMaximumAdvertisingDataLength();
@@ -253,4 +255,7 @@
boolean canBondWithoutDialog(in BluetoothDevice device, in AttributionSource attributionSource);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
void generateLocalOobData(in int transport, IBluetoothOobDataCallback callback, in AttributionSource attributionSource);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ boolean allowLowLatencyAudio(in boolean allowed, in BluetoothDevice device);
}
diff --git a/system/binder/android/bluetooth/IBluetoothHapClient.aidl b/system/binder/android/bluetooth/IBluetoothHapClient.aidl
new file mode 100644
index 0000000..672ab40
--- /dev/null
+++ b/system/binder/android/bluetooth/IBluetoothHapClient.aidl
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.AttributionSource;
+
+import com.android.modules.utils.SynchronousResultReceiver;
+
+/**
+ * APIs for Bluetooth Hearing Access Profile client
+ *
+ * @hide
+ */
+oneway interface IBluetoothHapClient {
+ // Public API
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+ void connect(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+ void disconnect(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+ void getConnectedDevices(in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+ void getDevicesMatchingConnectionStates(in int[] states, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+ void getConnectionState(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ void setConnectionPolicy(in BluetoothDevice device, int connectionPolicy, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+ void getConnectionPolicy(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+ void getHapGroup(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+ void getActivePresetIndex(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ void selectActivePreset(in BluetoothDevice device, int presetIndex, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ void groupSelectActivePreset(int groupId, int presetIndex, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ void nextActivePreset(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ void groupNextActivePreset(int groupId, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ void previousActivePreset(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ void groupPreviousActivePreset(int groupId, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+ void getPresetInfo(in BluetoothDevice device, int presetIndex, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+ void getAllPresetsInfo(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+ void getFeatures(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ void setPresetName(in BluetoothDevice device, int presetIndex, in String name, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ void groupSetPresetName(int groupId, int presetIndex, in String name, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+
+ const int PRESET_INDEX_UNAVAILABLE = 0;
+ const int GROUP_ID_UNAVAILABLE = -1;
+
+ const int STATUS_SET_NAME_NOT_ALLOWED = 1;
+ const int STATUS_OPERATION_NOT_SUPPORTED = 2;
+ const int STATUS_OPERATION_NOT_POSSIBLE = 3;
+ const int STATUS_INVALID_PRESET_NAME_LENGTH = 4;
+ const int STATUS_INVALID_PRESET_INDEX = 5;
+ const int STATUS_GROUP_OPERATION_NOT_SUPPORTED = 6;
+ const int STATUS_PROCEDURE_ALREADY_IN_PROGRESS = 7;
+
+ const int FEATURE_BIT_NUM_TYPE_MONAURAL = 0;
+ const int FEATURE_BIT_NUM_TYPE_BANDED = 1;
+ const int FEATURE_BIT_NUM_SYNCHRONIZATED_PRESETS = 2;
+ const int FEATURE_BIT_NUM_INDEPENDENT_PRESETS = 3;
+ const int FEATURE_BIT_NUM_DYNAMIC_PRESETS = 4;
+ const int FEATURE_BIT_NUM_WRITABLE_PRESETS = 5;
+
+ const int PRESET_INFO_REASON_ALL_PRESET_INFO = 0;
+ const int PRESET_INFO_REASON_PRESET_INFO_UPDATE = 1;
+ const int PRESET_INFO_REASON_PRESET_DELETED = 2;
+ const int PRESET_INFO_REASON_PRESET_AVAILABILITY_CHANGED = 3;
+ const int PRESET_INFO_REASON_PRESET_INFO_REQUEST_RESPONSE = 4;
+}
diff --git a/system/binder/android/bluetooth/IBluetoothPan.aidl b/system/binder/android/bluetooth/IBluetoothPan.aidl
index 1ba88ee..31dbebd 100644
--- a/system/binder/android/bluetooth/IBluetoothPan.aidl
+++ b/system/binder/android/bluetooth/IBluetoothPan.aidl
@@ -18,6 +18,7 @@
import android.bluetooth.BluetoothDevice;
import android.content.AttributionSource;
+import android.net.ITetheredInterfaceCallback;
import com.android.modules.utils.SynchronousResultReceiver;
@@ -31,7 +32,7 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
void isTetheringOn(in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED,android.Manifest.permission.TETHER_PRIVILEGED})")
- void setBluetoothTethering(boolean value, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ void setBluetoothTethering(ITetheredInterfaceCallback callback, boolean value, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
void connect(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
diff --git a/system/blueberry/tests/gd/cert/gd_base_test.py b/system/blueberry/tests/gd/cert/gd_base_test.py
index c3a36d7..da5b943 100644
--- a/system/blueberry/tests/gd/cert/gd_base_test.py
+++ b/system/blueberry/tests/gd/cert/gd_base_test.py
@@ -39,8 +39,26 @@
from mobly import base_test
+class Timeout:
+
+ def __init__(self, seconds=1, error_message='Timeout'):
+ self.seconds = seconds
+ self.error_message = error_message
+
+ def handle_timeout(self, signum, frame):
+ raise TimeoutError(self.error_message)
+
+ def __enter__(self):
+ signal.signal(signal.SIGALRM, self.handle_timeout)
+ signal.alarm(self.seconds)
+
+ def __exit__(self, type, value, traceback):
+ signal.alarm(0)
+
+
class GdBaseTestClass(base_test.BaseTestClass):
+ FUNCTION_CALL_TIMEOUT_SECONDS = 5
SUBPROCESS_WAIT_TIMEOUT_SECONDS = 10
def setup_class(self, dut_module, cert_module):
@@ -117,12 +135,15 @@
def teardown_test(self):
stack = ""
try:
- stack = "CERT"
- self.cert.rootservice.StopStack(facade_rootservice.StopStackRequest())
- stack = "DUT"
- self.dut.rootservice.StopStack(facade_rootservice.StopStackRequest())
+ with Timeout(seconds=self.FUNCTION_CALL_TIMEOUT_SECONDS):
+ stack = "CERT"
+ self.cert.rootservice.StopStack(facade_rootservice.StopStackRequest())
+ stack = "DUT"
+ self.dut.rootservice.StopStack(facade_rootservice.StopStackRequest())
except RpcError as rpc_error:
asserts.fail("Failed to stop {} stack, RpcError={!r}".format(stack, rpc_error))
+ except TimeoutError:
+ logging.error("Failed to stop {} stack in {} s".format(stack, self.FUNCTION_CALL_TIMEOUT_SECONDS))
finally:
# Destroy GD device objects
self._controller_manager.unregister_controllers()
diff --git a/system/bta/Android.bp b/system/bta/Android.bp
index 3058155..9b791bf 100644
--- a/system/bta/Android.bp
+++ b/system/bta/Android.bp
@@ -486,6 +486,7 @@
"packages/modules/Bluetooth/system/stack/include",
],
srcs : [
+ ":TestStubOsi",
":TestMockBtaLeAudioHalVerifier",
"gatt/database.cc",
"gatt/database_builder.cc",
diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc
index e0d40c5..fceaed7 100644
--- a/system/bta/dm/bta_dm_act.cc
+++ b/system/bta/dm/bta_dm_act.cc
@@ -25,6 +25,8 @@
#define LOG_TAG "bt_bta_dm"
+#include <base/logging.h>
+
#include <cstdint>
#include "bta/dm/bta_dm_int.h"
@@ -44,6 +46,7 @@
#include "osi/include/fixed_queue.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"
+#include "stack/btm/btm_ble_int.h"
#include "stack/btm/btm_sec.h"
#include "stack/btm/neighbor_inquiry.h"
#include "stack/gatt/connection_manager.h"
@@ -56,8 +59,6 @@
#include "types/bluetooth/uuid.h"
#include "types/raw_address.h"
-#include <base/logging.h>
-
#if (GAP_INCLUDED == TRUE)
#include "gap_api.h"
#endif
@@ -1809,6 +1810,10 @@
uint16_t service_class;
result.inq_res.bd_addr = p_inq->remote_bd_addr;
+
+ // Pass the original address to GattService#onScanResult
+ result.inq_res.original_bda = p_inq->original_bda;
+
memcpy(result.inq_res.dev_class, p_inq->dev_class, DEV_CLASS_LEN);
BTM_COD_SERVICE_CLASS(service_class, p_inq->dev_class);
result.inq_res.is_limited =
@@ -3312,6 +3317,7 @@
APPL_TRACE_DEBUG("bta_dm_observe_results_cb");
result.inq_res.bd_addr = p_inq->remote_bd_addr;
+ result.inq_res.original_bda = p_inq->original_bda;
result.inq_res.rssi = p_inq->rssi;
result.inq_res.ble_addr_type = p_inq->ble_addr_type;
result.inq_res.inq_result_type = p_inq->inq_result_type;
diff --git a/system/bta/hearing_aid/hearing_aid.cc b/system/bta/hearing_aid/hearing_aid.cc
index 738cda3..9a0b3fd 100644
--- a/system/bta/hearing_aid/hearing_aid.cc
+++ b/system/bta/hearing_aid/hearing_aid.cc
@@ -384,7 +384,6 @@
return;
}
- hearingDevice->connecting_actively = false;
hearingDevice->conn_id = conn_id;
/* We must update connection parameters one at a time, otherwise anchor
@@ -962,6 +961,7 @@
send_state_change_to_other_side(hearingDevice, inform_conn_state);
}
+ hearingDevice->connecting_actively = false;
hearingDevice->accepting_audio = true;
LOG(INFO) << __func__ << ": address=" << address
<< ", hi_sync_id=" << loghex(hearingDevice->hi_sync_id)
@@ -1327,15 +1327,12 @@
break;
}
- // TODO: handle properly!
case GAP_EVT_CONN_CLOSED:
LOG(INFO) << __func__
<< ": GAP_EVT_CONN_CLOSED: " << hearingDevice->address
<< ", playback_started=" << hearingDevice->playback_started;
- hearingDevice->accepting_audio = false;
- hearingDevice->gap_handle = 0;
- hearingDevice->playback_started = false;
- hearingDevice->command_acked = false;
+ /* Disconnect profile when data channel is not available */
+ Disconnect(hearingDevice->address);
break;
case GAP_EVT_CONN_DATA_AVAIL: {
DVLOG(2) << "GAP_EVT_CONN_DATA_AVAIL";
@@ -1460,6 +1457,7 @@
VLOG(2) << __func__ << ": " << address;
bool connected = hearingDevice->accepting_audio;
+ bool connecting_by_user = hearingDevice->connecting_actively;
LOG(INFO) << "GAP_EVT_CONN_CLOSED: " << hearingDevice->address
<< ", playback_started=" << hearingDevice->playback_started
@@ -1483,6 +1481,10 @@
hearingDevices.Remove(address);
if (!connected) {
+ /* In case user wanted to connect, sent DISCONNECTED state */
+ if (connecting_by_user)
+ callbacks->OnConnectionState(ConnectionState::DISCONNECTED, address);
+
return;
}
diff --git a/system/bta/include/bta_api.h b/system/bta/include/bta_api.h
index da6cefe..b7f0f54 100644
--- a/system/bta/include/bta_api.h
+++ b/system/bta/include/bta_api.h
@@ -434,6 +434,8 @@
tBT_DEVICE_TYPE device_type;
uint8_t flag;
bool include_rsi; /* true, if ADV contains RSI data */
+ RawAddress original_bda; /* original address to pass up to
+ GattService#onScanResult */
} tBTA_DM_INQ_RES;
/* Structure associated with BTA_DM_INQ_CMPL_EVT */
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index fd72d5b..3aa605e 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -167,7 +167,6 @@
callbacks_(callbacks_),
active_group_id_(bluetooth::groups::kGroupUnknown),
current_context_type_(LeAudioContextType::MEDIA),
- upcoming_context_type_(LeAudioContextType::MEDIA),
stream_setup_start_timestamp_(0),
stream_setup_end_timestamp_(0),
audio_receiver_state_(AudioState::IDLE),
@@ -530,6 +529,7 @@
LeAudioDeviceGroup* group = aseGroups_.FindById(group_id);
auto final_context_type = context_type;
+ DLOG(INFO) << __func__;
if (context_type >= static_cast<uint16_t>(LeAudioContextType::RFU)) {
LOG(ERROR) << __func__ << ", stream context type is not supported: "
<< loghex(context_type);
@@ -654,6 +654,8 @@
if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_);
StopAudio();
+ ClientAudioIntefraceRelease();
+
GroupStop(active_group_id_);
callbacks_->OnGroupStatus(active_group_id_, GroupStatus::INACTIVE);
active_group_id_ = group_id;
@@ -691,8 +693,11 @@
}
}
- /* Configure audio HAL sessions with most frequent context */
- UpdateCurrentHalSessions(group_id, current_context_type_);
+ /* Configure audio HAL sessions with most frequent context.
+ * If reconfiguration is not needed it means, context type is not supported
+ */
+ UpdateConfigAndCheckIfReconfigurationIsNeeded(group_id,
+ LeAudioContextType::MEDIA);
if (current_source_codec_config.IsInvalid() &&
current_sink_codec_config.IsInvalid()) {
LOG(WARNING) << __func__ << ", unsupported device configurations";
@@ -700,6 +705,18 @@
return;
}
+ /* Expose audio sessions */
+ audio_framework_source_config.data_interval_us =
+ current_source_codec_config.data_interval_us;
+ LeAudioClientAudioSource::Start(audio_framework_source_config,
+ audioSinkReceiver);
+
+ audio_framework_sink_config.data_interval_us =
+ current_source_codec_config.data_interval_us;
+
+ LeAudioClientAudioSink::Start(audio_framework_sink_config,
+ audioSourceReceiver);
+
active_group_id_ = group_id;
callbacks_->OnGroupStatus(active_group_id_, GroupStatus::ACTIVE);
}
@@ -1710,12 +1727,21 @@
if (audio_sender_state_ == AudioState::IDLE &&
audio_receiver_state_ == AudioState::IDLE) {
DLOG(INFO) << __func__
- << " Device not streaming but active. Lets update audio "
- "session to match needed channel number";
- UpdateCurrentHalSessions(active_group_id_, current_context_type_);
+ << " Device not streaming but active - nothing to do";
return;
}
+ auto num_of_devices =
+ get_num_of_devices_in_configuration(stream_conf->conf);
+
+ if (num_of_devices < group->NumOfConnected()) {
+ /* Second device got just paired. We need to reconfigure CIG */
+ stream_conf->reconfiguration_ongoing = true;
+ groupStateMachine_->StopStream(group);
+ return;
+ }
+
+ /* Second device got reconnect. Try to get it to the stream seamlessly */
le_audio::types::AudioLocations sink_group_audio_locations = 0;
uint8_t sink_num_of_active_ases = 0;
@@ -1785,7 +1811,7 @@
}
void get_mono_stream(const std::vector<uint8_t>& data,
- std::vector<int16_t>& chan_mono) {
+ std::vector<int16_t>& chan_mono, int pitch = 1) {
uint16_t num_of_frames_per_ch;
int dt_us = current_source_codec_config.data_interval_us;
@@ -1793,7 +1819,7 @@
num_of_frames_per_ch = lc3_frame_samples(dt_us, sr_hz);
chan_mono.reserve(num_of_frames_per_ch);
- for (int i = 0; i < num_of_frames_per_ch; i++) {
+ for (int i = 0; i < pitch * num_of_frames_per_ch; i += pitch) {
const uint8_t* sample = data.data() + i * 4;
int16_t left = (int16_t)((*(sample + 1) << 8) + *sample) >> 1;
@@ -1807,37 +1833,6 @@
}
}
- void get_left_and_right_stream(const std::vector<uint8_t>& data,
- std::vector<int16_t>& chan_left,
- std::vector<int16_t>& chan_right,
- bool prepare_mono = false) {
- uint16_t num_of_frames_per_ch;
-
- int dt_us = current_source_codec_config.data_interval_us;
- int sr_hz = current_source_codec_config.sample_rate;
- num_of_frames_per_ch = lc3_frame_samples(dt_us, sr_hz);
-
- chan_left.reserve(num_of_frames_per_ch);
- chan_right.reserve(num_of_frames_per_ch);
- for (int i = 0; i < num_of_frames_per_ch; i++) {
- const uint8_t* sample = data.data() + i * 4;
-
- int16_t left = (int16_t)((*(sample + 1) << 8) + *sample) >> 1;
-
- sample += 2;
- int16_t right = (int16_t)((*(sample + 1) << 8) + *sample) >> 1;
-
- if (prepare_mono) {
- uint16_t mono_data = (int16_t)(((uint32_t)left + (uint32_t)right) >> 1);
- left = mono_data;
- right = mono_data;
- }
-
- chan_left.push_back(left);
- chan_right.push_back(right);
- }
- }
-
void PrepareAndSendToTwoDevices(
const std::vector<uint8_t>& data,
struct le_audio::stream_configuration* stream_conf) {
@@ -1869,27 +1864,37 @@
std::vector<uint8_t> chan_right_enc(byte_count, 0);
bool mono = (left_cis_handle == 0) || (right_cis_handle == 0);
+
+ int af_hz = audio_framework_source_config.sample_rate;
+ LOG_ASSERT(af_hz >= sr_hz) << __func__ << " sample freq issue";
+
+ int pitch = af_hz / sr_hz;
+
+ LOG(INFO) << __func__ << " pitch " << pitch
+ << " data size: " << (int)data.size()
+ << " byte count: " << byte_count << " mono: " << mono;
if (!mono) {
- lc3_encode(lc3_encoder_left, (const int16_t*)data.data(), 2,
+ lc3_encode(lc3_encoder_left, (const int16_t*)data.data(), 2 * pitch,
chan_left_enc.size(), chan_left_enc.data());
- lc3_encode(lc3_encoder_right, ((const int16_t*)data.data()) + 1, 2,
- chan_right_enc.size(), chan_right_enc.data());
+ lc3_encode(lc3_encoder_right, ((const int16_t*)data.data()) + 1,
+ 2 * pitch, chan_right_enc.size(), chan_right_enc.data());
} else {
- std::vector<int16_t> chan_left;
- std::vector<int16_t> chan_right;
- get_left_and_right_stream(data, chan_left, chan_right, mono);
+ std::vector<int16_t> chan_mono;
+ get_mono_stream(data, chan_mono, pitch);
if (left_cis_handle) {
- lc3_encode(lc3_encoder_left, (const int16_t*)chan_left.data(), 1,
+ lc3_encode(lc3_encoder_left, (const int16_t*)chan_mono.data(), 1,
chan_left_enc.size(), chan_left_enc.data());
}
if (right_cis_handle) {
- lc3_encode(lc3_encoder_right, (const int16_t*)chan_right.data(), 1,
+ lc3_encode(lc3_encoder_right, (const int16_t*)chan_mono.data(), 1,
chan_right_enc.size(), chan_right_enc.data());
}
}
+ DLOG(INFO) << __func__ << " left_cis_handle: " << +left_cis_handle
+ << " right_cis_handle: " << right_cis_handle;
/* Send data to the controller */
if (left_cis_handle)
IsoManager::GetInstance()->SendIsoData(
@@ -1919,23 +1924,24 @@
}
std::vector<uint8_t> chan_encoded(num_channels * byte_count, 0);
+ int af_hz = audio_framework_source_config.sample_rate;
+ LOG_ASSERT(af_hz >= sr_hz) << __func__ << " sample freq issue";
+
+ int pitch = af_hz / sr_hz;
+
if (num_channels == 1) {
/* Since we always get two channels from framework, lets make it mono here
*/
std::vector<int16_t> chan_mono;
- get_mono_stream(data, chan_mono);
+ get_mono_stream(data, chan_mono, pitch);
lc3_encode(lc3_encoder_left, (const int16_t*)chan_mono.data(), 1,
byte_count, chan_encoded.data());
} else {
- std::vector<int16_t> chan_left;
- std::vector<int16_t> chan_right;
- get_left_and_right_stream(data, chan_left, chan_right, false);
-
- lc3_encode(lc3_encoder_left, (const int16_t*)chan_left.data(), 1,
+ lc3_encode(lc3_encoder_left, (const int16_t*)data.data(), 2 * pitch,
byte_count, chan_encoded.data());
- lc3_encode(lc3_encoder_right, (const int16_t*)chan_right.data(), 1,
+ lc3_encode(lc3_encoder_right, (const int16_t*)data.data() + 1, 2 * pitch,
byte_count, chan_encoded.data() + byte_count);
}
@@ -2083,7 +2089,8 @@
/* TODO: What to do if not all data sinked ? */
if (written != to_write) LOG(ERROR) << __func__ << ", not all data sinked";
- LOG(INFO) << __func__;
+ DLOG(INFO) << __func__
+ << " num of frames: " << (int)lc3_decoder->lc3Config.NF;
}
static inline Lc3Config::FrameDuration Lc3ConfigFrameDuration(
@@ -2236,10 +2243,7 @@
}
}
- void StopAudio(void) {
- SuspendAudio();
- ClientAudioIntefraceRelease();
- }
+ void StopAudio(void) { SuspendAudio(); }
void printSingleConfiguration(int fd, LeAudioCodecConfiguration* conf,
bool print_audio_state, bool sender = false) {
@@ -2261,14 +2265,26 @@
}
void printCurrentStreamConfiguration(int fd) {
- auto conf = ¤t_source_codec_config;
- dprintf(fd, " Speaker codec config \n");
+ auto conf = &audio_framework_source_config;
+ dprintf(fd, " Speaker codec config (audio framework) \n");
if (conf) {
printSingleConfiguration(fd, conf, false);
}
- dprintf(fd, " Microphone codec config \n");
+ dprintf(fd, " Microphone codec config (audio framework) \n");
+ conf = &audio_framework_sink_config;
+ if (conf) {
+ printSingleConfiguration(fd, conf, false);
+ }
+
+ conf = ¤t_source_codec_config;
+ dprintf(fd, " Speaker codec config (Bluetooth)\n");
+ if (conf) {
+ printSingleConfiguration(fd, conf, true, true);
+ }
+
conf = ¤t_sink_codec_config;
+ dprintf(fd, " Microphone codec config (Bluetooth)\n");
if (conf) {
printSingleConfiguration(fd, conf, true, false);
}
@@ -2277,7 +2293,6 @@
void Dump(int fd) {
dprintf(fd, " Active group: %d\n", active_group_id_);
dprintf(fd, " current content type: 0x%08hx\n", current_context_type_);
- dprintf(fd, " upcoming content type: 0x%08hx\n", upcoming_context_type_);
dprintf(
fd, " stream setup time if started: %d ms\n",
(int)((stream_setup_end_timestamp_ - stream_setup_start_timestamp_) /
@@ -2298,20 +2313,16 @@
if (gatt_if_) BTA_GATTC_AppDeregister(gatt_if_);
}
- void UpdateCurrentHalSessions(int group_id, LeAudioContextType context_type) {
- if (group_id == bluetooth::groups::kGroupUnknown) {
- LOG(WARNING) << ", cannot start straming if no active group set";
- return;
- }
-
+ bool UpdateConfigAndCheckIfReconfigurationIsNeeded(
+ int group_id, LeAudioContextType context_type) {
+ bool reconfiguration_needed = false;
auto group = aseGroups_.FindById(group_id);
if (!group) {
LOG(ERROR) << __func__
<< ", Invalid group: " << static_cast<int>(group_id);
- return;
+ return reconfiguration_needed;
}
- bool send_active = false;
std::optional<LeAudioCodecConfiguration> source_configuration =
group->GetCodecConfigurationByDirection(
context_type, le_audio::types::kLeAudioDirectionSink);
@@ -2319,42 +2330,15 @@
group->GetCodecConfigurationByDirection(
context_type, le_audio::types::kLeAudioDirectionSource);
- if (!sink_configuration) {
- /* Let's check if le_audio group supports conversational, if so,
- * expose DECODED session to the system
- */
- sink_configuration = group->GetCodecConfigurationByDirection(
- LeAudioContextType::CONVERSATIONAL,
- le_audio::types::kLeAudioDirectionSource);
- if (sink_configuration) {
- LOG(INFO) << __func__
- << " exposing DECODED session to the system even context: "
- << static_cast<int>(context_type) << " does not use it";
- }
- }
-
if (source_configuration) {
- /* Stream configuration differs from previous one */
- if (!current_source_codec_config.IsInvalid() &&
- (*source_configuration != current_source_codec_config)) {
- callbacks_->OnGroupStatus(group_id, GroupStatus::INACTIVE);
- send_active = true;
- LeAudioClientAudioSource::Stop();
+ if (*source_configuration != current_source_codec_config) {
+ current_source_codec_config = *source_configuration;
+ reconfiguration_needed = true;
}
-
- current_source_codec_config = *source_configuration;
-
- /*Let's always request 2 channels from the framework */
- auto audio_framework_configuration = current_source_codec_config;
- audio_framework_configuration.num_channels = 2;
-
- LeAudioClientAudioSource::Start(audio_framework_configuration,
- audioSinkReceiver);
-
} else {
if (!current_source_codec_config.IsInvalid()) {
- LeAudioClientAudioSource::Stop();
current_source_codec_config = {0, 0, 0, 0};
+ reconfiguration_needed = true;
}
LOG(INFO) << __func__
@@ -2364,24 +2348,14 @@
}
if (sink_configuration) {
- /* Stream configuration differs from previous one */
- if (!current_sink_codec_config.IsInvalid() &&
- (*sink_configuration != current_sink_codec_config)) {
- if (send_active == false) {
- callbacks_->OnGroupStatus(group_id, GroupStatus::INACTIVE);
- send_active = true;
- }
- LeAudioClientAudioSink::Stop();
+ if (*sink_configuration != current_sink_codec_config) {
+ current_sink_codec_config = *sink_configuration;
+ reconfiguration_needed = true;
}
-
- current_sink_codec_config = *sink_configuration;
-
- LeAudioClientAudioSink::Start(current_sink_codec_config,
- audioSourceReceiver);
} else {
if (!current_sink_codec_config.IsInvalid()) {
- LeAudioClientAudioSink::Stop();
current_sink_codec_config = {0, 0, 0, 0};
+ reconfiguration_needed = true;
}
LOG(INFO) << __func__
@@ -2390,24 +2364,20 @@
<< static_cast<int>(context_type);
}
- if (send_active) {
- callbacks_->OnGroupStatus(group_id, GroupStatus::ACTIVE);
+ if (reconfiguration_needed) {
+ LOG(INFO) << __func__
+ << " Session reconfiguration needed group: " << group->group_id_
+ << " for context type: " << static_cast<int>(context_type);
}
- current_context_type_ = upcoming_context_type_;
+
+ current_context_type_ = context_type;
+ return reconfiguration_needed;
}
bool OnAudioResume(LeAudioDeviceGroup* group) {
- if (upcoming_context_type_ != current_context_type_) {
- return false;
+ if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+ return true;
}
-
- /* Even context type is same, we might need more audio channels e.g. because
- * new device got connected */
- if (ReconfigureHalSessionIfNeeded(group, current_context_type_)) {
- return false;
- }
-
- /* TODO check if group already started streaming */
return InternalGroupStream(active_group_id_,
static_cast<uint16_t>(current_context_type_));
}
@@ -2498,8 +2468,6 @@
<< " audio_sender_state: " << audio_sender_state_ << "\n"
<< " current_context_type_: "
<< static_cast<int>(current_context_type_) << "\n"
- << " upcoming_context_type_: "
- << static_cast<int>(upcoming_context_type_) << "\n"
<< " group " << (group ? " exist " : " does not exist ") << "\n";
switch (audio_sender_state_) {
@@ -2530,8 +2498,16 @@
break;
case AudioState::RELEASING:
case AudioState::READY_TO_RELEASE:
+ /* If group is reconfiguring, reassing state and wait for
+ * the stream to be established
+ */
+ if (group->stream_conf.reconfiguration_ongoing) {
+ audio_sender_state_ = audio_receiver_state_;
+ return;
+ }
+ FALLTHROUGH;
default:
- LeAudioClientAudioSink::CancelStreamingRequest();
+ LeAudioClientAudioSource::CancelStreamingRequest();
break;
}
@@ -2628,8 +2604,6 @@
<< " audio_sender_state: " << audio_sender_state_ << "\n"
<< " current_context_type_: "
<< static_cast<int>(current_context_type_) << "\n"
- << " upcoming_context_type_: "
- << static_cast<int>(upcoming_context_type_) << "\n"
<< " group " << (group ? " exist " : " does not exist ") << "\n";
switch (audio_receiver_state_) {
@@ -2658,6 +2632,14 @@
break;
case AudioState::RELEASING:
case AudioState::READY_TO_RELEASE:
+ /* If group is reconfiguring, reassing state and wait for
+ * the stream to be established
+ */
+ if (group->stream_conf.reconfiguration_ongoing) {
+ audio_receiver_state_ = audio_sender_state_;
+ return;
+ }
+ FALLTHROUGH;
default:
LeAudioClientAudioSink::CancelStreamingRequest();
break;
@@ -2767,55 +2749,26 @@
return available_contents[0];
}
- bool ReconfigureHalSessionIfNeeded(LeAudioDeviceGroup* group,
- LeAudioContextType new_context_type) {
- std::optional<LeAudioCodecConfiguration> source_configuration =
- group->GetCodecConfigurationByDirection(
- new_context_type, le_audio::types::kLeAudioDirectionSink);
-
- std::optional<LeAudioCodecConfiguration> sink_configuration =
- group->GetCodecConfigurationByDirection(
- new_context_type, le_audio::types::kLeAudioDirectionSource);
-
- if (!sink_configuration) {
- /* Let's check if le_audio group supports conversational, if so,
- * expose DECODED session to the system
- */
- sink_configuration = group->GetCodecConfigurationByDirection(
- LeAudioContextType::CONVERSATIONAL,
- le_audio::types::kLeAudioDirectionSource);
- if (sink_configuration) {
- DLOG(INFO) << __func__
- << " exposing DECODED session to the system even context: "
- << static_cast<int>(new_context_type) << " does not use it";
- }
+ bool StopStreamIfNeeded(LeAudioDeviceGroup* group,
+ LeAudioContextType new_context_type) {
+ DLOG(INFO) << __func__ << " context type " << int(new_context_type);
+ if (!UpdateConfigAndCheckIfReconfigurationIsNeeded(group->group_id_,
+ new_context_type)) {
+ DLOG(INFO) << __func__ << " reconfiguration not needed";
+ return false;
}
- if ((source_configuration &&
- (*source_configuration != current_source_codec_config)) ||
- (sink_configuration &&
- (*sink_configuration != current_sink_codec_config))) {
- LOG(INFO) << __func__
- << " Session reconfiguration needed group: " << group->group_id_
- << "for context type: " << static_cast<int>(new_context_type);
-
- if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
- if (alarm_is_scheduled(suspend_timeout_))
- alarm_cancel(suspend_timeout_);
-
- GroupStop(group->group_id_);
- }
-
- upcoming_context_type_ = new_context_type;
- /* Schedule HAL Session update */
- do_in_main_thread(FROM_HERE,
- base::Bind(&LeAudioClientImpl::UpdateCurrentHalSessions,
- base::Unretained(instance), group->group_id_,
- upcoming_context_type_));
- return true;
+ if (group->GetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+ DLOG(INFO) << __func__ << " Group is not streaming ";
+ return false;
}
- return false;
+ if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_);
+
+ /* Need to reconfigure stream */
+ group->stream_conf.reconfiguration_ongoing = true;
+ GroupStop(group->group_id_);
+ return true;
}
void OnAudioMetadataUpdate(const source_metadata_t& source_metadata) {
@@ -2869,14 +2822,17 @@
return;
}
- if (ReconfigureHalSessionIfNeeded(group, new_context)) {
+ current_context_type_ = new_context;
+ if (StopStreamIfNeeded(group, new_context)) {
return;
}
- /* Configuration is the same for new context, just will do update
- * metadata of stream
- */
- GroupStream(active_group_id_, static_cast<uint16_t>(new_context));
+ if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+ /* Configuration is the same for new context, just will do update
+ * metadata of stream
+ */
+ GroupStream(active_group_id_, static_cast<uint16_t>(new_context));
+ }
}
static void OnGattReadRspStatic(uint16_t conn_id, tGATT_STATUS status,
@@ -3021,7 +2977,39 @@
rxUnreceivedPackets, duplicatePackets);
}
+ bool RestartStreamingAfterReconfiguration(int group_id) {
+ if (group_id != active_group_id_) return false;
+
+ DLOG(INFO) << __func__ << " audio_sender_state_: " << audio_sender_state_
+ << " audio_receiver_state_: " << audio_receiver_state_;
+
+ auto group = aseGroups_.FindById(group_id);
+ if (!group) return false;
+
+ auto stream_conf = &group->stream_conf;
+ DLOG(INFO) << __func__ << " stream_conf->reconfiguration_ongoing "
+ << stream_conf->reconfiguration_ongoing;
+
+ if (!stream_conf->reconfiguration_ongoing) return false;
+
+ if (!groupStateMachine_->StartStream(
+ group, static_cast<LeAudioContextType>(current_context_type_)))
+ return false;
+
+ if (audio_sender_state_ == AudioState::RELEASING)
+ audio_sender_state_ = AudioState::READY_TO_START;
+
+ if (audio_receiver_state_ == AudioState::RELEASING)
+ audio_receiver_state_ = AudioState::READY_TO_START;
+
+ stream_conf->reconfiguration_ongoing = false;
+ return true;
+ }
+
void StatusReportCb(int group_id, GroupStreamStatus status) {
+ DLOG(INFO) << __func__ << "status: " << static_cast<int>(status)
+ << " audio_sender_state_: " << audio_sender_state_
+ << " audio_receiver_state_: " << audio_receiver_state_;
switch (status) {
case GroupStreamStatus::STREAMING:
if (audio_sender_state_ == AudioState::READY_TO_START)
@@ -3036,15 +3024,23 @@
/** Stop Audio but don't release all the Audio resources */
SuspendAudio();
break;
- case GroupStreamStatus::IDLE:
+ case GroupStreamStatus::IDLE: {
stream_setup_end_timestamp_ = 0;
stream_setup_start_timestamp_ = 0;
- CancelStreamingRequest();
+
+ if (!RestartStreamingAfterReconfiguration(group_id))
+ CancelStreamingRequest();
+
break;
+ }
case GroupStreamStatus::RELEASING:
case GroupStreamStatus::SUSPENDING:
- audio_sender_state_ = AudioState::RELEASING;
- audio_receiver_state_ = AudioState::RELEASING;
+ if (audio_sender_state_ != AudioState::IDLE)
+ audio_sender_state_ = AudioState::RELEASING;
+
+ if (audio_receiver_state_ != AudioState::IDLE)
+ audio_receiver_state_ = AudioState::RELEASING;
+
break;
default:
break;
@@ -3059,7 +3055,6 @@
LeAudioGroupStateMachine* groupStateMachine_;
int active_group_id_;
LeAudioContextType current_context_type_;
- LeAudioContextType upcoming_context_type_;
uint64_t stream_setup_start_timestamp_;
uint64_t stream_setup_end_timestamp_;
@@ -3068,9 +3063,27 @@
/* Speaker(s) */
AudioState audio_sender_state_;
+ /* Current stream configuration */
LeAudioCodecConfiguration current_source_codec_config;
LeAudioCodecConfiguration current_sink_codec_config;
+ /* Static Audio Framework session configuration.
+ * Resampling will be done inside the bt stack
+ */
+ LeAudioCodecConfiguration audio_framework_source_config = {
+ .num_channels = 2,
+ .sample_rate = bluetooth::audio::le_audio::kSampleRate48000,
+ .bits_per_sample = bluetooth::audio::le_audio::kBitsPerSample16,
+ .data_interval_us = LeAudioCodecConfiguration::kInterval10000Us,
+ };
+
+ LeAudioCodecConfiguration audio_framework_sink_config = {
+ .num_channels = 1,
+ .sample_rate = bluetooth::audio::le_audio::kSampleRate16000,
+ .bits_per_sample = bluetooth::audio::le_audio::kBitsPerSample16,
+ .data_interval_us = LeAudioCodecConfiguration::kInterval10000Us,
+ };
+
void* lc3_encoder_left_mem;
void* lc3_encoder_right_mem;
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index 314cbea..40588f6 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -1187,6 +1187,8 @@
<< " active stream configuration name: "
<< (active_conf ? active_conf->name : " not set") << "\n"
<< " Last used stream configuration: \n"
+ << " reconfiguration_ongoing: "
+ << stream_conf.reconfiguration_ongoing << "\n"
<< " codec id : " << +(stream_conf.id.coding_format) << "\n"
<< " name: "
<< (stream_conf.conf != nullptr ? stream_conf.conf->name : " null ")
diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc
index b9f4d07..13f8067 100644
--- a/system/bta/le_audio/le_audio_client_test.cc
+++ b/system/bta/le_audio/le_audio_client_test.cc
@@ -30,6 +30,7 @@
#include "btm_iso_api.h"
#include "common/message_loop_thread.h"
#include "device/include/controller.h"
+#include "fake_osi.h"
#include "gatt/database_builder.h"
#include "hardware/bt_gatt_types.h"
#include "le_audio_types.h"
@@ -59,6 +60,8 @@
using namespace bluetooth::le_audio;
+extern struct fake_osi_alarm_set_on_mloop fake_osi_alarm_set_on_mloop_;
+
namespace bluetooth {
namespace audio {
namespace le_audio {
@@ -538,13 +541,21 @@
group->SetTargetState(
types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
group->SetState(group->GetTargetState());
- state_machine_callbacks_->StatusReportCb(
- group->group_id_, GroupStreamStatus::STREAMING);
streaming_groups[group->group_id_] = group;
/* Assume CIG is created */
group->cig_created_ = true;
+ do_in_main_thread(
+ FROM_HERE, base::BindOnce(
+ [](int group_id,
+ le_audio::LeAudioGroupStateMachine::Callbacks*
+ state_machine_callbacks) {
+ state_machine_callbacks->StatusReportCb(
+ group_id, GroupStreamStatus::STREAMING);
+ },
+ group->group_id_,
+ base::Unretained(this->state_machine_callbacks_)));
return true;
});
@@ -670,6 +681,8 @@
// Inject the state
group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
group->SetState(group->GetTargetState());
+ state_machine_callbacks_->StatusReportCb(
+ group->group_id_, GroupStreamStatus::RELEASING);
state_machine_callbacks_->StatusReportCb(group->group_id_,
GroupStreamStatus::IDLE);
});
@@ -939,7 +952,8 @@
ConnectLeAudio(addr);
}
- void UpdateMetadata(audio_usage_t usage, audio_content_type_t content_type) {
+ void UpdateMetadata(audio_usage_t usage, audio_content_type_t content_type,
+ bool reconfigure_existing_stream = false) {
std::promise<void> do_metadata_update_promise;
struct playback_track_metadata tracks_[2] = {
@@ -952,6 +966,10 @@
tracks_[0].usage = usage;
tracks_[0].content_type = content_type;
+ if (reconfigure_existing_stream) {
+ EXPECT_CALL(mock_audio_source_, ConfirmStreamingRequest()).Times(1);
+ }
+
auto do_metadata_update_future = do_metadata_update_promise.get_future();
audio_sink_receiver_->OnAudioMetadataUpdate(
std::move(do_metadata_update_promise), source_metadata);
@@ -959,25 +977,33 @@
}
void StartStreaming(audio_usage_t usage, audio_content_type_t content_type,
- int group_id, bool reconfigured_sink = false) {
+ int group_id, bool reconfigure_existing_stream = false) {
ASSERT_NE(audio_sink_receiver_, nullptr);
- UpdateMetadata(usage, content_type);
+ UpdateMetadata(usage, content_type, reconfigure_existing_stream);
- if (reconfigured_sink) {
- EXPECT_CALL(mock_audio_source_, CancelStreamingRequest()).Times(1);
- audio_sink_receiver_->OnAudioResume();
- }
- SyncOnMainLoop();
- audio_sink_receiver_->OnAudioResume();
+ /* Stream has been automatically restarted on UpdateMetadata */
+ if (reconfigure_existing_stream) return;
EXPECT_CALL(mock_audio_source_, ConfirmStreamingRequest()).Times(1);
- state_machine_callbacks_->StatusReportCb(group_id,
- GroupStreamStatus::STREAMING);
+ do_in_main_thread(FROM_HERE,
+ base::BindOnce(
+ [](LeAudioClientAudioSinkReceiver* sink_receiver) {
+ sink_receiver->OnAudioResume();
+ },
+ audio_sink_receiver_));
+
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_audio_source_);
if (usage == AUDIO_USAGE_VOICE_COMMUNICATION) {
ASSERT_NE(audio_source_receiver_, nullptr);
- audio_source_receiver_->OnAudioResume();
+ do_in_main_thread(
+ FROM_HERE, base::BindOnce(
+ [](LeAudioClientAudioSourceReceiver* source_receiver) {
+ source_receiver->OnAudioResume();
+ },
+ audio_source_receiver_));
}
}
@@ -2256,7 +2282,9 @@
uint8_t cis_count_out = 1;
uint8_t cis_count_in = 0;
+ // Audio sessions are started only when device gets active
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
EXPECT_CALL(mock_state_machine_, StartStream(_, _)).Times(1);
@@ -2319,7 +2347,9 @@
uint8_t cis_count_out = 1;
uint8_t cis_count_in = 0;
+ // Audio sessions are started only when device gets active
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
@@ -2349,6 +2379,7 @@
// Release
EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
EXPECT_CALL(mock_audio_source_, Release(_)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Release(_)).Times(1);
LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
Mock::VerifyAndClearExpectations(&mock_audio_source_);
}
@@ -2374,6 +2405,7 @@
// Start streaming
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
@@ -2429,17 +2461,14 @@
codec_spec_conf::kLeAudioLocationFrontRight, group_size,
group_id, 2 /* rank*/, true /*connect_through_csis*/);
+ // Start streaming
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
Mock::VerifyAndClearExpectations(&mock_audio_source_);
- // Start streaming with reconfiguration from default media stream setup
- EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
- EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
- EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
-
StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH,
- group_id, true /* reconfigure */);
+ group_id);
Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
Mock::VerifyAndClearExpectations(&mock_audio_source_);
@@ -2449,17 +2478,11 @@
// Verify Data transfer on two peer sinks and one source
uint8_t cis_count_out = 2;
uint8_t cis_count_in = 1;
- TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 640);
+ TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
// Suspend
- EXPECT_CALL(mock_audio_source_, Release(_)).Times(0);
- EXPECT_CALL(mock_audio_sink_, Release(_)).Times(0);
- EXPECT_CALL(mock_audio_source_, Stop()).Times(0);
- EXPECT_CALL(mock_audio_sink_, Stop()).Times(0);
LeAudioClient::Get()->GroupSuspend(group_id);
SyncOnMainLoop();
- Mock::VerifyAndClearExpectations(&mock_audio_source_);
- Mock::VerifyAndClearExpectations(&mock_audio_sink_);
// Resume
StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH,
@@ -2469,7 +2492,7 @@
Mock::VerifyAndClearExpectations(&mock_audio_sink_);
// Verify Data transfer still works
- TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 640);
+ TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
// Stop
StopStreaming(group_id, true);
@@ -2511,7 +2534,9 @@
codec_spec_conf::kLeAudioLocationFrontRight, group_size,
group_id, 2 /* rank*/, true /*connect_through_csis*/);
+ // Start streaming
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
Mock::VerifyAndClearExpectations(&mock_audio_source_);
@@ -2520,11 +2545,9 @@
mock_state_machine_,
StartStream(_, le_audio::types::LeAudioContextType::NOTIFICATIONS))
.Times(1);
- EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
- EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
- StartStreaming(AUDIO_USAGE_NOTIFICATION, AUDIO_CONTENT_TYPE_UNKNOWN, group_id,
- true /* reconfigure */);
+ StartStreaming(AUDIO_USAGE_NOTIFICATION, AUDIO_CONTENT_TYPE_UNKNOWN,
+ group_id);
Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
Mock::VerifyAndClearExpectations(&mock_audio_source_);
@@ -2582,6 +2605,7 @@
// Start streaming MEDIA
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
@@ -2597,16 +2621,12 @@
// Stop
StopStreaming(group_id);
+ // simulate suspend timeout passed, alarm executing
+ fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data);
Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
- // Start streaming
- EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
- EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
- EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
- LeAudioClient::Get()->GroupSetActive(group_id);
-
StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH,
- group_id, true /* reconfigure */);
+ group_id);
Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
Mock::VerifyAndClearExpectations(&mock_audio_source_);
@@ -2616,7 +2636,7 @@
// Verify Data transfer on two peer sinks and one source
cis_count_out = 2;
cis_count_in = 1;
- TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 640);
+ TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
}
TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) {
@@ -2636,6 +2656,7 @@
// Start streaming
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
@@ -2656,11 +2677,7 @@
codec_spec_conf::kLeAudioLocationFrontRight, group_size,
group_id, 2 /* rank*/, true /*connect_through_csis*/);
- /* We should expect two iso channels to be fed with data, but for now, when
- * second device is connected later, we just continue stream to one device.
- * TODO: improve it.
- */
- cis_count_out = 1;
+ cis_count_out = 2;
cis_count_in = 0;
TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
}
@@ -2687,8 +2704,9 @@
codec_spec_conf::kLeAudioLocationFrontRight, group_size,
group_id, 2 /* rank*/, true /*connect_through_csis*/);
- // Start streaming
+ // Audio sessions are started only when device gets active
EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
LeAudioClient::Get()->GroupSetActive(group_id);
StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc
index 111504f..c74e8ec 100644
--- a/system/bta/le_audio/le_audio_types.cc
+++ b/system/bta/le_audio/le_audio_types.cc
@@ -88,6 +88,11 @@
return group_size >= min_req_devices_cnt(audio_set_conf);
}
+uint8_t get_num_of_devices_in_configuration(
+ const AudioSetConfiguration* audio_set_conf) {
+ return min_req_devices_cnt(audio_set_conf);
+}
+
static bool IsCodecConfigurationSupported(const types::LeAudioLtvMap& pacs,
const LeAudioLc3Config& lc3_config) {
const auto& reqs = lc3_config.GetAsLtvMap();
diff --git a/system/bta/le_audio/le_audio_types.h b/system/bta/le_audio/le_audio_types.h
index 6371696..48b6901 100644
--- a/system/bta/le_audio/le_audio_types.h
+++ b/system/bta/le_audio/le_audio_types.h
@@ -952,9 +952,12 @@
const types::acs_ac_record& pac_record,
const CodecCapabilitySetting& codec_capability_setting);
const AudioSetConfigurations* get_confs_by_type(types::LeAudioContextType type);
+uint8_t get_num_of_devices_in_configuration(
+ const AudioSetConfiguration* audio_set_configuration);
} // namespace set_configurations
struct stream_configuration {
+ bool reconfiguration_ongoing;
types::LeAudioCodecId id;
diff --git a/system/btif/src/bluetooth.cc b/system/btif/src/bluetooth.cc
index 7427eb4..2d6bedb 100644
--- a/system/btif/src/bluetooth.cc
+++ b/system/btif/src/bluetooth.cc
@@ -591,6 +591,12 @@
return btif_set_dynamic_audio_buffer_size(codec, size);
}
+static bool allow_low_latency_audio(bool allowed, const RawAddress& address) {
+ LOG_INFO("%s %s", __func__, allowed ? "true" : "false");
+ // Call HAL here
+ return true;
+}
+
EXPORT_SYMBOL bt_interface_t bluetoothInterface = {
sizeof(bluetoothInterface),
init,
@@ -629,7 +635,8 @@
obfuscate_address,
get_metric_id,
set_dynamic_audio_buffer_size,
- generate_local_oob_data};
+ generate_local_oob_data,
+ allow_low_latency_audio};
// callback reporting helpers
diff --git a/system/btif/src/btif_ble_scanner.cc b/system/btif/src/btif_ble_scanner.cc
index fce9354..4d6c2f9 100644
--- a/system/btif/src/btif_ble_scanner.cc
+++ b/system/btif/src/btif_ble_scanner.cc
@@ -110,7 +110,7 @@
uint8_t ble_secondary_phy,
uint8_t ble_advertising_sid, int8_t ble_tx_power,
uint16_t ble_periodic_adv_int,
- vector<uint8_t> value) {
+ vector<uint8_t> value, RawAddress original_bda) {
uint8_t remote_name_len;
bt_device_type_t dev_type;
bt_property_t properties;
@@ -156,7 +156,8 @@
btif_storage_set_remote_addr_type(&bd_addr, addr_type);
HAL_CBACK(bt_gatt_callbacks, scanner->scan_result_cb, ble_evt_type, addr_type,
&bd_addr, ble_primary_phy, ble_secondary_phy, ble_advertising_sid,
- ble_tx_power, rssi, ble_periodic_adv_int, std::move(value));
+ ble_tx_power, rssi, ble_periodic_adv_int, std::move(value),
+ &original_bda);
}
void bta_scan_results_cb(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH* p_data) {
@@ -185,11 +186,11 @@
}
tBTA_DM_INQ_RES* r = &p_data->inq_res;
- do_in_jni_thread(Bind(bta_scan_results_cb_impl, r->bd_addr, r->device_type,
- r->rssi, r->ble_addr_type, r->ble_evt_type,
- r->ble_primary_phy, r->ble_secondary_phy,
- r->ble_advertising_sid, r->ble_tx_power,
- r->ble_periodic_adv_int, std::move(value)));
+ do_in_jni_thread(
+ Bind(bta_scan_results_cb_impl, r->bd_addr, r->device_type, r->rssi,
+ r->ble_addr_type, r->ble_evt_type, r->ble_primary_phy,
+ r->ble_secondary_phy, r->ble_advertising_sid, r->ble_tx_power,
+ r->ble_periodic_adv_int, std::move(value), r->original_bda));
}
void bta_track_adv_event_cb(tBTM_BLE_TRACK_ADV_DATA* p_track_adv_data) {
diff --git a/system/btif/src/btif_core.cc b/system/btif/src/btif_core.cc
index 28336dc..87dd06b 100644
--- a/system/btif/src/btif_core.cc
+++ b/system/btif/src/btif_core.cc
@@ -622,6 +622,11 @@
controller->supports_ble_periodic_advertising_sync_transfer_sender();
local_le_features.le_connected_isochronous_stream_central_supported =
controller->supports_ble_connected_isochronous_stream_central();
+ local_le_features.le_isochronous_broadcast_supported =
+ controller->supports_ble_isochronous_broadcaster();
+ local_le_features
+ .le_periodic_advertising_sync_transfer_recipient_supported =
+ controller->supports_ble_periodic_advertising_sync_transfer_recipient();
memcpy(prop.val, &local_le_features, prop.len);
} else if (prop.type == BT_PROPERTY_DYNAMIC_AUDIO_BUFFER) {
tBTM_BLE_VSC_CB cmn_vsc_cb;
diff --git a/system/gd/hci/acl_manager.cc b/system/gd/hci/acl_manager.cc
index 269a0cd..2a806cb 100644
--- a/system/gd/hci/acl_manager.cc
+++ b/system/gd/hci/acl_manager.cc
@@ -91,20 +91,14 @@
return;
}
uint16_t handle = packet->GetHandle();
- if (handle == kQualcommDebugHandle) {
+ if (handle == kQualcommDebugHandle) return;
+ if (classic_impl_->send_packet_upward(
+ handle, [&packet](struct acl_manager::assembler* assembler) { assembler->on_incoming_packet(*packet); }))
return;
- }
- auto connection_pair = classic_impl_->acl_connections_.find(handle);
- if (connection_pair != classic_impl_->acl_connections_.end()) {
- connection_pair->second.assembler_.on_incoming_packet(*packet);
- } else {
- auto le_connection_pair = le_impl_->le_acl_connections_.find(handle);
- if (le_connection_pair == le_impl_->le_acl_connections_.end()) {
- LOG_INFO("Dropping packet of size %zu to unknown connection 0x%0hx", packet->size(), handle);
- return;
- }
- le_connection_pair->second.assembler_.on_incoming_packet(*packet);
- }
+ if (le_impl_->send_packet_upward(
+ handle, [&packet](struct acl_manager::assembler* assembler) { assembler->on_incoming_packet(*packet); }))
+ return;
+ LOG_INFO("Dropping packet of size %zu to unknown connection 0x%0hx", packet->size(), packet->GetHandle());
}
void Dump(
diff --git a/system/gd/hci/acl_manager/classic_acl_connection.cc b/system/gd/hci/acl_manager/classic_acl_connection.cc
index 4410828..5fd5eaf 100644
--- a/system/gd/hci/acl_manager/classic_acl_connection.cc
+++ b/system/gd/hci/acl_manager/classic_acl_connection.cc
@@ -327,16 +327,19 @@
const Address& address,
uint16_t connection_handle)
: tracker(acl_connection_interface, address, connection_handle), queue_(std::move(queue)) {}
- ConnectionManagementCallbacks* GetEventCallbacks() {
- ASSERT(!callbacks_given_);
- callbacks_given_ = true;
+ ConnectionManagementCallbacks* GetEventCallbacks(std::function<void(uint16_t)> invalidate_callbacks) {
+ ASSERT_LOG(!invalidate_callbacks_, "Already returned event callbacks for this connection");
+ invalidate_callbacks_ = std::move(invalidate_callbacks);
return &tracker;
}
+ void PutEventCallbacks() {
+ if (invalidate_callbacks_) invalidate_callbacks_(tracker.connection_handle_);
+ invalidate_callbacks_ = {};
+ }
- bool callbacks_given_{false};
AclConnectionTracker tracker;
std::shared_ptr<Queue> queue_;
- std::shared_ptr<std::atomic<bool>> is_callback_valid_;
+ std::function<void(uint16_t)> invalidate_callbacks_;
};
ClassicAclConnection::ClassicAclConnection()
@@ -350,14 +353,13 @@
}
ClassicAclConnection::~ClassicAclConnection() {
- if (pimpl_->is_callback_valid_) *pimpl_->is_callback_valid_ = false;
+ pimpl_->PutEventCallbacks();
delete pimpl_;
}
ConnectionManagementCallbacks* ClassicAclConnection::GetEventCallbacks(
- std::shared_ptr<std::atomic<bool>> is_callback_valid) {
- pimpl_->is_callback_valid_ = is_callback_valid;
- return pimpl_->GetEventCallbacks();
+ std::function<void(uint16_t)> invalidate_callbacks) {
+ return pimpl_->GetEventCallbacks(std::move(invalidate_callbacks));
}
void ClassicAclConnection::RegisterCallbacks(ConnectionManagementCallbacks* callbacks, os::Handler* handler) {
diff --git a/system/gd/hci/acl_manager/classic_acl_connection.h b/system/gd/hci/acl_manager/classic_acl_connection.h
index a72f2df..328cbb1 100644
--- a/system/gd/hci/acl_manager/classic_acl_connection.h
+++ b/system/gd/hci/acl_manager/classic_acl_connection.h
@@ -75,7 +75,7 @@
virtual bool ReadRemoteExtendedFeatures(uint8_t page_number);
// Called once before passing the connection to the client
- virtual ConnectionManagementCallbacks* GetEventCallbacks(std::shared_ptr<std::atomic<bool>> is_callback_valid);
+ virtual ConnectionManagementCallbacks* GetEventCallbacks(std::function<void(uint16_t)> invalidate_callbacks);
private:
AclConnectionInterface* acl_connection_interface_;
diff --git a/system/gd/hci/acl_manager/classic_impl.h b/system/gd/hci/acl_manager/classic_impl.h
index d38ece7..3229227 100644
--- a/system/gd/hci/acl_manager/classic_impl.h
+++ b/system/gd/hci/acl_manager/classic_impl.h
@@ -33,12 +33,14 @@
struct acl_connection {
acl_connection(AddressWithType address_with_type, AclConnection::QueueDownEnd* queue_down_end, os::Handler* handler)
- : assembler_(address_with_type, queue_down_end, handler), address_with_type_(address_with_type) {}
- ~acl_connection() = default;
- struct acl_manager::assembler assembler_;
+ : address_with_type_(address_with_type),
+ assembler_(new acl_manager::assembler(address_with_type, queue_down_end, handler)) {}
+ ~acl_connection() {
+ delete assembler_;
+ }
AddressWithType address_with_type_;
+ struct acl_manager::assembler* assembler_;
ConnectionManagementCallbacks* connection_management_callbacks_ = nullptr;
- std::shared_ptr<std::atomic<bool>> is_callback_valid_ = std::make_shared<std::atomic<bool>>(true);
};
struct classic_impl : public security::ISecurityManagerListener {
@@ -48,13 +50,11 @@
os::Handler* handler,
RoundRobinScheduler* round_robin_scheduler,
bool crash_on_unknown_handle)
- : hci_layer_(hci_layer),
- controller_(controller),
- round_robin_scheduler_(round_robin_scheduler),
- crash_on_unknown_handle_(crash_on_unknown_handle) {
+ : hci_layer_(hci_layer), controller_(controller), round_robin_scheduler_(round_robin_scheduler) {
hci_layer_ = hci_layer;
controller_ = controller;
handler_ = handler;
+ connections.crash_on_unknown_handle_ = crash_on_unknown_handle;
should_accept_connection_ = common::Bind([](Address, ClassOfDevice) { return true; });
acl_connection_interface_ = hci_layer_->GetAclConnectionInterface(
handler_->BindOn(this, &classic_impl::on_classic_event),
@@ -64,18 +64,10 @@
~classic_impl() {
hci_layer_->PutAclConnectionInterface();
- acl_connections_.clear();
+ connections.reset();
security_manager_.reset();
}
- ConnectionManagementCallbacks* get_callbacks(uint16_t handle) {
- auto connection = acl_connections_.find(handle);
- if (connection == acl_connections_.end()) {
- return nullptr;
- }
- return (connection->second.is_callback_valid_) ? connection->second.connection_management_callbacks_ : nullptr;
- }
-
void on_classic_event(EventView event_packet) {
EventCode event_code = event_packet.GetEventCode();
switch (event_code) {
@@ -129,32 +121,107 @@
}
}
- void on_classic_disconnect(uint16_t handle, ErrorCode reason) {
- auto callbacks = get_callbacks(handle);
- if (callbacks != nullptr) {
- round_robin_scheduler_->Unregister(handle);
- callbacks->OnDisconnection(reason);
- acl_connections_.erase(handle);
- } else {
- // This handle is probably for SCO, so we use the callback workaround.
- if (non_acl_disconnect_callback_ != nullptr) {
- non_acl_disconnect_callback_(handle, static_cast<uint8_t>(reason));
+ private:
+ static constexpr uint16_t kIllegalConnectionHandle = 0xffff;
+ struct {
+ private:
+ std::map<uint16_t, acl_connection> acl_connections_;
+ mutable std::mutex acl_connections_guard_;
+ ConnectionManagementCallbacks* find_callbacks(uint16_t handle) {
+ auto connection = acl_connections_.find(handle);
+ if (connection == acl_connections_.end()) return nullptr;
+ return connection->second.connection_management_callbacks_;
+ }
+ ConnectionManagementCallbacks* find_callbacks(const Address& address) {
+ for (auto& connection_pair : acl_connections_) {
+ if (connection_pair.second.address_with_type_.GetAddress() == address) {
+ return connection_pair.second.connection_management_callbacks_;
+ }
+ }
+ return nullptr;
+ }
+ void remove(uint16_t handle) {
+ auto connection = acl_connections_.find(handle);
+ if (connection != acl_connections_.end()) {
+ connection->second.connection_management_callbacks_ = nullptr;
+ acl_connections_.erase(handle);
}
}
- }
- void handle_register_callbacks(ConnectionCallbacks* callbacks, os::Handler* handler) {
- ASSERT(client_callbacks_ == nullptr);
- ASSERT(client_handler_ == nullptr);
- client_callbacks_ = callbacks;
- client_handler_ = handler;
- }
+ public:
+ bool crash_on_unknown_handle_ = false;
+ bool is_empty() const {
+ std::unique_lock<std::mutex> lock(acl_connections_guard_);
+ return acl_connections_.empty();
+ }
+ void reset() {
+ std::unique_lock<std::mutex> lock(acl_connections_guard_);
+ acl_connections_.clear();
+ }
+ void invalidate(uint16_t handle) {
+ std::unique_lock<std::mutex> lock(acl_connections_guard_);
+ remove(handle);
+ }
+ void execute(
+ uint16_t handle,
+ std::function<void(ConnectionManagementCallbacks* callbacks)> execute,
+ bool remove_afterwards = false) {
+ std::unique_lock<std::mutex> lock(acl_connections_guard_);
+ auto callbacks = find_callbacks(handle);
+ if (callbacks != nullptr)
+ execute(callbacks);
+ else
+ ASSERT_LOG(!crash_on_unknown_handle_, "Received command for unknown handle:0x%x", handle);
+ if (remove_afterwards) remove(handle);
+ }
+ void execute(const Address& address, std::function<void(ConnectionManagementCallbacks* callbacks)> execute) {
+ std::unique_lock<std::mutex> lock(acl_connections_guard_);
+ auto callbacks = find_callbacks(address);
+ if (callbacks != nullptr) execute(callbacks);
+ }
+ bool send_packet_upward(uint16_t handle, std::function<void(struct acl_manager::assembler* assembler)> cb) {
+ std::unique_lock<std::mutex> lock(acl_connections_guard_);
+ auto connection = acl_connections_.find(handle);
+ if (connection != acl_connections_.end()) cb(connection->second.assembler_);
+ return connection != acl_connections_.end();
+ }
+ void add(
+ uint16_t handle,
+ const AddressWithType& remote_address,
+ AclConnection::QueueDownEnd* queue_end,
+ os::Handler* handler,
+ ConnectionManagementCallbacks* connection_management_callbacks) {
+ std::unique_lock<std::mutex> lock(acl_connections_guard_);
+ auto emplace_pair = acl_connections_.emplace(
+ std::piecewise_construct,
+ std::forward_as_tuple(handle),
+ std::forward_as_tuple(remote_address, queue_end, handler));
+ ASSERT(emplace_pair.second); // Make sure the connection is unique
+ emplace_pair.first->second.connection_management_callbacks_ = connection_management_callbacks;
+ }
+ uint16_t HACK_get_handle(const Address& address) const {
+ std::unique_lock<std::mutex> lock(acl_connections_guard_);
+ for (auto it = acl_connections_.begin(); it != acl_connections_.end(); it++) {
+ if (it->second.address_with_type_.GetAddress() == address) {
+ return it->first;
+ }
+ }
+ return kIllegalConnectionHandle;
+ }
+ bool is_classic_link_already_connected(const Address& address) const {
+ std::unique_lock<std::mutex> lock(acl_connections_guard_);
+ for (const auto& connection : acl_connections_) {
+ if (connection.second.address_with_type_.GetAddress() == address) {
+ return true;
+ }
+ }
+ return false;
+ }
+ } connections;
- void handle_unregister_callbacks(ConnectionCallbacks* callbacks, std::promise<void> promise) {
- ASSERT_LOG(client_callbacks_ == callbacks, "Registered callback entity is different then unregister request");
- client_callbacks_ = nullptr;
- client_handler_ = nullptr;
- promise.set_value();
+ public:
+ bool send_packet_upward(uint16_t handle, std::function<void(struct acl_manager::assembler* assembler)> cb) {
+ return connections.send_packet_upward(handle, cb);
}
void on_incoming_connection(EventView packet) {
@@ -200,12 +267,7 @@
}
bool is_classic_link_already_connected(Address address) {
- for (const auto& connection : acl_connections_) {
- if (connection.second.address_with_type_.GetAddress() == address) {
- return true;
- }
- }
- return false;
+ return connections.is_classic_link_already_connected(address);
}
void create_connection(Address address) {
@@ -266,29 +328,26 @@
}
uint16_t handle = connection_complete.GetConnectionHandle();
auto queue = std::make_shared<AclConnection::Queue>(10);
- auto conn_pair = acl_connections_.emplace(
- std::piecewise_construct,
- std::forward_as_tuple(handle),
- std::forward_as_tuple(
- AddressWithType{address, AddressType::PUBLIC_DEVICE_ADDRESS}, queue->GetDownEnd(), handler_));
- ASSERT(conn_pair.second); // Make sure it's not a duplicate
+ auto queue_down_end = queue->GetDownEnd();
round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, queue);
std::unique_ptr<ClassicAclConnection> connection(
new ClassicAclConnection(std::move(queue), acl_connection_interface_, handle, address));
connection->locally_initiated_ = locally_initiated;
- auto& connection_proxy = conn_pair.first->second;
- connection_proxy.connection_management_callbacks_ =
- connection->GetEventCallbacks(connection_proxy.is_callback_valid_);
- if (delayed_role_change_ != nullptr) {
- if (delayed_role_change_->GetBdAddr() == address) {
+ connections.add(
+ handle,
+ AddressWithType{address, AddressType::PUBLIC_DEVICE_ADDRESS},
+ queue_down_end,
+ handler_,
+ connection->GetEventCallbacks([this](uint16_t handle) { this->connections.invalidate(handle); }));
+ connections.execute(address, [=](ConnectionManagementCallbacks* callbacks) {
+ if (delayed_role_change_ == nullptr) {
+ callbacks->OnRoleChange(hci::ErrorCode::SUCCESS, current_role);
+ } else if (delayed_role_change_->GetBdAddr() == address) {
LOG_INFO("Sending delayed role change for %s", delayed_role_change_->GetBdAddr().ToString().c_str());
- connection_proxy.connection_management_callbacks_->OnRoleChange(
- delayed_role_change_->GetStatus(), delayed_role_change_->GetNewRole());
+ callbacks->OnRoleChange(delayed_role_change_->GetStatus(), delayed_role_change_->GetNewRole());
+ delayed_role_change_.reset();
}
- delayed_role_change_ = nullptr;
- } else {
- connection_proxy.connection_management_callbacks_->OnRoleChange(hci::ErrorCode::SUCCESS, current_role);
- }
+ });
client_handler_->Post(common::BindOnce(&ConnectionCallbacks::OnConnectSuccess,
common::Unretained(client_callbacks_), std::move(connection)));
while (!pending_outgoing_connections_.empty()) {
@@ -306,6 +365,34 @@
}
}
+ void cancel_connect(Address address) {
+ if (outgoing_connecting_address_ == address) {
+ LOG_INFO("Cannot cancel non-existent connection to %s", address.ToString().c_str());
+ return;
+ }
+ std::unique_ptr<CreateConnectionCancelBuilder> packet = CreateConnectionCancelBuilder::Create(address);
+ acl_connection_interface_->EnqueueCommand(
+ std::move(packet), handler_->BindOnce(&check_command_complete<CreateConnectionCancelCompleteView>));
+ }
+
+ static constexpr bool kRemoveConnectionAfterwards = true;
+ void on_classic_disconnect(uint16_t handle, ErrorCode reason) {
+ bool event_also_routes_to_other_receivers = connections.crash_on_unknown_handle_;
+ connections.crash_on_unknown_handle_ = false;
+ connections.execute(
+ handle,
+ [=](ConnectionManagementCallbacks* callbacks) {
+ round_robin_scheduler_->Unregister(handle);
+ callbacks->OnDisconnection(reason);
+ },
+ kRemoveConnectionAfterwards);
+ // This handle is probably for SCO, so we use the callback workaround.
+ if (non_acl_disconnect_callback_ != nullptr) {
+ non_acl_disconnect_callback_(handle, static_cast<uint8_t>(reason));
+ }
+ connections.crash_on_unknown_handle_ = event_also_routes_to_other_receivers;
+ }
+
void on_connection_packet_type_changed(EventView packet) {
ConnectionPacketTypeChangedView packet_type_changed = ConnectionPacketTypeChangedView::Create(packet);
if (!packet_type_changed.IsValid()) {
@@ -318,13 +405,9 @@
return;
}
uint16_t handle = packet_type_changed.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- // We don't handle this event; we didn't do this in legacy stack either.
+ connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+ // We don't handle this event; we didn't do this in legacy stack either.
+ });
}
void on_central_link_key_complete(EventView packet) {
@@ -339,14 +422,10 @@
return;
}
uint16_t handle = complete_view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- KeyFlag key_flag = complete_view.GetKeyFlag();
- callbacks->OnCentralLinkKeyComplete(key_flag);
+ connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+ KeyFlag key_flag = complete_view.GetKeyFlag();
+ callbacks->OnCentralLinkKeyComplete(key_flag);
+ });
}
void on_authentication_complete(EventView packet) {
@@ -356,49 +435,9 @@
return;
}
uint16_t handle = authentication_complete.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- callbacks->OnAuthenticationComplete(authentication_complete.GetStatus());
- }
-
- void cancel_connect(Address address) {
- if (outgoing_connecting_address_ == address) {
- LOG_INFO("Cannot cancel non-existent connection to %s", address.ToString().c_str());
- return;
- }
- std::unique_ptr<CreateConnectionCancelBuilder> packet = CreateConnectionCancelBuilder::Create(address);
- acl_connection_interface_->EnqueueCommand(
- std::move(packet), handler_->BindOnce(&check_command_complete<CreateConnectionCancelCompleteView>));
- }
-
- void central_link_key(KeyFlag key_flag) {
- std::unique_ptr<CentralLinkKeyBuilder> packet = CentralLinkKeyBuilder::Create(key_flag);
- acl_connection_interface_->EnqueueCommand(
- std::move(packet), handler_->BindOnce(&check_command_status<CentralLinkKeyStatusView>));
- }
-
- void switch_role(Address address, Role role) {
- std::unique_ptr<SwitchRoleBuilder> packet = SwitchRoleBuilder::Create(address, role);
- acl_connection_interface_->EnqueueCommand(std::move(packet),
- handler_->BindOnce(&check_command_status<SwitchRoleStatusView>));
- }
-
- void write_default_link_policy_settings(uint16_t default_link_policy_settings) {
- std::unique_ptr<WriteDefaultLinkPolicySettingsBuilder> packet =
- WriteDefaultLinkPolicySettingsBuilder::Create(default_link_policy_settings);
- acl_connection_interface_->EnqueueCommand(
- std::move(packet), handler_->BindOnce(&check_command_complete<WriteDefaultLinkPolicySettingsCompleteView>));
- }
-
- void accept_connection(Address address) {
- auto role = AcceptConnectionRequestRole::BECOME_CENTRAL; // We prefer to be central
- acl_connection_interface_->EnqueueCommand(
- AcceptConnectionRequestBuilder::Create(address, role),
- handler_->BindOnceOn(this, &classic_impl::on_accept_connection_status, address));
+ connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+ callbacks->OnAuthenticationComplete(authentication_complete.GetStatus());
+ });
}
void on_change_connection_link_key_complete(EventView packet) {
@@ -413,13 +452,8 @@
return;
}
uint16_t handle = complete_view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- callbacks->OnChangeConnectionLinkKeyComplete();
+ connections.execute(
+ handle, [=](ConnectionManagementCallbacks* callbacks) { callbacks->OnChangeConnectionLinkKeyComplete(); });
}
void on_read_clock_offset_complete(EventView packet) {
@@ -434,14 +468,10 @@
return;
}
uint16_t handle = complete_view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- uint16_t clock_offset = complete_view.GetClockOffset();
- callbacks->OnReadClockOffsetComplete(clock_offset);
+ connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+ uint16_t clock_offset = complete_view.GetClockOffset();
+ callbacks->OnReadClockOffsetComplete(clock_offset);
+ });
}
void on_mode_change(EventView packet) {
@@ -451,14 +481,10 @@
return;
}
uint16_t handle = mode_change_view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- callbacks->OnModeChange(
- mode_change_view.GetStatus(), mode_change_view.GetCurrentMode(), mode_change_view.GetInterval());
+ connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+ callbacks->OnModeChange(
+ mode_change_view.GetStatus(), mode_change_view.GetCurrentMode(), mode_change_view.GetInterval());
+ });
}
void on_sniff_subrating(EventView packet) {
@@ -468,18 +494,14 @@
return;
}
uint16_t handle = sniff_subrating_view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- callbacks->OnSniffSubrating(
- sniff_subrating_view.GetStatus(),
- sniff_subrating_view.GetMaximumTransmitLatency(),
- sniff_subrating_view.GetMaximumReceiveLatency(),
- sniff_subrating_view.GetMinimumRemoteTimeout(),
- sniff_subrating_view.GetMinimumLocalTimeout());
+ connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+ callbacks->OnSniffSubrating(
+ sniff_subrating_view.GetStatus(),
+ sniff_subrating_view.GetMaximumTransmitLatency(),
+ sniff_subrating_view.GetMaximumReceiveLatency(),
+ sniff_subrating_view.GetMinimumRemoteTimeout(),
+ sniff_subrating_view.GetMinimumLocalTimeout());
+ });
}
void on_qos_setup_complete(EventView packet) {
@@ -494,46 +516,14 @@
return;
}
uint16_t handle = complete_view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- ServiceType service_type = complete_view.GetServiceType();
- uint32_t token_rate = complete_view.GetTokenRate();
- uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
- uint32_t latency = complete_view.GetLatency();
- uint32_t delay_variation = complete_view.GetDelayVariation();
- callbacks->OnQosSetupComplete(service_type, token_rate, peak_bandwidth, latency, delay_variation);
- }
-
- void on_role_change(EventView packet) {
- RoleChangeView role_change_view = RoleChangeView::Create(packet);
- if (!role_change_view.IsValid()) {
- LOG_ERROR("Received on_role_change with invalid packet");
- return;
- }
- bool sent = false;
- auto hci_status = role_change_view.GetStatus();
- Address bd_addr = role_change_view.GetBdAddr();
- Role new_role = role_change_view.GetNewRole();
- for (auto& connection_pair : acl_connections_) {
- if (connection_pair.second.address_with_type_.GetAddress() == bd_addr) {
- connection_pair.second.connection_management_callbacks_->OnRoleChange(hci_status, new_role);
- sent = true;
- }
- }
- if (!sent) {
- if (delayed_role_change_ != nullptr) {
- LOG_WARN("Second delayed role change (@%s dropped)", delayed_role_change_->GetBdAddr().ToString().c_str());
- }
- LOG_INFO(
- "Role change for %s with no matching connection (new role: %s)",
- role_change_view.GetBdAddr().ToString().c_str(),
- RoleText(role_change_view.GetNewRole()).c_str());
- delayed_role_change_ = std::make_unique<RoleChangeView>(role_change_view);
- }
+ connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+ ServiceType service_type = complete_view.GetServiceType();
+ uint32_t token_rate = complete_view.GetTokenRate();
+ uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
+ uint32_t latency = complete_view.GetLatency();
+ uint32_t delay_variation = complete_view.GetDelayVariation();
+ callbacks->OnQosSetupComplete(service_type, token_rate, peak_bandwidth, latency, delay_variation);
+ });
}
void on_flow_specification_complete(EventView packet) {
@@ -548,20 +538,16 @@
return;
}
uint16_t handle = complete_view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- FlowDirection flow_direction = complete_view.GetFlowDirection();
- ServiceType service_type = complete_view.GetServiceType();
- uint32_t token_rate = complete_view.GetTokenRate();
- uint32_t token_bucket_size = complete_view.GetTokenBucketSize();
- uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
- uint32_t access_latency = complete_view.GetAccessLatency();
- callbacks->OnFlowSpecificationComplete(
- flow_direction, service_type, token_rate, token_bucket_size, peak_bandwidth, access_latency);
+ connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+ FlowDirection flow_direction = complete_view.GetFlowDirection();
+ ServiceType service_type = complete_view.GetServiceType();
+ uint32_t token_rate = complete_view.GetTokenRate();
+ uint32_t token_bucket_size = complete_view.GetTokenBucketSize();
+ uint32_t peak_bandwidth = complete_view.GetPeakBandwidth();
+ uint32_t access_latency = complete_view.GetAccessLatency();
+ callbacks->OnFlowSpecificationComplete(
+ flow_direction, service_type, token_rate, token_bucket_size, peak_bandwidth, access_latency);
+ });
}
void on_flush_occurred(EventView packet) {
@@ -571,51 +557,78 @@
return;
}
uint16_t handle = flush_occurred_view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- callbacks->OnFlushOccurred();
+ connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) { callbacks->OnFlushOccurred(); });
}
void on_read_remote_version_information(
hci::ErrorCode hci_status, uint16_t handle, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version) {
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- callbacks->OnReadRemoteVersionInformationComplete(hci_status, version, manufacturer_name, sub_version);
+ connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+ callbacks->OnReadRemoteVersionInformationComplete(hci_status, version, manufacturer_name, sub_version);
+ });
}
void on_read_remote_supported_features_complete(EventView packet) {
auto view = ReadRemoteSupportedFeaturesCompleteView::Create(packet);
ASSERT_LOG(view.IsValid(), "Read remote supported features packet invalid");
uint16_t handle = view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- callbacks->OnReadRemoteSupportedFeaturesComplete(view.GetLmpFeatures());
+ connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+ callbacks->OnReadRemoteSupportedFeaturesComplete(view.GetLmpFeatures());
+ });
}
void on_read_remote_extended_features_complete(EventView packet) {
auto view = ReadRemoteExtendedFeaturesCompleteView::Create(packet);
ASSERT_LOG(view.IsValid(), "Read remote extended features packet invalid");
uint16_t handle = view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
+ connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+ callbacks->OnReadRemoteExtendedFeaturesComplete(
+ view.GetPageNumber(), view.GetMaximumPageNumber(), view.GetExtendedLmpFeatures());
+ });
+ }
+
+ void OnEncryptionStateChanged(EncryptionChangeView encryption_change_view) override {
+ if (!encryption_change_view.IsValid()) {
+ LOG_ERROR("Invalid packet");
+ return;
+ } else if (encryption_change_view.GetStatus() != ErrorCode::SUCCESS) {
+ auto status = encryption_change_view.GetStatus();
+ std::string error_code = ErrorCodeText(status);
+ LOG_ERROR("error_code %s", error_code.c_str());
return;
}
- callbacks->OnReadRemoteExtendedFeaturesComplete(
- view.GetPageNumber(), view.GetMaximumPageNumber(), view.GetExtendedLmpFeatures());
+ uint16_t handle = encryption_change_view.GetConnectionHandle();
+ connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
+ EncryptionEnabled enabled = encryption_change_view.GetEncryptionEnabled();
+ callbacks->OnEncryptionChange(enabled);
+ });
+ }
+
+ void on_role_change(EventView packet) {
+ RoleChangeView role_change_view = RoleChangeView::Create(packet);
+ if (!role_change_view.IsValid()) {
+ LOG_ERROR("Received on_role_change with invalid packet");
+ return;
+ }
+ auto hci_status = role_change_view.GetStatus();
+ Address bd_addr = role_change_view.GetBdAddr();
+ Role new_role = role_change_view.GetNewRole();
+ bool sent = false;
+ connections.execute(bd_addr, [=, &sent](ConnectionManagementCallbacks* callbacks) {
+ if (callbacks != nullptr) {
+ callbacks->OnRoleChange(hci_status, new_role);
+ sent = true;
+ }
+ });
+ if (!sent) {
+ if (delayed_role_change_ != nullptr) {
+ LOG_WARN("Second delayed role change (@%s dropped)", delayed_role_change_->GetBdAddr().ToString().c_str());
+ }
+ LOG_INFO(
+ "Role change for %s with no matching connection (new role: %s)",
+ role_change_view.GetBdAddr().ToString().c_str(),
+ RoleText(role_change_view.GetNewRole()).c_str());
+ delayed_role_change_ = std::make_unique<RoleChangeView>(role_change_view);
+ }
}
void on_link_supervision_timeout_changed(EventView packet) {
@@ -632,6 +645,32 @@
}
}
+ void central_link_key(KeyFlag key_flag) {
+ std::unique_ptr<CentralLinkKeyBuilder> packet = CentralLinkKeyBuilder::Create(key_flag);
+ acl_connection_interface_->EnqueueCommand(
+ std::move(packet), handler_->BindOnce(&check_command_status<CentralLinkKeyStatusView>));
+ }
+
+ void switch_role(Address address, Role role) {
+ std::unique_ptr<SwitchRoleBuilder> packet = SwitchRoleBuilder::Create(address, role);
+ acl_connection_interface_->EnqueueCommand(
+ std::move(packet), handler_->BindOnce(&check_command_status<SwitchRoleStatusView>));
+ }
+
+ void write_default_link_policy_settings(uint16_t default_link_policy_settings) {
+ std::unique_ptr<WriteDefaultLinkPolicySettingsBuilder> packet =
+ WriteDefaultLinkPolicySettingsBuilder::Create(default_link_policy_settings);
+ acl_connection_interface_->EnqueueCommand(
+ std::move(packet), handler_->BindOnce(&check_command_complete<WriteDefaultLinkPolicySettingsCompleteView>));
+ }
+
+ void accept_connection(Address address) {
+ auto role = AcceptConnectionRequestRole::BECOME_CENTRAL; // We prefer to be central
+ acl_connection_interface_->EnqueueCommand(
+ AcceptConnectionRequestBuilder::Create(address, role),
+ handler_->BindOnceOn(this, &classic_impl::on_accept_connection_status, address));
+ }
+
void reject_connection(std::unique_ptr<RejectConnectionRequestBuilder> builder) {
acl_connection_interface_->EnqueueCommand(
std::move(builder), handler_->BindOnce(&check_command_status<RejectConnectionRequestStatusView>));
@@ -641,45 +680,33 @@
void OnDeviceUnbonded(bluetooth::hci::AddressWithType device) override {}
void OnDeviceBondFailed(bluetooth::hci::AddressWithType device, security::PairingFailure status) override {}
- void OnEncryptionStateChanged(EncryptionChangeView encryption_change_view) override {
- if (!encryption_change_view.IsValid()) {
- LOG_ERROR("Invalid packet");
- return;
- } else if (encryption_change_view.GetStatus() != ErrorCode::SUCCESS) {
- auto status = encryption_change_view.GetStatus();
- std::string error_code = ErrorCodeText(status);
- LOG_ERROR("error_code %s", error_code.c_str());
- return;
- }
- uint16_t handle = encryption_change_view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Unknown connection handle 0x%04hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- EncryptionEnabled enabled = encryption_change_view.GetEncryptionEnabled();
- callbacks->OnEncryptionChange(enabled);
- }
-
void set_security_module(security::SecurityModule* security_module) {
security_manager_ = security_module->GetSecurityManager();
security_manager_->RegisterCallbackListener(this, handler_);
}
uint16_t HACK_get_handle(Address address) {
- for (auto it = acl_connections_.begin(); it != acl_connections_.end(); it++) {
- if (it->second.address_with_type_.GetAddress() == address) {
- return it->first;
- }
- }
- return 0xFFFF;
+ return connections.HACK_get_handle(address);
}
void HACK_SetNonAclDisconnectCallback(std::function<void(uint16_t, uint8_t)> callback) {
non_acl_disconnect_callback_ = callback;
}
+ void handle_register_callbacks(ConnectionCallbacks* callbacks, os::Handler* handler) {
+ ASSERT(client_callbacks_ == nullptr);
+ ASSERT(client_handler_ == nullptr);
+ client_callbacks_ = callbacks;
+ client_handler_ = handler;
+ }
+
+ void handle_unregister_callbacks(ConnectionCallbacks* callbacks, std::promise<void> promise) {
+ ASSERT_LOG(client_callbacks_ == callbacks, "Registered callback entity is different then unregister request");
+ client_callbacks_ = nullptr;
+ client_handler_ = nullptr;
+ promise.set_value();
+ }
+
HciLayer* hci_layer_ = nullptr;
Controller* controller_ = nullptr;
RoundRobinScheduler* round_robin_scheduler_ = nullptr;
@@ -687,7 +714,6 @@
os::Handler* handler_ = nullptr;
ConnectionCallbacks* client_callbacks_ = nullptr;
os::Handler* client_handler_ = nullptr;
- std::map<uint16_t, acl_connection> acl_connections_;
Address outgoing_connecting_address_{Address::kEmpty};
Address incoming_connecting_address_{Address::kEmpty};
common::Callback<bool(Address, ClassOfDevice)> should_accept_connection_;
@@ -695,7 +721,6 @@
std::unique_ptr<RoleChangeView> delayed_role_change_ = nullptr;
std::unique_ptr<security::SecurityManager> security_manager_;
- bool crash_on_unknown_handle_ = false;
std::function<void(uint16_t, uint8_t)> non_acl_disconnect_callback_;
};
diff --git a/system/gd/hci/acl_manager/le_acl_connection.cc b/system/gd/hci/acl_manager/le_acl_connection.cc
index 400f2f6..d57aab2 100644
--- a/system/gd/hci/acl_manager/le_acl_connection.cc
+++ b/system/gd/hci/acl_manager/le_acl_connection.cc
@@ -85,16 +85,17 @@
struct LeAclConnection::impl {
impl(LeAclConnectionInterface* le_acl_connection_interface, std::shared_ptr<Queue> queue, uint16_t connection_handle)
: queue_(std::move(queue)), tracker(le_acl_connection_interface, connection_handle) {}
- LeConnectionManagementCallbacks* GetEventCallbacks() {
- ASSERT(!callbacks_given_);
- callbacks_given_ = true;
+ LeConnectionManagementCallbacks* GetEventCallbacks(std::function<void(uint16_t)> invalidate_callbacks) {
+ ASSERT_LOG(!invalidate_callbacks_, "Already returned event callbacks for this connection");
+ invalidate_callbacks_ = std::move(invalidate_callbacks);
return &tracker;
}
-
- bool callbacks_given_{false};
+ void PutEventCallbacks() {
+ if (invalidate_callbacks_) invalidate_callbacks_(tracker.connection_handle_);
+ }
std::shared_ptr<Queue> queue_;
LeAclConnectionTracker tracker;
- std::shared_ptr<std::atomic<bool>> is_callback_valid_;
+ std::function<void(uint16_t)> invalidate_callbacks_;
};
LeAclConnection::LeAclConnection()
@@ -116,7 +117,7 @@
}
LeAclConnection::~LeAclConnection() {
- if (pimpl_->is_callback_valid_) *pimpl_->is_callback_valid_ = false;
+ pimpl_->PutEventCallbacks();
delete pimpl_;
}
@@ -140,9 +141,8 @@
}
LeConnectionManagementCallbacks* LeAclConnection::GetEventCallbacks(
- std::shared_ptr<std::atomic<bool>> is_callback_valid) {
- pimpl_->is_callback_valid_ = is_callback_valid;
- return pimpl_->GetEventCallbacks();
+ std::function<void(uint16_t)> invalidate_callbacks) {
+ return pimpl_->GetEventCallbacks(std::move(invalidate_callbacks));
}
bool LeAclConnection::LeConnectionUpdate(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t conn_latency,
diff --git a/system/gd/hci/acl_manager/le_acl_connection.h b/system/gd/hci/acl_manager/le_acl_connection.h
index 7dc3a86..fd81111 100644
--- a/system/gd/hci/acl_manager/le_acl_connection.h
+++ b/system/gd/hci/acl_manager/le_acl_connection.h
@@ -71,7 +71,7 @@
// TODO implement LeRemoteConnectionParameterRequestReply, LeRemoteConnectionParameterRequestNegativeReply
// Called once before passing the connection to the client
- virtual LeConnectionManagementCallbacks* GetEventCallbacks(std::shared_ptr<std::atomic<bool>> is_callback_valid);
+ virtual LeConnectionManagementCallbacks* GetEventCallbacks(std::function<void(uint16_t)> invalidate_callbacks);
private:
virtual bool check_connection_parameters(
diff --git a/system/gd/hci/acl_manager/le_impl.h b/system/gd/hci/acl_manager/le_impl.h
index 0da24b6..b666fbd 100644
--- a/system/gd/hci/acl_manager/le_impl.h
+++ b/system/gd/hci/acl_manager/le_impl.h
@@ -48,12 +48,14 @@
struct le_acl_connection {
le_acl_connection(AddressWithType remote_address, AclConnection::QueueDownEnd* queue_down_end, os::Handler* handler)
- : assembler_(remote_address, queue_down_end, handler), remote_address_(remote_address) {}
- ~le_acl_connection() = default;
- struct acl_manager::assembler assembler_;
+ : remote_address_(remote_address),
+ assembler_(new acl_manager::assembler(remote_address, queue_down_end, handler)) {}
+ ~le_acl_connection() {
+ delete assembler_;
+ }
AddressWithType remote_address_;
+ acl_manager::assembler* assembler_;
LeConnectionManagementCallbacks* le_connection_management_callbacks_ = nullptr;
- std::shared_ptr<std::atomic<bool>> is_callback_valid_ = std::make_shared<std::atomic<bool>>(true);
};
struct le_impl : public bluetooth::hci::LeAddressManagerCallback {
@@ -63,13 +65,11 @@
os::Handler* handler,
RoundRobinScheduler* round_robin_scheduler,
bool crash_on_unknown_handle)
- : hci_layer_(hci_layer),
- controller_(controller),
- round_robin_scheduler_(round_robin_scheduler),
- crash_on_unknown_handle_(crash_on_unknown_handle) {
+ : hci_layer_(hci_layer), controller_(controller), round_robin_scheduler_(round_robin_scheduler) {
hci_layer_ = hci_layer;
controller_ = controller;
handler_ = handler;
+ connections.crash_on_unknown_handle_ = crash_on_unknown_handle;
le_acl_connection_interface_ = hci_layer_->GetLeAclConnectionInterface(
handler_->BindOn(this, &le_impl::on_le_event),
handler_->BindOn(this, &le_impl::on_le_disconnect),
@@ -88,7 +88,7 @@
}
delete le_address_manager_;
hci_layer_->PutLeAclConnectionInterface();
- le_acl_connections_.clear();
+ connections.reset();
}
void on_le_event(LeMetaEventView event_packet) {
@@ -117,22 +117,91 @@
}
}
- LeConnectionManagementCallbacks* get_callbacks(uint16_t handle) {
- auto connection = le_acl_connections_.find(handle);
- if (connection == le_acl_connections_.end()) {
- return nullptr;
+ private:
+ static constexpr uint16_t kIllegalConnectionHandle = 0xffff;
+ struct {
+ private:
+ std::map<uint16_t, le_acl_connection> le_acl_connections_;
+ mutable std::mutex le_acl_connections_guard_;
+ LeConnectionManagementCallbacks* find_callbacks(uint16_t handle) {
+ auto connection = le_acl_connections_.find(handle);
+ if (connection == le_acl_connections_.end()) return nullptr;
+ return connection->second.le_connection_management_callbacks_;
}
- return (connection->second.is_callback_valid_) ? connection->second.le_connection_management_callbacks_ : nullptr;
+ void remove(uint16_t handle) {
+ auto connection = le_acl_connections_.find(handle);
+ if (connection != le_acl_connections_.end()) {
+ connection->second.le_connection_management_callbacks_ = nullptr;
+ le_acl_connections_.erase(handle);
+ }
+ }
+
+ public:
+ bool crash_on_unknown_handle_ = false;
+ bool is_empty() const {
+ std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+ return le_acl_connections_.empty();
+ }
+ void reset() {
+ std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+ le_acl_connections_.clear();
+ }
+ void invalidate(uint16_t handle) {
+ std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+ remove(handle);
+ }
+ void execute(
+ uint16_t handle,
+ std::function<void(LeConnectionManagementCallbacks* callbacks)> execute,
+ bool remove_afterwards = false) {
+ std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+ auto callbacks = find_callbacks(handle);
+ if (callbacks != nullptr)
+ execute(callbacks);
+ else
+ ASSERT_LOG(!crash_on_unknown_handle_, "Received command for unknown handle:0x%x", handle);
+ if (remove_afterwards) remove(handle);
+ }
+ bool send_packet_upward(uint16_t handle, std::function<void(struct acl_manager::assembler* assembler)> cb) {
+ std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+ auto connection = le_acl_connections_.find(handle);
+ if (connection != le_acl_connections_.end()) cb(connection->second.assembler_);
+ return connection != le_acl_connections_.end();
+ }
+ void add(
+ uint16_t handle,
+ const AddressWithType& remote_address,
+ AclConnection::QueueDownEnd* queue_end,
+ os::Handler* handler,
+ LeConnectionManagementCallbacks* le_connection_management_callbacks) {
+ std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+ auto emplace_pair = le_acl_connections_.emplace(
+ std::piecewise_construct,
+ std::forward_as_tuple(handle),
+ std::forward_as_tuple(remote_address, queue_end, handler));
+ ASSERT(emplace_pair.second); // Make sure the connection is unique
+ emplace_pair.first->second.le_connection_management_callbacks_ = le_connection_management_callbacks;
+ }
+ uint16_t HACK_get_handle(Address address) const {
+ std::unique_lock<std::mutex> lock(le_acl_connections_guard_);
+ for (auto it = le_acl_connections_.begin(); it != le_acl_connections_.end(); it++) {
+ if (it->second.remote_address_.GetAddress() == address) {
+ return it->first;
+ }
+ }
+ return kIllegalConnectionHandle;
+ }
+ } connections;
+
+ public:
+ void enqueue_command(std::unique_ptr<CommandBuilder> command_packet) {
+ hci_layer_->EnqueueCommand(
+ std::move(command_packet),
+ handler_->BindOnce(&LeAddressManager::OnCommandComplete, common::Unretained(le_address_manager_)));
}
- void on_le_disconnect(uint16_t handle, ErrorCode reason) {
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- return;
- }
- round_robin_scheduler_->Unregister(handle);
- callbacks->OnDisconnection(reason);
- le_acl_connections_.erase(handle);
+ bool send_packet_upward(uint16_t handle, std::function<void(struct acl_manager::assembler* assembler)> cb) {
+ return connections.send_packet_upward(handle, cb);
}
void on_common_le_connection_complete(AddressWithType address_with_type) {
@@ -194,18 +263,15 @@
auto role = connection_complete.GetRole();
uint16_t handle = connection_complete.GetConnectionHandle();
auto queue = std::make_shared<AclConnection::Queue>(10);
- auto emplace_pair = le_acl_connections_.emplace(
- std::piecewise_construct,
- std::forward_as_tuple(handle),
- std::forward_as_tuple(remote_address, queue->GetDownEnd(), handler_));
- ASSERT(emplace_pair.second); // Make sure the connection is unique
- auto& connection_proxy = emplace_pair.first->second;
+ auto queue_down_end = queue->GetDownEnd();
round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, handle, queue);
std::unique_ptr<LeAclConnection> connection(new LeAclConnection(
std::move(queue), le_acl_connection_interface_, handle, local_address, remote_address, role));
connection->peer_address_with_type_ = AddressWithType(address, peer_address_type);
- connection_proxy.le_connection_management_callbacks_ =
- connection->GetEventCallbacks(connection_proxy.is_callback_valid_);
+ connections.add(
+ handle, remote_address, queue_down_end, handler_, connection->GetEventCallbacks([this](uint16_t handle) {
+ this->connections.invalidate(handle);
+ }));
le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectSuccess,
common::Unretained(le_client_callbacks_), remote_address,
std::move(connection)));
@@ -267,23 +333,34 @@
}
uint16_t handle = connection_complete.GetConnectionHandle();
auto queue = std::make_shared<AclConnection::Queue>(10);
- auto emplace_pair = le_acl_connections_.emplace(
- std::piecewise_construct,
- std::forward_as_tuple(handle),
- std::forward_as_tuple(remote_address, queue->GetDownEnd(), handler_));
- ASSERT(emplace_pair.second); // Make sure it's not a duplicate
- auto& connection_proxy = emplace_pair.first->second;
+ auto queue_down_end = queue->GetDownEnd();
round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, handle, queue);
std::unique_ptr<LeAclConnection> connection(new LeAclConnection(
std::move(queue), le_acl_connection_interface_, handle, local_address, remote_address, role));
connection->peer_address_with_type_ = AddressWithType(address, peer_address_type);
- connection_proxy.le_connection_management_callbacks_ =
- connection->GetEventCallbacks(connection_proxy.is_callback_valid_);
+ connections.add(
+ handle, remote_address, queue_down_end, handler_, connection->GetEventCallbacks([this](uint16_t handle) {
+ this->connections.invalidate(handle);
+ }));
le_client_handler_->Post(common::BindOnce(&LeConnectionCallbacks::OnLeConnectSuccess,
common::Unretained(le_client_callbacks_), remote_address,
std::move(connection)));
}
+ static constexpr bool kRemoveConnectionAfterwards = true;
+ void on_le_disconnect(uint16_t handle, ErrorCode reason) {
+ bool event_also_routes_to_other_receivers = connections.crash_on_unknown_handle_;
+ connections.crash_on_unknown_handle_ = false;
+ connections.execute(
+ handle,
+ [=](LeConnectionManagementCallbacks* callbacks) {
+ round_robin_scheduler_->Unregister(handle);
+ callbacks->OnDisconnection(reason);
+ },
+ kRemoveConnectionAfterwards);
+ connections.crash_on_unknown_handle_ = event_also_routes_to_other_receivers;
+ }
+
void on_le_connection_update_complete(LeMetaEventView view) {
auto complete_view = LeConnectionUpdateCompleteView::Create(view);
if (!complete_view.IsValid()) {
@@ -291,17 +368,13 @@
return;
}
auto handle = complete_view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Can't find connection 0x%hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- callbacks->OnConnectionUpdate(
- complete_view.GetStatus(),
- complete_view.GetConnInterval(),
- complete_view.GetConnLatency(),
- complete_view.GetSupervisionTimeout());
+ connections.execute(handle, [=](LeConnectionManagementCallbacks* callbacks) {
+ callbacks->OnConnectionUpdate(
+ complete_view.GetStatus(),
+ complete_view.GetConnInterval(),
+ complete_view.GetConnLatency(),
+ complete_view.GetSupervisionTimeout());
+ });
}
void on_le_phy_update_complete(LeMetaEventView view) {
@@ -311,29 +384,16 @@
return;
}
auto handle = complete_view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Can't find connection 0x%hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- callbacks->OnPhyUpdate(complete_view.GetStatus(), complete_view.GetTxPhy(), complete_view.GetRxPhy());
+ connections.execute(handle, [=](LeConnectionManagementCallbacks* callbacks) {
+ callbacks->OnPhyUpdate(complete_view.GetStatus(), complete_view.GetTxPhy(), complete_view.GetRxPhy());
+ });
}
void on_le_read_remote_version_information(
hci::ErrorCode hci_status, uint16_t handle, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version) {
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_INFO("No le connection registered for 0x%hx", handle);
- return;
- }
- callbacks->OnReadRemoteVersionInformationComplete(hci_status, version, manufacturer_name, sub_version);
- }
-
- void enqueue_command(std::unique_ptr<CommandBuilder> command_packet) {
- hci_layer_->EnqueueCommand(
- std::move(command_packet),
- handler_->BindOnce(&LeAddressManager::OnCommandComplete, common::Unretained(le_address_manager_)));
+ connections.execute(handle, [=](LeConnectionManagementCallbacks* callbacks) {
+ callbacks->OnReadRemoteVersionInformationComplete(hci_status, version, manufacturer_name, sub_version);
+ });
}
void on_data_length_change(LeMetaEventView view) {
@@ -343,17 +403,13 @@
return;
}
auto handle = data_length_view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Can't find connection 0x%hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- callbacks->OnDataLengthChange(
- data_length_view.GetMaxTxOctets(),
- data_length_view.GetMaxTxTime(),
- data_length_view.GetMaxRxOctets(),
- data_length_view.GetMaxRxTime());
+ connections.execute(handle, [=](LeConnectionManagementCallbacks* callbacks) {
+ callbacks->OnDataLengthChange(
+ data_length_view.GetMaxTxOctets(),
+ data_length_view.GetMaxTxTime(),
+ data_length_view.GetMaxRxOctets(),
+ data_length_view.GetMaxRxTime());
+ });
}
void on_remote_connection_parameter_request(LeMetaEventView view) {
@@ -364,24 +420,30 @@
}
auto handle = request_view.GetConnectionHandle();
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Can't find connection 0x%hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- // TODO: this is blindly accepting any parameters, just so we don't hang connection
- // have proper parameter negotiation
- le_acl_connection_interface_->EnqueueCommand(
- LeRemoteConnectionParameterRequestReplyBuilder::Create(
- handle,
- request_view.GetIntervalMin(),
- request_view.GetIntervalMax(),
- request_view.GetLatency(),
- request_view.GetTimeout(),
- 0,
- 0),
- handler_->BindOnce([](CommandCompleteView status) {}));
+ connections.execute(handle, [=](LeConnectionManagementCallbacks* callbacks) {
+ // TODO: this is blindly accepting any parameters, just so we don't hang connection
+ // have proper parameter negotiation
+ le_acl_connection_interface_->EnqueueCommand(
+ LeRemoteConnectionParameterRequestReplyBuilder::Create(
+ handle,
+ request_view.GetIntervalMin(),
+ request_view.GetIntervalMax(),
+ request_view.GetLatency(),
+ request_view.GetTimeout(),
+ 0,
+ 0),
+ handler_->BindOnce([](CommandCompleteView status) {}));
+ });
+ }
+
+ uint16_t HACK_get_handle(Address address) {
+ return connections.HACK_get_handle(address);
+ }
+
+ void UpdateLocalAddress(uint16_t handle, hci::AddressWithType address_with_type) {
+ connections.execute(handle, [=](LeConnectionManagementCallbacks* callbacks) {
+ callbacks->OnLocalAddressUpdate(address_with_type);
+ });
}
void add_device_to_connect_list(AddressWithType address_with_type) {
@@ -705,25 +767,6 @@
}
}
- uint16_t HACK_get_handle(Address address) {
- for (auto it = le_acl_connections_.begin(); it != le_acl_connections_.end(); it++) {
- if (it->second.remote_address_.GetAddress() == address) {
- return it->first;
- }
- }
- return 0xFFFF;
- }
-
- void UpdateLocalAddress(uint16_t handle, hci::AddressWithType address_with_type) {
- auto callbacks = get_callbacks(handle);
- if (callbacks == nullptr) {
- LOG_WARN("Can't find connection 0x%hx", handle);
- ASSERT(!crash_on_unknown_handle_);
- return;
- }
- callbacks->OnLocalAddressUpdate(address_with_type);
- }
-
void register_with_address_manager() {
if (!address_manager_registered) {
le_address_manager_->Register(this);
@@ -733,7 +776,7 @@
}
void check_for_unregister() {
- if (le_acl_connections_.empty() && connecting_le_.empty() && address_manager_registered && ready_to_unregister) {
+ if (connections.is_empty() && connecting_le_.empty() && address_manager_registered && ready_to_unregister) {
le_address_manager_->Unregister(this);
address_manager_registered = false;
pause_connection = false;
@@ -751,7 +794,6 @@
LeAclConnectionInterface* le_acl_connection_interface_ = nullptr;
LeConnectionCallbacks* le_client_callbacks_ = nullptr;
os::Handler* le_client_handler_ = nullptr;
- std::map<uint16_t, le_acl_connection> le_acl_connections_;
std::set<AddressWithType> connecting_le_;
std::set<AddressWithType> canceled_connections_;
std::set<AddressWithType> direct_connections_;
@@ -760,7 +802,6 @@
bool address_manager_registered = false;
bool ready_to_unregister = false;
bool pause_connection = false;
- bool crash_on_unknown_handle_ = false;
std::map<AddressWithType, os::Alarm> create_connection_timeout_alarms_;
};
diff --git a/system/gd/hci/hci_layer.cc b/system/gd/hci/hci_layer.cc
index 37d00f3..7e73e7b 100644
--- a/system/gd/hci/hci_layer.cc
+++ b/system/gd/hci/hci_layer.cc
@@ -487,6 +487,7 @@
}
void HciLayer::Disconnect(uint16_t handle, ErrorCode reason) {
+ std::unique_lock<std::mutex> lock(callback_handlers_guard_);
for (auto callback : disconnect_handlers_) {
callback.Invoke(handle, reason);
}
@@ -505,6 +506,7 @@
void HciLayer::ReadRemoteVersion(
hci::ErrorCode hci_status, uint16_t handle, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version) {
+ std::unique_lock<std::mutex> lock(callback_handlers_guard_);
for (auto callback : read_remote_version_handlers_) {
callback.Invoke(hci_status, handle, version, manufacturer_name, sub_version);
}
@@ -516,8 +518,11 @@
ContextualCallback<
void(hci::ErrorCode hci_status, uint16_t, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version)>
on_read_remote_version) {
- disconnect_handlers_.push_back(on_disconnect);
- read_remote_version_handlers_.push_back(on_read_remote_version);
+ {
+ std::unique_lock<std::mutex> lock(callback_handlers_guard_);
+ disconnect_handlers_.push_back(on_disconnect);
+ read_remote_version_handlers_.push_back(on_read_remote_version);
+ }
for (const auto event : AclConnectionEvents) {
RegisterEventHandler(event, event_handler);
}
@@ -528,8 +533,11 @@
for (const auto event : AclConnectionEvents) {
UnregisterEventHandler(event);
}
- disconnect_handlers_.clear();
- read_remote_version_handlers_.clear();
+ {
+ std::unique_lock<std::mutex> lock(callback_handlers_guard_);
+ disconnect_handlers_.clear();
+ read_remote_version_handlers_.clear();
+ }
}
LeAclConnectionInterface* HciLayer::GetLeAclConnectionInterface(
@@ -538,8 +546,11 @@
ContextualCallback<
void(hci::ErrorCode hci_status, uint16_t, uint8_t version, uint16_t manufacturer_name, uint16_t sub_version)>
on_read_remote_version) {
- disconnect_handlers_.push_back(on_disconnect);
- read_remote_version_handlers_.push_back(on_read_remote_version);
+ {
+ std::unique_lock<std::mutex> lock(callback_handlers_guard_);
+ disconnect_handlers_.push_back(on_disconnect);
+ read_remote_version_handlers_.push_back(on_read_remote_version);
+ }
for (const auto event : LeConnectionManagementEvents) {
RegisterLeEventHandler(event, event_handler);
}
@@ -550,8 +561,11 @@
for (const auto event : LeConnectionManagementEvents) {
UnregisterLeEventHandler(event);
}
- disconnect_handlers_.clear();
- read_remote_version_handlers_.clear();
+ {
+ std::unique_lock<std::mutex> lock(callback_handlers_guard_);
+ disconnect_handlers_.clear();
+ read_remote_version_handlers_.clear();
+ }
}
SecurityInterface* HciLayer::GetSecurityInterface(ContextualCallback<void(EventView)> event_handler) {
diff --git a/system/gd/hci/hci_layer.h b/system/gd/hci/hci_layer.h
index 3f4511d..8ff74d3 100644
--- a/system/gd/hci/hci_layer.h
+++ b/system/gd/hci/hci_layer.h
@@ -143,6 +143,7 @@
std::list<common::ContextualCallback<void(uint16_t, ErrorCode)>> disconnect_handlers_;
std::list<common::ContextualCallback<void(hci::ErrorCode, uint16_t, uint8_t, uint16_t, uint16_t)>>
read_remote_version_handlers_;
+ std::mutex callback_handlers_guard_;
void on_disconnection_complete(EventView event_view);
void on_read_remote_version_complete(EventView event_view);
diff --git a/system/gd/packet/parser/Android.bp b/system/gd/packet/parser/Android.bp
index 41901de..aaa0805 100644
--- a/system/gd/packet/parser/Android.bp
+++ b/system/gd/packet/parser/Android.bp
@@ -33,10 +33,10 @@
"fields/variable_length_struct_field.cc",
"checksum_def.cc",
"custom_field_def.cc",
- "packet_dependency.cc",
"enum_def.cc",
"enum_gen.cc",
"packet_def.cc",
+ "packet_dependency.cc",
"parent_def.cc",
"struct_def.cc",
"struct_parser_generator.cc",
diff --git a/system/gd/packet/parser/BUILD.gn b/system/gd/packet/parser/BUILD.gn
index 3964551..c9ac6c3 100644
--- a/system/gd/packet/parser/BUILD.gn
+++ b/system/gd/packet/parser/BUILD.gn
@@ -62,6 +62,7 @@
"gen_rust.cc",
"main.cc",
"packet_def.cc",
+ "packet_dependency.cc",
"parent_def.cc",
"struct_def.cc",
"struct_parser_generator.cc",
diff --git a/system/gd/packet/parser/packet_def.cc b/system/gd/packet/parser/packet_def.cc
index 82ebda6..0229360 100644
--- a/system/gd/packet/parser/packet_def.cc
+++ b/system/gd/packet/parser/packet_def.cc
@@ -21,6 +21,7 @@
#include <set>
#include "fields/all_fields.h"
+#include "packet_dependency.h"
#include "util.h"
PacketDef::PacketDef(std::string name, FieldList fields) : ParentDef(name, fields, nullptr) {}
@@ -883,8 +884,9 @@
}
void PacketDef::GenRustStructImpls(std::ostream& s) const {
- s << "impl " << name_ << "Data {";
+ auto packet_dep = PacketDependency(GetRootDef());
+ s << "impl " << name_ << "Data {";
// conforms function
s << "fn conforms(bytes: &[u8]) -> bool {";
GenRustConformanceCheck(s);
@@ -904,15 +906,15 @@
s << " true";
s << "}";
- // parse function
- if (parent_constraints_.empty() && children_.size() > 1 && parent_ != nullptr) {
- auto constraint = FindConstraintField();
- auto constraint_field = GetParamList().GetField(constraint);
+ auto parse_params = packet_dep.GetDependencies(name_);
+ s << "fn parse(bytes: &[u8]";
+ for (auto field_name : parse_params) {
+ auto constraint_field = GetParamList().GetField(field_name);
auto constraint_type = constraint_field->GetRustDataType();
- s << "fn parse(bytes: &[u8], " << constraint << ": " << constraint_type << ") -> Result<Self> {";
- } else {
- s << "fn parse(bytes: &[u8]) -> Result<Self> {";
+ s << ", " << field_name << ": " << constraint_type;
}
+ s << ") -> Result<Self> {";
+
fields = fields_.GetFieldsWithoutTypes({
BodyField::kFieldType,
});
@@ -940,51 +942,123 @@
payload_offset = GetOffsetForField(payload_field[0]->GetName(), false);
}
- auto constraint_name = FindConstraintField();
- auto constrained_descendants = FindDescendantsWithConstraint(constraint_name);
-
if (children_.size() > 1) {
- s << "let child = match " << constraint_name << " {";
+ auto match_on_variables = packet_dep.GetChildrenDependencies(name_);
+ // If match_on_variables is empty, this means there are multiple abstract packets which will
+ // specialize to a child down the packet tree.
+ // In this case match variables will be the union of parent fields and parse params of children.
+ if (match_on_variables.empty()) {
+ for (auto& field : fields_) {
+ if (std::any_of(children_.begin(), children_.end(), [&](auto child) {
+ auto pass_me = packet_dep.GetDependencies(child->name_);
+ return std::find(pass_me.begin(), pass_me.end(), field->GetName()) != pass_me.end();
+ })) {
+ match_on_variables.push_back(field->GetName());
+ }
+ }
+ }
- for (const auto& desc : constrained_descendants) {
- auto desc_path = FindPathToDescendant(desc.first->name_);
- std::reverse(desc_path.begin(), desc_path.end());
- auto constraint_field = GetParamList().GetField(constraint_name);
+ s << "let child = match (";
+
+ for (auto var : match_on_variables) {
+ if (var == match_on_variables[match_on_variables.size() - 1]) {
+ s << var;
+ } else {
+ s << var << ", ";
+ }
+ }
+ s << ") {";
+
+ auto get_match_val = [&](
+ std::string& match_var,
+ std::variant<int64_t,
+ std::string> constraint) -> std::string {
+ auto constraint_field = GetParamList().GetField(match_var);
auto constraint_type = constraint_field->GetFieldType();
if (constraint_type == EnumField::kFieldType) {
- auto type = std::get<std::string>(desc.second);
+ auto type = std::get<std::string>(constraint);
auto variant_name = type.substr(type.find("::") + 2, type.length());
auto enum_type = type.substr(0, type.find("::"));
- auto enum_variant = enum_type + "::"
- + util::UnderscoreToCamelCase(util::ToLowerCase(variant_name));
- s << enum_variant;
- s << " if " << desc_path[0]->name_ << "Data::conforms(&bytes[..])";
- s << " => {";
- s << name_ << "DataChild::";
- s << desc_path[0]->name_ << "(Arc::new(";
- if (desc_path[0]->parent_constraints_.empty()) {
- s << desc_path[0]->name_ << "Data::parse(&bytes[..]";
- s << ", " << enum_variant << ")?))";
- } else {
- s << desc_path[0]->name_ << "Data::parse(&bytes[..])?))";
- }
- } else if (constraint_type == ScalarField::kFieldType) {
- s << std::get<int64_t>(desc.second) << " => {";
- s << "unimplemented!();";
+ return enum_type + "::" + util::UnderscoreToCamelCase(util::ToLowerCase(variant_name));
}
+ if (constraint_type == ScalarField::kFieldType) {
+ return std::to_string(std::get<int64_t>(constraint));
+ }
+ return "_";
+ };
+
+ for (auto& child : children_) {
+ s << "(";
+ for (auto var : match_on_variables) {
+ std::string match_val = "_";
+
+ if (child->parent_constraints_.find(var) != child->parent_constraints_.end()) {
+ match_val = get_match_val(var, child->parent_constraints_[var]);
+ } else {
+ auto dcs = child->FindDescendantsWithConstraint(var);
+ std::vector<std::string> all_match_vals;
+ for (auto& desc : dcs) {
+ all_match_vals.push_back(get_match_val(var, desc.second));
+ }
+ match_val = "";
+ for (std::size_t i = 0; i < all_match_vals.size(); ++i) {
+ match_val += all_match_vals[i];
+ if (i != all_match_vals.size() - 1) {
+ match_val += " | ";
+ }
+ }
+ match_val = (match_val == "") ? "_" : match_val;
+ }
+
+ if (var == match_on_variables[match_on_variables.size() - 1]) {
+ s << match_val << ")";
+ } else {
+ s << match_val << ", ";
+ }
+ }
+ s << " if " << child->name_ << "Data::conforms(&bytes[..])";
+ s << " => {";
+ s << name_ << "DataChild::";
+ s << child->name_ << "(Arc::new(";
+
+ auto child_parse_params = packet_dep.GetDependencies(child->name_);
+ if (child_parse_params.size() == 0) {
+ s << child->name_ << "Data::parse(&bytes[..]";
+ } else {
+ s << child->name_ << "Data::parse(&bytes[..], ";
+ }
+
+ for (auto var : child_parse_params) {
+ if (var == child_parse_params[child_parse_params.size() - 1]) {
+ s << var;
+ } else {
+ s << var << ", ";
+ }
+ }
+ s << ")?))";
s << "}\n";
}
- if (!constrained_descendants.empty()) {
- s << "v => return Err(Error::ConstraintOutOfBounds{field: \"" << constraint_name
- << "\".to_string(), value: v as u64}),";
+ s << "(";
+ for (int i = 1; i <= match_on_variables.size(); i++) {
+ if (i == match_on_variables.size()) {
+ s << "_";
+ } else {
+ s << "_, ";
+ }
}
-
+ s << ")";
+ s << " => return Err(Error::InvalidPacketError),";
s << "};\n";
} else if (children_.size() == 1) {
auto child = children_.at(0);
- s << "let child = match " << child->name_ << "Data::parse(&bytes[..]) {";
+ auto params = packet_dep.GetDependencies(child->name_);
+ s << "let child = match " << child->name_ << "Data::parse(&bytes[..]";
+ for (auto field_name : params) {
+ s << ", " << field_name;
+ }
+ s << ") {";
s << " Ok(c) if " << child->name_ << "Data::conforms(&bytes[..]) => {";
s << name_ << "DataChild::" << child->name_ << "(Arc::new(c))";
s << " },";
diff --git a/system/include/hardware/ble_scanner.h b/system/include/hardware/ble_scanner.h
index 67e805b..eb966b2 100644
--- a/system/include/hardware/ble_scanner.h
+++ b/system/include/hardware/ble_scanner.h
@@ -46,7 +46,8 @@
uint8_t secondary_phy,
uint8_t advertising_sid, int8_t tx_power,
int8_t rssi, uint16_t periodic_adv_int,
- std::vector<uint8_t> adv_data);
+ std::vector<uint8_t> adv_data,
+ RawAddress* original_bda);
typedef struct {
scan_result_callback scan_result_cb;
diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h
index a1d16ce..c174d91 100644
--- a/system/include/hardware/bluetooth.h
+++ b/system/include/hardware/bluetooth.h
@@ -207,6 +207,8 @@
uint32_t dynamic_audio_buffer_supported;
bool le_periodic_advertising_sync_transfer_sender_supported;
bool le_connected_isochronous_stream_central_supported;
+ bool le_isochronous_broadcast_supported;
+ bool le_periodic_advertising_sync_transfer_recipient_supported;
} bt_local_le_features_t;
/* Stored the default/maximum/minimum buffer time for dynamic audio buffer.
@@ -754,6 +756,15 @@
* Fetches the local Out of Band data.
*/
int (*generate_local_oob_data)(tBT_TRANSPORT transport);
+
+ /**
+ * Allow or disallow audio low latency
+ *
+ * @param allowed true if allowing audio low latency
+ * @param address Bluetooth MAC address of Bluetooth device
+ * @return true if audio low latency is successfully allowed or disallowed
+ */
+ bool (*allow_low_latency_audio)(bool allowed, const RawAddress& address);
} bt_interface_t;
#define BLUETOOTH_INTERFACE_STRING "bluetoothInterface"
diff --git a/system/main/shim/btm.cc b/system/main/shim/btm.cc
index c72030c..b79292c 100644
--- a/system/main/shim/btm.cc
+++ b/system/main/shim/btm.cc
@@ -63,7 +63,7 @@
uint16_t event_type, uint8_t address_type, const RawAddress& raw_address,
uint8_t primary_phy, uint8_t secondary_phy, uint8_t advertising_sid,
int8_t tx_power, int8_t rssi, uint16_t periodic_adv_int, uint8_t data_len,
- const uint8_t* data);
+ const uint8_t* data, const RawAddress& original_bda);
extern void btm_api_process_inquiry_result(const RawAddress& raw_address,
uint8_t page_scan_rep_mode,
@@ -128,12 +128,14 @@
btm_ble_process_adv_addr(raw_address, &ble_address_type);
}
+ // Pass up to GattService#onScanResult
+ RawAddress original_bda = raw_address;
btm_ble_process_adv_addr(raw_address, &ble_address_type);
- btm_ble_process_adv_pkt_cont(extended_event_type, ble_address_type,
- raw_address, primary_phy, secondary_phy,
- advertising_sid, tx_power, rssi,
- periodic_advertising_interval,
- advertising_data.size(), &advertising_data[0]);
+ btm_ble_process_adv_pkt_cont(
+ extended_event_type, ble_address_type, raw_address, primary_phy,
+ secondary_phy, advertising_sid, tx_power, rssi,
+ periodic_advertising_interval, advertising_data.size(),
+ &advertising_data[0], original_bda);
}
void Btm::ScanningCallbacks::OnTrackAdvFoundLost(
diff --git a/system/main/shim/btm_api.cc b/system/main/shim/btm_api.cc
index d871f76..a4ad2f9 100644
--- a/system/main/shim/btm_api.cc
+++ b/system/main/shim/btm_api.cc
@@ -1227,7 +1227,8 @@
}
bool bluetooth::shim::BTM_SecAddDevice(const RawAddress& bd_addr,
- DEV_CLASS dev_class, BD_NAME bd_name,
+ DEV_CLASS dev_class,
+ const BD_NAME& bd_name,
uint8_t* features, LinkKey* link_key,
uint8_t key_type, uint8_t pin_length) {
// Check if GD has a security record for the device
diff --git a/system/main/shim/btm_api.h b/system/main/shim/btm_api.h
index c276d01..e6ac3fe 100644
--- a/system/main/shim/btm_api.h
+++ b/system/main/shim/btm_api.h
@@ -1403,8 +1403,8 @@
*
******************************************************************************/
bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
- BD_NAME bd_name, uint8_t* features, LinkKey* link_key,
- uint8_t key_type, uint8_t pin_length);
+ const BD_NAME& bd_name, uint8_t* features,
+ LinkKey* link_key, uint8_t key_type, uint8_t pin_length);
/** Free resources associated with the device associated with |bd_addr| address.
*
diff --git a/system/service/bluetooth_interface.cc b/system/service/bluetooth_interface.cc
index ee58ef3..155b13d 100644
--- a/system/service/bluetooth_interface.cc
+++ b/system/service/bluetooth_interface.cc
@@ -584,6 +584,12 @@
return btif_set_dynamic_audio_buffer_size(codec, size);
}
+static bool allow_low_latency_audio(bool allowed, const RawAddress& address) {
+ LOG_INFO("%s %s", __func__, allowed ? "true" : "false");
+ // Call HAL here
+ return true;
+}
+
EXPORT_SYMBOL bt_interface_t bluetoothInterface = {
sizeof(bluetoothInterface),
init,
@@ -622,7 +628,8 @@
obfuscate_address,
get_metric_id,
set_dynamic_audio_buffer_size,
- generate_local_oob_data};
+ generate_local_oob_data,
+ allow_low_latency_audio};
// callback reporting helpers
diff --git a/system/service/gatt_server_old.cc b/system/service/gatt_server_old.cc
index 7e2b6ab..c4d3790 100644
--- a/system/service/gatt_server_old.cc
+++ b/system/service/gatt_server_old.cc
@@ -377,7 +377,8 @@
uint8_t ble_secondary_phy, uint8_t ble_advertising_sid,
int8_t ble_tx_power, int8_t rssi,
uint16_t ble_periodic_adv_int,
- std::vector<uint8_t> adv_data) {
+ std::vector<uint8_t> adv_data,
+ RawAddress* original_bda) {
std::string addr(BtAddrString(bda));
std::lock_guard<std::mutex> lock(g_internal->lock);
g_internal->scan_results[addr] = rssi;
diff --git a/system/service/hal/bluetooth_gatt_interface.cc b/system/service/hal/bluetooth_gatt_interface.cc
index 2131d7b..c9d165b 100644
--- a/system/service/hal/bluetooth_gatt_interface.cc
+++ b/system/service/hal/bluetooth_gatt_interface.cc
@@ -93,12 +93,13 @@
RegisterClientCallback(g_interface, status, client_if, app_uuid));
}
-void ScanResultCallback(
- uint16_t ble_evt_type, uint8_t addr_type, RawAddress* bda,
- uint8_t ble_primary_phy, uint8_t ble_secondary_phy,
- uint8_t ble_advertising_sid, int8_t ble_tx_power, int8_t rssi,
- uint16_t ble_periodic_adv_int,
- std::vector<uint8_t> adv_data) { // NOLINT(pass-by-value)
+void ScanResultCallback(uint16_t ble_evt_type, uint8_t addr_type,
+ RawAddress* bda, uint8_t ble_primary_phy,
+ uint8_t ble_secondary_phy, uint8_t ble_advertising_sid,
+ int8_t ble_tx_power, int8_t rssi,
+ uint16_t ble_periodic_adv_int,
+ std::vector<uint8_t> adv_data,
+ RawAddress* original_bda) { // NOLINT(pass-by-value)
shared_lock<shared_mutex_impl> lock(g_instance_lock);
VERIFY_INTERFACE_OR_RETURN();
CHECK(bda);
diff --git a/system/service/hal/fake_bluetooth_interface.cc b/system/service/hal/fake_bluetooth_interface.cc
index e32c380..729778d 100644
--- a/system/service/hal/fake_bluetooth_interface.cc
+++ b/system/service/hal/fake_bluetooth_interface.cc
@@ -80,6 +80,7 @@
nullptr, /* get_metric_id */
nullptr, /* set_dynamic_audio_buffer_size */
nullptr, /* generate_local_oob_data */
+ nullptr, /* allow_low_latency_audio */
};
} // namespace
diff --git a/system/stack/btm/btm_ble_adv_filter.cc b/system/stack/btm/btm_ble_adv_filter.cc
index cb9d3ef..93c898b 100644
--- a/system/stack/btm/btm_ble_adv_filter.cc
+++ b/system/stack/btm/btm_ble_adv_filter.cc
@@ -478,19 +478,60 @@
UINT8_TO_STREAM(p, filt_index);
if (action != BTM_BLE_SCAN_COND_CLEAR) {
- if (addr.type == 2 /* DEVICE_TYPE_ALL in ScanFilterQueue.java */) {
- LOG(INFO) << __func__ << " Filter address " << addr.bda
- << " has DEVICE_TYPE_ALL, try to get identity address";
- /* If no matching identity address is found for the input address,
- * this call will have no effect. */
- uint8_t dummy_addr_type;
- btm_random_pseudo_to_identity_addr(&addr.bda, &dummy_addr_type);
- }
+ LOG(INFO) << __func__ << " Filter address " << addr.bda
+ << " has DEVICE_TYPE_ALL, try to get identity address";
+ /*
+ * Always do the pseudo to id address check!
+ *
+ * In the happy path case this should be checking only random types.
+ *
+ * However, the Java layer only knows PUBLIC and RANDOM which leaves us with
+ * 0 and 1 respectively.
+ *
+ * In the native host stack we have 4 values.
+ *  - Public = 0
+ *  - Random = 1
+ *  - Public ID = 2
+ *  - Random ID = 3
+ *
+ * So we should really only need to do it for Random = 1.
+ *
+ * The app layer won't know the ID address since it didn't see it when it
+ * was scanning.
+ *
+ * Two possible CUJ:
+ * 1. app scans, finds RPA, bonds. App will only know RPA (pseudo address)
+ * 2. app knows the (preshared) ID address (e.g. DCK+OOB+IRK)
+ *
+ * We cannot lock it to RANDOM here otherwise we break CUJ #1.
+ *
+ * Thus, we must always try to do the conversion.
+ */
+ btm_random_pseudo_to_identity_addr(&addr.bda, &addr.type);
LOG(INFO) << __func__
<< " Adding scan filter with peer address: " << addr.bda;
BDADDR_TO_STREAM(p, addr.bda);
+ /*
+ * DANGER: Thar be dragons!
+ *
+ * The vendor command (APCF Filtering 0x0157) takes Public (0) or Random (1)
+ * or Any (2).
+ *
+ * Advertising results have four types:
+ *  - Public = 0
+ *  - Random = 1
+ *  - Public ID = 2
+ *  - Random ID = 3
+ *
+ * e.g. specifying PUBLIC (0) will only return results with a public
+ * address. It will ignore resolved addresses, since they return PUBLIC
+ * IDENTITY (2). For this, Any (0x02) must be specified. This should also
+ * cover if the RPA is derived from RANDOM STATIC.
+ */
+ /* ALWAYS FORCE 2 for this vendor command! */
+ addr.type = 0x02; // Really, you will break scanning if you change this.
UINT8_TO_STREAM(p, addr.type);
}
diff --git a/system/stack/btm/btm_ble_gap.cc b/system/stack/btm/btm_ble_gap.cc
index 694bcde..bf7439e 100644
--- a/system/stack/btm/btm_ble_gap.cc
+++ b/system/stack/btm/btm_ble_gap.cc
@@ -176,7 +176,8 @@
uint8_t secondary_phy,
uint8_t advertising_sid, int8_t tx_power,
int8_t rssi, uint16_t periodic_adv_int,
- uint8_t data_len, const uint8_t* data);
+ uint8_t data_len, const uint8_t* data,
+ const RawAddress& original_bda);
static uint8_t btm_set_conn_mode_adv_init_addr(RawAddress& p_peer_addr_ptr,
tBLE_ADDR_TYPE* p_peer_addr_type,
tBLE_ADDR_TYPE* p_own_addr_type);
@@ -1847,13 +1848,17 @@
rssi);
}
+ // Store this to pass up the callback chain to GattService#onScanResult for
+ // the check in ScanFilter#matches
+ RawAddress original_bda = bda;
+
if (addr_type != BLE_ADDR_ANONYMOUS) {
btm_ble_process_adv_addr(bda, &addr_type);
}
- btm_ble_process_adv_pkt_cont(event_type, addr_type, bda, primary_phy,
- secondary_phy, advertising_sid, tx_power, rssi,
- periodic_adv_int, pkt_data_len, pkt_data);
+ btm_ble_process_adv_pkt_cont(
+ event_type, addr_type, bda, primary_phy, secondary_phy, advertising_sid,
+ tx_power, rssi, periodic_adv_int, pkt_data_len, pkt_data, original_bda);
}
}
@@ -1902,6 +1907,10 @@
pkt_data_len, rssi);
}
+ // Pass up the address to GattService#onScanResult to use in
+ // ScanFilter#matches
+ RawAddress original_bda = bda;
+
btm_ble_process_adv_addr(bda, &addr_type);
uint16_t event_type;
@@ -1933,7 +1942,7 @@
btm_ble_process_adv_pkt_cont(
event_type, addr_type, bda, PHY_LE_1M, PHY_LE_NO_PACKET, NO_ADI_PRESENT,
TX_POWER_NOT_PRESENT, rssi, 0x00 /* no periodic adv */, pkt_data_len,
- pkt_data);
+ pkt_data, original_bda);
}
}
@@ -1946,7 +1955,8 @@
uint8_t secondary_phy,
uint8_t advertising_sid, int8_t tx_power,
int8_t rssi, uint16_t periodic_adv_int,
- uint8_t data_len, const uint8_t* data) {
+ uint8_t data_len, const uint8_t* data,
+ const RawAddress& original_bda) {
tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;
bool update = true;
@@ -2071,6 +2081,9 @@
const_cast<uint8_t*>(adv_data.data()), adv_data.size());
}
+ // Pass address up to GattService#onScanResult
+ p_i->inq_info.results.original_bda = original_bda;
+
tBTM_INQ_RESULTS_CB* p_obs_results_cb = btm_cb.ble_ctr_cb.p_obs_results_cb;
if (p_obs_results_cb && (result & BTM_BLE_OBS_RESULT)) {
(p_obs_results_cb)((tBTM_INQ_RESULTS*)&p_i->inq_info.results,
diff --git a/system/stack/btm/btm_dev.cc b/system/stack/btm/btm_dev.cc
index 95f43e4..18f05a2 100644
--- a/system/stack/btm/btm_dev.cc
+++ b/system/stack/btm/btm_dev.cc
@@ -62,8 +62,9 @@
*
******************************************************************************/
bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
- BD_NAME bd_name, uint8_t* features, LinkKey* p_link_key,
- uint8_t key_type, uint8_t pin_length) {
+ const BD_NAME& bd_name, uint8_t* features,
+ LinkKey* p_link_key, uint8_t key_type,
+ uint8_t pin_length) {
tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
if (!p_dev_rec) {
p_dev_rec = btm_sec_allocate_dev_rec();
diff --git a/system/stack/btm/btm_dev.h b/system/stack/btm/btm_dev.h
index fefa628..9da2e89 100644
--- a/system/stack/btm/btm_dev.h
+++ b/system/stack/btm/btm_dev.h
@@ -21,27 +21,6 @@
#include "stack/include/bt_octets.h"
#include "types/raw_address.h"
-/*******************************************************************************
- *
- * Function BTM_SecAddDevice
- *
- * Description Add/modify device. This function will be normally called
- * during host startup to restore all required information
- * stored in the NVRAM.
- *
- * Parameters: bd_addr - BD address of the peer
- * dev_class - Device Class
- * bd_name - Name of the peer device. NULL if unknown.
- * features - Remote device's features (up to 3 pages).
- * NULL if not known
- * link_key - Connection link key. NULL if unknown.
- *
- * Returns true if added OK, else false
- *
- ******************************************************************************/
-bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
- BD_NAME bd_name, uint8_t* features, LinkKey* p_link_key,
- uint8_t key_type, uint8_t pin_length);
void wipe_secrets_and_remove(tBTM_SEC_DEV_REC* p_dev_rec);
/** Free resources associated with the device associated with |bd_addr| address.
diff --git a/system/stack/btm/btm_iso_impl.h b/system/stack/btm/btm_iso_impl.h
index 2d5fc41..623696a 100644
--- a/system/stack/btm/btm_iso_impl.h
+++ b/system/stack/btm/btm_iso_impl.h
@@ -60,8 +60,9 @@
};
struct iso_sync_info sync_info;
- uint8_t state_flags;
+ std::atomic_uint8_t state_flags;
uint32_t sdu_itv;
+ std::atomic_uint16_t used_credits;
};
typedef iso_base iso_cis;
@@ -121,11 +122,14 @@
STREAM_TO_UINT16(conn_handle, stream);
evt.conn_handles.push_back(conn_handle);
- conn_hdl_to_cis_map_[conn_handle] = std::unique_ptr<iso_cis>(
- new iso_cis({.sync_info = {.first_sync_ts = 0, .seq_nb = 0},
- .cig_id = cig_id,
- .state_flags = kStateFlagsNone,
- .sdu_itv = sdu_itv_mtos}));
+
+ auto cis = std::unique_ptr<iso_cis>(new iso_cis());
+ cis->cig_id = cig_id;
+ cis->sdu_itv = sdu_itv_mtos;
+ cis->sync_info = {.first_sync_ts = 0, .seq_nb = 0};
+ cis->used_credits = 0;
+ cis->state_flags = kStateFlagsNone;
+ conn_hdl_to_cis_map_[conn_handle] = std::move(cis);
}
}
@@ -434,6 +438,7 @@
}
iso_credits_--;
+ iso->used_credits++;
BT_HDR* packet =
prepare_ts_hci_packet(iso_handle, ts, iso->sync_info.seq_nb, data_len);
@@ -495,6 +500,11 @@
cig_callbacks_->OnCisEvent(kIsoEventCisDisconnected, &evt);
cis->state_flags &= ~kStateFlagIsConnected;
+
+ /* return used credits */
+ iso_credits_ += cis->used_credits;
+ cis->used_credits = 0;
+
/* Data path is considered still valid, but can be reconfigured only once
* CIS is reestablished.
*/
@@ -514,20 +524,35 @@
STREAM_TO_UINT16(handle, p);
STREAM_TO_UINT16(num_sent, p);
- if ((conn_hdl_to_cis_map_.find(handle) == conn_hdl_to_cis_map_.end()) &&
- (conn_hdl_to_bis_map_.find(handle) == conn_hdl_to_bis_map_.end()))
+ auto iter = conn_hdl_to_cis_map_.find(handle);
+ if (iter != conn_hdl_to_cis_map_.end()) {
+ iter->second->used_credits -= num_sent;
+ iso_credits_ += num_sent;
continue;
+ }
- iso_credits_ += num_sent;
+ iter = conn_hdl_to_bis_map_.find(handle);
+ if (iter != conn_hdl_to_bis_map_.end()) {
+ iter->second->used_credits -= num_sent;
+ iso_credits_ += num_sent;
+ continue;
+ }
}
}
void handle_gd_num_completed_pkts(uint16_t handle, uint16_t credits) {
- if ((conn_hdl_to_cis_map_.find(handle) == conn_hdl_to_cis_map_.end()) &&
- (conn_hdl_to_bis_map_.find(handle) == conn_hdl_to_bis_map_.end()))
+ auto iter = conn_hdl_to_cis_map_.find(handle);
+ if (iter != conn_hdl_to_cis_map_.end()) {
+ iter->second->used_credits -= credits;
+ iso_credits_ += credits;
return;
+ }
- iso_credits_ += credits;
+ iter = conn_hdl_to_bis_map_.find(handle);
+ if (iter != conn_hdl_to_bis_map_.end()) {
+ iter->second->used_credits -= credits;
+ iso_credits_ += credits;
+ }
}
void process_create_big_cmpl_pkt(uint8_t len, uint8_t* data) {
@@ -563,11 +588,13 @@
LOG_INFO(" received BIS conn_hdl %d", +conn_handle);
if (evt.status == HCI_SUCCESS) {
- conn_hdl_to_bis_map_[conn_handle] = std::unique_ptr<iso_bis>(
- new iso_bis({.sync_info = {.first_sync_ts = ts, .seq_nb = 0},
- .big_handle = evt.big_id,
- .state_flags = kStateFlagIsBroadcast,
- .sdu_itv = last_big_create_req_sdu_itv_}));
+ auto bis = std::unique_ptr<iso_bis>(new iso_bis());
+ bis->big_handle = evt.big_id;
+ bis->sdu_itv = last_big_create_req_sdu_itv_;
+ bis->sync_info = {.first_sync_ts = ts, .seq_nb = 0};
+ bis->used_credits = 0;
+ bis->state_flags = kStateFlagIsBroadcast;
+ conn_hdl_to_bis_map_[conn_handle] = std::move(bis);
}
}
diff --git a/system/stack/btm/neighbor_inquiry.h b/system/stack/btm/neighbor_inquiry.h
index eb9a459..be8231f 100644
--- a/system/stack/btm/neighbor_inquiry.h
+++ b/system/stack/btm/neighbor_inquiry.h
@@ -118,6 +118,7 @@
uint16_t ble_periodic_adv_int;
uint8_t flag;
bool include_rsi;
+ RawAddress original_bda;
} tBTM_INQ_RESULTS;
/****************************************
diff --git a/system/stack/btm/security_device_record.h b/system/stack/btm/security_device_record.h
index a9d5f01..06639c0 100644
--- a/system/stack/btm/security_device_record.h
+++ b/system/stack/btm/security_device_record.h
@@ -224,7 +224,7 @@
private:
friend bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
- BD_NAME bd_name, uint8_t* features,
+ const BD_NAME& bd_name, uint8_t* features,
LinkKey* p_link_key, uint8_t key_type,
uint8_t pin_length);
friend void BTM_PINCodeReply(const RawAddress& bd_addr, tBTM_STATUS res,
diff --git a/system/stack/include/btm_api.h b/system/stack/include/btm_api.h
index 522e05c..29b2679 100644
--- a/system/stack/include/btm_api.h
+++ b/system/stack/include/btm_api.h
@@ -613,8 +613,8 @@
*
******************************************************************************/
bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
- BD_NAME bd_name, uint8_t* features, LinkKey* link_key,
- uint8_t key_type, uint8_t pin_length);
+ const BD_NAME& bd_name, uint8_t* features,
+ LinkKey* link_key, uint8_t key_type, uint8_t pin_length);
/** Free resources associated with the device associated with |bd_addr| address.
*
diff --git a/system/stack/include/btm_client_interface.h b/system/stack/include/btm_client_interface.h
index cca01e4..dc41cc7 100644
--- a/system/stack/include/btm_client_interface.h
+++ b/system/stack/include/btm_client_interface.h
@@ -120,7 +120,7 @@
struct {
bool (*BTM_SecAddDevice)(const RawAddress& bd_addr, DEV_CLASS dev_class,
- BD_NAME bd_name, uint8_t* features,
+ const BD_NAME& bd_name, uint8_t* features,
LinkKey* link_key, uint8_t key_type,
uint8_t pin_length);
bool (*BTM_SecAddRmtNameNotifyCallback)(tBTM_RMT_NAME_CALLBACK* p_callback);
diff --git a/system/stack/test/btm_iso_test.cc b/system/stack/test/btm_iso_test.cc
index 0181387..94e77fc 100644
--- a/system/stack/test/btm_iso_test.cc
+++ b/system/stack/test/btm_iso_test.cc
@@ -2059,6 +2059,46 @@
}
}
+TEST_F(IsoManagerTest, SendIsoDataCreditsReturnedByDisconnection) {
+ uint8_t num_buffers = controller_interface_.GetIsoBufferCount();
+ std::vector<uint8_t> data_vec(108, 0);
+
+ // Check on CIG
+ IsoManager::GetInstance()->CreateCig(
+ volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
+
+ bluetooth::hci::iso_manager::cis_establish_params params;
+ for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+ params.conn_pairs.push_back({handle, 1});
+ }
+ IsoManager::GetInstance()->EstablishCis(params);
+
+ for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
+ IsoManager::GetInstance()->SetupIsoDataPath(handle,
+ kDefaultIsoDataPathParams);
+ }
+
+ /* Sending lot of ISO data to first ISO and getting all the credits */
+ EXPECT_CALL(bte_interface_, HciSend).Times(num_buffers).RetiresOnSaturation();
+ for (uint8_t i = 0; i < num_buffers; i++) {
+ IsoManager::GetInstance()->SendIsoData(
+ volatile_test_cig_create_cmpl_evt_.conn_handles[0], data_vec.data(),
+ data_vec.size());
+ }
+
+ /* Return all credits by disconnecting CIS */
+ IsoManager::GetInstance()->HandleDisconnect(
+ volatile_test_cig_create_cmpl_evt_.conn_handles[0], 16);
+
+ /* Try to send ISO data on the second ISO. Expect credits being available.*/
+ EXPECT_CALL(bte_interface_, HciSend).Times(num_buffers).RetiresOnSaturation();
+ for (uint8_t i = 0; i < num_buffers; i++) {
+ IsoManager::GetInstance()->SendIsoData(
+ volatile_test_cig_create_cmpl_evt_.conn_handles[1], data_vec.data(),
+ data_vec.size());
+ }
+}
+
TEST_F(IsoManagerDeathTest, SendIsoDataWithNoDataPath) {
std::vector<uint8_t> data_vec(108, 0);
diff --git a/system/test/mock/mock_bluetooth_interface.cc b/system/test/mock/mock_bluetooth_interface.cc
index c778c16..8f1423e 100644
--- a/system/test/mock/mock_bluetooth_interface.cc
+++ b/system/test/mock/mock_bluetooth_interface.cc
@@ -145,6 +145,10 @@
static int set_dynamic_audio_buffer_size(int codec, int size) { return 0; }
+static bool allow_low_latency_audio(bool allowed, const RawAddress& address) {
+ return true;
+}
+
EXPORT_SYMBOL bt_interface_t bluetoothInterface = {
sizeof(bluetoothInterface),
init,
@@ -183,7 +187,8 @@
obfuscate_address,
get_metric_id,
set_dynamic_audio_buffer_size,
- generate_local_oob_data};
+ generate_local_oob_data,
+ allow_low_latency_audio};
// callback reporting helpers
diff --git a/system/test/mock/mock_main_shim_btm_api.cc b/system/test/mock/mock_main_shim_btm_api.cc
index 33f9672..f0223d2 100644
--- a/system/test/mock/mock_main_shim_btm_api.cc
+++ b/system/test/mock/mock_main_shim_btm_api.cc
@@ -87,7 +87,8 @@
return false;
}
bool bluetooth::shim::BTM_SecAddDevice(const RawAddress& bd_addr,
- DEV_CLASS dev_class, BD_NAME bd_name,
+ DEV_CLASS dev_class,
+ const BD_NAME& bd_name,
uint8_t* features, LinkKey* link_key,
uint8_t key_type, uint8_t pin_length) {
mock_function_count_map[__func__]++;
diff --git a/system/test/mock/mock_stack_btm_ble_gap.cc b/system/test/mock/mock_stack_btm_ble_gap.cc
index 9ea33b9..11f8507 100644
--- a/system/test/mock/mock_stack_btm_ble_gap.cc
+++ b/system/test/mock/mock_stack_btm_ble_gap.cc
@@ -156,7 +156,8 @@
uint8_t secondary_phy,
uint8_t advertising_sid, int8_t tx_power,
int8_t rssi, uint16_t periodic_adv_int,
- uint8_t data_len, const uint8_t* data) {
+ uint8_t data_len, const uint8_t* data,
+ const RawAddress& original_bda) {
mock_function_count_map[__func__]++;
}
void btm_ble_process_adv_pkt_cont_for_inquiry(
diff --git a/system/test/mock/mock_stack_btm_dev.cc b/system/test/mock/mock_stack_btm_dev.cc
index 928e83a..a01217b 100644
--- a/system/test/mock/mock_stack_btm_dev.cc
+++ b/system/test/mock/mock_stack_btm_dev.cc
@@ -44,8 +44,9 @@
#endif
bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
- BD_NAME bd_name, uint8_t* features, LinkKey* p_link_key,
- uint8_t key_type, uint8_t pin_length) {
+ const BD_NAME& bd_name, uint8_t* features,
+ LinkKey* p_link_key, uint8_t key_type,
+ uint8_t pin_length) {
mock_function_count_map[__func__]++;
return false;
}
diff --git a/system/tools/scripts/dump_le_audio.py b/system/tools/scripts/dump_le_audio.py
index 6c41df1..aaf622c 100755
--- a/system/tools/scripts/dump_le_audio.py
+++ b/system/tools/scripts/dump_le_audio.py
@@ -117,6 +117,7 @@
packet_number = 0
debug_enable = False
add_header = False
+ase_handle = 0xFFFF
class Connection:
@@ -231,6 +232,10 @@
def parse_att_write_cmd(packet, connection_handle, timestamp):
attribute_handle, packet = unpack_data(packet, 2, False)
+ global ase_handle
+ if ase_handle != 0xFFFF:
+ connection_map[connection_handle].ase_handle = ase_handle
+
if connection_map[connection_handle].ase_handle == attribute_handle:
if debug_enable:
debug_print("Action with ASE Control point")
@@ -497,18 +502,26 @@
"--header",
help="Add the header for LC3 Conformance Interoperability Test Software V.1.0.3.",
action="store_true")
+ parser.add_argument(
+ "--ase_handle",
+ help="Set the ASE handle manually.",
+ type=int)
argv = parser.parse_args()
BTSNOOP_FILE_NAME = argv.btsnoop_file
global debug_enable
global add_header
+ global ase_handle
if argv.verbose:
debug_enable = True
if argv.header:
add_header = True
+ if argv.ase_handle:
+ ase_handle = int(argv.ase_handle)
+
with open(BTSNOOP_FILE_NAME, "rb") as btsnoop_file:
if btsnoop_file.read(16) != BTSNOOP_HEADER:
print("Invalid btsnoop header")