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

Commit 90365c4d authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Automerger Merge Worker
Browse files

Merge changes I02e1827c,Ic7ad61a8 am: 2e2f0d0a am: 35507ba7 am: 5019c308...

Merge changes I02e1827c,Ic7ad61a8 am: 2e2f0d0a am: 35507ba7 am: 5019c308 am: af14de18 am: 6f9de6e0

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/2239598



Change-Id: I16cad81a08f6273f89c8233b5256a8fda12065c6
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents ac37fc52 6f9de6e0
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -597,6 +597,24 @@ public class CsipSetCoordinatorService extends ProfileService {
                .collect(Collectors.toList());
    }

    /**
     * Get group ID for a given device and UUID
     * @param device potential group member
     * @param uuid profile context UUID
     * @return group ID
     */
    public Integer getGroupId(BluetoothDevice device, ParcelUuid uuid) {
        Map<Integer, Integer> device_groups =
                mDeviceGroupIdRankMap.getOrDefault(device, new HashMap<>());
        return mGroupIdToUuidMap.entrySet()
                .stream()
                .filter(e -> (device_groups.containsKey(e.getKey())
                        && e.getValue().equals(uuid)))
                .map(Map.Entry::getKey)
                .findFirst()
                .orElse(IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID);
    }

    /**
     * Get device's groups/
     * @param device group member device
+7 −0
Original line number Diff line number Diff line
@@ -1933,6 +1933,13 @@ public class LeAudioService extends ProfileService {
    }

    private void notifyGroupNodeAdded(BluetoothDevice device, int groupId) {
        if (mVolumeControlService == null) {
            mVolumeControlService = mServiceFactory.getVolumeControlService();
        }
        if (mVolumeControlService != null) {
            mVolumeControlService.handleGroupNodeAdded(groupId, device);
        }

        if (mLeAudioCallbacks != null) {
            int n = mLeAudioCallbacks.beginBroadcast();
            for (int i = 0; i < n; i++) {
+38 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.BluetoothVolumeControl;
import android.bluetooth.IBluetoothCsipSetCoordinator;
import android.bluetooth.IBluetoothLeAudio;
import android.bluetooth.IBluetoothVolumeControl;
import android.bluetooth.IBluetoothVolumeControlCallback;
@@ -47,6 +48,7 @@ import com.android.bluetooth.btservice.AdapterService;
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.le_audio.LeAudioService;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.SynchronousResultReceiver;
@@ -193,7 +195,8 @@ public class VolumeControlService extends ProfileService {
    private BroadcastReceiver mBondStateChangedReceiver;
    private BroadcastReceiver mConnectionStateChangedReceiver;

    private final ServiceFactory mFactory = new ServiceFactory();
    @VisibleForTesting
    ServiceFactory mFactory = new ServiceFactory();

    public static boolean isEnabled() {
        return BluetoothProperties.isProfileVcpControllerEnabled().orElse(false);
@@ -632,6 +635,29 @@ public class VolumeControlService extends ProfileService {
        mVolumeControlNativeInterface.unmuteGroup(groupId);
    }

    /**
     * {@hide}
     */
    public void handleGroupNodeAdded(int groupId, BluetoothDevice device) {
        // Ignore disconnected device, its volume will be set once it connects
        synchronized (mStateMachines) {
            VolumeControlStateMachine sm = mStateMachines.get(device);
            if (sm == null) {
                return;
            }
            if (sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
                return;
            }
        }

        // If group volume has already changed, the new group member should set it
        Integer groupVolume = mGroupVolumeCache.getOrDefault(groupId,
                IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME);
        if (groupVolume != IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) {
            mVolumeControlNativeInterface.setVolume(device, groupVolume);
        }
    }

    void handleVolumeControlChanged(BluetoothDevice device, int groupId,
                                    int volume, boolean mute, boolean isAutonomous) {

@@ -974,6 +1000,17 @@ public class VolumeControlService extends ProfileService {
                }
                removeStateMachine(device);
            }
        } else if (toState == BluetoothProfile.STATE_CONNECTED) {
            // Restore the group volume if it was changed while the device was not yet connected.
            CsipSetCoordinatorService csipClient = mFactory.getCsipSetCoordinatorService();
            Integer groupId = csipClient.getGroupId(device, BluetoothUuid.CAP);
            if (groupId != IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID) {
                Integer groupVolume = mGroupVolumeCache.getOrDefault(groupId,
                        IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME);
                if (groupVolume != IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) {
                    mVolumeControlNativeInterface.setVolume(device, groupVolume);
                }
            }
        }
    }

