Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +57 −7 Original line number Diff line number Diff line Loading @@ -24,9 +24,9 @@ import static com.android.bluetooth.flags.Flags.leaudioBroadcastFeatureSupport; import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; import static com.android.modules.utils.build.SdkLevel.isAtLeastU; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeAudio; Loading Loading @@ -56,6 +56,7 @@ import android.media.AudioDeviceCallback; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.BluetoothProfileConnectionInfo; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; Loading @@ -63,6 +64,8 @@ import android.os.Parcel; import android.os.ParcelUuid; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.sysprop.BluetoothProperties; import android.util.Log; import android.util.Pair; Loading Loading @@ -127,6 +130,13 @@ public class LeAudioService extends ProfileService { */ private static final int AUDIO_DIRECTION_INPUT_BIT = 0x02; /** * This is used by application read-only for checking the fallback active group id. * */ public static final String BLUETOOTH_LE_BROADCAST_FALLBACK_ACTIVE_GROUP_ID = "bluetooth_le_broadcast_fallback_active_group_id"; private AdapterService mAdapterService; private DatabaseManager mDatabaseManager; private HandlerThread mStateMachinesThread; Loading Loading @@ -1756,6 +1766,16 @@ public class LeAudioService extends ProfileService { + mExposedActiveDevice); } if (isBroadcastActive() && currentlyActiveGroupId == LE_AUDIO_GROUP_ID_INVALID && mUnicastGroupIdDeactivatedForBroadcastTransition != LE_AUDIO_GROUP_ID_INVALID && groupId != LE_AUDIO_GROUP_ID_INVALID) { // If broadcast is ongoing and need to update unicast fallback active group // we need to update the cached group id and skip changing the active device updateFallbackUnicastGroupIdForBroadcast(groupId); return; } LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(currentlyActiveGroupId); if (groupDescriptor != null && groupId == currentlyActiveGroupId) { /* Make sure active group is already exposed to audio framework. Loading Loading @@ -2345,7 +2365,7 @@ public class LeAudioService extends ProfileService { if (unicastDevice == null) { Log.e(TAG, "EVENT_TYPE_BROADCAST_DESTROYED: No valid unicast device for group ID: " + mUnicastGroupIdDeactivatedForBroadcastTransition); mUnicastGroupIdDeactivatedForBroadcastTransition = LE_AUDIO_GROUP_ID_INVALID; updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID); return; } Loading @@ -2356,8 +2376,7 @@ public class LeAudioService extends ProfileService { + unicastDevice); } mUnicastGroupIdDeactivatedForBroadcastTransition = LE_AUDIO_GROUP_ID_INVALID; updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID); setActiveDevice(unicastDevice); } Loading Loading @@ -2575,14 +2594,14 @@ public class LeAudioService extends ProfileService { /* Check if broadcast was deactivated due to unicast */ if (mBroadcastIdDeactivatedForUnicastTransition.isPresent()) { mUnicastGroupIdDeactivatedForBroadcastTransition = groupId; updateFallbackUnicastGroupIdForBroadcast(groupId); mQueuedInCallValue = Optional.empty(); startBroadcast(mBroadcastIdDeactivatedForUnicastTransition.get()); mBroadcastIdDeactivatedForUnicastTransition = Optional.empty(); } if (!mCreateBroadcastQueue.isEmpty()) { mUnicastGroupIdDeactivatedForBroadcastTransition = groupId; updateFallbackUnicastGroupIdForBroadcast(groupId); BluetoothLeBroadcastSettings settings = mCreateBroadcastQueue.remove(); createBroadcast(settings); } Loading Loading @@ -3471,7 +3490,7 @@ public class LeAudioService extends ProfileService { mGroupDescriptors.remove(groupId); if (mUnicastGroupIdDeactivatedForBroadcastTransition == groupId) { mUnicastGroupIdDeactivatedForBroadcastTransition = LE_AUDIO_GROUP_ID_INVALID; updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID); } } notifyGroupNodeRemoved(device, groupId); Loading Loading @@ -3651,6 +3670,37 @@ public class LeAudioService extends ProfileService { } } /** * Update the fallback unicast group id during the handover to broadcast Also store the fallback * group id in Settings store. * * @param groupId group id to update */ private void updateFallbackUnicastGroupIdForBroadcast(int groupId) { Log.i( TAG, "Update unicast fallback active group from: " + mUnicastGroupIdDeactivatedForBroadcastTransition + " to : " + groupId); mUnicastGroupIdDeactivatedForBroadcastTransition = groupId; // waive WRITE_SECURE_SETTINGS permission check final long callingIdentity = Binder.clearCallingIdentity(); try { Context userContext = getApplicationContext() .createContextAsUser( UserHandle.of(ActivityManager.getCurrentUser()), 0); Settings.Secure.putInt( userContext.getContentResolver(), BLUETOOTH_LE_BROADCAST_FALLBACK_ACTIVE_GROUP_ID, groupId); } finally { Binder.restoreCallingIdentity(callingIdentity); } } /** * Gets the current codec status (configuration and capability). * Loading android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +43 −0 Original line number Diff line number Diff line Loading @@ -1370,6 +1370,49 @@ public class LeAudioServiceTest { verify(mTbsService, times(0)).clearInbandRingtoneSupport(mSingleDevice); } /** Test update unicast fallback active group when broadcast is ongoing */ @Test public void testUpdateUnicastFallbackActiveDeviceGroupDuringBroadcast() { int groupId = 1; int preGroupId = 2; /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ int direction = 1; int snkAudioLocation = 3; int srcAudioLocation = 4; int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE; // Not connected device assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); // Connected device doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mSingleDevice, testGroupId); mService.mUnicastGroupIdDeactivatedForBroadcastTransition = preGroupId; // mock create broadcast and currentlyActiveGroupId remains LE_AUDIO_GROUP_ID_INVALID LeAudioStackEvent broadcastCreatedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED); broadcastCreatedEvent.device = mSingleDevice; broadcastCreatedEvent.valueInt1 = 1; broadcastCreatedEvent.valueBool1 = true; mService.messageFromNative(broadcastCreatedEvent); LeAudioStackEvent audioConfChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); audioConfChangedEvent.device = mSingleDevice; audioConfChangedEvent.valueInt1 = direction; audioConfChangedEvent.valueInt2 = groupId; audioConfChangedEvent.valueInt3 = snkAudioLocation; audioConfChangedEvent.valueInt4 = srcAudioLocation; audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); // Verify only update the fallback group and not proceed to change active assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); assertThat(mService.mUnicastGroupIdDeactivatedForBroadcastTransition).isEqualTo(groupId); verify(mNativeInterface, times(0)).groupSetActive(anyInt()); } /** * Test getting active device */ Loading Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +57 −7 Original line number Diff line number Diff line Loading @@ -24,9 +24,9 @@ import static com.android.bluetooth.flags.Flags.leaudioBroadcastFeatureSupport; import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; import static com.android.modules.utils.build.SdkLevel.isAtLeastU; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeAudio; Loading Loading @@ -56,6 +56,7 @@ import android.media.AudioDeviceCallback; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.BluetoothProfileConnectionInfo; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; Loading @@ -63,6 +64,8 @@ import android.os.Parcel; import android.os.ParcelUuid; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.sysprop.BluetoothProperties; import android.util.Log; import android.util.Pair; Loading Loading @@ -127,6 +130,13 @@ public class LeAudioService extends ProfileService { */ private static final int AUDIO_DIRECTION_INPUT_BIT = 0x02; /** * This is used by application read-only for checking the fallback active group id. * */ public static final String BLUETOOTH_LE_BROADCAST_FALLBACK_ACTIVE_GROUP_ID = "bluetooth_le_broadcast_fallback_active_group_id"; private AdapterService mAdapterService; private DatabaseManager mDatabaseManager; private HandlerThread mStateMachinesThread; Loading Loading @@ -1756,6 +1766,16 @@ public class LeAudioService extends ProfileService { + mExposedActiveDevice); } if (isBroadcastActive() && currentlyActiveGroupId == LE_AUDIO_GROUP_ID_INVALID && mUnicastGroupIdDeactivatedForBroadcastTransition != LE_AUDIO_GROUP_ID_INVALID && groupId != LE_AUDIO_GROUP_ID_INVALID) { // If broadcast is ongoing and need to update unicast fallback active group // we need to update the cached group id and skip changing the active device updateFallbackUnicastGroupIdForBroadcast(groupId); return; } LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(currentlyActiveGroupId); if (groupDescriptor != null && groupId == currentlyActiveGroupId) { /* Make sure active group is already exposed to audio framework. Loading Loading @@ -2345,7 +2365,7 @@ public class LeAudioService extends ProfileService { if (unicastDevice == null) { Log.e(TAG, "EVENT_TYPE_BROADCAST_DESTROYED: No valid unicast device for group ID: " + mUnicastGroupIdDeactivatedForBroadcastTransition); mUnicastGroupIdDeactivatedForBroadcastTransition = LE_AUDIO_GROUP_ID_INVALID; updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID); return; } Loading @@ -2356,8 +2376,7 @@ public class LeAudioService extends ProfileService { + unicastDevice); } mUnicastGroupIdDeactivatedForBroadcastTransition = LE_AUDIO_GROUP_ID_INVALID; updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID); setActiveDevice(unicastDevice); } Loading Loading @@ -2575,14 +2594,14 @@ public class LeAudioService extends ProfileService { /* Check if broadcast was deactivated due to unicast */ if (mBroadcastIdDeactivatedForUnicastTransition.isPresent()) { mUnicastGroupIdDeactivatedForBroadcastTransition = groupId; updateFallbackUnicastGroupIdForBroadcast(groupId); mQueuedInCallValue = Optional.empty(); startBroadcast(mBroadcastIdDeactivatedForUnicastTransition.get()); mBroadcastIdDeactivatedForUnicastTransition = Optional.empty(); } if (!mCreateBroadcastQueue.isEmpty()) { mUnicastGroupIdDeactivatedForBroadcastTransition = groupId; updateFallbackUnicastGroupIdForBroadcast(groupId); BluetoothLeBroadcastSettings settings = mCreateBroadcastQueue.remove(); createBroadcast(settings); } Loading Loading @@ -3471,7 +3490,7 @@ public class LeAudioService extends ProfileService { mGroupDescriptors.remove(groupId); if (mUnicastGroupIdDeactivatedForBroadcastTransition == groupId) { mUnicastGroupIdDeactivatedForBroadcastTransition = LE_AUDIO_GROUP_ID_INVALID; updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID); } } notifyGroupNodeRemoved(device, groupId); Loading Loading @@ -3651,6 +3670,37 @@ public class LeAudioService extends ProfileService { } } /** * Update the fallback unicast group id during the handover to broadcast Also store the fallback * group id in Settings store. * * @param groupId group id to update */ private void updateFallbackUnicastGroupIdForBroadcast(int groupId) { Log.i( TAG, "Update unicast fallback active group from: " + mUnicastGroupIdDeactivatedForBroadcastTransition + " to : " + groupId); mUnicastGroupIdDeactivatedForBroadcastTransition = groupId; // waive WRITE_SECURE_SETTINGS permission check final long callingIdentity = Binder.clearCallingIdentity(); try { Context userContext = getApplicationContext() .createContextAsUser( UserHandle.of(ActivityManager.getCurrentUser()), 0); Settings.Secure.putInt( userContext.getContentResolver(), BLUETOOTH_LE_BROADCAST_FALLBACK_ACTIVE_GROUP_ID, groupId); } finally { Binder.restoreCallingIdentity(callingIdentity); } } /** * Gets the current codec status (configuration and capability). * Loading
android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +43 −0 Original line number Diff line number Diff line Loading @@ -1370,6 +1370,49 @@ public class LeAudioServiceTest { verify(mTbsService, times(0)).clearInbandRingtoneSupport(mSingleDevice); } /** Test update unicast fallback active group when broadcast is ongoing */ @Test public void testUpdateUnicastFallbackActiveDeviceGroupDuringBroadcast() { int groupId = 1; int preGroupId = 2; /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ int direction = 1; int snkAudioLocation = 3; int srcAudioLocation = 4; int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE; // Not connected device assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); // Connected device doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mSingleDevice, testGroupId); mService.mUnicastGroupIdDeactivatedForBroadcastTransition = preGroupId; // mock create broadcast and currentlyActiveGroupId remains LE_AUDIO_GROUP_ID_INVALID LeAudioStackEvent broadcastCreatedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED); broadcastCreatedEvent.device = mSingleDevice; broadcastCreatedEvent.valueInt1 = 1; broadcastCreatedEvent.valueBool1 = true; mService.messageFromNative(broadcastCreatedEvent); LeAudioStackEvent audioConfChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); audioConfChangedEvent.device = mSingleDevice; audioConfChangedEvent.valueInt1 = direction; audioConfChangedEvent.valueInt2 = groupId; audioConfChangedEvent.valueInt3 = snkAudioLocation; audioConfChangedEvent.valueInt4 = srcAudioLocation; audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); // Verify only update the fallback group and not proceed to change active assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); assertThat(mService.mUnicastGroupIdDeactivatedForBroadcastTransition).isEqualTo(groupId); verify(mNativeInterface, times(0)).groupSetActive(anyInt()); } /** * Test getting active device */ Loading