chromium/device/bluetooth/bluez/bluetooth_bluez_unittest.cc

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <optional>
#include <utility>
#include <vector>

#include "base/containers/contains.h"
#include "base/debug/dump_without_crashing.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/current_thread.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/bind.h"
#include "base/test/mock_callback.h"
#include "base/test/repeating_test_future.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "dbus/object_path.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_common.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
#include "device/bluetooth/bluez/bluetooth_adapter_bluez.h"
#include "device/bluetooth/bluez/bluetooth_device_bluez.h"
#include "device/bluetooth/bluez/bluetooth_pairing_bluez.h"
#include "device/bluetooth/dbus/bluez_dbus_manager.h"
#include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_admin_policy_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_agent_manager_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_battery_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_device_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_gatt_service_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_input_client.h"
#include "device/bluetooth/test/mock_pairing_delegate.h"
#include "device/bluetooth/test/test_bluetooth_adapter_observer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "chromeos/constants/chromeos_features.h"
#include "device/bluetooth/bluetooth_low_energy_scan_filter.h"
#include "device/bluetooth/bluetooth_low_energy_scan_session.h"
#include "device/bluetooth/chromeos/bluetooth_utils.h"
#include "device/bluetooth/dbus/fake_bluetooth_advertisement_monitor_manager_client.h"
#include "device/bluetooth/dbus/fake_bluetooth_le_advertising_manager_client.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "services/data_decoder/public/mojom/ble_scan_parser.mojom.h"
#endif  // BUILDFLAG(IS_CHROMEOS)

namespace {

BluetoothAdapter;
BluetoothAdapterFactory;
BluetoothDevice;
BatteryInfo;
BatteryType;
BluetoothDeviceType;
BluetoothDiscoveryFilter;
BluetoothDiscoverySession;
BluetoothUUID;
MockPairingDelegate;
TestBluetoothAdapterObserver;
_;
StrictMock;

#if BUILDFLAG(IS_CHROMEOS)
// Background scanning filter values.
constexpr int16_t kBackgroundScanningDeviceFoundRSSIThreshold = -80;
constexpr int16_t kBackgroundScanningDeviceLostRSSIThreshold = -100;
constexpr base::TimeDelta kBackgroundScanningDeviceFoundTimeout =
    base::Seconds(1);
constexpr base::TimeDelta kBackgroundScanningDeviceLostTimeout =
    base::Seconds(5);
// This pattern value encodes the Fast Initiation service ID of 0xfe2c and the
// model ID of 0xfc128e.
constexpr uint8_t kBackgroundScanningFilterPatternValue[] = {0x2c, 0xfe, 0xfc,
                                                             0x12, 0x8e};
std::unique_ptr<device::BluetoothLowEnergyScanFilter>
CreateLowEnergyScanFilter() {
  auto pattern_value =
      std::vector<uint8_t>(std::begin(kBackgroundScanningFilterPatternValue),
                           std::end(kBackgroundScanningFilterPatternValue));
  device::BluetoothLowEnergyScanFilter::Pattern pattern(
      /*start_position=*/0,
      device::BluetoothLowEnergyScanFilter::AdvertisementDataType::kServiceData,
      std::move(pattern_value));
  return device::BluetoothLowEnergyScanFilter::Create(
      kBackgroundScanningDeviceFoundRSSIThreshold,
      kBackgroundScanningDeviceLostRSSIThreshold,
      kBackgroundScanningDeviceFoundTimeout,
      kBackgroundScanningDeviceLostTimeout, {pattern},
      /*rssi_sampling_period=*/std::nullopt);
}

bluez::FakeBluetoothAdvertisementMonitorApplicationServiceProvider*
GetAdvertisementMonitorApplicationManger() {
  return static_cast<bluez::FakeBluetoothAdvertisementMonitorManagerClient*>(
             bluez::BluezDBusManager::Get()
                 ->GetBluetoothAdvertisementMonitorManagerClient())
      ->application_provider();
}
#endif  // BUILDFLAG(IS_CHROMEOS)

void ScheduleAsynchronousCancelPairing(BluetoothDevice* device) {}

void ScheduleAsynchronousRejectPairing(BluetoothDevice* device) {}

}  // namespace

namespace bluez {

namespace {

bool* dump_without_crashing_flag;
extern "C" void HandleDumpWithoutCrashing() {}

// Callback for BluetoothDevice::GetConnectionInfo() that simply saves the
// connection info to the bound argument.
void SaveConnectionInfo(BluetoothDevice::ConnectionInfo* out,
                        const BluetoothDevice::ConnectionInfo& conn_info) {}

// Find |address| in |devices|, if found returns the index otherwise returns -1.
int GetDeviceIndexByAddress(const BluetoothAdapter::DeviceList& devices,
                            const char* address) {}

#if BUILDFLAG(IS_CHROMEOS)
class FakeBleScanParserImpl : public data_decoder::mojom::BleScanParser {
 public:
  FakeBleScanParserImpl() = default;

