Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +20 −8 Original line number Diff line number Diff line Loading @@ -249,7 +249,7 @@ public class LeAudioService extends ProfileService { mLostLeadDeviceWhileStreaming = null; mCurrentLeadDevice = null; mInbandRingtoneEnabled = isInbandRingtonEnabled; mAvailableContexts = 0; mAvailableContexts = Flags.leaudioUnicastNoAvailableContexts() ? null : 0; mInputSelectableConfig = new ArrayList<>(); mOutputSelectableConfig = new ArrayList<>(); mInactivatedDueToContextType = false; Loading Loading @@ -2952,9 +2952,13 @@ public class LeAudioService extends ProfileService { return; } boolean ringtoneContextAvailable = ((groupDescriptor.mAvailableContexts & BluetoothLeAudio.CONTEXT_TYPE_RINGTONE) != 0); boolean ringtoneContextAvailable; if (groupDescriptor.mAvailableContexts != null) { ringtoneContextAvailable = ((groupDescriptor.mAvailableContexts & BluetoothLeAudio.CONTEXT_TYPE_RINGTONE) != 0); } else { ringtoneContextAvailable = false; } Log.d( TAG, Loading Loading @@ -3492,9 +3496,10 @@ public class LeAudioService extends ProfileService { BluetoothLeAudio.GROUP_STATUS_INACTIVE)); } } boolean isInitial = descriptor.mAvailableContexts == null; boolean availableContextChanged = Integer.bitCount(descriptor.mAvailableContexts) != Integer.bitCount(available_contexts); isInitial ? true : descriptor.mAvailableContexts != available_contexts; descriptor.mDirection = direction; descriptor.mAvailableContexts = available_contexts; Loading @@ -3519,6 +3524,13 @@ public class LeAudioService extends ProfileService { + " due to unavailable context types"); descriptor.mInactivatedDueToContextType = true; setActiveGroupWithDevice(null, false); } else if (isInitial) { Log.i( TAG, " New group " + groupId + " with no context types available"); descriptor.mInactivatedDueToContextType = true; } return; } Loading Loading @@ -4144,7 +4156,7 @@ public class LeAudioService extends ProfileService { if (getConnectedPeerDevices(groupId).isEmpty()) { descriptor.mIsConnected = false; descriptor.mInactivatedDueToContextType = false; descriptor.mAvailableContexts = Flags.leaudioUnicastNoAvailableContexts() ? null : 0; if (descriptor.isActive()) { /* Notify Native layer */ removeActiveDevice(hasFallbackDevice); Loading Loading @@ -4495,7 +4507,7 @@ public class LeAudioService extends ProfileService { Log.e(TAG, "getGroupId: No valid descriptor for groupId: " + groupId); return false; } return descriptor.mAvailableContexts != 0; return descriptor.mAvailableContexts != null && descriptor.mAvailableContexts != 0; } finally { mGroupReadLock.unlock(); } Loading android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +312 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ import android.media.BluetoothProfileConnectionInfo; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.sysprop.BluetoothProperties; Loading Loading @@ -3109,6 +3110,317 @@ public class LeAudioServiceTest { any(BluetoothProfileConnectionInfo.class)); } /** * Test the group is activated once the available contexts are back. * * Scenario: * 1. Have a group of 2 devices that initially does not expose any available contexts. * The group shall be inactive at this point. * 2. Once the available contexts are updated with non-zero value, * the group shall become active. * 3. The available contexts are changed to zero. Group becomes inactive. * 4. The available contexts are back again. Group becomes active. */ @Test @EnableFlags(Flags.FLAG_LEAUDIO_UNICAST_NO_AVAILABLE_CONTEXTS) public void testActivateGroupWhenAvailableContextAreBack_Scenario1() { int groupId = 1; /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ int direction = 1; int snkAudioLocation = 3; int srcAudioLocation = 4; int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE; doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mLeftDevice, groupId); connectTestDevice(mRightDevice, groupId); // Checks group device lists for groupId 1 List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId); assertThat(groupDevicesById.size()).isEqualTo(2); assertThat(groupDevicesById.contains(mLeftDevice)).isTrue(); assertThat(groupDevicesById.contains(mRightDevice)).isTrue(); // Add location support LeAudioStackEvent audioConfChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); audioConfChangedEvent.valueInt1 = direction; audioConfChangedEvent.valueInt2 = groupId; audioConfChangedEvent.valueInt3 = snkAudioLocation; audioConfChangedEvent.valueInt4 = srcAudioLocation; audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(mLeftDevice)).isFalse(); verify(mNativeInterface, times(0)).groupSetActive(groupId); // Expect device to be active audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); verify(mNativeInterface, times(1)).groupSetActive(groupId); // Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(BluetoothDevice.class), eq(null), any(BluetoothProfileConnectionInfo.class)); reset(mAudioManager); reset(mNativeInterface); // Expect device to be inactive audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); verify(mNativeInterface, times(1)).groupSetActive(-1); injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( eq(null), any(BluetoothDevice.class), any(BluetoothProfileConnectionInfo.class)); reset(mNativeInterface); reset(mAudioManager); // Expect device to be active audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); verify(mNativeInterface, times(1)).groupSetActive(groupId); reset(mNativeInterface); // Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(BluetoothDevice.class), eq(null), any(BluetoothProfileConnectionInfo.class)); } /** * Test the group is activated once the available contexts are back. * * Scenario: * 1. Have a group of 2 devices. The available contexts are non-zero. * The group shall be active at this point. * 2. Once the available contexts are updated with zero value, * the group shall become inactive. * 3. All group devices are disconnected. * 4. Group devices are reconnected. The available contexts are still zero. * 4. The available contexts are updated with non-zero value. Group becomes active. */ @Test @EnableFlags(Flags.FLAG_LEAUDIO_UNICAST_NO_AVAILABLE_CONTEXTS) public void testActivateDeviceWhenAvailableContextAreBack_Scenario2() { int groupId = 1; /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ int direction = 1; int snkAudioLocation = 3; int srcAudioLocation = 4; int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE; doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mLeftDevice, groupId); connectTestDevice(mRightDevice, groupId); // Checks group device lists for groupId 1 List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId); assertThat(groupDevicesById.size()).isEqualTo(2); assertThat(groupDevicesById.contains(mLeftDevice)).isTrue(); assertThat(groupDevicesById.contains(mRightDevice)).isTrue(); // Add location support LeAudioStackEvent audioConfChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); audioConfChangedEvent.valueInt1 = direction; audioConfChangedEvent.valueInt2 = groupId; audioConfChangedEvent.valueInt3 = snkAudioLocation; audioConfChangedEvent.valueInt4 = srcAudioLocation; audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); verify(mNativeInterface, times(1)).groupSetActive(groupId); // Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(BluetoothDevice.class), eq(null), any(BluetoothProfileConnectionInfo.class)); reset(mAudioManager); reset(mNativeInterface); // Expect device to be inactive audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); verify(mNativeInterface, times(1)).groupSetActive(-1); injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( eq(null), any(BluetoothDevice.class), any(BluetoothProfileConnectionInfo.class)); reset(mNativeInterface); reset(mAudioManager); // Send a message to trigger disconnection completed to the left device injectAndVerifyDeviceDisconnected(mLeftDevice); // Send a message to trigger disconnection completed to the right device injectAndVerifyDeviceDisconnected(mRightDevice); // Verify the list of connected devices assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isFalse(); assertThat(mService.getConnectedDevices().contains(mRightDevice)).isFalse(); // Expect device to be inactive audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); assertThat(mService.getConnectionState(mLeftDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isTrue(); // Expect device to be inactive audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); generateConnectionMessageFromNative( mRightDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); assertThat(mService.getConnectionState(mRightDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getConnectedDevices().contains(mRightDevice)).isTrue(); // Expect device to be active audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); verify(mNativeInterface, times(1)).groupSetActive(groupId); // Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(BluetoothDevice.class), eq(null), any(BluetoothProfileConnectionInfo.class)); } /** * Test the group is activated once the available contexts are back. * * Scenario: * 1. Have a group of 2 devices. The available contexts are non-zero. * The group shall be active at this point. * 2. All group devices are disconnected. * 3. Group devices are reconnected. The available contexts are zero. * 4. The available contexts are updated with non-zero value. Group becomes active. */ @Test @EnableFlags(Flags.FLAG_LEAUDIO_UNICAST_NO_AVAILABLE_CONTEXTS) public void testActivateDeviceWhenAvailableContextAreBack_Scenario3() { int groupId = 1; /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ int direction = 1; int snkAudioLocation = 3; int srcAudioLocation = 4; int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE; doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mLeftDevice, groupId); connectTestDevice(mRightDevice, groupId); // Checks group device lists for groupId 1 List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId); assertThat(groupDevicesById.size()).isEqualTo(2); assertThat(groupDevicesById.contains(mLeftDevice)).isTrue(); assertThat(groupDevicesById.contains(mRightDevice)).isTrue(); // Add location support LeAudioStackEvent audioConfChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); audioConfChangedEvent.valueInt1 = direction; audioConfChangedEvent.valueInt2 = groupId; audioConfChangedEvent.valueInt3 = snkAudioLocation; audioConfChangedEvent.valueInt4 = srcAudioLocation; audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); verify(mNativeInterface, times(1)).groupSetActive(groupId); // Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(BluetoothDevice.class), eq(null), any(BluetoothProfileConnectionInfo.class)); reset(mNativeInterface); reset(mAudioManager); // Send a message to trigger disconnection completed to the right device injectAndVerifyDeviceDisconnected(mRightDevice); // Send a message to trigger disconnection completed to the left device injectAndVerifyDeviceDisconnected(mLeftDevice); reset(mNativeInterface); reset(mAudioManager); // Expect device to be inactive audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); assertThat(mService.getConnectionState(mLeftDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isTrue(); // Expect device to be inactive audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); generateConnectionMessageFromNative( mRightDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); assertThat(mService.getConnectionState(mRightDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getConnectedDevices().contains(mRightDevice)).isTrue(); // Expect device to be active audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); verify(mNativeInterface, times(1)).groupSetActive(groupId); // Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(BluetoothDevice.class), eq(null), any(BluetoothProfileConnectionInfo.class)); } /** Test setting allowed contexts for active group */ @Test public void testSetAllowedContextsForActiveGroup() { Loading Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +20 −8 Original line number Diff line number Diff line Loading @@ -249,7 +249,7 @@ public class LeAudioService extends ProfileService { mLostLeadDeviceWhileStreaming = null; mCurrentLeadDevice = null; mInbandRingtoneEnabled = isInbandRingtonEnabled; mAvailableContexts = 0; mAvailableContexts = Flags.leaudioUnicastNoAvailableContexts() ? null : 0; mInputSelectableConfig = new ArrayList<>(); mOutputSelectableConfig = new ArrayList<>(); mInactivatedDueToContextType = false; Loading Loading @@ -2952,9 +2952,13 @@ public class LeAudioService extends ProfileService { return; } boolean ringtoneContextAvailable = ((groupDescriptor.mAvailableContexts & BluetoothLeAudio.CONTEXT_TYPE_RINGTONE) != 0); boolean ringtoneContextAvailable; if (groupDescriptor.mAvailableContexts != null) { ringtoneContextAvailable = ((groupDescriptor.mAvailableContexts & BluetoothLeAudio.CONTEXT_TYPE_RINGTONE) != 0); } else { ringtoneContextAvailable = false; } Log.d( TAG, Loading Loading @@ -3492,9 +3496,10 @@ public class LeAudioService extends ProfileService { BluetoothLeAudio.GROUP_STATUS_INACTIVE)); } } boolean isInitial = descriptor.mAvailableContexts == null; boolean availableContextChanged = Integer.bitCount(descriptor.mAvailableContexts) != Integer.bitCount(available_contexts); isInitial ? true : descriptor.mAvailableContexts != available_contexts; descriptor.mDirection = direction; descriptor.mAvailableContexts = available_contexts; Loading @@ -3519,6 +3524,13 @@ public class LeAudioService extends ProfileService { + " due to unavailable context types"); descriptor.mInactivatedDueToContextType = true; setActiveGroupWithDevice(null, false); } else if (isInitial) { Log.i( TAG, " New group " + groupId + " with no context types available"); descriptor.mInactivatedDueToContextType = true; } return; } Loading Loading @@ -4144,7 +4156,7 @@ public class LeAudioService extends ProfileService { if (getConnectedPeerDevices(groupId).isEmpty()) { descriptor.mIsConnected = false; descriptor.mInactivatedDueToContextType = false; descriptor.mAvailableContexts = Flags.leaudioUnicastNoAvailableContexts() ? null : 0; if (descriptor.isActive()) { /* Notify Native layer */ removeActiveDevice(hasFallbackDevice); Loading Loading @@ -4495,7 +4507,7 @@ public class LeAudioService extends ProfileService { Log.e(TAG, "getGroupId: No valid descriptor for groupId: " + groupId); return false; } return descriptor.mAvailableContexts != 0; return descriptor.mAvailableContexts != null && descriptor.mAvailableContexts != 0; } finally { mGroupReadLock.unlock(); } Loading
android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +312 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ import android.media.BluetoothProfileConnectionInfo; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.sysprop.BluetoothProperties; Loading Loading @@ -3109,6 +3110,317 @@ public class LeAudioServiceTest { any(BluetoothProfileConnectionInfo.class)); } /** * Test the group is activated once the available contexts are back. * * Scenario: * 1. Have a group of 2 devices that initially does not expose any available contexts. * The group shall be inactive at this point. * 2. Once the available contexts are updated with non-zero value, * the group shall become active. * 3. The available contexts are changed to zero. Group becomes inactive. * 4. The available contexts are back again. Group becomes active. */ @Test @EnableFlags(Flags.FLAG_LEAUDIO_UNICAST_NO_AVAILABLE_CONTEXTS) public void testActivateGroupWhenAvailableContextAreBack_Scenario1() { int groupId = 1; /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ int direction = 1; int snkAudioLocation = 3; int srcAudioLocation = 4; int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE; doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mLeftDevice, groupId); connectTestDevice(mRightDevice, groupId); // Checks group device lists for groupId 1 List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId); assertThat(groupDevicesById.size()).isEqualTo(2); assertThat(groupDevicesById.contains(mLeftDevice)).isTrue(); assertThat(groupDevicesById.contains(mRightDevice)).isTrue(); // Add location support LeAudioStackEvent audioConfChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); audioConfChangedEvent.valueInt1 = direction; audioConfChangedEvent.valueInt2 = groupId; audioConfChangedEvent.valueInt3 = snkAudioLocation; audioConfChangedEvent.valueInt4 = srcAudioLocation; audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(mLeftDevice)).isFalse(); verify(mNativeInterface, times(0)).groupSetActive(groupId); // Expect device to be active audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); verify(mNativeInterface, times(1)).groupSetActive(groupId); // Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(BluetoothDevice.class), eq(null), any(BluetoothProfileConnectionInfo.class)); reset(mAudioManager); reset(mNativeInterface); // Expect device to be inactive audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); verify(mNativeInterface, times(1)).groupSetActive(-1); injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( eq(null), any(BluetoothDevice.class), any(BluetoothProfileConnectionInfo.class)); reset(mNativeInterface); reset(mAudioManager); // Expect device to be active audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); verify(mNativeInterface, times(1)).groupSetActive(groupId); reset(mNativeInterface); // Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(BluetoothDevice.class), eq(null), any(BluetoothProfileConnectionInfo.class)); } /** * Test the group is activated once the available contexts are back. * * Scenario: * 1. Have a group of 2 devices. The available contexts are non-zero. * The group shall be active at this point. * 2. Once the available contexts are updated with zero value, * the group shall become inactive. * 3. All group devices are disconnected. * 4. Group devices are reconnected. The available contexts are still zero. * 4. The available contexts are updated with non-zero value. Group becomes active. */ @Test @EnableFlags(Flags.FLAG_LEAUDIO_UNICAST_NO_AVAILABLE_CONTEXTS) public void testActivateDeviceWhenAvailableContextAreBack_Scenario2() { int groupId = 1; /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ int direction = 1; int snkAudioLocation = 3; int srcAudioLocation = 4; int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE; doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mLeftDevice, groupId); connectTestDevice(mRightDevice, groupId); // Checks group device lists for groupId 1 List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId); assertThat(groupDevicesById.size()).isEqualTo(2); assertThat(groupDevicesById.contains(mLeftDevice)).isTrue(); assertThat(groupDevicesById.contains(mRightDevice)).isTrue(); // Add location support LeAudioStackEvent audioConfChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); audioConfChangedEvent.valueInt1 = direction; audioConfChangedEvent.valueInt2 = groupId; audioConfChangedEvent.valueInt3 = snkAudioLocation; audioConfChangedEvent.valueInt4 = srcAudioLocation; audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); verify(mNativeInterface, times(1)).groupSetActive(groupId); // Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(BluetoothDevice.class), eq(null), any(BluetoothProfileConnectionInfo.class)); reset(mAudioManager); reset(mNativeInterface); // Expect device to be inactive audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); verify(mNativeInterface, times(1)).groupSetActive(-1); injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( eq(null), any(BluetoothDevice.class), any(BluetoothProfileConnectionInfo.class)); reset(mNativeInterface); reset(mAudioManager); // Send a message to trigger disconnection completed to the left device injectAndVerifyDeviceDisconnected(mLeftDevice); // Send a message to trigger disconnection completed to the right device injectAndVerifyDeviceDisconnected(mRightDevice); // Verify the list of connected devices assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isFalse(); assertThat(mService.getConnectedDevices().contains(mRightDevice)).isFalse(); // Expect device to be inactive audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); assertThat(mService.getConnectionState(mLeftDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isTrue(); // Expect device to be inactive audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); generateConnectionMessageFromNative( mRightDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); assertThat(mService.getConnectionState(mRightDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getConnectedDevices().contains(mRightDevice)).isTrue(); // Expect device to be active audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); verify(mNativeInterface, times(1)).groupSetActive(groupId); // Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(BluetoothDevice.class), eq(null), any(BluetoothProfileConnectionInfo.class)); } /** * Test the group is activated once the available contexts are back. * * Scenario: * 1. Have a group of 2 devices. The available contexts are non-zero. * The group shall be active at this point. * 2. All group devices are disconnected. * 3. Group devices are reconnected. The available contexts are zero. * 4. The available contexts are updated with non-zero value. Group becomes active. */ @Test @EnableFlags(Flags.FLAG_LEAUDIO_UNICAST_NO_AVAILABLE_CONTEXTS) public void testActivateDeviceWhenAvailableContextAreBack_Scenario3() { int groupId = 1; /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ int direction = 1; int snkAudioLocation = 3; int srcAudioLocation = 4; int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE; doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mLeftDevice, groupId); connectTestDevice(mRightDevice, groupId); // Checks group device lists for groupId 1 List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId); assertThat(groupDevicesById.size()).isEqualTo(2); assertThat(groupDevicesById.contains(mLeftDevice)).isTrue(); assertThat(groupDevicesById.contains(mRightDevice)).isTrue(); // Add location support LeAudioStackEvent audioConfChangedEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); audioConfChangedEvent.valueInt1 = direction; audioConfChangedEvent.valueInt2 = groupId; audioConfChangedEvent.valueInt3 = snkAudioLocation; audioConfChangedEvent.valueInt4 = srcAudioLocation; audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); verify(mNativeInterface, times(1)).groupSetActive(groupId); // Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(BluetoothDevice.class), eq(null), any(BluetoothProfileConnectionInfo.class)); reset(mNativeInterface); reset(mAudioManager); // Send a message to trigger disconnection completed to the right device injectAndVerifyDeviceDisconnected(mRightDevice); // Send a message to trigger disconnection completed to the left device injectAndVerifyDeviceDisconnected(mLeftDevice); reset(mNativeInterface); reset(mAudioManager); // Expect device to be inactive audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); assertThat(mService.getConnectionState(mLeftDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isTrue(); // Expect device to be inactive audioConfChangedEvent.valueInt5 = 0; mService.messageFromNative(audioConfChangedEvent); generateConnectionMessageFromNative( mRightDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); assertThat(mService.getConnectionState(mRightDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getConnectedDevices().contains(mRightDevice)).isTrue(); // Expect device to be active audioConfChangedEvent.valueInt5 = availableContexts; mService.messageFromNative(audioConfChangedEvent); verify(mNativeInterface, times(1)).groupSetActive(groupId); // Set group and device as active. injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( any(BluetoothDevice.class), eq(null), any(BluetoothProfileConnectionInfo.class)); } /** Test setting allowed contexts for active group */ @Test public void testSetAllowedContextsForActiveGroup() { Loading