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

Commit 413b256f authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes Ic1b35a35,I67f4949e into main

* changes:
  BassClientService: Execute one selectSource for all sinks for the same broadcaster
  le_periodic_sync_manager: Check if established sync was pending
parents a8562987 e3de0cd5
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -2560,11 +2560,21 @@ public class BassClientService extends ProfileService {
                if (broadcastId != BassConstants.INVALID_BROADCAST_ID
                        && getCachedBroadcast(broadcastId) != null) {
                    // If the source has been synced before, try to re-sync
                    // with the source by previously cached scan result
                    addSelectSourceRequest(getCachedBroadcast(broadcastId), true);
                    // with the source by previously cached scan result.
                    // Check if not added already
                    boolean alreadyAdded = false;
                    synchronized (mPendingSourcesToAdd) {
                        for (AddSourceData pendingSourcesToAdd : mPendingSourcesToAdd) {
                            if (pendingSourcesToAdd.mSourceMetadata.getBroadcastId()
                                    == broadcastId) {
                                alreadyAdded = true;
                            }
                        }
                        mPendingSourcesToAdd.add(
                                new AddSourceData(sink, sourceMetadata, isGroupOp));
                        if (!alreadyAdded) {
                            addSelectSourceRequest(getCachedBroadcast(broadcastId), true);
                        }
                    }
                } else {
                    log("AddSource: broadcast not cached or invalid, broadcastId: " + broadcastId);
+132 −0
Original line number Diff line number Diff line
@@ -983,6 +983,138 @@ public class BassClientServiceTest {
                        any(), any(), anyInt(), anyInt(), any(), any());
    }

    @Test
    public void testMultipleAddSourceToUnsyncedBroadcaster() {
        mSetFlagsRule.enableFlags(
                Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE);

        prepareConnectedDeviceGroup();
        startSearchingForSources();

        // Scan and sync 1
        onScanResult(mSourceDevice, TEST_BROADCAST_ID);
        InOrder inOrder = inOrder(mMethodProxy);
        inOrder.verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
        onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);

        // Stop searching to unsync broadcaster
        mBassClientService.stopSearchingForSources();

        // Sink1 aAdd source to unsynced broadcast, causes synchronization first
        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
        mBassClientService.addSource(mCurrentDevice, meta, false);
        handleHandoverSupport();
        inOrder.verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());

        // Sink2 add source to unsynced broadcast
        mBassClientService.addSource(mCurrentDevice1, meta, false);
        handleHandoverSupport();

        // Sync established
        onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
        TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());

        // Both add sources should be called to state machines
        assertThat(mStateMachines.size()).isEqualTo(2);
        for (BassClientStateMachine sm : mStateMachines.values()) {
            ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
            verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());

            Message msg =
                    messageCaptor.getAllValues().stream()
                            .filter(
                                    m ->
                                            (m.what == BassClientStateMachine.ADD_BCAST_SOURCE)
                                                    && (m.obj == meta))
                            .findFirst()
                            .orElse(null);
            assertThat(msg).isNotNull();
        }

        // There should be no second selectSource call
        inOrder.verify(mMethodProxy, never())
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
    }

    @Test
    public void testMultipleAddSourceToUnsyncedInactiveBroadcaster() {
        mSetFlagsRule.enableFlags(
                Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE);

        prepareConnectedDeviceGroup();
        startSearchingForSources();

        // Scan and sync 1
        onScanResult(mSourceDevice, TEST_BROADCAST_ID);
        InOrder inOrder = inOrder(mMethodProxy);
        inOrder.verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
        onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);

        // Stop searching to unsync broadcaster
        mBassClientService.stopSearchingForSources();

        // Sink1 aAdd source to unsynced broadcast, causes synchronization first
        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
        mBassClientService.addSource(mCurrentDevice, meta, false);
        handleHandoverSupport();
        inOrder.verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());

        // Sink2 add source to unsynced broadcast
        mBassClientService.addSource(mCurrentDevice1, meta, false);
        handleHandoverSupport();

        // Error in syncEstablished causes soureLost, sourceAddFailed notification for both sinks
        BassClientService.PACallback callback = mBassClientService.new PACallback();
        callback.onSyncEstablished(
                TEST_SYNC_HANDLE,
                mSourceDevice,
                TEST_ADVERTISER_SID,
                0,
                200,
                BluetoothGatt.GATT_FAILURE);
        TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
        InOrder inOrderCallback = inOrder(mCallback);
        try {
            inOrderCallback.verify(mCallback).onSourceLost(eq(TEST_BROADCAST_ID));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        try {
            inOrderCallback
                    .verify(mCallback)
                    .onSourceAddFailed(
                            eq(mCurrentDevice),
                            eq(meta),
                            eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        try {
            inOrderCallback
                    .verify(mCallback)
                    .onSourceAddFailed(
                            eq(mCurrentDevice1),
                            eq(meta),
                            eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

        // There should be no second selectSource call
        inOrder.verify(mMethodProxy, never())
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
    }

    @Test
    public void testStopSearchingForSources_timeoutForActiveSync() {
        mSetFlagsRule.enableFlags(
+14 −10
Original line number Diff line number Diff line
@@ -337,6 +337,7 @@ public:
      AdvanceRequest();
      return;
    }
    if (periodic_sync->sync_state == PERIODIC_SYNC_STATE_PENDING) {
      periodic_sync->sync_handle = event_view.GetSyncHandle();
      periodic_sync->sync_state = PERIODIC_SYNC_STATE_ESTABLISHED;
      callbacks_->OnPeriodicSyncStarted(periodic_sync->request_id, (uint8_t)event_view.GetStatus(),
@@ -349,6 +350,9 @@ public:
          periodic_syncs_.erase(periodic_sync);
        }
      }
    } else {
      log::debug("[PSync]: Wrong sync state={}", (uint8_t)(periodic_sync->sync_state));
    }

    AdvanceRequest();
  }
+63 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <flag_macros.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>

#include "hci/le_scanning_callback.h"
#include "hci/le_scanning_interface.h"
@@ -136,6 +137,7 @@ private:
class PeriodicSyncManagerTest : public ::testing::Test {
protected:
  void SetUp() override {
    __android_log_set_minimum_priority(ANDROID_LOG_VERBOSE);
    thread_ = new os::Thread("thread", os::Thread::Priority::NORMAL);
    handler_ = new os::Handler(thread_);
    test_le_scanning_interface_ = new TestLeScanningInterface();
@@ -814,6 +816,67 @@ TEST_F(PeriodicSyncManagerTest,
  sync_handler();
}

TEST_F(PeriodicSyncManagerTest, syncEstablished_pendingCheckToCorrectTheOrder) {
  uint16_t sync_handle = 0x12;
  uint8_t advertiser_sid = 0x02;
  Address address;
  Address::FromString("00:11:22:33:44:55", address);
  AddressWithType address_with_type = AddressWithType(address, AddressType::PUBLIC_DEVICE_ADDRESS);

  // start scan
  int request_id_1 = 0x01;
  PeriodicSyncStates request{
          .request_id = request_id_1,
          .advertiser_sid = advertiser_sid,
          .address_with_type = address_with_type,
          .sync_handle = sync_handle,
          .sync_state = PeriodicSyncState::PERIODIC_SYNC_STATE_IDLE,
  };
  periodic_sync_manager_->StartSync(request, 0x04, 0x0A);

  EXPECT_CALL(
          mock_callbacks_,
          OnPeriodicSyncStarted(request_id_1, static_cast<uint8_t>(ErrorCode::ADVERTISING_TIMEOUT),
                                _, _, _, _, _))
          .Times(1);

  // First timeout
  periodic_sync_manager_->OnStartSyncTimeout();

  // Second request with the same data but different id
  int request_id_2 = 0x02;
  request.request_id = request_id_2;
  periodic_sync_manager_->StartSync(request, 0x04, 0x0A);

  // Get LePeriodicAdvertisingSyncEstablished for the first request
  auto builder = LePeriodicAdvertisingSyncEstablishedBuilder::Create(
          ErrorCode::OPERATION_CANCELLED_BY_HOST, sync_handle, advertiser_sid,
          address_with_type.GetAddressType(), address_with_type.GetAddress(),
          SecondaryPhyType::LE_1M, 0xFF, ClockAccuracy::PPM_250);
  auto event_view = LePeriodicAdvertisingSyncEstablishedView::Create(
          LeMetaEventView::Create(EventView::Create(GetPacketView(std::move(builder)))));
  periodic_sync_manager_->HandleLePeriodicAdvertisingSyncEstablished(event_view);

  EXPECT_CALL(
          mock_callbacks_,
          OnPeriodicSyncStarted(request_id_2, static_cast<uint8_t>(ErrorCode::ADVERTISING_TIMEOUT),
                                _, _, _, _, _))
          .Times(1);

  // Second timeout
  periodic_sync_manager_->OnStartSyncTimeout();

  // Get LePeriodicAdvertisingSyncEstablished for the second request
  auto builder2 = LePeriodicAdvertisingSyncEstablishedBuilder::Create(
          ErrorCode::OPERATION_CANCELLED_BY_HOST, sync_handle, advertiser_sid,
          address_with_type.GetAddressType(), address_with_type.GetAddress(),
          SecondaryPhyType::LE_1M, 0xFF, ClockAccuracy::PPM_250);
  event_view = LePeriodicAdvertisingSyncEstablishedView::Create(
          LeMetaEventView::Create(EventView::Create(GetPacketView(std::move(builder2)))));
  periodic_sync_manager_->HandleLePeriodicAdvertisingSyncEstablished(event_view);
  sync_handler();
}

TEST_F(PeriodicSyncManagerTest, handle_periodic_advertising_report_test) {
  uint16_t sync_handle = 0x12;
  uint8_t advertiser_sid = 0x02;