  FakeBleScanParserImpl(const FakeBleScanParserImpl&) = delete;
  FakeBleScanParserImpl& operator=(const FakeBleScanParserImpl&) = delete;

  ~FakeBleScanParserImpl() override = default;

  // mojom::BleScanParser:
  void Parse(const std::vector<uint8_t>& advertisement_data,
             ParseCallback callback) override {
    std::move(callback).Run(nullptr);
  }
};
#endif  // BUILDFLAG(IS_CHROMEOS)

MockDBusErrorCallback;

class FakeBluetoothProfileServiceProviderDelegate
    : public bluez::BluetoothProfileServiceProvider::Delegate {};

#if BUILDFLAG(IS_CHROMEOS)
class FakeBluetoothLowEnergyScanSessionDelegate
    : public device::BluetoothLowEnergyScanSession::Delegate {
 public:
  FakeBluetoothLowEnergyScanSessionDelegate() = default;

  // device::BluetoothLowEnergyScanSession::Delegate
  void OnSessionStarted(
      device::BluetoothLowEnergyScanSession* scan_session,
      std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
          error_code) override {
    sessions_started_.push_back(std::make_pair(scan_session, error_code));
  }
  void OnDeviceFound(device::BluetoothLowEnergyScanSession* scan_session,
                     device::BluetoothDevice* device) override {
    devices_found_.push_back(std::make_pair(scan_session, device));
  }
  void OnDeviceLost(device::BluetoothLowEnergyScanSession* scan_session,
                    device::BluetoothDevice* device) override {
    devices_lost_.push_back(std::make_pair(scan_session, device));
  }
  void OnSessionInvalidated(
      device::BluetoothLowEnergyScanSession* scan_session) override {
    sessions_invalidated_.push_back(scan_session);
  }

  const std::vector<std::pair<
      device::BluetoothLowEnergyScanSession*,
      std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>>>&
  sessions_started() const {
    return sessions_started_;
  }

  const std::vector<std::pair<device::BluetoothLowEnergyScanSession*,
                              device::BluetoothDevice*>>&
  devices_found() const {
    return devices_found_;
  }

  const std::vector<std::pair<device::BluetoothLowEnergyScanSession*,
                              device::BluetoothDevice*>>&
  devices_lost() const {
    return devices_lost_;
  }

  const std::vector<
      raw_ptr<device::BluetoothLowEnergyScanSession, VectorExperimental>>&
  sessions_invalidated() const {
    return sessions_invalidated_;
  }

  base::WeakPtr<FakeBluetoothLowEnergyScanSessionDelegate> GetWeakPtr() {
    return weak_ptr_factory_.GetWeakPtr();
  }

 private:
  std::vector<std::pair<
      device::BluetoothLowEnergyScanSession*,
      std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>>>
      sessions_started_;
  std::vector<std::pair<device::BluetoothLowEnergyScanSession*,
                        device::BluetoothDevice*>>
      devices_found_;
  std::vector<std::pair<device::BluetoothLowEnergyScanSession*,
                        device::BluetoothDevice*>>
      devices_lost_;
  std::vector<
      raw_ptr<device::BluetoothLowEnergyScanSession, VectorExperimental>>
      sessions_invalidated_;

  base::WeakPtrFactory<FakeBluetoothLowEnergyScanSessionDelegate>
      weak_ptr_factory_{this};
};
#endif  // BUILDFLAG(IS_CHROMEOS)
}  // namespace

class BluetoothBlueZTest : public testing::Test {};

// This class was created to test BluetoothDeviceBluez::Connect() and
// BluetoothDeviceBluez::ConnectClassic(), two nearly identical methods, without
// having to repeat each test. Each test that would call either of these methods
// invokes PerformConnect() which will choose either
// BluetoothDeviceBluez::Connect() or BluetoothDeviceBluez::ConnectClassic()
// depending on the boolean returned by GetParam().
class BluetoothBlueZTestP : public BluetoothBlueZTest,
                            public testing::WithParamInterface<bool> {};

#if BUILDFLAG(IS_CHROMEOS)
INSTANTIATE_TEST_SUITE_P(All,
                         BluetoothBlueZTestP,
                         /*should_use_connect_classic=*/testing::Bool());
#else   // BUILDFLAG(IS_CHROMEOS)
INSTANTIATE_TEST_SUITE_P();
#endif  // BUILDFLAG(IS_CHROMEOS)

const char BluetoothBlueZTest::kGapUuid[] =;
const char BluetoothBlueZTest::kGattUuid[] =;
const char BluetoothBlueZTest::kPnpUuid[] =;
const char BluetoothBlueZTest::kHeadsetUuid[] =;

TEST_F(BluetoothBlueZTest, AlreadyPresent) {}

TEST_F(BluetoothBlueZTest, BecomePresent) {}

TEST_F(BluetoothBlueZTest, BecomeNotPresent) {}

TEST_F(BluetoothBlueZTest, SecondAdapter) {}

TEST_F(BluetoothBlueZTest, BecomePowered) {}

TEST_F(BluetoothBlueZTest, BecomeNotPowered) {}

TEST_F(BluetoothBlueZTest, SetPoweredWhenNotPresent) {}

TEST_F(BluetoothBlueZTest, ChangeAdapterName) {}

TEST_F(BluetoothBlueZTest, ChangeAdapterNameWhenNotPresent) {}

TEST_F(BluetoothBlueZTest, GetUUIDs) {}

TEST_F(BluetoothBlueZTest, BecomeDiscoverable) {}

TEST_F(BluetoothBlueZTest, BecomeNotDiscoverable) {}

TEST_F(BluetoothBlueZTest, SetDiscoverableWhenNotPresent) {}

TEST_F(BluetoothBlueZTest, StopDiscovery) {}

TEST_F(BluetoothBlueZTest, Discovery) {}

TEST_F(BluetoothBlueZTest, PoweredAndDiscovering) {}

// This unit test asserts that a DiscoverySession which is queued to start does
// not get interrupted by a stop discovery call being executed.
TEST_F(BluetoothBlueZTest, StopAndStartDiscoverySimultaneously) {}

// This unit test asserts that the basic reference counting logic works
// correctly for discovery requests done via the BluetoothAdapter.
TEST_F(BluetoothBlueZTest, MultipleDiscoverySessions) {}

// This unit test asserts that the reference counting logic works correctly in
// the cases when the adapter gets reset and D-Bus calls are made outside of
// the BluetoothAdapter.
TEST_F(BluetoothBlueZTest, UnexpectedChangesDuringMultipleDiscoverySessions) {}

TEST_F(BluetoothBlueZTest, InvalidatedDiscoverySessions) {}

TEST_F(BluetoothBlueZTest, StartDiscoverySession) {}

TEST_F(BluetoothBlueZTest, SetDiscoveryFilterBeforeStartDiscovery) {}

TEST_F(BluetoothBlueZTest, SetDiscoveryFilterBeforeStartDiscoveryFail) {}

// This unit test asserts that the basic reference counting, and filter merging
// works correctly for discovery requests done via the BluetoothAdapter.
TEST_F(BluetoothBlueZTest, SetDiscoveryFilterBeforeStartDiscoveryMultiple) {}

// This unit test asserts that filter merging logic works correctly for filtered
// discovery requests done via the BluetoothAdapter.
TEST_F(BluetoothBlueZTest, SetDiscoveryFilterMergingTest) {}

TEST_F(BluetoothBlueZTest, DeviceProperties) {}

TEST_F(BluetoothBlueZTest, DeviceAddressType) {}

TEST_F(BluetoothBlueZTest, DeviceClassChanged) {}

TEST_F(BluetoothBlueZTest, DeviceAppearance) {}

TEST_F(BluetoothBlueZTest, DeviceTypebyAppearanceNotBluetoothClass) {}

TEST_F(BluetoothBlueZTest, DeviceNameChanged) {}

TEST_F(BluetoothBlueZTest, DeviceAddressChanged) {}

#if BUILDFLAG(IS_CHROMEOS)
TEST_F(BluetoothBlueZTest, DeviceBondedChanged) {
  // Simulate a change of bonded state of a device.
  GetAdapter();

  BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
  ASSERT_EQ(2U, devices.size());

  int idx = GetDeviceIndexByAddress(
      devices, bluez::FakeBluetoothDeviceClient::kPairedDeviceAddress);
  ASSERT_NE(-1, idx);
  ASSERT_EQ(bluez::FakeBluetoothDeviceClient::kPairedDeviceAddress,
            devices[idx]->GetAddress());
  ASSERT_EQ(true, devices[idx]->IsBonded());

  // Install an observer; expect the DeviceBondedChanged method to be called
  // when we change the bonded state of the device.
  TestBluetoothAdapterObserver observer(adapter_);

  bluez::FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(dbus::ObjectPath(
          bluez::FakeBluetoothDeviceClient::kPairedDevicePath));

  properties->bonded.ReplaceValue(false);

  EXPECT_EQ(1, observer.device_changed_count());
  EXPECT_EQ(1, observer.device_bonded_changed_count());
  EXPECT_FALSE(observer.device_new_bonded_status());
  EXPECT_EQ(devices[idx], observer.last_device());

  // Change the bonded state back to true to examine the consistent behavior of
  // DevicePairedChanged method.
  properties->bonded.ReplaceValue(true);

  EXPECT_EQ(2, observer.device_changed_count());
  EXPECT_EQ(2, observer.device_bonded_changed_count());
  EXPECT_TRUE(observer.device_new_bonded_status());
  EXPECT_EQ(devices[idx], observer.last_device());
}
#endif

#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
TEST_F(BluetoothBlueZTest, DevicePairedChanged) {}

TEST_F(BluetoothBlueZTest, DeviceMTUChanged) {}

TEST_F(BluetoothBlueZTest, DeviceAdvertisementReceived_LowEnergyDeviceAdded) {}

TEST_F(BluetoothBlueZTest, DeviceAdvertisementReceived_PropertyChanged) {}

TEST_F(BluetoothBlueZTest, DeviceConnectedStateChanged) {}
#endif

TEST_F(BluetoothBlueZTest, DeviceUuidsChanged) {}

TEST_F(BluetoothBlueZTest, DeviceInquiryRSSIInvalidated) {}

TEST_F(BluetoothBlueZTest, DeviceInquiryTxPowerInvalidated) {}

TEST_F(BluetoothBlueZTest, ForgetDevice) {}

TEST_P(BluetoothBlueZTestP, ForgetUnpairedDevice) {}

TEST_P(BluetoothBlueZTestP, ConnectPairedDevice) {}

TEST_P(BluetoothBlueZTestP, ConnectUnpairableDevice) {}

TEST_P(BluetoothBlueZTestP, ConnectConnectedDevice) {}

TEST_P(BluetoothBlueZTestP, ConnectDeviceFails) {}

// Tests that discovery is unpaused if the device gets removed during a
// connection.
TEST_P(BluetoothBlueZTestP, RemoveDeviceDuringConnection) {}

TEST_P(BluetoothBlueZTestP, DisconnectDevice) {}

TEST_F(BluetoothBlueZTest, DisconnectUnconnectedDevice) {}

TEST_F(BluetoothBlueZTest, PairTrustedDevice) {}

TEST_F(BluetoothBlueZTest, PairAlreadyPairedDevice) {}

TEST_P(BluetoothBlueZTestP, PairLegacyAutopair) {}

TEST_P(BluetoothBlueZTestP, PairDisplayPinCode) {}

TEST_P(BluetoothBlueZTestP, PairDisplayPasskey) {}

TEST_P(BluetoothBlueZTestP, PairRequestPinCode) {}

TEST_P(BluetoothBlueZTestP, PairConfirmPasskey) {}

TEST_P(BluetoothBlueZTestP, PairRequestPasskey) {}

TEST_P(BluetoothBlueZTestP, PairJustWorks) {}

TEST_P(BluetoothBlueZTestP, PairUnpairableDeviceFails) {}

TEST_P(BluetoothBlueZTestP, PairingFails) {}

TEST_P(BluetoothBlueZTestP, PairingFailsAtConnection) {}

TEST_P(BluetoothBlueZTestP, PairingRejectedAtPinCode) {}

TEST_P(BluetoothBlueZTestP, PairingCancelledAtPinCode) {}

TEST_P(BluetoothBlueZTestP, PairingRejectedAtPasskey) {}

TEST_P(BluetoothBlueZTestP, PairingCancelledAtPasskey) {}

TEST_P(BluetoothBlueZTestP, PairingRejectedAtConfirmation) {}

TEST_P(BluetoothBlueZTestP, PairingCancelledAtConfirmation) {}

TEST_P(BluetoothBlueZTestP, PairingCancelledInFlight) {}

TEST_F(BluetoothBlueZTest, IncomingPairRequestPinCode) {}

TEST_F(BluetoothBlueZTest, IncomingPairConfirmPasskey) {}

TEST_F(BluetoothBlueZTest, IncomingPairRequestPasskey) {}

TEST_F(BluetoothBlueZTest, IncomingPairJustWorks) {}

TEST_F(BluetoothBlueZTest, IncomingPairRequestPinCodeWithoutDelegate) {}

TEST_F(BluetoothBlueZTest, IncomingPairConfirmPasskeyWithoutDelegate) {}

TEST_F(BluetoothBlueZTest, IncomingPairRequestPasskeyWithoutDelegate) {}

TEST_F(BluetoothBlueZTest, IncomingPairJustWorksWithoutDelegate) {}

TEST_F(BluetoothBlueZTest, RemovePairingDelegateDuringPairing) {}

TEST_F(BluetoothBlueZTest, DeviceId) {}

TEST_F(BluetoothBlueZTest, GetConnectionInfoForDisconnectedDevice) {}

TEST_P(BluetoothBlueZTestP, GetConnectionInfoForConnectedDevice) {}

TEST_F(BluetoothBlueZTest, GetDiscoverableTimeout) {}

// Verifies Shutdown shuts down the adapter as expected.
TEST_F(BluetoothBlueZTest, Shutdown) {}

TEST_F(BluetoothBlueZTest, StartDiscovery_DiscoveringStopped_StartAgain) {}

// Verifies post-Shutdown of discovery sessions and OnStartDiscovery.
TEST_F(BluetoothBlueZTest, Shutdown_OnStartDiscovery) {}

// Verifies post-Shutdown of discovery sessions and OnStartDiscoveryError.
TEST_F(BluetoothBlueZTest, Shutdown_OnStartDiscoveryError) {}

TEST_F(BluetoothBlueZTest, StartDiscoveryError_ThenStartAgain) {}

TEST_F(BluetoothBlueZTest, ManufacturerDataChanged) {}

TEST_F(BluetoothBlueZTest, AdvertisingDataFlagsChanged) {}

TEST_F(BluetoothBlueZTest, SetConnectionLatency) {}

#if BUILDFLAG(IS_CHROMEOS)
TEST_F(BluetoothBlueZTest, AdminPolicyEvents) {
  // Simulate the addition, removal, and change of admin policy.
  GetAdapter();

  // Create a device that will have related admin policy events with.
  dbus::ObjectPath device_path =
      dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath);
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
      device_path);
  BluetoothDevice* device =
      adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
  EXPECT_TRUE(device);
  // A new device is not blocked by policy.
  EXPECT_FALSE(device->IsBlockedByPolicy());

  // Create an admin policy, check that the device policy is updated.
  fake_bluetooth_admin_policy_client_->CreateAdminPolicy(
      device_path,
      /*is_blocked_by_policy=*/true);
  EXPECT_TRUE(device->IsBlockedByPolicy());

  // Change the admin policy, check that the device policy is updated.
  fake_bluetooth_admin_policy_client_->ChangeAdminPolicy(
      device_path,
      /*is_blocked_by_policy=*/false);
  EXPECT_FALSE(device->IsBlockedByPolicy());

  // Change the admin policy again, check that the device policy is updated.
  fake_bluetooth_admin_policy_client_->ChangeAdminPolicy(
      device_path,
      /*is_blocked_by_policy=*/true);
  EXPECT_TRUE(device->IsBlockedByPolicy());

  // Remove the admin policy. The device policy should be set back to default
  // (false).
  fake_bluetooth_admin_policy_client_->RemoveAdminPolicy(device_path);
  EXPECT_FALSE(device->IsBlockedByPolicy());
}

