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

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

Merge changes I0441e46e,Iee94cbd4 into main

* changes:
  le_periodic_sync_manager: Prevent from operation on invalid iterator
  BassClientService: Remove cached broadcast only on active searching
parents 04eabe2c 8a754ad9
Loading
Loading
Loading
Loading
+34 −7
Original line number Diff line number Diff line
@@ -1892,9 +1892,12 @@ public class BassClientService extends ProfileService {
                int skip,
                int timeout,
                int status) {
            int broadcastId = getBroadcastIdForSyncHandle(BassConstants.INVALID_SYNC_HANDLE);
            log(
                    "onSyncEstablished syncHandle: "
                            + syncHandle
                            + ", broadcastId: "
                            + broadcastId
                            + ", device: "
                            + device
                            + ", advertisingSid: "
@@ -1944,9 +1947,7 @@ public class BassClientService extends ProfileService {
                    Iterator<AddSourceData> iterator = mPendingSourcesToAdd.iterator();
                    while (iterator.hasNext()) {
                        AddSourceData pendingSourcesToAdd = iterator.next();
                        BluetoothDevice sourceDevice =
                                pendingSourcesToAdd.mSourceMetadata.getSourceDevice();
                        if (sourceDevice.equals(device)) {
                        if (pendingSourcesToAdd.mSourceMetadata.getBroadcastId() == broadcastId) {
                            addSource(
                                    pendingSourcesToAdd.mSink,
                                    pendingSourcesToAdd.mSourceMetadata,
@@ -1957,9 +1958,31 @@ public class BassClientService extends ProfileService {
                }
            } else {
                // remove failed sync handle
                int broadcastId = getBroadcastIdForSyncHandle(BassConstants.INVALID_SYNC_HANDLE);
                log("onSyncEstablished failed for broadcast id: " + broadcastId);
                boolean notifiedOfLost = false;
                synchronized (mPendingSourcesToAdd) {
                    Iterator<AddSourceData> iterator = mPendingSourcesToAdd.iterator();
                    while (iterator.hasNext()) {
                        AddSourceData pendingSourcesToAdd = iterator.next();
                        if (pendingSourcesToAdd.mSourceMetadata.getBroadcastId() == broadcastId) {
                            if (!notifiedOfLost) {
                                notifiedOfLost = true;
                                mCallbacks.notifySourceLost(broadcastId);
                            }
                            mCallbacks.notifySourceAddFailed(
                                    pendingSourcesToAdd.mSink,
                                    pendingSourcesToAdd.mSourceMetadata,
                                    BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES);
                            iterator.remove();
                        }
                    }
                }
                synchronized (mSearchScanCallbackLock) {
                    // Clear from cache to make possible sync again (only during active searching)
                    if (mSearchScanCallback != null) {
                        mCachedBroadcasts.remove(broadcastId);
                    }
                }
                mPeriodicAdvCallbacksMap.remove(BassConstants.INVALID_SYNC_HANDLE);
            }
            handleSelectSourceRequest();
@@ -2029,9 +2052,13 @@ public class BassClientService extends ProfileService {
                }
            }
            clearAllDataForSyncHandle(syncHandle);
            // Clear from cache to make possible sync again
            // Clear from cache to make possible sync again (only during active searching)
            synchronized (mSearchScanCallbackLock) {
                if (mSearchScanCallback != null) {
                    mCachedBroadcasts.remove(broadcastId);
                }
            }
        }

        @Override
        public void onBigInfoAdvertisingReport(int syncHandle, boolean encrypted) {
+210 −0
Original line number Diff line number Diff line
@@ -773,6 +773,216 @@ public class BassClientServiceTest {
        }
    }

    @Test
    public void testNotRemovingCachedBroadcastOnLostWithoutScanning() {
        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);

        // Sync lost during scanning removes cached broadcast
        BassClientService.PACallback callback = mBassClientService.new PACallback();
        callback.onSyncLost(TEST_SYNC_HANDLE);

        // Add source to not cached broadcast cause addFailed notification
        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
        mBassClientService.addSource(mCurrentDevice, meta, true);
        handleHandoverSupport();
        TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
        try {
            verify(mCallback)
                    .onSourceAddFailed(
                            eq(mCurrentDevice),
                            eq(meta),
                            eq(BluetoothStatusCodes.ERROR_BAD_PARAMETERS));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

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

        // Stop searching
        mBassClientService.stopSearchingForSources();

        // Sync lost without active scanning should not remove broadcast cache
        callback.onSyncLost(TEST_SYNC_HANDLE);

        // Add source to unsynced broadcast, causes synchronization first
        mBassClientService.addSource(mCurrentDevice, meta, true);
        handleHandoverSupport();
        inOrder.verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
    }

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

        final BluetoothDevice device1 =
                mBluetoothAdapter.getRemoteLeDevice(
                        "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM);
        final BluetoothDevice device2 =
                mBluetoothAdapter.getRemoteLeDevice(
                        "00:11:22:33:44:22", BluetoothDevice.ADDRESS_TYPE_RANDOM);
        final BluetoothDevice device3 =
                mBluetoothAdapter.getRemoteLeDevice(
                        "00:11:22:33:44:33", BluetoothDevice.ADDRESS_TYPE_RANDOM);
        final BluetoothDevice device4 =
                mBluetoothAdapter.getRemoteLeDevice(
                        "00:11:22:33:44:44", BluetoothDevice.ADDRESS_TYPE_RANDOM);
        final BluetoothDevice device5 =
                mBluetoothAdapter.getRemoteLeDevice(
                        "00:11:22:33:44:55", BluetoothDevice.ADDRESS_TYPE_RANDOM);
        final int handle1 = 0;
        final int handle2 = 1;
        final int handle3 = 2;
        final int handle4 = 3;
        final int handle5 = 4;
        final int broadcastId1 = 1111;
        final int broadcastId2 = 2222;
        final int broadcastId3 = 3333;
        final int broadcastId4 = 4444;
        final int broadcastId5 = 5555;

        prepareConnectedDeviceGroup();
        startSearchingForSources();

        // Scan and sync 5 sources cause removing 1 synced element
        onScanResult(device1, broadcastId1);
        onSyncEstablished(device1, handle1);
        onScanResult(device2, broadcastId2);
        onSyncEstablished(device2, handle2);
        onScanResult(device3, broadcastId3);
        onSyncEstablished(device3, handle3);
        onScanResult(device4, broadcastId4);
        onSyncEstablished(device4, handle4);
        onScanResult(device5, broadcastId5);
        onSyncEstablished(device5, handle5);
        InOrder inOrder = inOrder(mMethodProxy);
        inOrder.verify(mMethodProxy, times(5))
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());

        BluetoothLeBroadcastMetadata.Builder builder =
                new BluetoothLeBroadcastMetadata.Builder()
                        .setEncrypted(false)
                        .setSourceDevice(device1, BluetoothDevice.ADDRESS_TYPE_RANDOM)
                        .setSourceAdvertisingSid(TEST_ADVERTISER_SID)
                        .setBroadcastId(broadcastId1)
                        .setBroadcastCode(null)
                        .setPaSyncInterval(TEST_PA_SYNC_INTERVAL)
                        .setPresentationDelayMicros(TEST_PRESENTATION_DELAY_MS);
        // builder expect at least one subgroup
        builder.addSubgroup(createBroadcastSubgroup());
        BluetoothLeBroadcastMetadata meta = builder.build();

        // Add source to unsynced broadcast, causes synchronization first
        mBassClientService.addSource(mCurrentDevice, meta, true);
        handleHandoverSupport();
        inOrder.verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());

        // Error in syncEstablished causes soureLost, sourceAddFailed notification
        // and removing cache because scanning is active
        BassClientService.PACallback callback = mBassClientService.new PACallback();
        callback.onSyncEstablished(
                handle1, device1, TEST_ADVERTISER_SID, 0, 200, BluetoothGatt.GATT_FAILURE);
        TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
        InOrder inOrderCallback = inOrder(mCallback);
        try {
            inOrderCallback.verify(mCallback).onSourceLost(eq(broadcastId1));
        } 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();
        }

        // Add source to not cached broadcast causes addFailed notification
        mBassClientService.addSource(mCurrentDevice, meta, true);
        handleHandoverSupport();
        TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
        try {
            inOrderCallback
                    .verify(mCallback)
                    .onSourceAddFailed(
                            eq(mCurrentDevice),
                            eq(meta),
                            eq(BluetoothStatusCodes.ERROR_BAD_PARAMETERS));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

        // Scan and sync again
        onScanResult(device1, broadcastId1);
        inOrder.verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
        onSyncEstablished(device1, handle1);

        // Stop searching
        mBassClientService.stopSearchingForSources();

        // Add source to unsynced broadcast, causes synchronization first
        mBassClientService.addSource(mCurrentDevice, meta, true);
        handleHandoverSupport();
        inOrder.verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());

        // Error in syncEstablished causes soureLost, sourceAddFailed notification
        // and not removing cache because scanning is inactice
        callback.onSyncEstablished(
                handle1, device1, TEST_ADVERTISER_SID, 0, 200, BluetoothGatt.GATT_FAILURE);
        TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
        try {
            inOrderCallback.verify(mCallback).onSourceLost(eq(broadcastId1));
        } 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();
        }

        // Add source to unsynced broadcast, causes synchronization first
        mBassClientService.addSource(mCurrentDevice, meta, true);
        handleHandoverSupport();
        inOrder.verify(mMethodProxy)
                .periodicAdvertisingManagerRegisterSync(
                        any(), any(), anyInt(), anyInt(), any(), any());
    }

    @Test
    public void testStopSearchingForSources_timeoutForActiveSync() {
        mSetFlagsRule.enableFlags(
+43 −14
Original line number Diff line number Diff line
@@ -109,7 +109,7 @@ public:
    log::debug("[PSync]: handle = {}", handle);
    auto periodic_sync = GetEstablishedSyncFromHandle(handle);
    if (periodic_sync == periodic_syncs_.end()) {
      log::error("[PSync]: invalid index for handle {}", handle);
      log::error("[PSync]: index not found for handle {}", handle);
      le_scanning_interface_->EnqueueCommand(
              hci::LePeriodicAdvertisingTerminateSyncBuilder::Create(handle),
              handler_->BindOnce(check_complete<LePeriodicAdvertisingTerminateSyncCompleteView>));
@@ -125,7 +125,7 @@ public:
    log::debug("[PSync]");
    auto periodic_sync = GetSyncFromAddressAndSid(address, adv_sid);
    if (periodic_sync == periodic_syncs_.end()) {
      log::error("[PSync]:Invalid index for sid={}", adv_sid);
      log::error("[PSync]: index not found for address={} and SID={:04X}", address, adv_sid);
      return;
    }

@@ -211,15 +211,24 @@ public:
    log::assert_that(status_view.IsValid(), "assert failed: status_view.IsValid()");
    auto status = status_view.GetStatus();
    if (status != ErrorCode::SUCCESS) {
      if (pending_sync_requests_.empty()) {
        log::error("pending_sync_requests_ empty");
        return;
      }
      auto& request = pending_sync_requests_.front();
      request.sync_timeout_alarm.Cancel();
      log::warn("Got a Command status {}, status {}, SID={:04X}, bd_addr={}",
                OpCodeText(view.GetCommandOpCode()), ErrorCodeText(status), request.advertiser_sid,
                request.address_with_type);

      uint8_t adv_sid = request.advertiser_sid;
      AddressWithType address_with_type = request.address_with_type;
      auto sync = GetSyncFromAddressWithTypeAndSid(address_with_type, adv_sid);
      auto sync =
              GetSyncFromAddressWithTypeAndSid(request.address_with_type, request.advertiser_sid);
      if (sync == periodic_syncs_.end()) {
        log::error("[PSync]: index not found for address={} and SID={:04X}",
                   request.address_with_type, request.advertiser_sid);
        AdvanceRequest();
        return;
      }
      callbacks_->OnPeriodicSyncStarted(sync->request_id, (uint8_t)status, 0, sync->advertiser_sid,
                                        request.address_with_type, 0, 0);
      periodic_syncs_.erase(sync);
@@ -237,6 +246,10 @@ public:
    log::assert_that(status_view.IsValid(), "assert failed: status_view.IsValid()");
    auto status = status_view.GetStatus();
    if (status != ErrorCode::SUCCESS) {
      if (pending_sync_requests_.empty()) {
        log::error("pending_sync_requests_ empty");
        return;
      }
      auto& request = pending_sync_requests_.front();
      request.sync_timeout_alarm.Cancel();
      log::warn("Got a Command complete {}, status {}, SID={:04X}, bd_addr={}",
@@ -308,11 +321,13 @@ public:
        break;
    }

    auto periodic_sync = GetSyncFromAddressWithTypeAndSid(
            AddressWithType(event_view.GetAdvertiserAddress(), temp_address_type),
    auto address_with_temp_type =
            AddressWithType(event_view.GetAdvertiserAddress(), temp_address_type);
    auto periodic_sync = GetSyncFromAddressWithTypeAndSid(address_with_temp_type,
                                                          event_view.GetAdvertisingSid());
    if (periodic_sync == periodic_syncs_.end()) {
      log::warn("[PSync]: Invalid address and sid for sync established");
      log::warn("[PSync]: index not found for address={} and SID={:04X}", address_with_temp_type,
                event_view.GetAdvertisingSid());
      if (event_view.GetStatus() == ErrorCode::SUCCESS) {
        log::warn("Terminate sync");
        le_scanning_interface_->EnqueueCommand(
@@ -410,17 +425,24 @@ public:
  }

  void OnStartSyncTimeout() {
    if (pending_sync_requests_.empty()) {
      log::error("pending_sync_requests_ empty");
      return;
    }
    auto& request = pending_sync_requests_.front();
    log::warn("sync timeout SID={:04X}, bd_addr={}", request.advertiser_sid,
              request.address_with_type);
    uint8_t adv_sid = request.advertiser_sid;
    AddressWithType address_with_type = request.address_with_type;
    auto sync = GetSyncFromAddressWithTypeAndSid(address_with_type, adv_sid);
    le_scanning_interface_->EnqueueCommand(
            hci::LePeriodicAdvertisingCreateSyncCancelBuilder::Create(),
            handler_->BindOnceOn(
                    this, &PeriodicSyncManager::HandlePeriodicAdvertisingCreateSyncCancelStatus<
                                  LePeriodicAdvertisingCreateSyncCancelCompleteView>));
    auto sync = GetSyncFromAddressWithTypeAndSid(request.address_with_type, request.advertiser_sid);
    if (sync == periodic_syncs_.end()) {
      log::error("[PSync]: index not found for address={} and SID={:04X}",
                 request.address_with_type, request.advertiser_sid);
      return;
    }
    int status = static_cast<int>(ErrorCode::ADVERTISING_TIMEOUT);
    callbacks_->OnPeriodicSyncStarted(sync->request_id, status, 0, sync->advertiser_sid,
                                      request.address_with_type, 0, 0);
@@ -513,8 +535,6 @@ private:
                    PeriodicSyncCteType::AVOID_AOD_CONSTANT_TONE_EXTENSION_WITH_ONE_US_SLOTS) |
            static_cast<uint8_t>(
                    PeriodicSyncCteType::AVOID_AOD_CONSTANT_TONE_EXTENSION_WITH_TWO_US_SLOTS);
    auto sync = GetSyncFromAddressWithTypeAndSid(address_with_type, sid);
    sync->sync_state = PERIODIC_SYNC_STATE_PENDING;
    AdvertisingAddressType advertisingAddressType =
            static_cast<AdvertisingAddressType>(address_with_type.GetAddressType());
    le_scanning_interface_->EnqueueCommand(
@@ -540,6 +560,15 @@ private:
    }
    request.busy = true;
    request.sync_timeout_alarm.Cancel();

    auto sync = GetSyncFromAddressWithTypeAndSid(request.address_with_type, request.advertiser_sid);
    if (sync == periodic_syncs_.end()) {
      log::warn("[PSync]: index not found for address={} and SID={:04X}", request.address_with_type,
                request.advertiser_sid);
      AdvanceRequest();
      return;
    }
    sync->sync_state = PERIODIC_SYNC_STATE_PENDING;
    HandleStartSyncRequest(request.advertiser_sid, request.address_with_type, request.skip,
                           request.sync_timeout);
    request.sync_timeout_alarm.Schedule(
+61 −0
Original line number Diff line number Diff line
@@ -753,6 +753,67 @@ TEST_F_WITH_FLAGS(PeriodicSyncManagerTest,
  sync_handler();
}

TEST_F(PeriodicSyncManagerTest, onStartSyncTimeout_callWithoutPendingRequestsAndPeriodicSyncs) {
  periodic_sync_manager_->OnStartSyncTimeout();
  sync_handler();
}

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

  uint8_t advertiser_sid_1 = 0x02;
  int request_id_1 = 0x01;
  PeriodicSyncStates request{
          .request_id = request_id_1,
          .advertiser_sid = advertiser_sid_1,
          .address_with_type = address_with_type,
          .sync_handle = sync_handle,
          .sync_state = PeriodicSyncState::PERIODIC_SYNC_STATE_IDLE,
  };
  ASSERT_NO_FATAL_FAILURE(test_le_scanning_interface_->SetCommandFuture());
  periodic_sync_manager_->StartSync(request, 0x04, 0x0A);

  // First timeout to erase periodic_syncs_
  periodic_sync_manager_->OnStartSyncTimeout();
  // Second to actual check
  periodic_sync_manager_->OnStartSyncTimeout();
  sync_handler();
}

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

  int request_id_1 = 0x01;
  uint8_t advertiser_sid_1 = 0x02;
  PeriodicSyncStates request{
          .request_id = request_id_1,
          .advertiser_sid = advertiser_sid_1,
          .address_with_type = address_with_type,
          .sync_handle = sync_handle,
          .sync_state = PeriodicSyncState::PERIODIC_SYNC_STATE_IDLE,
  };
  ASSERT_NO_FATAL_FAILURE(test_le_scanning_interface_->SetCommandFuture());
  periodic_sync_manager_->StartSync(request, 0x04, 0x0A);

  // Timeout to erase periodic_syncs_
  periodic_sync_manager_->OnStartSyncTimeout();

  auto packet =
          test_le_scanning_interface_->GetCommand(OpCode::LE_PERIODIC_ADVERTISING_CREATE_SYNC);
  LePeriodicAdvertisingCreateSyncView::Create(LeScanningCommandView::Create(packet));
  test_le_scanning_interface_->CommandStatusCallback(
          LePeriodicAdvertisingCreateSyncStatusBuilder::Create(ErrorCode::MEMORY_CAPACITY_EXCEEDED,
                                                               0x00));
  sync_handler();
}

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