Loading android/app/src/com/android/bluetooth/bass_client/BassClientService.java +67 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; Loading Loading @@ -90,6 +91,9 @@ public class BassClientService extends ProfileService { new ConcurrentHashMap<>(); private final Map<BluetoothDevice, List<Integer>> mActiveSourceMap = new ConcurrentHashMap<>(); private final FeatureFlags mFeatureFlags; private final Map<BluetoothDevice, BluetoothLeBroadcastMetadata> mBroadcastMetadataMap = new ConcurrentHashMap<>(); private final LinkedList<BluetoothDevice> mPausedBroadcastSinks = new LinkedList<>(); private HandlerThread mStateMachinesThread; private HandlerThread mCallbackHandlerThread; Loading Loading @@ -424,6 +428,9 @@ public class BassClientService extends ProfileService { if (mCachedBroadcasts != null) { mCachedBroadcasts.clear(); } if (mBroadcastMetadataMap != null) { mBroadcastMetadataMap.clear(); } if (mSyncHandleToBroadcastIdMap != null) { mSyncHandleToBroadcastIdMap.clear(); mSyncHandleToBroadcastIdMap = null; Loading Loading @@ -1210,6 +1217,9 @@ public class BassClientService extends ProfileService { return; } /* Store metadata for sink device */ mBroadcastMetadataMap.put(sink, sourceMetadata); byte[] code = sourceMetadata.getBroadcastCode(); for (BluetoothDevice device : devices) { BassClientStateMachine stateMachine = getOrCreateStateMachine(device); Loading Loading @@ -1301,6 +1311,9 @@ public class BassClientService extends ProfileService { return; } /* Update metadata for sink device */ mBroadcastMetadataMap.put(sink, updatedMetadata); byte[] code = updatedMetadata.getBroadcastCode(); for (Map.Entry<BluetoothDevice, Integer> deviceSourceIdPair : devices.entrySet()) { BluetoothDevice device = deviceSourceIdPair.getKey(); Loading Loading @@ -1379,6 +1392,12 @@ public class BassClientService extends ProfileService { BluetoothDevice device = deviceSourceIdPair.getKey(); Integer deviceSourceId = deviceSourceIdPair.getValue(); BassClientStateMachine stateMachine = getOrCreateStateMachine(device); /* Removes metadata for sink device if not paused */ if (!mPausedBroadcastSinks.contains(device)) { mBroadcastMetadataMap.remove(device); } if (stateMachine == null) { log("removeSource: Error bad parameters: device = " + device); mCallbacks.notifySourceRemoveFailed(device, sourceId, Loading Loading @@ -1510,6 +1529,54 @@ public class BassClientService extends ProfileService { } } private void stopLocalSourceReceivers(int broadcastId, boolean store) { if (DBG) { Log.d(TAG, "stopLocalSourceReceivers()"); } if (store && !mPausedBroadcastSinks.isEmpty()) { Log.w(TAG, "stopLocalSourceReceivers(), paused broadcast sinks are replaced"); mPausedBroadcastSinks.clear(); } for (BluetoothDevice device : getConnectedDevices()) { for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) { /* Check if local/last broadcast is the synced one */ if (receiveState.getBroadcastId() != broadcastId) continue; removeSource(device, receiveState.getSourceId()); if (store && !mPausedBroadcastSinks.contains(device)) { mPausedBroadcastSinks.add(device); } } } } /** Request receivers to suspend broadcast sources synchronization */ public void suspendReceiversSourceSynchronization(int broadcastId) { sEventLogger.logd(DBG, TAG, "Suspend receivers source synchronization: " + broadcastId); stopLocalSourceReceivers(broadcastId, true); } /** Request receivers to stop broadcast sources synchronization and remove them */ public void stopReceiversSourceSynchronization(int broadcastId) { sEventLogger.logd(DBG, TAG, "Stop receivers source synchronization: " + broadcastId); stopLocalSourceReceivers(broadcastId, false); } /** Request receivers to resume broadcast source synchronization */ public void resumeReceiversSourceSynchronization(int broadcastId) { sEventLogger.logd(DBG, TAG, "Resume receivers source synchronization: " + broadcastId); while (!mPausedBroadcastSinks.isEmpty()) { BluetoothDevice sink = mPausedBroadcastSinks.remove(); BluetoothLeBroadcastMetadata metadata = mBroadcastMetadataMap.get(sink); addSource(sink, metadata, true); } } /** * Callback handler */ Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +30 −0 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ 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.bass_client.BassClientService; import com.android.bluetooth.flags.FeatureFlags; import com.android.bluetooth.flags.FeatureFlagsImpl; import com.android.bluetooth.hap.HapClientService; Loading Loading @@ -167,6 +168,8 @@ public class LeAudioService extends ProfileService { @VisibleForTesting CsipSetCoordinatorService mCsipSetCoordinatorService; @VisibleForTesting BassClientService mBassClientService; @VisibleForTesting RemoteCallbackList<IBluetoothLeBroadcastCallback> mBroadcastCallbacks; Loading Loading @@ -489,6 +492,7 @@ public class LeAudioService extends ProfileService { mTbsService = null; mVolumeControlService = null; mCsipSetCoordinatorService = null; mBassClientService = null; return true; } Loading Loading @@ -528,6 +532,16 @@ public class LeAudioService extends ProfileService { return mVolumeControlService; } BassClientService getBassClientService() { if (mBassClientService == null) { mBassClientService = mServiceFactory.getBassClientService(); if (mBassClientService == null) { Log.e(TAG, "BASS service is not available"); } } return mBassClientService; } @VisibleForTesting int getAudioDeviceGroupVolume(int groupId) { VolumeControlService volumeControlService = getVolumeControlService(); Loading Loading @@ -2543,6 +2557,11 @@ public class LeAudioService extends ProfileService { // TODO: Improve reason reporting or extend the native stack event with reason code notifyOnBroadcastStopped(broadcastId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); BassClientService bassClientService = getBassClientService(); if (bassClientService != null) { bassClientService.stopReceiversSourceSynchronization(broadcastId); } LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId); if (descriptor == null) { Log.e(TAG, "EVENT_TYPE_BROADCAST_DESTROYED: No valid descriptor for broadcastId: " Loading Loading @@ -2570,6 +2589,7 @@ public class LeAudioService extends ProfileService { } previousState = descriptor.mState; descriptor.mState = state; BassClientService bassClientService = getBassClientService(); switch (descriptor.mState) { case LeAudioStackEvent.BROADCAST_STATE_STOPPED: Loading Loading @@ -2610,6 +2630,10 @@ public class LeAudioService extends ProfileService { notifyPlaybackStopped(broadcastId, BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST); if (bassClientService != null) { bassClientService.suspendReceiversSourceSynchronization(broadcastId); } // Notify audio manager updateBroadcastActiveDevice(null, mActiveAudioOutDevice); Loading @@ -2626,6 +2650,12 @@ public class LeAudioService extends ProfileService { notifyPlaybackStarted(broadcastId, BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST); if (previousState == LeAudioStackEvent.BROADCAST_STATE_PAUSED) { if (bassClientService != null) { bassClientService.resumeReceiversSourceSynchronization(broadcastId); } } // Notify audio manager if (mBroadcastDescriptors.values().stream() .anyMatch( Loading Loading
android/app/src/com/android/bluetooth/bass_client/BassClientService.java +67 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; Loading Loading @@ -90,6 +91,9 @@ public class BassClientService extends ProfileService { new ConcurrentHashMap<>(); private final Map<BluetoothDevice, List<Integer>> mActiveSourceMap = new ConcurrentHashMap<>(); private final FeatureFlags mFeatureFlags; private final Map<BluetoothDevice, BluetoothLeBroadcastMetadata> mBroadcastMetadataMap = new ConcurrentHashMap<>(); private final LinkedList<BluetoothDevice> mPausedBroadcastSinks = new LinkedList<>(); private HandlerThread mStateMachinesThread; private HandlerThread mCallbackHandlerThread; Loading Loading @@ -424,6 +428,9 @@ public class BassClientService extends ProfileService { if (mCachedBroadcasts != null) { mCachedBroadcasts.clear(); } if (mBroadcastMetadataMap != null) { mBroadcastMetadataMap.clear(); } if (mSyncHandleToBroadcastIdMap != null) { mSyncHandleToBroadcastIdMap.clear(); mSyncHandleToBroadcastIdMap = null; Loading Loading @@ -1210,6 +1217,9 @@ public class BassClientService extends ProfileService { return; } /* Store metadata for sink device */ mBroadcastMetadataMap.put(sink, sourceMetadata); byte[] code = sourceMetadata.getBroadcastCode(); for (BluetoothDevice device : devices) { BassClientStateMachine stateMachine = getOrCreateStateMachine(device); Loading Loading @@ -1301,6 +1311,9 @@ public class BassClientService extends ProfileService { return; } /* Update metadata for sink device */ mBroadcastMetadataMap.put(sink, updatedMetadata); byte[] code = updatedMetadata.getBroadcastCode(); for (Map.Entry<BluetoothDevice, Integer> deviceSourceIdPair : devices.entrySet()) { BluetoothDevice device = deviceSourceIdPair.getKey(); Loading Loading @@ -1379,6 +1392,12 @@ public class BassClientService extends ProfileService { BluetoothDevice device = deviceSourceIdPair.getKey(); Integer deviceSourceId = deviceSourceIdPair.getValue(); BassClientStateMachine stateMachine = getOrCreateStateMachine(device); /* Removes metadata for sink device if not paused */ if (!mPausedBroadcastSinks.contains(device)) { mBroadcastMetadataMap.remove(device); } if (stateMachine == null) { log("removeSource: Error bad parameters: device = " + device); mCallbacks.notifySourceRemoveFailed(device, sourceId, Loading Loading @@ -1510,6 +1529,54 @@ public class BassClientService extends ProfileService { } } private void stopLocalSourceReceivers(int broadcastId, boolean store) { if (DBG) { Log.d(TAG, "stopLocalSourceReceivers()"); } if (store && !mPausedBroadcastSinks.isEmpty()) { Log.w(TAG, "stopLocalSourceReceivers(), paused broadcast sinks are replaced"); mPausedBroadcastSinks.clear(); } for (BluetoothDevice device : getConnectedDevices()) { for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) { /* Check if local/last broadcast is the synced one */ if (receiveState.getBroadcastId() != broadcastId) continue; removeSource(device, receiveState.getSourceId()); if (store && !mPausedBroadcastSinks.contains(device)) { mPausedBroadcastSinks.add(device); } } } } /** Request receivers to suspend broadcast sources synchronization */ public void suspendReceiversSourceSynchronization(int broadcastId) { sEventLogger.logd(DBG, TAG, "Suspend receivers source synchronization: " + broadcastId); stopLocalSourceReceivers(broadcastId, true); } /** Request receivers to stop broadcast sources synchronization and remove them */ public void stopReceiversSourceSynchronization(int broadcastId) { sEventLogger.logd(DBG, TAG, "Stop receivers source synchronization: " + broadcastId); stopLocalSourceReceivers(broadcastId, false); } /** Request receivers to resume broadcast source synchronization */ public void resumeReceiversSourceSynchronization(int broadcastId) { sEventLogger.logd(DBG, TAG, "Resume receivers source synchronization: " + broadcastId); while (!mPausedBroadcastSinks.isEmpty()) { BluetoothDevice sink = mPausedBroadcastSinks.remove(); BluetoothLeBroadcastMetadata metadata = mBroadcastMetadataMap.get(sink); addSource(sink, metadata, true); } } /** * Callback handler */ Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +30 −0 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ 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.bass_client.BassClientService; import com.android.bluetooth.flags.FeatureFlags; import com.android.bluetooth.flags.FeatureFlagsImpl; import com.android.bluetooth.hap.HapClientService; Loading Loading @@ -167,6 +168,8 @@ public class LeAudioService extends ProfileService { @VisibleForTesting CsipSetCoordinatorService mCsipSetCoordinatorService; @VisibleForTesting BassClientService mBassClientService; @VisibleForTesting RemoteCallbackList<IBluetoothLeBroadcastCallback> mBroadcastCallbacks; Loading Loading @@ -489,6 +492,7 @@ public class LeAudioService extends ProfileService { mTbsService = null; mVolumeControlService = null; mCsipSetCoordinatorService = null; mBassClientService = null; return true; } Loading Loading @@ -528,6 +532,16 @@ public class LeAudioService extends ProfileService { return mVolumeControlService; } BassClientService getBassClientService() { if (mBassClientService == null) { mBassClientService = mServiceFactory.getBassClientService(); if (mBassClientService == null) { Log.e(TAG, "BASS service is not available"); } } return mBassClientService; } @VisibleForTesting int getAudioDeviceGroupVolume(int groupId) { VolumeControlService volumeControlService = getVolumeControlService(); Loading Loading @@ -2543,6 +2557,11 @@ public class LeAudioService extends ProfileService { // TODO: Improve reason reporting or extend the native stack event with reason code notifyOnBroadcastStopped(broadcastId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); BassClientService bassClientService = getBassClientService(); if (bassClientService != null) { bassClientService.stopReceiversSourceSynchronization(broadcastId); } LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId); if (descriptor == null) { Log.e(TAG, "EVENT_TYPE_BROADCAST_DESTROYED: No valid descriptor for broadcastId: " Loading Loading @@ -2570,6 +2589,7 @@ public class LeAudioService extends ProfileService { } previousState = descriptor.mState; descriptor.mState = state; BassClientService bassClientService = getBassClientService(); switch (descriptor.mState) { case LeAudioStackEvent.BROADCAST_STATE_STOPPED: Loading Loading @@ -2610,6 +2630,10 @@ public class LeAudioService extends ProfileService { notifyPlaybackStopped(broadcastId, BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST); if (bassClientService != null) { bassClientService.suspendReceiversSourceSynchronization(broadcastId); } // Notify audio manager updateBroadcastActiveDevice(null, mActiveAudioOutDevice); Loading @@ -2626,6 +2650,12 @@ public class LeAudioService extends ProfileService { notifyPlaybackStarted(broadcastId, BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST); if (previousState == LeAudioStackEvent.BROADCAST_STATE_PAUSED) { if (bassClientService != null) { bassClientService.resumeReceiversSourceSynchronization(broadcastId); } } // Notify audio manager if (mBroadcastDescriptors.values().stream() .anyMatch( Loading