TEST_F(BluetoothBlueZTest, AdminPolicyInitBeforeDevice) {
  // Simulate the admin policy being added before the device is added.
  GetAdapter();

  dbus::ObjectPath device_path =
      dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath);

  // Create an admin policy.
  fake_bluetooth_admin_policy_client_->CreateAdminPolicy(
      device_path,
      /*is_blocked_by_policy=*/true);

  // Create the associated device.
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
      device_path);
  BluetoothDevice* device =
      adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress);
  EXPECT_TRUE(device);
  // The new device should contain the admin policy.
  EXPECT_TRUE(device->IsBlockedByPolicy());
}
#endif

TEST_F(BluetoothBlueZTest, BatteryEvents) {}

TEST_F(BluetoothBlueZTest, DeviceUUIDsCombinedFromServiceAndAdvertisement) {}
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(BluetoothBlueZTest, StartLowEnergyScanSessionAdapterPresent) {
  GetAdapter();

  FakeBluetoothAdvertisementMonitorApplicationServiceProvider*
      application_manager = GetAdvertisementMonitorApplicationManger();

  auto filter = CreateLowEnergyScanFilter();
  FakeBluetoothLowEnergyScanSessionDelegate delegate;

  auto background_scan_session = adapter_->StartLowEnergyScanSession(
      std::move(filter), /*delegate=*/delegate.GetWeakPtr());

  // Check that advertisement monitor was added to d-bus layer.
  ASSERT_EQ(1u, application_manager->AdvertisementMonitorsCount());

  // Check that advertisement monitor gets removed from d-bus layer when the
  // scan session is destroyed.
  background_scan_session.reset();
  ASSERT_EQ(0u, application_manager->AdvertisementMonitorsCount());
}

