Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2e2f8a4b authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Łukasz Rymanowski (xWF)
Browse files

vc: Improve handlig Reset Flag

After reconnection of the remote device, which has persist flag set to 0x00
(reset volume), Android shall always try to set its own volume. Either
already groupVolume (which is there if one of the set member is
connected or group was previously connected), or volume taken from audio
framework.

Without this patch, if during streaming to first bud, second bud would
connect with reset volume != 0, the reset volume was applied to both
buds which is no expected behavior.

Bug: 365858502
Test: atest VolumeControlServiceTest
Test: manual on UPF
Flag: Exempt, regression tested with unit tests
Change-Id: I126aa05fa252ab280e04f2dc718604217a1e18fe
parent cdf04e40
Loading
Loading
Loading
Loading
+17 −14
Original line number Diff line number Diff line
@@ -805,15 +805,18 @@ public class VolumeControlService extends ProfileService {
                            + (", mute: " + mute)
                            + (", flags: " + flags));
            /* We are here, because system has just started and LeAudio device is connected. If
             * remote device has User Persistent flag set or the volume != 0, Android sets the
             * volume to local cache and to the audio system. If Reset Flag is set and remote has
             * volume set to 0, then Android sets to remote devices either cached volume volume
             * taken from audio manager. Note, to match BR/EDR behavior, don't show volume change in
             * UI here
             * remote device has User Persistent flag set, Android sets the volume to local cache
             * and to the audio system.
             * If Reset Flag is set, then Android sets to remote devices either cached volume volume
             * taken from audio manager.
             * Note, to match BR/EDR behavior, don't show volume change in UI here
             */
            if ((flags & VOLUME_FLAGS_PERSISTED_USER_SET_VOLUME_MASK) == 0x01 || (volume != 0)) {
            if ((flags & VOLUME_FLAGS_PERSISTED_USER_SET_VOLUME_MASK) == 0x01) {
                updateGroupCacheAndAudioSystem(groupId, volume, mute, false);
            } else {
                return;
            }

            // Reset flag is used
            if (groupVolume != IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) {
                Log.i(TAG, "Setting volume: " + groupVolume + " to the group: " + groupId);
                setGroupVolume(groupId, groupVolume);
@@ -822,7 +825,7 @@ public class VolumeControlService extends ProfileService {
                Log.i(TAG, "Setting system volume: " + vol + " to the group: " + groupId);
                setGroupVolume(groupId, getBleVolumeFromCurrentStream());
            }
            }

            return;
        }

