Loading android/app/aidl/android/bluetooth/IBluetoothLeBroadcastAssistantCallback.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -40,4 +40,5 @@ interface IBluetoothLeBroadcastAssistantCallback { void onSourceRemoveFailed(in BluetoothDevice sink, in int sourceId, in int reason); void onReceiveStateChanged(in BluetoothDevice sink, in int sourceId, in BluetoothLeBroadcastReceiveState state); void onSourceLost(in int broadcastId); } android/app/src/com/android/bluetooth/bass_client/BassClientService.java +27 −2 Original line number Diff line number Diff line Loading @@ -56,6 +56,8 @@ import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.ServiceFactory; import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.csip.CsipSetCoordinatorService; import com.android.bluetooth.flags.FeatureFlags; import com.android.bluetooth.flags.FeatureFlagsImpl; import com.android.bluetooth.le_audio.LeAudioService; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -89,6 +91,7 @@ public class BassClientService extends ProfileService { private final Map<BluetoothDevice, List<Integer>> mGroupManagedSources = new ConcurrentHashMap<>(); private final Map<BluetoothDevice, List<Integer>> mActiveSourceMap = new ConcurrentHashMap<>(); private final FeatureFlags mFeatureFlags; private HandlerThread mStateMachinesThread; private HandlerThread mCallbackHandlerThread; Loading Loading @@ -122,6 +125,17 @@ public class BassClientService extends ProfileService { @VisibleForTesting ServiceFactory mServiceFactory = new ServiceFactory(); BassClientService() { mFeatureFlags = new FeatureFlagsImpl(); } @VisibleForTesting BassClientService(Context ctx, FeatureFlags featureFlags) { attachBaseContext(ctx); mFeatureFlags = featureFlags; onCreate(); } public static boolean isEnabled() { return BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false); } Loading Loading @@ -701,8 +715,10 @@ public class BassClientService extends ProfileService { return null; } log("Creating a new state machine for " + device); stateMachine = BassObjectsFactory.getInstance().makeStateMachine( device, this, mStateMachinesThread.getLooper()); stateMachine = BassObjectsFactory.getInstance() .makeStateMachine( device, this, mStateMachinesThread.getLooper(), mFeatureFlags); mStateMachines.put(device, stateMachine); return stateMachine; } Loading Loading @@ -1536,6 +1552,7 @@ public class BassClientService extends ProfileService { private static final int MSG_SOURCE_REMOVED = 10; private static final int MSG_SOURCE_REMOVED_FAILED = 11; private static final int MSG_RECEIVESTATE_CHANGED = 12; private static final int MSG_SOURCE_LOST = 13; private final RemoteCallbackList<IBluetoothLeBroadcastAssistantCallback> mCallbacks = new RemoteCallbackList<>(); Loading Loading @@ -1661,6 +1678,9 @@ public class BassClientService extends ProfileService { (BluetoothLeBroadcastReceiveState) param.mObj2; callback.onReceiveStateChanged(sink, sourceId, state); break; case MSG_SOURCE_LOST: callback.onSourceLost(sourceId); break; default: Log.e(TAG, "Invalid msg: " + msg.what); break; Loading Loading @@ -1810,6 +1830,11 @@ public class BassClientService extends ProfileService { + subgroupState); obtainMessage(MSG_RECEIVESTATE_CHANGED, 0, sourceId, param).sendToTarget(); } void notifySourceLost(int broadcastId) { sEventLogger.logd(TAG, "notifySourceLost: " + ", broadcastId: " + broadcastId); obtainMessage(MSG_SOURCE_LOST, 0, broadcastId).sendToTarget(); } } @Override Loading android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java +27 −7 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ import com.android.bluetooth.BluetoothMethodProxy; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.ServiceFactory; import com.android.bluetooth.flags.FeatureFlags; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.State; import com.android.internal.util.StateMachine; Loading @@ -70,6 +71,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Scanner; import java.util.UUID; import java.util.stream.IntStream; Loading Loading @@ -126,6 +128,8 @@ public class BassClientStateMachine extends StateMachine { private final Connected mConnected = new Connected(); private final Connecting mConnecting = new Connecting(); private final ConnectedProcessing mConnectedProcessing = new ConnectedProcessing(); private final FeatureFlags mFeatureFlags; @VisibleForTesting final List<BluetoothGattCharacteristic> mBroadcastCharacteristics = new ArrayList<BluetoothGattCharacteristic>(); Loading Loading @@ -175,15 +179,20 @@ public class BassClientStateMachine extends StateMachine { @VisibleForTesting BluetoothGattTestableWrapper mBluetoothGatt = null; BluetoothGattCallback mGattCallback = null; PeriodicAdvertisingCallback mLocalPeriodicAdvCallback = new PACallback(); @VisibleForTesting PeriodicAdvertisingCallback mLocalPeriodicAdvCallback = new PACallback(); int mMaxSingleAttributeWriteValueLen = 0; BassClientStateMachine(BluetoothDevice device, BassClientService svc, Looper looper, int connectTimeoutMs) { BassClientStateMachine( BluetoothDevice device, BassClientService svc, Looper looper, int connectTimeoutMs, FeatureFlags featureFlags) { super(TAG + "(" + device.toString() + ")", looper); mDevice = device; mService = svc; mConnectTimeoutMs = connectTimeoutMs; mFeatureFlags = Objects.requireNonNull(featureFlags, "Feature Flags cannot be null"); addState(mDisconnected); addState(mConnected); addState(mConnecting); Loading @@ -205,11 +214,15 @@ public class BassClientStateMachine extends StateMachine { Binder.restoreCallingIdentity(token); } static BassClientStateMachine make(BluetoothDevice device, BassClientService svc, Looper looper) { static BassClientStateMachine make( BluetoothDevice device, BassClientService svc, Looper looper, FeatureFlags featureFlags) { Log.d(TAG, "make for device " + device); BassClientStateMachine BassclientSm = new BassClientStateMachine(device, svc, looper, BassConstants.CONNECT_TIMEOUT_MS); BassClientStateMachine BassclientSm = new BassClientStateMachine( device, svc, looper, BassConstants.CONNECT_TIMEOUT_MS, featureFlags); BassclientSm.start(); return BassclientSm; } Loading Loading @@ -1099,6 +1112,13 @@ public class BassClientStateMachine extends StateMachine { @Override public void onSyncLost(int syncHandle) { log("OnSyncLost" + syncHandle); if (mFeatureFlags.leaudioBroadcastMonitorSourceSyncStatus()) { int broadcastId = mService.getBroadcastIdForSyncHandle(syncHandle); if (broadcastId != BassConstants.INVALID_BROADCAST_ID) { log("Notify broadcast source lost, broadcast id: " + broadcastId); mService.getCallbacks().notifySourceLost(broadcastId); } } cancelActiveSync(syncHandle); } Loading android/app/src/com/android/bluetooth/bass_client/BassObjectsFactory.java +8 −3 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.os.Looper; import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.flags.FeatureFlags; import com.android.internal.annotations.VisibleForTesting; /** Loading Loading @@ -68,11 +69,15 @@ public class BassObjectsFactory { * @param device the remote device associated with this state machine * @param svc the bass client service * @param looper the thread that the state machine is supposed to run on * @param featureFlags feature flag from bass client service * @return a state machine that is initialized and started, ready to go */ public BassClientStateMachine makeStateMachine(BluetoothDevice device, BassClientService svc, Looper looper) { return BassClientStateMachine.make(device, svc, looper); public BassClientStateMachine makeStateMachine( BluetoothDevice device, BassClientService svc, Looper looper, FeatureFlags featureFlags) { return BassClientStateMachine.make(device, svc, looper, featureFlags); } /** Loading android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java +20 −11 Original line number Diff line number Diff line Loading @@ -223,15 +223,24 @@ public class BassClientServiceTest { }).when(mAdapterService).getBondedDevices(); // Mock methods in BassObjectsFactory doAnswer(invocation -> { doAnswer( invocation -> { assertThat(mCurrentDevice).isNotNull(); final BassClientStateMachine stateMachine = mock(BassClientStateMachine.class); final BassClientStateMachine stateMachine = mock(BassClientStateMachine.class); doReturn(new ArrayList<>()).when(stateMachine).getAllSources(); doReturn(TEST_NUM_SOURCES).when(stateMachine).getMaximumSourceCapacity(); doReturn((BluetoothDevice)invocation.getArgument(0)).when(stateMachine).getDevice(); mStateMachines.put((BluetoothDevice)invocation.getArgument(0), stateMachine); doReturn(TEST_NUM_SOURCES) .when(stateMachine) .getMaximumSourceCapacity(); doReturn((BluetoothDevice) invocation.getArgument(0)) .when(stateMachine) .getDevice(); mStateMachines.put( (BluetoothDevice) invocation.getArgument(0), stateMachine); return stateMachine; }).when(mObjectsFactory).makeStateMachine(any(), any(), any()); }) .when(mObjectsFactory) .makeStateMachine(any(), any(), any(), any()); doReturn(mBluetoothLeScannerWrapper).when(mObjectsFactory) .getBluetoothLeScannerWrapper(any()); Loading Loading @@ -337,8 +346,8 @@ public class BassClientServiceTest { mCurrentDevice = TestUtils.getTestDevice(mBluetoothAdapter, 0); assertThat(mBassClientService.connect(mCurrentDevice)).isTrue(); verify(mObjectsFactory).makeStateMachine( eq(mCurrentDevice), eq(mBassClientService), any()); verify(mObjectsFactory) .makeStateMachine(eq(mCurrentDevice), eq(mBassClientService), any(), any()); BassClientStateMachine stateMachine = mStateMachines.get(mCurrentDevice); assertThat(stateMachine).isNotNull(); verify(stateMachine).sendMessage(BassClientStateMachine.CONNECT); Loading Loading
android/app/aidl/android/bluetooth/IBluetoothLeBroadcastAssistantCallback.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -40,4 +40,5 @@ interface IBluetoothLeBroadcastAssistantCallback { void onSourceRemoveFailed(in BluetoothDevice sink, in int sourceId, in int reason); void onReceiveStateChanged(in BluetoothDevice sink, in int sourceId, in BluetoothLeBroadcastReceiveState state); void onSourceLost(in int broadcastId); }
android/app/src/com/android/bluetooth/bass_client/BassClientService.java +27 −2 Original line number Diff line number Diff line Loading @@ -56,6 +56,8 @@ import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.ServiceFactory; import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.csip.CsipSetCoordinatorService; import com.android.bluetooth.flags.FeatureFlags; import com.android.bluetooth.flags.FeatureFlagsImpl; import com.android.bluetooth.le_audio.LeAudioService; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -89,6 +91,7 @@ public class BassClientService extends ProfileService { private final Map<BluetoothDevice, List<Integer>> mGroupManagedSources = new ConcurrentHashMap<>(); private final Map<BluetoothDevice, List<Integer>> mActiveSourceMap = new ConcurrentHashMap<>(); private final FeatureFlags mFeatureFlags; private HandlerThread mStateMachinesThread; private HandlerThread mCallbackHandlerThread; Loading Loading @@ -122,6 +125,17 @@ public class BassClientService extends ProfileService { @VisibleForTesting ServiceFactory mServiceFactory = new ServiceFactory(); BassClientService() { mFeatureFlags = new FeatureFlagsImpl(); } @VisibleForTesting BassClientService(Context ctx, FeatureFlags featureFlags) { attachBaseContext(ctx); mFeatureFlags = featureFlags; onCreate(); } public static boolean isEnabled() { return BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false); } Loading Loading @@ -701,8 +715,10 @@ public class BassClientService extends ProfileService { return null; } log("Creating a new state machine for " + device); stateMachine = BassObjectsFactory.getInstance().makeStateMachine( device, this, mStateMachinesThread.getLooper()); stateMachine = BassObjectsFactory.getInstance() .makeStateMachine( device, this, mStateMachinesThread.getLooper(), mFeatureFlags); mStateMachines.put(device, stateMachine); return stateMachine; } Loading Loading @@ -1536,6 +1552,7 @@ public class BassClientService extends ProfileService { private static final int MSG_SOURCE_REMOVED = 10; private static final int MSG_SOURCE_REMOVED_FAILED = 11; private static final int MSG_RECEIVESTATE_CHANGED = 12; private static final int MSG_SOURCE_LOST = 13; private final RemoteCallbackList<IBluetoothLeBroadcastAssistantCallback> mCallbacks = new RemoteCallbackList<>(); Loading Loading @@ -1661,6 +1678,9 @@ public class BassClientService extends ProfileService { (BluetoothLeBroadcastReceiveState) param.mObj2; callback.onReceiveStateChanged(sink, sourceId, state); break; case MSG_SOURCE_LOST: callback.onSourceLost(sourceId); break; default: Log.e(TAG, "Invalid msg: " + msg.what); break; Loading Loading @@ -1810,6 +1830,11 @@ public class BassClientService extends ProfileService { + subgroupState); obtainMessage(MSG_RECEIVESTATE_CHANGED, 0, sourceId, param).sendToTarget(); } void notifySourceLost(int broadcastId) { sEventLogger.logd(TAG, "notifySourceLost: " + ", broadcastId: " + broadcastId); obtainMessage(MSG_SOURCE_LOST, 0, broadcastId).sendToTarget(); } } @Override Loading
android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java +27 −7 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ import com.android.bluetooth.BluetoothMethodProxy; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.ServiceFactory; import com.android.bluetooth.flags.FeatureFlags; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.State; import com.android.internal.util.StateMachine; Loading @@ -70,6 +71,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Scanner; import java.util.UUID; import java.util.stream.IntStream; Loading Loading @@ -126,6 +128,8 @@ public class BassClientStateMachine extends StateMachine { private final Connected mConnected = new Connected(); private final Connecting mConnecting = new Connecting(); private final ConnectedProcessing mConnectedProcessing = new ConnectedProcessing(); private final FeatureFlags mFeatureFlags; @VisibleForTesting final List<BluetoothGattCharacteristic> mBroadcastCharacteristics = new ArrayList<BluetoothGattCharacteristic>(); Loading Loading @@ -175,15 +179,20 @@ public class BassClientStateMachine extends StateMachine { @VisibleForTesting BluetoothGattTestableWrapper mBluetoothGatt = null; BluetoothGattCallback mGattCallback = null; PeriodicAdvertisingCallback mLocalPeriodicAdvCallback = new PACallback(); @VisibleForTesting PeriodicAdvertisingCallback mLocalPeriodicAdvCallback = new PACallback(); int mMaxSingleAttributeWriteValueLen = 0; BassClientStateMachine(BluetoothDevice device, BassClientService svc, Looper looper, int connectTimeoutMs) { BassClientStateMachine( BluetoothDevice device, BassClientService svc, Looper looper, int connectTimeoutMs, FeatureFlags featureFlags) { super(TAG + "(" + device.toString() + ")", looper); mDevice = device; mService = svc; mConnectTimeoutMs = connectTimeoutMs; mFeatureFlags = Objects.requireNonNull(featureFlags, "Feature Flags cannot be null"); addState(mDisconnected); addState(mConnected); addState(mConnecting); Loading @@ -205,11 +214,15 @@ public class BassClientStateMachine extends StateMachine { Binder.restoreCallingIdentity(token); } static BassClientStateMachine make(BluetoothDevice device, BassClientService svc, Looper looper) { static BassClientStateMachine make( BluetoothDevice device, BassClientService svc, Looper looper, FeatureFlags featureFlags) { Log.d(TAG, "make for device " + device); BassClientStateMachine BassclientSm = new BassClientStateMachine(device, svc, looper, BassConstants.CONNECT_TIMEOUT_MS); BassClientStateMachine BassclientSm = new BassClientStateMachine( device, svc, looper, BassConstants.CONNECT_TIMEOUT_MS, featureFlags); BassclientSm.start(); return BassclientSm; } Loading Loading @@ -1099,6 +1112,13 @@ public class BassClientStateMachine extends StateMachine { @Override public void onSyncLost(int syncHandle) { log("OnSyncLost" + syncHandle); if (mFeatureFlags.leaudioBroadcastMonitorSourceSyncStatus()) { int broadcastId = mService.getBroadcastIdForSyncHandle(syncHandle); if (broadcastId != BassConstants.INVALID_BROADCAST_ID) { log("Notify broadcast source lost, broadcast id: " + broadcastId); mService.getCallbacks().notifySourceLost(broadcastId); } } cancelActiveSync(syncHandle); } Loading
android/app/src/com/android/bluetooth/bass_client/BassObjectsFactory.java +8 −3 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.os.Looper; import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.flags.FeatureFlags; import com.android.internal.annotations.VisibleForTesting; /** Loading Loading @@ -68,11 +69,15 @@ public class BassObjectsFactory { * @param device the remote device associated with this state machine * @param svc the bass client service * @param looper the thread that the state machine is supposed to run on * @param featureFlags feature flag from bass client service * @return a state machine that is initialized and started, ready to go */ public BassClientStateMachine makeStateMachine(BluetoothDevice device, BassClientService svc, Looper looper) { return BassClientStateMachine.make(device, svc, looper); public BassClientStateMachine makeStateMachine( BluetoothDevice device, BassClientService svc, Looper looper, FeatureFlags featureFlags) { return BassClientStateMachine.make(device, svc, looper, featureFlags); } /** Loading
android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java +20 −11 Original line number Diff line number Diff line Loading @@ -223,15 +223,24 @@ public class BassClientServiceTest { }).when(mAdapterService).getBondedDevices(); // Mock methods in BassObjectsFactory doAnswer(invocation -> { doAnswer( invocation -> { assertThat(mCurrentDevice).isNotNull(); final BassClientStateMachine stateMachine = mock(BassClientStateMachine.class); final BassClientStateMachine stateMachine = mock(BassClientStateMachine.class); doReturn(new ArrayList<>()).when(stateMachine).getAllSources(); doReturn(TEST_NUM_SOURCES).when(stateMachine).getMaximumSourceCapacity(); doReturn((BluetoothDevice)invocation.getArgument(0)).when(stateMachine).getDevice(); mStateMachines.put((BluetoothDevice)invocation.getArgument(0), stateMachine); doReturn(TEST_NUM_SOURCES) .when(stateMachine) .getMaximumSourceCapacity(); doReturn((BluetoothDevice) invocation.getArgument(0)) .when(stateMachine) .getDevice(); mStateMachines.put( (BluetoothDevice) invocation.getArgument(0), stateMachine); return stateMachine; }).when(mObjectsFactory).makeStateMachine(any(), any(), any()); }) .when(mObjectsFactory) .makeStateMachine(any(), any(), any(), any()); doReturn(mBluetoothLeScannerWrapper).when(mObjectsFactory) .getBluetoothLeScannerWrapper(any()); Loading Loading @@ -337,8 +346,8 @@ public class BassClientServiceTest { mCurrentDevice = TestUtils.getTestDevice(mBluetoothAdapter, 0); assertThat(mBassClientService.connect(mCurrentDevice)).isTrue(); verify(mObjectsFactory).makeStateMachine( eq(mCurrentDevice), eq(mBassClientService), any()); verify(mObjectsFactory) .makeStateMachine(eq(mCurrentDevice), eq(mBassClientService), any(), any()); BassClientStateMachine stateMachine = mStateMachines.get(mCurrentDevice); assertThat(stateMachine).isNotNull(); verify(stateMachine).sendMessage(BassClientStateMachine.CONNECT); Loading