TEST_F(BluetoothBlueZTest, StartLowEnergyScanSessionAdapterAddedLater) {
  fake_bluetooth_adapter_client_->SetPresent(false);
  GetAdapter();
  ASSERT_FALSE(adapter_->IsPresent());

  FakeBluetoothAdvertisementMonitorApplicationServiceProvider*
      application_manager = GetAdvertisementMonitorApplicationManger();

  auto filter = CreateLowEnergyScanFilter();

  FakeBluetoothLowEnergyScanSessionDelegate delegate;
  auto background_scan_session = adapter_->StartLowEnergyScanSession(
      std::move(filter), /*delegate=*/delegate.GetWeakPtr());

  // Check that the advertisement monitor is not yet added to the d-bus layer.
  // It is queued up until the adapter gets added.
  ASSERT_EQ(0u, application_manager->AdvertisementMonitorsCount());

  fake_bluetooth_adapter_client_->SetPresent(true);
  EXPECT_TRUE(adapter_->IsPresent());

  ASSERT_EQ(1u, application_manager->AdvertisementMonitorsCount());

  background_scan_session.reset();
  ASSERT_EQ(0u, application_manager->AdvertisementMonitorsCount());
}

TEST_F(BluetoothBlueZTest, StartLowEnergyScanSessionAdapterBecomeNotPresent) {
  GetAdapter();
  ASSERT_TRUE(adapter_->IsPresent());

  // Remove Adapter
  fake_bluetooth_adapter_client_->SetPresent(false);

  FakeBluetoothAdvertisementMonitorApplicationServiceProvider*
      application_manager = GetAdvertisementMonitorApplicationManger();

  auto filter = CreateLowEnergyScanFilter();
  FakeBluetoothLowEnergyScanSessionDelegate delegate;

  auto background_scan_session = adapter_->StartLowEnergyScanSession(
      std::move(filter), /*delegate=*/delegate.GetWeakPtr());

  // Check that advertisement monitor was not added to d-bus layer since there
  // is no adapter present.
  ASSERT_EQ(0u, application_manager->AdvertisementMonitorsCount());

  // Add Adapter
  fake_bluetooth_adapter_client_->SetPresent(true);

  // Check that queued advertisement monitor was added to d-bus after adapter
  // becomes present.
  ASSERT_EQ(1u, application_manager->AdvertisementMonitorsCount());
}