+96 −0
Original line number Diff line number Diff line
@@ -41,7 +41,9 @@ import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;

import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ServiceFactory;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.csip.CsipSetCoordinatorService;
import com.android.bluetooth.R;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.x.com.android.modules.utils.SynchronousResultReceiver;
@@ -73,6 +75,7 @@ public class VolumeControlServiceTest {
    private VolumeControlService mService;
    private VolumeControlService.BluetoothVolumeControlBinder mServiceBinder;
    private BluetoothDevice mDevice;
    private BluetoothDevice mDeviceTwo;
    private HashMap<BluetoothDevice, LinkedBlockingQueue<Intent>> mDeviceQueueMap;
    private static final int TIMEOUT_MS = 1000;
    private static final int BT_LE_AUDIO_MAX_VOL = 255;
@@ -87,6 +90,8 @@ public class VolumeControlServiceTest {
    @Mock private DatabaseManager mDatabaseManager;
    @Mock private VolumeControlNativeInterface mNativeInterface;
    @Mock private AudioManager mAudioManager;
    @Mock private ServiceFactory mServiceFactory;
    @Mock private CsipSetCoordinatorService mCsipService;

    @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();

@@ -119,9 +124,12 @@ public class VolumeControlServiceTest {
        startService();
        mService.mVolumeControlNativeInterface = mNativeInterface;
        mService.mAudioManager = mAudioManager;
        mService.mFactory = mServiceFactory;
        mServiceBinder = (VolumeControlService.BluetoothVolumeControlBinder) mService.initBinder();
        mServiceBinder.mIsTesting = true;

        doReturn(mCsipService).when(mServiceFactory).getCsipSetCoordinatorService();

        // Override the timeout value to speed up the test
        VolumeControlStateMachine.sConnectTimeoutMs = TIMEOUT_MS;    // 1s

@@ -134,8 +142,10 @@ public class VolumeControlServiceTest {

        // Get a device for testing
        mDevice = TestUtils.getTestDevice(mAdapter, 0);
        mDeviceTwo = TestUtils.getTestDevice(mAdapter, 1);
        mDeviceQueueMap = new HashMap<>();
        mDeviceQueueMap.put(mDevice, new LinkedBlockingQueue<>());
        mDeviceQueueMap.put(mDeviceTwo, new LinkedBlockingQueue<>());
        doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService)
                .getBondState(any(BluetoothDevice.class));
        doReturn(new ParcelUuid[]{BluetoothUuid.VOLUME_CONTROL}).when(mAdapterService)
@@ -631,6 +641,92 @@ public class VolumeControlServiceTest {
        Assert.assertEquals(volume, mService.getGroupVolume(groupId));
    }

    /**
     * Test setting volume for a group member who connects after the volume level
     * for a group was already changed and cached.
     */
    @Test
    public void testLateConnectingDevice() throws Exception {
        int groupId = 1;
        int groupVolume = 56;

        // 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));

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

        mService.setGroupVolume(groupId, groupVolume);
        verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(groupVolume));
        verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume));

        // Verify that second device gets the proper group volume level when connected
        generateConnectionMessageFromNative(mDeviceTwo, BluetoothProfile.STATE_CONNECTED,
                BluetoothProfile.STATE_DISCONNECTED);
        Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
                mService.getConnectionState(mDeviceTwo));
        Assert.assertTrue(mService.getDevices().contains(mDeviceTwo));
        verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume));
    }

    /**
     * Test setting volume for a new group member who is discovered after the volume level
     * for a group was already changed and cached.
     */
    @Test
    public void testLateDiscoveredGroupMember() throws Exception {
        int groupId = 1;
        int groupVolume = 56;

        // For now only one device is in the group
        when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId);
        when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(-1);

        // 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));

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

        // Set the group volume
        mService.setGroupVolume(groupId, groupVolume);

        // Verify that second device will not get the group volume level if it is not a group member
        generateConnectionMessageFromNative(mDeviceTwo, BluetoothProfile.STATE_CONNECTED,
                BluetoothProfile.STATE_DISCONNECTED);
        Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
                mService.getConnectionState(mDeviceTwo));
        Assert.assertTrue(mService.getDevices().contains(mDeviceTwo));
        verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume));

        // But gets the volume when it becomes the group member
        when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId);
        mService.handleGroupNodeAdded(groupId, mDeviceTwo);
        verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume));
    }

    @Test
    public void testServiceBinderGetDevicesMatchingConnectionStates() throws Exception {
        final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+35 −20
Original line number Diff line number Diff line
@@ -743,13 +743,23 @@ class VolumeControlImpl : public VolumeControl {
                                     int group_id, bool is_autonomous,
                                     uint8_t opcode,
                                     std::vector<uint8_t>& arguments) {
    DLOG(INFO) << __func__ << " num of devices: " << devices.size()
               << " group_id: " << group_id
               << " is_autonomous: " << is_autonomous << " opcode: " << +opcode
               << " arg size: " << arguments.size();

    LOG_DEBUG(
        "num of devices: %zu, group_id: %d, is_autonomous: %s  opcode: %d, arg "
        "size: %zu",
        devices.size(), group_id, is_autonomous ? "true" : "false", +opcode,
        arguments.size());

    if (std::find_if(ongoing_operations_.begin(), ongoing_operations_.end(),
                     [opcode, &arguments](const VolumeOperation& op) {
                       return (op.opcode_ == opcode) &&
                              std::equal(op.arguments_.begin(),
                                         op.arguments_.end(),
                                         arguments.begin());
                     }) == ongoing_operations_.end()) {
      ongoing_operations_.emplace_back(latest_operation_id_++, group_id,
                                     is_autonomous, opcode, arguments, devices);
                                       is_autonomous, opcode, arguments,
                                       devices);
    }
  }

  void MuteUnmute(std::variant<RawAddress, int> addr_or_group_id, bool mute) {
@@ -760,11 +770,13 @@ class VolumeControlImpl : public VolumeControl {
    if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
      LOG_DEBUG("Address: %s: ",
                (std::get<RawAddress>(addr_or_group_id)).ToString().c_str());
      std::vector<RawAddress> devices = {
          std::get<RawAddress>(addr_or_group_id)};

      VolumeControlDevice* dev = volume_control_devices_.FindByAddress(
          std::get<RawAddress>(addr_or_group_id));
      if (dev && dev->IsConnected()) {
        std::vector<RawAddress> devices = {dev->address};
        PrepareVolumeControlOperation(devices, bluetooth::groups::kGroupUnknown,
                                      false, opcode, arg);
      }
    } else {
      /* Handle group change */
      auto group_id = std::get<int>(addr_or_group_id);
@@ -815,14 +827,17 @@ class VolumeControlImpl : public VolumeControl {
    uint8_t opcode = kControlPointOpcodeSetAbsoluteVolume;

    if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
      DLOG(INFO) << __func__ << " " << std::get<RawAddress>(addr_or_group_id);
      std::vector<RawAddress> devices = {
          std::get<RawAddress>(addr_or_group_id)};

      LOG_DEBUG("Address: %s: ",
                std::get<RawAddress>(addr_or_group_id).ToString().c_str());
      VolumeControlDevice* dev = volume_control_devices_.FindByAddress(
          std::get<RawAddress>(addr_or_group_id));
      if (dev && dev->IsConnected() && (dev->volume != volume)) {
        std::vector<RawAddress> devices = {dev->address};
        RemovePendingVolumeControlOperations(devices,
                                             bluetooth::groups::kGroupUnknown);
        PrepareVolumeControlOperation(devices, bluetooth::groups::kGroupUnknown,
                                      false, opcode, arg);
      }
    } else {
      /* Handle group change */
      auto group_id = std::get<int>(addr_or_group_id);
Loading