+30 −76
Original line number Diff line number Diff line
@@ -804,79 +804,15 @@ public class VolumeControlServiceTest {
        verify(mAudioManager, times(1)).setStreamVolume(anyInt(), eq(expectedAfVol), anyInt());
    }

    /** Test if phone will set volume which is read from the buds */
    @Test
    public void testConnectedDeviceWithResetFlagSetWithNonZeroVolume() throws Exception {
    private void testConnectedDeviceWithResetFlag(
            int resetVolumeDeviceOne, int resetVolumeDeviceTwo) {
        int groupId = 1;
        int volumeDevice = 56;
        int volumeDeviceTwo = 100;
        int flags = 0;
        boolean initialMuteState = false;
        boolean initialAutonomousFlag = true;

        // Both devices are in the same group
        when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId);
        when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId);

        // Update the device policy so okToConnect() returns true
        when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
        when(mDatabaseManager.getProfileConnectionPolicy(
                        any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL)))
                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
        doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));

        generateDeviceAvailableMessageFromNative(mDevice, 1);
        generateConnectionMessageFromNative(
                mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
        Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice));
        Assert.assertTrue(mService.getDevices().contains(mDevice));

        // Group is not active, AF will not be notified
        generateVolumeStateChanged(
                mDevice, groupId, volumeDevice, flags, initialMuteState, initialAutonomousFlag);
        verify(mAudioManager, times(0)).setStreamVolume(anyInt(), anyInt(), anyInt());

        // Make device Active now. This will trigger setting volume to AF
        when(mLeAudioService.getActiveGroupId()).thenReturn(groupId);
        mServiceBinder.setGroupActive(groupId, true, mAttributionSource);
        int expectedAfVol =
                (int) Math.round((double) (volumeDevice * MEDIA_MAX_VOL) / BT_LE_AUDIO_MAX_VOL);
        verify(mAudioManager, times(1)).setStreamVolume(anyInt(), eq(expectedAfVol), anyInt());

        // Connect second device and read different volume. Expect it will be set to AF and to
        // another set member
        generateDeviceAvailableMessageFromNative(mDeviceTwo, 1);
        generateConnectionMessageFromNative(
                mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
        Assert.assertEquals(
                BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo));
        Assert.assertTrue(mService.getDevices().contains(mDeviceTwo));

        // Group is now active, AF will be notified. Native will take care to sync the volume
        generateVolumeStateChanged(
                mDeviceTwo,
                groupId,
                volumeDeviceTwo,
                flags,
                initialMuteState,
                initialAutonomousFlag);
        expectedAfVol =
                (int) Math.round((double) (volumeDeviceTwo * MEDIA_MAX_VOL) / BT_LE_AUDIO_MAX_VOL);
        verify(mAudioManager, times(1)).setStreamVolume(anyInt(), eq(expectedAfVol), anyInt());
    }
        int streamVolume = 30;
        int streamMaxVolume = 100;
        int resetFlag = 0;

    /** Test if phone will set volume to buds which has no volume */
    @Test
    public void testConnectedDeviceWithResetFlagSetWithZeroVolume() throws Exception {
        int groupId = 1;
        int volumeDevice = 0;
        int volumeDeviceTwo = 0;
        int flags = 0;
        boolean initialMuteState = false;
        boolean initialAutonomousFlag = true;
        int streamVolume = 50;
        int streamMaxVolume = 100;

        // Both devices are in the same group
        when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId);
@@ -899,19 +835,25 @@ public class VolumeControlServiceTest {
        Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice));
        Assert.assertTrue(mService.getDevices().contains(mDevice));

        // Group is not active, AF will not be notified but device will get phone volume
        int expectedDeviceVol =
        int expectedAfVol =
                (int) Math.round((double) streamVolume * BT_LE_AUDIO_MAX_VOL / streamMaxVolume);

        // Group is not active, AF will not be notified
        generateVolumeStateChanged(
                mDevice, groupId, volumeDevice, flags, initialMuteState, initialAutonomousFlag);
                mDevice,
                groupId,
                resetVolumeDeviceOne,
                resetFlag,
                initialMuteState,
                initialAutonomousFlag);
        verify(mAudioManager, times(0)).setStreamVolume(anyInt(), anyInt(), anyInt());
        verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(expectedDeviceVol));

        // Make device Active now. This will trigger setting volume to AF
        when(mLeAudioService.getActiveGroupId()).thenReturn(groupId);
        mServiceBinder.setGroupActive(groupId, true, mAttributionSource);

        verify(mAudioManager, times(1)).setStreamVolume(anyInt(), eq(streamVolume), anyInt());
        verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(expectedAfVol));

        // Connect second device and read different volume. Expect it will be set to AF and to
        // another set member
@@ -926,13 +868,25 @@ public class VolumeControlServiceTest {
        generateVolumeStateChanged(
                mDeviceTwo,
                groupId,
                volumeDeviceTwo,
                flags,
                resetVolumeDeviceTwo,
                resetFlag,
                initialMuteState,
                initialAutonomousFlag);

        verify(mAudioManager, times(1)).setStreamVolume(anyInt(), anyInt(), anyInt());
        verify(mNativeInterface, times(2)).setGroupVolume(eq(groupId), eq(expectedDeviceVol));
        verify(mNativeInterface, times(2)).setGroupVolume(eq(groupId), eq(expectedAfVol));
    }

    /** Test if phone will set volume which is read from the buds */
    @Test
    public void testConnectedDeviceWithResetFlagSetWithNonZeroVolume() throws Exception {
        testConnectedDeviceWithResetFlag(56, 100);
    }

    /** Test if phone will set volume to buds which has no volume */
    @Test
    public void testConnectedDeviceWithResetFlagSetWithZeroVolume() throws Exception {
        testConnectedDeviceWithResetFlag(0, 0);
    }

    /**