TEST_F(BluetoothBlueZTest, BluetoothLowEnergyScanSessionBlueZDeviceFound) {
  GetAdapter();
  ASSERT_TRUE(adapter_->IsPresent());

  FakeBluetoothAdvertisementMonitorApplicationServiceProvider*
      application_manager = GetAdvertisementMonitorApplicationManger();
  FakeBluetoothLowEnergyScanSessionDelegate delegate;
  auto background_scan_session = adapter_->StartLowEnergyScanSession(
      CreateLowEnergyScanFilter(), /*delegate=*/delegate.GetWeakPtr());

  // Check that advertisement monitor was added to d-bus layer.
  EXPECT_EQ(1u, application_manager->AdvertisementMonitorsCount());

  // Get advertisement fake advertisement monitor to forward events to
  // BluetoothLowEnergyScanSessionBlueZ.
  FakeBluetoothAdvertisementMonitorServiceProvider* advertisement_monitor =
      application_manager->GetLastAddedAdvertisementMonitorServiceProvider();
  ASSERT_TRUE(advertisement_monitor);

  // Simulate a device found event.
  advertisement_monitor->delegate()->OnDeviceFound(
      dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kPairedDevicePath));
  EXPECT_EQ(1u, delegate.devices_found().size());

  std::pair<device::BluetoothLowEnergyScanSession*, device::BluetoothDevice*>
      devices_found_pair = delegate.devices_found()[0];
  EXPECT_EQ(background_scan_session.get(), devices_found_pair.first);
  EXPECT_EQ(adapter_->GetDevice(
                bluez::FakeBluetoothDeviceClient::kPairedDeviceAddress),
            devices_found_pair.second);
}

