summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--android/app/aidl/android/bluetooth/IBluetoothLeBroadcastAssistant.aidl2
-rw-r--r--android/app/src/com/android/bluetooth/bass_client/BassClientService.java33
-rw-r--r--android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java8
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java34
-rw-r--r--framework/api/system-current.txt1
-rw-r--r--framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java50
-rw-r--r--system/btif/include/btif_bqr.h6
-rw-r--r--system/gd/hci/acl_manager/classic_impl.h10
8 files changed, 139 insertions, 5 deletions
diff --git a/android/app/aidl/android/bluetooth/IBluetoothLeBroadcastAssistant.aidl b/android/app/aidl/android/bluetooth/IBluetoothLeBroadcastAssistant.aidl
index bf95bb5cee..54b947c061 100644
--- a/android/app/aidl/android/bluetooth/IBluetoothLeBroadcastAssistant.aidl
+++ b/android/app/aidl/android/bluetooth/IBluetoothLeBroadcastAssistant.aidl
@@ -60,4 +60,6 @@ interface IBluetoothLeBroadcastAssistant {
List<BluetoothLeBroadcastReceiveState> getAllSources(in BluetoothDevice sink, in AttributionSource source);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
int getMaximumSourceCapacity(in BluetoothDevice sink, in AttributionSource source);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ BluetoothLeBroadcastMetadata getSourceMetadata(in BluetoothDevice sink, in int sourceId, in AttributionSource source);
}
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
index 7a0389ad2c..957b02c322 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -23,6 +23,7 @@ import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask;
import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState;
+import static com.android.bluetooth.flags.Flags.leaudioBroadcastApiGetLocalMetadata;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastResyncHelper;
@@ -3273,6 +3274,27 @@ public class BassClientService extends ProfileService {
return stateMachine.getMaximumSourceCapacity();
}
+ /**
+ * Get metadata of source that stored on this Broadcast Sink
+ *
+ * @param sink Broadcast Sink device
+ * @param sourceId Broadcast source id
+ * @return metadata of source that stored on this Broadcast Sink
+ */
+ BluetoothLeBroadcastMetadata getSourceMetadata(BluetoothDevice sink, int sourceId) {
+ if (!leaudioBroadcastApiGetLocalMetadata()) {
+ return null;
+ }
+
+ log("getSourceMetadata: device = " + sink + " with source id = " + sourceId);
+ BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);
+ if (stateMachine == null) {
+ log("stateMachine is null");
+ return null;
+ }
+ return stateMachine.getCurrentBroadcastMetadata(sourceId);
+ }
+
private boolean isLocalBroadcast(int broadcastId) {
LeAudioService leAudioService = mServiceFactory.getLeAudioService();
if (leAudioService == null) {
@@ -4630,5 +4652,16 @@ public class BassClientService extends ProfileService {
}
return service.getMaximumSourceCapacity(sink);
}
+
+ @Override
+ public BluetoothLeBroadcastMetadata getSourceMetadata(
+ BluetoothDevice sink, int sourceId, AttributionSource source) {
+ BassClientService service = getServiceAndEnforceConnect(source);
+ if (service == null) {
+ Log.e(TAG, "Service is null");
+ return null;
+ }
+ return service.getSourceMetadata(sink, sourceId);
+ }
}
}
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java b/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
index b59ff2fcd2..021b73289b 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
@@ -376,10 +376,6 @@ class BassClientStateMachine extends StateMachine {
return mPendingSourceToSwitch != null;
}
- BluetoothLeBroadcastMetadata getCurrentBroadcastMetadata(Integer sourceId) {
- return mCurrentMetadata.getOrDefault(sourceId, null);
- }
-
private void setCurrentBroadcastMetadata(
Integer sourceId, BluetoothLeBroadcastMetadata metadata) {
if (metadata != null) {
@@ -2619,6 +2615,10 @@ class BassClientStateMachine extends StateMachine {
return mNumOfBroadcastReceiverStates;
}
+ BluetoothLeBroadcastMetadata getCurrentBroadcastMetadata(Integer sourceId) {
+ return mCurrentMetadata.getOrDefault(sourceId, null);
+ }
+
BluetoothDevice getDevice() {
return mDevice;
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
index 12a0f7d9ef..e6e9f40cae 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
@@ -2039,6 +2039,40 @@ public class BassClientServiceTest {
}
}
+ @Test
+ @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_API_GET_LOCAL_METADATA)
+ public void testGetSourceMetadata() {
+ prepareConnectedDeviceGroup();
+ startSearchingForSources();
+ onScanResult(mSourceDevice, TEST_BROADCAST_ID);
+ onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
+ BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
+
+ for (BassClientStateMachine sm : mStateMachines.values()) {
+ injectRemoteSourceStateSourceAdded(
+ sm,
+ meta,
+ TEST_SOURCE_ID,
+ BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
+ meta.isEncrypted()
+ ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+ : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+ null);
+ doReturn(null).when(sm).getCurrentBroadcastMetadata(eq(TEST_SOURCE_ID));
+ assertThat(mBassClientService.getSourceMetadata(sm.getDevice(), TEST_SOURCE_ID))
+ .isEqualTo(null);
+
+ doReturn(meta).when(sm).getCurrentBroadcastMetadata(eq(TEST_SOURCE_ID));
+ doReturn(true).when(sm).isSyncedToTheSource(eq(TEST_SOURCE_ID));
+ assertThat(mBassClientService.getSourceMetadata(sm.getDevice(), TEST_SOURCE_ID))
+ .isEqualTo(meta);
+ }
+
+ for (BassClientStateMachine sm : mStateMachines.values()) {
+ injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID);
+ }
+ }
+
/** Test whether the group operation flag is set on addSource() and removed on removeSource */
@Test
public void testGroupStickyFlagSetUnset() {
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index ebc0542421..cd02d1805e 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -597,6 +597,7 @@ package android.bluetooth {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getMaximumSourceCapacity(@NonNull android.bluetooth.BluetoothDevice);
+ method @FlaggedApi("com.android.bluetooth.flags.leaudio_broadcast_api_get_local_metadata") @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothLeBroadcastMetadata getSourceMetadata(@NonNull android.bluetooth.BluetoothDevice, @IntRange(from=0, to=255) int);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isSearchInProgress();
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void modifySource(@NonNull android.bluetooth.BluetoothDevice, int, @NonNull android.bluetooth.BluetoothLeBroadcastMetadata);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothLeBroadcastAssistant.Callback);
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java
index 28b4e779ee..829ee2f301 100644
--- a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java
+++ b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java
@@ -21,7 +21,9 @@ import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.Manifest.permission.BLUETOOTH_SCAN;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresNoPermission;
@@ -40,6 +42,8 @@ import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.bluetooth.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -1205,6 +1209,52 @@ public final class BluetoothLeBroadcastAssistant implements BluetoothProfile, Au
return defaultValue;
}
+ /**
+ * Gets the {@link BluetoothLeBroadcastMetadata} of a specified source added to this Broadcast
+ * Sink.
+ *
+ * <p>This method retrieves the {@link BluetoothLeBroadcastMetadata} associated with the
+ * specified source. The metadata is obtained from the {@link BluetoothLeBroadcastReceiveState}
+ * maintained by this Broadcast Sink. If no matching metadata is found, this method returns
+ * {@code null}.
+ *
+ * <p>The source's {@link BluetoothLeBroadcastMetadata} is initially set by {@link
+ * #addSource(BluetoothDevice, BluetoothLeBroadcastMetadata, boolean)} and can be updated with
+ * {@link #modifySource(BluetoothDevice, int, BluetoothLeBroadcastMetadata)}.
+ *
+ * @param sink Broadcast Sink device
+ * @param sourceId Broadcast source id. Valid range is [0, 0xFF] as defined in the Broadcast
+ * Audio Scan Service 1.0 specification (section 3.2).
+ * @return metadata {@link BluetoothLeBroadcastMetadata} associated with the specified source.
+ * @throws IllegalArgumentException if sourceID is not [0, 0xFF].
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_LEAUDIO_BROADCAST_API_GET_LOCAL_METADATA)
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
+ public @Nullable BluetoothLeBroadcastMetadata getSourceMetadata(
+ @NonNull BluetoothDevice sink, @IntRange(from = 0x00, to = 0xFF) int sourceId) {
+ log("getSourceMetadata()");
+ Objects.requireNonNull(sink, "sink cannot be null");
+ if (sourceId < 0x00 || sourceId > 0xFF) {
+ throw new IllegalArgumentException(
+ "sourceId " + sourceId + " does not fall between 0x00 and 0xFF");
+ }
+ final IBluetoothLeBroadcastAssistant service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mBluetoothAdapter.isEnabled()) {
+ try {
+ return service.getSourceMetadata(sink, sourceId, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
+ }
+ return null;
+ }
+
private static void log(@NonNull String msg) {
if (DBG) {
Log.d(TAG, msg);
diff --git a/system/btif/include/btif_bqr.h b/system/btif/include/btif_bqr.h
index 71105ef903..909335b411 100644
--- a/system/btif/include/btif_bqr.h
+++ b/system/btif/include/btif_bqr.h
@@ -88,6 +88,8 @@ static constexpr uint32_t kQualityEventMaskLeAudioChoppy = 0x1 << 6;
static constexpr uint32_t kQualityEventMaskConnectFail = 0x1 << 7;
static constexpr uint32_t kQualityEventMaskAdvRFStatsEvent = 0x1 << 8;
static constexpr uint32_t kQualityEventMaskAdvRFStatsMonitor = 0x1 << 9;
+static constexpr uint32_t kQualityEventMaskHealthMonitorStatsEvent = 0x1 << 10;
+static constexpr uint32_t kQualityEventMaskControllerHealthMonitor = 0x1 << 11;
static constexpr uint32_t kQualityEventMaskVendorSpecificQuality = 0x1 << 15;
static constexpr uint32_t kQualityEventMaskLmpMessageTrace = 0x1 << 16;
static constexpr uint32_t kQualityEventMaskBtSchedulingTrace = 0x1 << 17;
@@ -99,6 +101,7 @@ static constexpr uint32_t kQualityEventMaskAll =
kQualityEventMaskRootInflammation | kQualityEventMaskEnergyMonitoring |
kQualityEventMaskLeAudioChoppy | kQualityEventMaskConnectFail |
kQualityEventMaskAdvRFStatsEvent | kQualityEventMaskAdvRFStatsMonitor |
+ kQualityEventMaskHealthMonitorStatsEvent | kQualityEventMaskControllerHealthMonitor |
kQualityEventMaskVendorSpecificQuality | kQualityEventMaskLmpMessageTrace |
kQualityEventMaskBtSchedulingTrace | kQualityEventMaskControllerDbgInfo |
kQualityEventMaskVendorSpecificTrace;
@@ -132,6 +135,7 @@ static constexpr uint8_t kLogDumpParamTotalLen = 3;
static constexpr uint8_t kVersion5_0ParamsTotalLen = 7;
// Added in BQR V6.0
static constexpr uint8_t kVersion6_0ParamsTotalLen = 6;
+
// Warning criteria of the RSSI value.
static constexpr int8_t kCriWarnRssi = -80;
// Warning criteria of the unused AFH channel count.
@@ -176,7 +180,7 @@ static constexpr uint16_t kBqrVndLogVersion = 0x102;
static constexpr uint16_t kBqrVersion5_0 = 0x103;
// The REPORT_ACTION_QUERY and BQR_Report_interval starting v1.04(260)
static constexpr uint16_t kBqrVersion6_0 = 0x104;
-
+static constexpr uint16_t kBqrVersion7_0 = 0x105;
// Action definition
//
// Action to Add, Delete or Clear the reporting of quality event(s).
diff --git a/system/gd/hci/acl_manager/classic_impl.h b/system/gd/hci/acl_manager/classic_impl.h
index ca0baf7417..08cdcbd231 100644
--- a/system/gd/hci/acl_manager/classic_impl.h
+++ b/system/gd/hci/acl_manager/classic_impl.h
@@ -624,6 +624,11 @@ public:
auto view = ReadRemoteSupportedFeaturesCompleteView::Create(packet);
log::assert_that(view.IsValid(), "Read remote supported features packet invalid");
uint16_t handle = view.GetConnectionHandle();
+ auto status = view.GetStatus();
+ if (status != ErrorCode::SUCCESS) {
+ log::error("handle:{} status:{}", handle, ErrorCodeText(status));
+ return;
+ }
bluetooth::os::LogMetricBluetoothRemoteSupportedFeatures(connections.get_address(handle), 0,
view.GetLmpFeatures(), handle);
connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
@@ -635,6 +640,11 @@ public:
auto view = ReadRemoteExtendedFeaturesCompleteView::Create(packet);
log::assert_that(view.IsValid(), "Read remote extended features packet invalid");
uint16_t handle = view.GetConnectionHandle();
+ auto status = view.GetStatus();
+ if (status != ErrorCode::SUCCESS) {
+ log::error("handle:{} status:{}", handle, ErrorCodeText(status));
+ return;
+ }
bluetooth::os::LogMetricBluetoothRemoteSupportedFeatures(connections.get_address(handle),
view.GetPageNumber(),
view.GetExtendedLmpFeatures(), handle);