TEST_F(BluetoothBlueZTest, BluetoothLowEnergyScanSessionBlueZDeviceNULL) {
  GetAdapter();
  ASSERT_TRUE(adapter_->IsPresent());

  FakeBluetoothAdvertisementMonitorApplicationServiceProvider*
      application_manager = GetAdvertisementMonitorApplicationManger();
  FakeBluetoothLowEnergyScanSessionDelegate delegate;
  auto background_scan_session = adapter_->StartLowEnergyScanSession(
      CreateLowEnergyScanFilter(), /*delegate=*/delegate.GetWeakPtr());

  // Check that advertisement monitor was added to d-bus layer.
  EXPECT_EQ(1u, application_manager->AdvertisementMonitorsCount());

  // Get advertisement fake advertisement monitor to forward events to
  // BluetoothLowEnergyScanSessionBlueZ.
  FakeBluetoothAdvertisementMonitorServiceProvider* advertisement_monitor =
      application_manager->GetLastAddedAdvertisementMonitorServiceProvider();
  ASSERT_TRUE(advertisement_monitor);

  bool did_dump_without_crashing = false;
  dump_without_crashing_flag = &did_dump_without_crashing;
  base::debug::SetDumpWithoutCrashingFunction(&HandleDumpWithoutCrashing);
  advertisement_monitor->delegate()->OnDeviceFound(dbus::ObjectPath(""));

  EXPECT_TRUE(did_dump_without_crashing);

  base::debug::SetDumpWithoutCrashingFunction(nullptr);
}

TEST_F(BluetoothBlueZTest, BluetoothLowEnergyScanSessionBlueZDeviceLost) {
  GetAdapter();
  ASSERT_TRUE(adapter_->IsPresent());

  FakeBluetoothAdvertisementMonitorApplicationServiceProvider*
      application_manager = GetAdvertisementMonitorApplicationManger();
  FakeBluetoothLowEnergyScanSessionDelegate delegate;
  auto background_scan_session = adapter_->StartLowEnergyScanSession(
      CreateLowEnergyScanFilter(), /*delegate=*/delegate.GetWeakPtr());

  // Check that advertisement monitor was added to d-bus layer.
  EXPECT_EQ(1u, application_manager->AdvertisementMonitorsCount());

  // Get advertisement fake advertisement monitor to forward events to
  // BluetoothLowEnergyScanSessionBlueZ.
  FakeBluetoothAdvertisementMonitorServiceProvider* advertisement_monitor =
      application_manager->GetLastAddedAdvertisementMonitorServiceProvider();
  ASSERT_TRUE(advertisement_monitor);

  // Simulate a device lost event.
  advertisement_monitor->delegate()->OnDeviceLost(
      dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kPairedDevicePath));
  EXPECT_EQ(1u, delegate.devices_lost().size());

  std::pair<device::BluetoothLowEnergyScanSession*, device::BluetoothDevice*>
      devices_lost_pair = delegate.devices_lost()[0];
  EXPECT_EQ(background_scan_session.get(), devices_lost_pair.first);
  EXPECT_EQ(adapter_->GetDevice(
                bluez::FakeBluetoothDeviceClient::kPairedDeviceAddress),
            devices_lost_pair.second);
}

TEST_F(BluetoothBlueZTest,
       BluetoothLowEnergyScanSessionBlueZStartThenInvalidate) {
  GetAdapter();
  ASSERT_TRUE(adapter_->IsPresent());

  FakeBluetoothAdvertisementMonitorApplicationServiceProvider*
      application_manager = GetAdvertisementMonitorApplicationManger();
  FakeBluetoothLowEnergyScanSessionDelegate delegate;
  auto background_scan_session = adapter_->StartLowEnergyScanSession(
      CreateLowEnergyScanFilter(), /*delegate=*/delegate.GetWeakPtr());

  // Check that advertisement monitor was added to d-bus layer.
  EXPECT_EQ(1u, application_manager->AdvertisementMonitorsCount());

  // Get advertisement fake advertisement monitor to forward events to
  // BluetoothLowEnergyScanSessionBlueZ.
  FakeBluetoothAdvertisementMonitorServiceProvider* advertisement_monitor =
      application_manager->GetLastAddedAdvertisementMonitorServiceProvider();
  ASSERT_TRUE(advertisement_monitor);

  // Successfully start scan session.
  advertisement_monitor->delegate()->OnActivate();
  EXPECT_EQ(1u, delegate.sessions_started().size());
  std::pair<device::BluetoothLowEnergyScanSession*,
            std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>>
      session_started_pair = delegate.sessions_started()[0];

  // Check that the correct scan session is started.
  EXPECT_EQ(background_scan_session.get(), session_started_pair.first);

  // Check that there was no error when starting the scan session.
  EXPECT_FALSE(session_started_pair.second.has_value());

  // Invalidate scan session after successful start.
  advertisement_monitor->delegate()->OnRelease();
  EXPECT_EQ(1u, delegate.sessions_invalidated().size());
  EXPECT_EQ(background_scan_session.get(),
            delegate.sessions_invalidated().front());
}

TEST_F(BluetoothBlueZTest, BluetoothLowEnergyScanSessionBlueZFailsToStart) {
  GetAdapter();
  ASSERT_TRUE(adapter_->IsPresent());

  FakeBluetoothAdvertisementMonitorApplicationServiceProvider*
      application_manager = GetAdvertisementMonitorApplicationManger();
  FakeBluetoothLowEnergyScanSessionDelegate delegate;
  auto background_scan_session = adapter_->StartLowEnergyScanSession(
      CreateLowEnergyScanFilter(), /*delegate=*/delegate.GetWeakPtr());

  // Check that advertisement monitor was added to d-bus layer.
  EXPECT_EQ(1u, application_manager->AdvertisementMonitorsCount());

  // Get advertisement fake advertisement monitor to forward events to
  // BluetoothLowEnergyScanSessionBlueZ.
  FakeBluetoothAdvertisementMonitorServiceProvider* advertisement_monitor =
      application_manager->GetLastAddedAdvertisementMonitorServiceProvider();
  ASSERT_TRUE(advertisement_monitor);

  // Scan session failed to start.
  advertisement_monitor->delegate()->OnRelease();
  EXPECT_EQ(1u, delegate.sessions_started().size());

  std::pair<device::BluetoothLowEnergyScanSession*,
            std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>>
      session_started_pair = delegate.sessions_started()[0];

  // Check that the correct scan session.
  EXPECT_EQ(background_scan_session.get(), session_started_pair.first);

  // Check that there was an error indicating failure to start.
  EXPECT_TRUE(session_started_pair.second.has_value());
}

TEST_F(BluetoothBlueZTest,
       LowEnergyScanSession_HardwareOffloadingNotSupported) {
  GetAdapter();
  ASSERT_TRUE(adapter_->IsPresent());

  // We haven't added any supported features to the list, so this should report
  // hardware offloading as not supported.
  EXPECT_EQ(adapter_->GetLowEnergyScanSessionHardwareOffloadingStatus(),
            BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus::
                kNotSupported);
}

TEST_F(BluetoothBlueZTest, LowEnergyScanSession_HardwareOffloadingSupport) {
  GetAdapter();
  ASSERT_TRUE(adapter_->IsPresent());

  // Install an observer;
  TestBluetoothAdapterObserver observer(adapter_);

  BluetoothAdapterBlueZ* adapter_bluez =
      static_cast<BluetoothAdapterBlueZ*>(adapter_.get());
  FakeBluetoothAdvertisementMonitorManagerClient* client =
      static_cast<bluez::FakeBluetoothAdvertisementMonitorManagerClient*>(
          bluez::BluezDBusManager::Get()
              ->GetBluetoothAdvertisementMonitorManagerClient());

  // If no properties are returned we should get |kUndetermined| status.
  client->RemoveProperties();
  EXPECT_EQ(adapter_->GetLowEnergyScanSessionHardwareOffloadingStatus(),
            BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus::
                kUndetermined);
  EXPECT_EQ(observer.last_low_energy_scan_session_hardware_offloading_status(),
            BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus::
                kUndetermined);

  // Once we add the "controller-patterns" feature, the adapter should report
  // hardware offloading as supported.
  client->InitializeProperties();
  BluetoothAdvertisementMonitorManagerClient::Properties* properties =
      client->GetProperties(adapter_bluez->object_path());
  ASSERT_TRUE(properties);
  properties->supported_features.ReplaceValue(
      {bluetooth_advertisement_monitor_manager::
           kSupportedFeaturesControllerPatterns});
  EXPECT_EQ(adapter_->GetLowEnergyScanSessionHardwareOffloadingStatus(),
            BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus::
                kSupported);
  EXPECT_EQ(observer.last_low_energy_scan_session_hardware_offloading_status(),
            BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus::
                kSupported);

  properties->supported_features.ReplaceValue({});
  EXPECT_EQ(adapter_->GetLowEnergyScanSessionHardwareOffloadingStatus(),
            BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus::
                kNotSupported);
  EXPECT_EQ(observer.last_low_energy_scan_session_hardware_offloading_status(),
            BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus::
                kNotSupported);

  // Ensure that if no properties are returned we get the |kUndetermined|
  // status.
  client->RemoveProperties();
  EXPECT_EQ(adapter_->GetLowEnergyScanSessionHardwareOffloadingStatus(),
            BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus::
                kUndetermined);
}

TEST_F(BluetoothBlueZTest, IsExtendedAdvertisementsAvailable) {
  GetAdapter();

  BluetoothAdapterBlueZ* adapter_bluez =
      static_cast<BluetoothAdapterBlueZ*>(adapter_.get());

  FakeBluetoothLEAdvertisingManagerClient* client =
      static_cast<bluez::FakeBluetoothLEAdvertisingManagerClient*>(
          bluez::BluezDBusManager::Get()
              ->GetBluetoothLEAdvertisingManagerClient());

  BluetoothLEAdvertisingManagerClient::Properties* properties =
      client->GetProperties(adapter_bluez->object_path());
  ASSERT_TRUE(properties);

  std::vector<std::string> supported_features = {};

  properties->supported_features.ReplaceValue(supported_features);

  // Empty supported feature indicates the adapter doesn't support Ext
  // Advertising
  EXPECT_FALSE(adapter_bluez->IsExtendedAdvertisementsAvailable());

  supported_features.push_back(std::string(
      bluetooth_advertising_manager::kSupportedFeaturesHardwareOffload));

  // HardwareOffload indicates the adapter support Ext Advertising
  properties->supported_features.ReplaceValue(supported_features);

  EXPECT_TRUE(adapter_bluez->IsExtendedAdvertisementsAvailable());
}

TEST_F(BluetoothBlueZTest, GetSupportedRoles) {
  std::vector<std::string> adapter_roles;
  GetAdapter();

  ASSERT_TRUE(adapter_->GetSupportedRoles().empty());

  // An unknown role should be ignored
  adapter_roles.push_back("unknown-role");
  fake_bluetooth_adapter_client_->SetRoles(adapter_roles);
  ASSERT_TRUE(adapter_->GetSupportedRoles().empty());

  adapter_roles.push_back("central");
  fake_bluetooth_adapter_client_->SetRoles(adapter_roles);
  EXPECT_EQ(1u, adapter_->GetSupportedRoles().size());
  ASSERT_TRUE(base::Contains(adapter_->GetSupportedRoles(),
                             BluetoothAdapter::BluetoothRole::kCentral));

  adapter_roles.push_back("peripheral");
  fake_bluetooth_adapter_client_->SetRoles(adapter_roles);
  EXPECT_EQ(2u, adapter_->GetSupportedRoles().size());
  ASSERT_TRUE(base::Contains(adapter_->GetSupportedRoles(),
                             BluetoothAdapter::BluetoothRole::kPeripheral));

  adapter_roles.push_back("central-peripheral");
  fake_bluetooth_adapter_client_->SetRoles(adapter_roles);
  EXPECT_EQ(3u, adapter_->GetSupportedRoles().size());
  ASSERT_TRUE(
      base::Contains(adapter_->GetSupportedRoles(),
                     BluetoothAdapter::BluetoothRole::kCentralPeripheral));
}
#endif  // BUILDFLAG(IS_CHROMEOS)

}  // namespace bluez