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

#include "device/bluetooth/bluetooth_adapter.h"

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

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

#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "build/build_config.h"
#include "device/bluetooth/bluetooth_common.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
#include "device/bluetooth/bluetooth_gatt_service.h"
#include "device/bluetooth/bluetooth_local_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_local_gatt_descriptor.h"
#include "device/bluetooth/bluetooth_local_gatt_service.h"
#include "device/bluetooth/test/bluetooth_test.h"
#include "device/bluetooth/test/test_bluetooth_adapter_observer.h"
#include "testing/gtest/include/gtest/gtest.h"

#if defined(OS_ANDROID)
#include "device/bluetooth/test/bluetooth_test_android.h"
#elif defined(OS_MACOSX)
#include "device/bluetooth/test/bluetooth_test_mac.h"
#elif defined(OS_WIN)
#include "base/win/windows_version.h"
#include "device/bluetooth/test/bluetooth_test_win.h"
#elif defined(USE_CAST_BLUETOOTH_ADAPTER)
#include "device/bluetooth/test/bluetooth_test_cast.h"
#elif defined(OS_CHROMEOS) || defined(OS_LINUX)
#include "device/bluetooth/test/bluetooth_test_bluez.h"
#endif

using device::BluetoothDevice;

namespace device {

class TestBluetoothAdapter : public BluetoothAdapter {
 public:
  TestBluetoothAdapter() = default;

  std::string GetAddress() const override { return ""; }

  std::string GetName() const override { return ""; }

  void SetName(const std::string& name,
               const base::Closure& callback,
               const ErrorCallback& error_callback) override {}

  bool IsInitialized() const override { return false; }

  bool IsPresent() const override { return false; }

  bool IsPowered() const override { return false; }

  void SetPowered(bool powered,
                  const base::Closure& callback,
                  const ErrorCallback& error_callback) override {}

  bool IsDiscoverable() const override { return false; }

  void SetDiscoverable(bool discoverable,
                       const base::Closure& callback,
                       const ErrorCallback& error_callback) override {}

  bool IsDiscovering() const override { return false; }

  void StartDiscoverySessionWithFilter(
      std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter,
      const DiscoverySessionCallback& callback,
      const ErrorCallback& error_callback) override {
    OnStartDiscoverySession(std::move(discovery_filter), callback);
  }

  void StartDiscoverySession(const DiscoverySessionCallback& callback,
                             const ErrorCallback& error_callback) override {}

  UUIDList GetUUIDs() const override { return UUIDList(); }

  void CreateRfcommService(
      const BluetoothUUID& uuid,
      const ServiceOptions& options,
      const CreateServiceCallback& callback,
      const CreateServiceErrorCallback& error_callback) override {}

  void CreateL2capService(
      const BluetoothUUID& uuid,
      const ServiceOptions& options,
      const CreateServiceCallback& callback,
      const CreateServiceErrorCallback& error_callback) override {}

  void RegisterAdvertisement(
      std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
      const CreateAdvertisementCallback& callback,
      const AdvertisementErrorCallback& error_callback) override {}

#if defined(OS_CHROMEOS) || defined(OS_LINUX)
  void SetAdvertisingInterval(
      const base::TimeDelta& min,
      const base::TimeDelta& max,
      const base::Closure& callback,
      const AdvertisementErrorCallback& error_callback) override {}
  void ResetAdvertising(
      const base::Closure& callback,
      const AdvertisementErrorCallback& error_callback) override {}
#endif

  BluetoothLocalGattService* GetGattService(
      const std::string& identifier) const override {
    return nullptr;
  }

  void TestErrorCallback() {}

  std::vector<std::unique_ptr<BluetoothDiscoverySession>> discovery_sessions_;

  void TestOnStartDiscoverySession(
      std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) {
    discovery_sessions_.push_back(std::move(discovery_session));
  }

  void CleanupSessions() { discovery_sessions_.clear(); }

  void InjectFilteredSession(
      std::unique_ptr<device::BluetoothDiscoveryFilter> discovery_filter) {
    StartDiscoverySessionWithFilter(
        std::move(discovery_filter),
        base::Bind(&TestBluetoothAdapter::TestOnStartDiscoverySession,
                   base::Unretained(this)),
        base::Bind(&TestBluetoothAdapter::TestErrorCallback,
                   base::Unretained(this)));
  }

 protected:
  ~TestBluetoothAdapter() override = default;

  bool SetPoweredImpl(bool powered) override { return false; }

  void AddDiscoverySession(
      BluetoothDiscoveryFilter* discovery_filter,
      const base::Closure& callback,
      DiscoverySessionErrorCallback error_callback) override {}

  void RemoveDiscoverySession(
      BluetoothDiscoveryFilter* discovery_filter,
      const base::Closure& callback,
      DiscoverySessionErrorCallback error_callback) override {}

  void SetDiscoveryFilter(
      std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter,
      const base::Closure& callback,
      DiscoverySessionErrorCallback error_callback) override {}

  void RemovePairingDelegateInternal(
      BluetoothDevice::PairingDelegate* pairing_delegate) override {}
};

class TestPairingDelegate : public BluetoothDevice::PairingDelegate {
 public:
  void RequestPinCode(BluetoothDevice* device) override {}
  void RequestPasskey(BluetoothDevice* device) override {}
  void DisplayPinCode(BluetoothDevice* device,
                      const std::string& pincode) override {}
  void DisplayPasskey(BluetoothDevice* device, uint32_t passkey) override {}
  void KeysEntered(BluetoothDevice* device, uint32_t entered) override {}
  void ConfirmPasskey(BluetoothDevice* device, uint32_t passkey) override {}
  void AuthorizePairing(BluetoothDevice* device) override {}
};

TEST(BluetoothAdapterTest, NoDefaultPairingDelegate) {
  scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();

  // Verify that when there is no registered pairing delegate, NULL is returned.
  EXPECT_TRUE(adapter->DefaultPairingDelegate() == NULL);
}

TEST(BluetoothAdapterTest, OneDefaultPairingDelegate) {
  scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();

  // Verify that when there is one registered pairing delegate, it is returned.
  TestPairingDelegate delegate;

  adapter->AddPairingDelegate(&delegate,
                              BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);

  EXPECT_EQ(&delegate, adapter->DefaultPairingDelegate());
}

TEST(BluetoothAdapterTest, SamePriorityDelegates) {
  scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();

  // Verify that when there are two registered pairing delegates of the same
  // priority, the first one registered is returned.
  TestPairingDelegate delegate1, delegate2;

  adapter->AddPairingDelegate(&delegate1,
                              BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
  adapter->AddPairingDelegate(&delegate2,
                              BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);

  EXPECT_EQ(&delegate1, adapter->DefaultPairingDelegate());

  // After unregistering the first, the second can be returned.
  adapter->RemovePairingDelegate(&delegate1);

  EXPECT_EQ(&delegate2, adapter->DefaultPairingDelegate());
}

TEST(BluetoothAdapterTest, HighestPriorityDelegate) {
  scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();

  // Verify that when there are two registered pairing delegates, the one with
  // the highest priority is returned.
  TestPairingDelegate delegate1, delegate2;

  adapter->AddPairingDelegate(&delegate1,
                              BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
  adapter->AddPairingDelegate(&delegate2,
                              BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);

  EXPECT_EQ(&delegate2, adapter->DefaultPairingDelegate());
}

TEST(BluetoothAdapterTest, UnregisterDelegate) {
  scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();

  // Verify that after unregistering a delegate, NULL is returned.
  TestPairingDelegate delegate;

  adapter->AddPairingDelegate(&delegate,
                              BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
  adapter->RemovePairingDelegate(&delegate);

  EXPECT_TRUE(adapter->DefaultPairingDelegate() == NULL);
}

TEST(BluetoothAdapterTest, GetMergedDiscoveryFilterEmpty) {
  scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();
  std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter;

  discovery_filter = adapter->GetMergedDiscoveryFilter();
  EXPECT_TRUE(discovery_filter.get() == nullptr);

  discovery_filter = adapter->GetMergedDiscoveryFilterMasked(nullptr);
  EXPECT_TRUE(discovery_filter.get() == nullptr);
}

TEST(BluetoothAdapterTest, GetMergedDiscoveryFilterRegular) {
  scoped_refptr<TestBluetoothAdapter> adapter = new TestBluetoothAdapter();
  std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter;

  // make sure adapter have one session wihout filtering.
  adapter->InjectFilteredSession(std::move(discovery_filter));

  // having one reglar session should result in no filter
  std::unique_ptr<BluetoothDiscoveryFilter> resulting_filter =
      adapter->GetMergedDiscoveryFilter();
  EXPECT_TRUE(resulting_filter.get() == nullptr);

  // omiting no filter when having one reglar session should result in no filter
  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(nullptr);
  EXPECT_TRUE(resulting_filter.get() == nullptr);

  adapter->CleanupSessions();
}

TEST(BluetoothAdapterTest, GetMergedDiscoveryFilterRssi) {
  scoped_refptr<TestBluetoothAdapter> adapter = new TestBluetoothAdapter();
  int16_t resulting_rssi;
  uint16_t resulting_pathloss;
  std::unique_ptr<BluetoothDiscoveryFilter> resulting_filter;

  BluetoothDiscoveryFilter* df =
      new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE);
  df->SetRSSI(-30);
  std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter(df);

  BluetoothDiscoveryFilter* df2 =
      new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE);
  df2->SetRSSI(-65);
  std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter2(df2);

  // make sure adapter have one session wihout filtering.
  adapter->InjectFilteredSession(std::move(discovery_filter));

  // DO_NOTHING should have no impact
  resulting_filter = adapter->GetMergedDiscoveryFilter();
  resulting_filter->GetRSSI(&resulting_rssi);
  EXPECT_EQ(-30, resulting_rssi);

  // should not use df2 at all, as it's not associated with adapter yet
  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(df2);
  resulting_filter->GetRSSI(&resulting_rssi);
  EXPECT_EQ(-30, resulting_rssi);

  adapter->InjectFilteredSession(std::move(discovery_filter2));

  // result of merging two rssi values should be lower one
  resulting_filter = adapter->GetMergedDiscoveryFilter();
  resulting_filter->GetRSSI(&resulting_rssi);
  EXPECT_EQ(-65, resulting_rssi);

  // ommit bigger value, result should stay same
  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(df);
  resulting_filter->GetRSSI(&resulting_rssi);
  EXPECT_EQ(-65, resulting_rssi);

  // ommit lower value, result should change
  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(df2);
  resulting_filter->GetRSSI(&resulting_rssi);
  EXPECT_EQ(-30, resulting_rssi);

  BluetoothDiscoveryFilter* df3 =
      new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE);
  df3->SetPathloss(60);
  std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter3(df3);

  // when rssi and pathloss are merged, both should be cleared, becuase there is
  // no way to tell which filter will be more generic
  adapter->InjectFilteredSession(std::move(discovery_filter3));
  resulting_filter = adapter->GetMergedDiscoveryFilter();
  EXPECT_FALSE(resulting_filter->GetRSSI(&resulting_rssi));
  EXPECT_FALSE(resulting_filter->GetPathloss(&resulting_pathloss));

  adapter->CleanupSessions();
}

TEST(BluetoothAdapterTest, GetMergedDiscoveryFilterTransport) {
  scoped_refptr<TestBluetoothAdapter> adapter = new TestBluetoothAdapter();
  std::unique_ptr<BluetoothDiscoveryFilter> resulting_filter;

  BluetoothDiscoveryFilter* df =
      new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_CLASSIC);
  std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter(df);

  BluetoothDiscoveryFilter* df2 =
      new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE);
  std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter2(df2);

  adapter->InjectFilteredSession(std::move(discovery_filter));

  // Just one filter, make sure transport was properly rewritten
  resulting_filter = adapter->GetMergedDiscoveryFilter();
  EXPECT_EQ(BLUETOOTH_TRANSPORT_CLASSIC, resulting_filter->GetTransport());

  adapter->InjectFilteredSession(std::move(discovery_filter2));

  // Two filters, should have OR of both transport's
  resulting_filter = adapter->GetMergedDiscoveryFilter();
  EXPECT_EQ(BLUETOOTH_TRANSPORT_DUAL, resulting_filter->GetTransport());

  // When 1st filter is masked, 2nd filter transport should be returned.
  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(df);
  EXPECT_EQ(BLUETOOTH_TRANSPORT_LE, resulting_filter->GetTransport());

  // When 2nd filter is masked, 1st filter transport should be returned.
  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(df2);
  EXPECT_EQ(BLUETOOTH_TRANSPORT_CLASSIC, resulting_filter->GetTransport());

  BluetoothDiscoveryFilter* df3 =
      new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE);
  df3->CopyFrom(BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_DUAL));
  std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter3(df3);

  // Merging empty filter in should result in empty filter
  adapter->InjectFilteredSession(std::move(discovery_filter3));
  resulting_filter = adapter->GetMergedDiscoveryFilter();
  EXPECT_TRUE(resulting_filter->IsDefault());

  adapter->CleanupSessions();
}

TEST(BluetoothAdapterTest, GetMergedDiscoveryFilterAllFields) {
  scoped_refptr<TestBluetoothAdapter> adapter = new TestBluetoothAdapter();
  int16_t resulting_rssi;
  std::set<device::BluetoothUUID> resulting_uuids;

  BluetoothDiscoveryFilter* df =
      new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE);
  df->SetRSSI(-60);
  df->AddUUID(device::BluetoothUUID("1000"));
  std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter(df);

  BluetoothDiscoveryFilter* df2 =
      new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE);
  df2->SetRSSI(-85);
  df2->SetTransport(BLUETOOTH_TRANSPORT_LE);
  df2->AddUUID(device::BluetoothUUID("1020"));
  df2->AddUUID(device::BluetoothUUID("1001"));
  std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter2(df2);

  BluetoothDiscoveryFilter* df3 =
      new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE);
  df3->SetRSSI(-65);
  df3->SetTransport(BLUETOOTH_TRANSPORT_CLASSIC);
  df3->AddUUID(device::BluetoothUUID("1020"));
  df3->AddUUID(device::BluetoothUUID("1003"));
  std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter3(df3);

  // make sure adapter have one session wihout filtering.
  adapter->InjectFilteredSession(std::move(discovery_filter));
  adapter->InjectFilteredSession(std::move(discovery_filter2));
  adapter->InjectFilteredSession(std::move(discovery_filter3));

  std::unique_ptr<BluetoothDiscoveryFilter> resulting_filter =
      adapter->GetMergedDiscoveryFilter();
  resulting_filter->GetRSSI(&resulting_rssi);
  resulting_filter->GetUUIDs(resulting_uuids);
  EXPECT_TRUE(resulting_filter->GetTransport());
  EXPECT_EQ(BLUETOOTH_TRANSPORT_DUAL, resulting_filter->GetTransport());
  EXPECT_EQ(-85, resulting_rssi);
  EXPECT_EQ(4UL, resulting_uuids.size());
  EXPECT_TRUE(resulting_uuids.find(device::BluetoothUUID("1000")) !=
              resulting_uuids.end());
  EXPECT_TRUE(resulting_uuids.find(device::BluetoothUUID("1001")) !=
              resulting_uuids.end());
  EXPECT_TRUE(resulting_uuids.find(device::BluetoothUUID("1003")) !=
              resulting_uuids.end());
  EXPECT_TRUE(resulting_uuids.find(device::BluetoothUUID("1020")) !=
              resulting_uuids.end());

  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(df);
  EXPECT_EQ(BLUETOOTH_TRANSPORT_DUAL, resulting_filter->GetTransport());

  adapter->CleanupSessions();
}

// TODO(scheib): Enable BluetoothTest fixture tests on all platforms.
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_ConstructDefaultAdapter ConstructDefaultAdapter
#else
#define MAYBE_ConstructDefaultAdapter DISABLED_ConstructDefaultAdapter
#endif

#if defined(OS_WIN)
TEST_P(BluetoothTestWinrt, ConstructDefaultAdapter) {
#else
TEST_F(BluetoothTest, MAYBE_ConstructDefaultAdapter) {
#endif
  InitWithDefaultAdapter();
  if (!adapter_->IsPresent()) {
    LOG(WARNING) << "Bluetooth adapter not present; skipping unit test.";
    return;
  }

  bool expected = false;
// MacOS returns empty for name and address if the adapter is off.
#if defined(OS_MACOSX)
  expected = !adapter_->IsPowered();
#endif  // defined(OS_MACOSX)

  EXPECT_EQ(expected, adapter_->GetAddress().empty());
  EXPECT_EQ(expected, adapter_->GetName().empty());

  EXPECT_TRUE(adapter_->IsPresent());
  // Don't know on test machines if adapter will be powered or not, but
  // the call should be safe to make and consistent.
  EXPECT_EQ(adapter_->IsPowered(), adapter_->IsPowered());
  EXPECT_FALSE(adapter_->IsDiscoverable());
  EXPECT_FALSE(adapter_->IsDiscovering());
}

// TODO(scheib): Enable BluetoothTest fixture tests on all platforms.
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_ConstructWithoutDefaultAdapter ConstructWithoutDefaultAdapter
#else
#define MAYBE_ConstructWithoutDefaultAdapter \
  DISABLED_ConstructWithoutDefaultAdapter
#endif

#if defined(OS_WIN)
TEST_P(BluetoothTestWinrt, ConstructWithoutDefaultAdapter) {
#else
TEST_F(BluetoothTest, MAYBE_ConstructWithoutDefaultAdapter) {
#endif  // defined(OS_WIN)
  InitWithoutDefaultAdapter();
  EXPECT_EQ(adapter_->GetAddress(), "");
  EXPECT_EQ(adapter_->GetName(), "");
  EXPECT_FALSE(adapter_->IsPresent());
  EXPECT_FALSE(adapter_->IsPowered());
  EXPECT_FALSE(adapter_->IsDiscoverable());
  EXPECT_FALSE(adapter_->IsDiscovering());
}

// TODO(scheib): Enable BluetoothTest fixture tests on all platforms.
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_ConstructFakeAdapter ConstructFakeAdapter
#else
#define MAYBE_ConstructFakeAdapter DISABLED_ConstructFakeAdapter
#endif

#if defined(OS_WIN)
TEST_P(BluetoothTestWinrt, ConstructFakeAdapter) {
#else
TEST_F(BluetoothTest, MAYBE_ConstructFakeAdapter) {
#endif  // defined(OS_WIN)
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  EXPECT_EQ(adapter_->GetAddress(), kTestAdapterAddress);
  EXPECT_EQ(adapter_->GetName(), kTestAdapterName);
  EXPECT_TRUE(adapter_->IsPresent());
  EXPECT_TRUE(adapter_->IsPowered());
  EXPECT_FALSE(adapter_->IsDiscoverable());
  EXPECT_FALSE(adapter_->IsDiscovering());
}

// TODO(scheib): Enable BluetoothTest fixture tests on all platforms.
#if defined(OS_ANDROID)
#define MAYBE_DiscoverySession DiscoverySession
#else
#define MAYBE_DiscoverySession DISABLED_DiscoverySession
#endif
// Starts and Stops a discovery session.
TEST_F(BluetoothTest, MAYBE_DiscoverySession) {
  InitWithFakeAdapter();
  EXPECT_FALSE(adapter_->IsDiscovering());

  StartLowEnergyDiscoverySession();
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)1, discovery_sessions_.size());
  EXPECT_TRUE(discovery_sessions_[0]->IsActive());

  ResetEventCounts();
  discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED),
                               GetErrorCallback(Call::NOT_EXPECTED));
  EXPECT_FALSE(adapter_->IsDiscovering());
  EXPECT_FALSE(discovery_sessions_[0]->IsActive());
}

// Android only: this test is specific for Android and should not be
// enabled for other platforms.
#if defined(OS_ANDROID)
TEST_F(BluetoothTest, AdapterIllegalStateBeforeStartScan) {
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  ForceIllegalStateException();
  StartLowEnergyDiscoverySessionExpectedToFail();
  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_FALSE(adapter_->IsDiscovering());
}
#endif  // defined(OS_ANDROID)

// Android only: this test is specific for Android and should not be
// enabled for other platforms.
#if defined(OS_ANDROID)
TEST_F(BluetoothTest, AdapterIllegalStateBeforeStopScan) {
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  StartLowEnergyDiscoverySession();
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ForceIllegalStateException();
  discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED),
                               GetErrorCallback(Call::NOT_EXPECTED));
  EXPECT_FALSE(adapter_->IsDiscovering());
}
#endif  // defined(OS_ANDROID)

#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_NoPermissions NoPermissions
#else
#define MAYBE_NoPermissions DISABLED_NoPermissions
#endif
// Checks that discovery fails (instead of hanging) when permissions are denied.
TEST_F(BluetoothTest, MAYBE_NoPermissions) {
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  if (!DenyPermission()) {
    // Platform always gives permission to scan.
    return;
  }

  StartLowEnergyDiscoverySessionExpectedToFail();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
}

// Android-only: Only Android requires location services to be turned on to scan
// for Bluetooth devices.
#if defined(OS_ANDROID)
// Checks that discovery fails (instead of hanging) when location services are
// turned off.
TEST_F(BluetoothTest, NoLocationServices) {
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  SimulateLocationServicesOff();

  StartLowEnergyDiscoverySessionExpectedToFail();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
}
#endif  // defined(OS_ANDROID)

#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_DiscoverLowEnergyDevice DiscoverLowEnergyDevice
#else
#define MAYBE_DiscoverLowEnergyDevice DISABLED_DiscoverLowEnergyDevice
#endif
// Discovers a device.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrt, DiscoverLowEnergyDevice) {
#else
TEST_F(BluetoothTest, MAYBE_DiscoverLowEnergyDevice) {
#endif
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  // Start discovery and find a device.
  StartLowEnergyDiscoverySession();
  SimulateLowEnergyDevice(1);
  EXPECT_EQ(1, observer.device_added_count());
  BluetoothDevice* device = adapter_->GetDevice(observer.last_device_address());
  EXPECT_TRUE(device);
}

#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_DiscoverLowEnergyDeviceTwice DiscoverLowEnergyDeviceTwice
#else
#define MAYBE_DiscoverLowEnergyDeviceTwice DISABLED_DiscoverLowEnergyDeviceTwice
#endif
// Discovers the same device multiple times.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrt, DiscoverLowEnergyDeviceTwice) {
#else
TEST_F(BluetoothTest, MAYBE_DiscoverLowEnergyDeviceTwice) {
#endif
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  // Start discovery and find a device.
  StartLowEnergyDiscoverySession();
  SimulateLowEnergyDevice(1);
  EXPECT_EQ(1, observer.device_added_count());
  BluetoothDevice* device = adapter_->GetDevice(observer.last_device_address());
  EXPECT_TRUE(device);

  // Find the same device again. This should not create a new device object.
  observer.Reset();
  SimulateLowEnergyDevice(1);
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(0, observer.device_added_count());
  EXPECT_EQ(1u, adapter_->GetDevices().size());
}

#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_DiscoverLowEnergyDeviceWithUpdatedUUIDs \
  DiscoverLowEnergyDeviceWithUpdatedUUIDs
#else
#define MAYBE_DiscoverLowEnergyDeviceWithUpdatedUUIDs \
  DISABLED_DiscoverLowEnergyDeviceWithUpdatedUUIDs
#endif
// Discovers a device, and then again with new Service UUIDs.
// Makes sure we don't create another device when we've found the
// device in the past.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrt, DiscoverLowEnergyDeviceWithUpdatedUUIDs) {
#else
TEST_F(BluetoothTest, MAYBE_DiscoverLowEnergyDeviceWithUpdatedUUIDs) {
#endif
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  // Start discovery and find a device.
  StartLowEnergyDiscoverySession();
  BluetoothDevice* device = SimulateLowEnergyDevice(1);

  // Discover same device again with updated UUIDs:
  observer.Reset();
  SimulateLowEnergyDevice(2);
  EXPECT_EQ(0, observer.device_added_count());
  EXPECT_EQ(1, observer.device_changed_count());
  EXPECT_EQ(1u, adapter_->GetDevices().size());
  EXPECT_EQ(device, observer.last_device());

  // Discover same device again with empty UUIDs:
  observer.Reset();
  SimulateLowEnergyDevice(3);
  EXPECT_EQ(0, observer.device_added_count());
  EXPECT_EQ(1, observer.device_changed_count());
  EXPECT_EQ(1u, adapter_->GetDevices().size());
}

#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_DiscoverMultipleLowEnergyDevices DiscoverMultipleLowEnergyDevices
#else
#define MAYBE_DiscoverMultipleLowEnergyDevices \
  DISABLED_DiscoverMultipleLowEnergyDevices
#endif
// Discovers multiple devices when addresses vary.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrt, DiscoverMultipleLowEnergyDevices) {
#else
TEST_F(BluetoothTest, MAYBE_DiscoverMultipleLowEnergyDevices) {
#endif
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  // Start discovery and find a device.
  StartLowEnergyDiscoverySession();
  SimulateLowEnergyDevice(1);
  SimulateLowEnergyDevice(4);
  EXPECT_EQ(2, observer.device_added_count());
  EXPECT_EQ(2u, adapter_->GetDevices().size());
}

// TODO(https://crbug.com/804356): Enable this test on old Windows versions as
// well.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, TogglePowerFakeAdapter) {
#else
TEST_F(BluetoothTest, TogglePowerFakeAdapter) {
#endif
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }

  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  ASSERT_TRUE(adapter_->IsPresent());
  ASSERT_TRUE(adapter_->IsPowered());
  EXPECT_EQ(0, observer.powered_changed_count());

  // Check if power can be turned off.
  adapter_->SetPowered(false, GetCallback(Call::EXPECTED),
                       GetErrorCallback(Call::NOT_EXPECTED));
  scoped_task_environment_.RunUntilIdle();
  EXPECT_FALSE(adapter_->IsPowered());
  EXPECT_EQ(1, observer.powered_changed_count());

  // Check if power can be turned on again.
  adapter_->SetPowered(true, GetCallback(Call::EXPECTED),
                       GetErrorCallback(Call::NOT_EXPECTED));
  scoped_task_environment_.RunUntilIdle();
  EXPECT_TRUE(adapter_->IsPowered());
  EXPECT_EQ(2, observer.powered_changed_count());
}

#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_TogglePowerFakeAdapter_Twice TogglePowerFakeAdapter_Twice
#else
#define MAYBE_TogglePowerFakeAdapter_Twice DISABLED_TogglePowerFakeAdapter_Twice
#endif
// These tests are not relevant for BlueZ and old Windows versions. On these
// platforms the corresponding system APIs are blocking or use callbacks, so
// that it is not necessary to store pending callbacks and wait for the
// appropriate events.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, TogglePowerFakeAdapter_Twice) {
#else
TEST_F(BluetoothTest, MAYBE_TogglePowerFakeAdapter_Twice) {
#endif
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }

  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  ASSERT_TRUE(adapter_->IsPresent());
  ASSERT_TRUE(adapter_->IsPowered());
  EXPECT_EQ(0, observer.powered_changed_count());

  // Post two pending turn off requests, the second should fail due to the
  // presence of another callback.
  adapter_->SetPowered(false, GetCallback(Call::EXPECTED),
                       GetErrorCallback(Call::NOT_EXPECTED));
  adapter_->SetPowered(false, GetCallback(Call::NOT_EXPECTED),
                       GetErrorCallback(Call::EXPECTED));
  scoped_task_environment_.RunUntilIdle();
  EXPECT_FALSE(adapter_->IsPowered());
  EXPECT_EQ(1, observer.powered_changed_count());

  // Post two pending turn on requests, the second should fail due to the
  // presence of another callback.
  adapter_->SetPowered(true, GetCallback(Call::EXPECTED),
                       GetErrorCallback(Call::NOT_EXPECTED));
  adapter_->SetPowered(true, GetCallback(Call::NOT_EXPECTED),
                       GetErrorCallback(Call::EXPECTED));
  scoped_task_environment_.RunUntilIdle();
  EXPECT_TRUE(adapter_->IsPowered());
  EXPECT_EQ(2, observer.powered_changed_count());
}

#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_TogglePowerFakeAdapter_WithinCallback_On_Off \
  TogglePowerFakeAdapter_WithinCallback_On_Off
#else
#define MAYBE_TogglePowerFakeAdapter_WithinCallback_On_Off \
  DISABLED_TogglePowerFakeAdapter_WithinCallback_On_Off
#endif

#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, TogglePowerFakeAdapter_WithinCallback_On_Off) {
#else
TEST_F(BluetoothTest, MAYBE_TogglePowerFakeAdapter_WithinCallback_On_Off) {
#endif
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }

  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  ASSERT_TRUE(adapter_->IsPresent());
  ASSERT_TRUE(adapter_->IsPowered());
  EXPECT_EQ(0, observer.powered_changed_count());

  // Turn adapter off, while powering it on in the callback.
  adapter_->SetPowered(false, base::BindLambdaForTesting([&] {
                         adapter_->SetPowered(
                             true, GetCallback(Call::EXPECTED),
                             GetErrorCallback(Call::NOT_EXPECTED));
                       }),
                       GetErrorCallback(Call::NOT_EXPECTED));
  scoped_task_environment_.RunUntilIdle();
  EXPECT_TRUE(adapter_->IsPowered());
  EXPECT_EQ(2, observer.powered_changed_count());
}

#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_TogglePowerFakeAdapter_WithinCallback_Off_On \
  TogglePowerFakeAdapter_WithinCallback_Off_On
#else
#define MAYBE_TogglePowerFakeAdapter_WithinCallback_Off_On \
  DISABLED_TogglePowerFakeAdapter_WithinCallback_Off_On
#endif

#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, TogglePowerFakeAdapter_WithinCallback_Off_On) {
#else
TEST_F(BluetoothTest, MAYBE_TogglePowerFakeAdapter_WithinCallback_Off_On) {
#endif
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }

  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  ASSERT_TRUE(adapter_->IsPresent());
  ASSERT_TRUE(adapter_->IsPowered());
  EXPECT_EQ(0, observer.powered_changed_count());

  // Turn power off.
  adapter_->SetPowered(false, GetCallback(Call::EXPECTED),
                       GetErrorCallback(Call::NOT_EXPECTED));
  scoped_task_environment_.RunUntilIdle();
  EXPECT_FALSE(adapter_->IsPowered());
  EXPECT_EQ(1, observer.powered_changed_count());

  // Turn adapter on, while powering it off in the callback.
  adapter_->SetPowered(true, base::BindLambdaForTesting([&] {
                         adapter_->SetPowered(
                             false, GetCallback(Call::EXPECTED),
                             GetErrorCallback(Call::NOT_EXPECTED));
                       }),
                       GetErrorCallback(Call::NOT_EXPECTED));
  scoped_task_environment_.RunUntilIdle();
  EXPECT_FALSE(adapter_->IsPowered());
  EXPECT_EQ(3, observer.powered_changed_count());
}

#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_TogglePowerFakeAdapter_DestroyWithPending \
  TogglePowerFakeAdapter_DestroyWithPending
#else
#define MAYBE_TogglePowerFakeAdapter_DestroyWithPending \
  DISABLED_TogglePowerFakeAdapter_DestroyWithPending
#endif

#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, TogglePowerFakeAdapter_DestroyWithPending) {
#else
TEST_F(BluetoothTest, MAYBE_TogglePowerFakeAdapter_DestroyWithPending) {
#endif
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }

  InitWithFakeAdapter();
  ASSERT_TRUE(adapter_->IsPresent());
  ASSERT_TRUE(adapter_->IsPowered());

  // Schedule pending power off request and cause destruction of the adapter by
  // dropping the reference count to 0. Note that we are intentionally not using
  // a TestBluetoothAdapterObserver, as this would hold another reference to the
  // adapter and thus interfere with the intended destruction.
  // We expect the error callback to be invoked, and any other subsequent calls
  // to SetPowered() should fail as well.
  bool error_callback_called = false;
  BluetoothAdapter* adapter = adapter_.get();
  adapter->SetPowered(
      false, GetCallback(Call::NOT_EXPECTED),
      base::BindLambdaForTesting(
          // Note that we explicitly need to capture a pointer to the
          // underlying adapter, even though we pass |this| implicitly. This is
          // because by the time this callback is invoked, |adapter_| is already
          // set to null, but the pointed to adapter instance is still alive. So
          // using the pointer is safe, but dereferencing |adapter_| crashes.
          [&] {
            error_callback_called = true;
            adapter->SetPowered(false, GetCallback(Call::NOT_EXPECTED),
                                GetErrorCallback(Call::EXPECTED));
            adapter->SetPowered(true, GetCallback(Call::NOT_EXPECTED),
                                GetErrorCallback(Call::EXPECTED));
          }));

  adapter_ = nullptr;
  // Empty the message loop to make sure posted callbacks get run.
  scoped_task_environment_.RunUntilIdle();
  EXPECT_TRUE(error_callback_called);
}

#if defined(OS_ANDROID)
#define MAYBE_TogglePowerBeforeScan TogglePowerBeforeScan
#else
#define MAYBE_TogglePowerBeforeScan DISABLED_TogglePowerBeforeScan
#endif
TEST_F(BluetoothTest, MAYBE_TogglePowerBeforeScan) {
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  ASSERT_TRUE(adapter_->IsPresent());
  ASSERT_TRUE(adapter_->IsPowered());
  EXPECT_EQ(0, observer.powered_changed_count());

  // Turn off adapter.
  adapter_->SetPowered(false, GetCallback(Call::EXPECTED),
                       GetErrorCallback(Call::NOT_EXPECTED));
  scoped_task_environment_.RunUntilIdle();
  ASSERT_FALSE(adapter_->IsPowered());
  EXPECT_EQ(1, observer.powered_changed_count());

  // Try to perform a scan.
  StartLowEnergyDiscoverySessionExpectedToFail();

  // Turn on adapter.
  adapter_->SetPowered(true, GetCallback(Call::EXPECTED),
                       GetErrorCallback(Call::NOT_EXPECTED));
  scoped_task_environment_.RunUntilIdle();
  ASSERT_TRUE(adapter_->IsPowered());
  EXPECT_EQ(2, observer.powered_changed_count());

  // Try to perform a scan again.
  ResetEventCounts();
  StartLowEnergyDiscoverySession();
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)1, discovery_sessions_.size());
  EXPECT_TRUE(discovery_sessions_[0]->IsActive());
}

#if defined(OS_MACOSX)
#define MAYBE_TurnOffAdapterWithConnectedDevice \
  TurnOffAdapterWithConnectedDevice
#else
#define MAYBE_TurnOffAdapterWithConnectedDevice \
  DISABLED_TurnOffAdapterWithConnectedDevice
#endif
// TODO(crbug.com/725270): Enable on relevant platforms.
TEST_F(BluetoothTest, MAYBE_TurnOffAdapterWithConnectedDevice) {
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }

  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  StartLowEnergyDiscoverySession();
  BluetoothDevice* device = SimulateLowEnergyDevice(3);

  device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
                               GetConnectErrorCallback(Call::NOT_EXPECTED));
  SimulateGattConnection(device);
  base::RunLoop().RunUntilIdle();

  ASSERT_TRUE(device->IsGattConnected());

  ResetEventCounts();
  SimulateAdapterPoweredOff();

  EXPECT_EQ(2, observer.device_changed_count());
  EXPECT_FALSE(device->IsConnected());
  EXPECT_FALSE(device->IsGattConnected());
}

#if (defined(OS_CHROMEOS) || defined(OS_LINUX)) && \
    !defined(USE_CAST_BLUETOOTH_ADAPTER)
#define MAYBE_RegisterLocalGattServices RegisterLocalGattServices
#else
#define MAYBE_RegisterLocalGattServices DISABLED_RegisterLocalGattServices
#endif
TEST_F(BluetoothTest, MAYBE_RegisterLocalGattServices) {
  InitWithFakeAdapter();
  base::WeakPtr<BluetoothLocalGattService> service =
      BluetoothLocalGattService::Create(
          adapter_.get(), BluetoothUUID(kTestUUIDGenericAttribute), true,
          nullptr, nullptr);
  base::WeakPtr<BluetoothLocalGattCharacteristic> characteristic1 =
      BluetoothLocalGattCharacteristic::Create(
          BluetoothUUID(kTestUUIDGenericAttribute),
          device::BluetoothLocalGattCharacteristic::Properties(),
          device::BluetoothLocalGattCharacteristic::Permissions(),
          service.get());

  base::WeakPtr<BluetoothLocalGattCharacteristic> characteristic2 =
      BluetoothLocalGattCharacteristic::Create(
          BluetoothUUID(kTestUUIDGenericAttribute),
          device::BluetoothLocalGattCharacteristic::Properties(),
          device::BluetoothLocalGattCharacteristic::Permissions(),
          service.get());

  base::WeakPtr<BluetoothLocalGattDescriptor> descriptor =
      BluetoothLocalGattDescriptor::Create(
          BluetoothUUID(kTestUUIDGenericAttribute),
          device::BluetoothLocalGattCharacteristic::Permissions(),
          characteristic1.get());

  service->Register(GetCallback(Call::EXPECTED),
                    GetGattErrorCallback(Call::NOT_EXPECTED));
  service->Register(GetCallback(Call::NOT_EXPECTED),
                    GetGattErrorCallback(Call::EXPECTED));
  service->Unregister(GetCallback(Call::EXPECTED),
                      GetGattErrorCallback(Call::NOT_EXPECTED));
  service->Unregister(GetCallback(Call::NOT_EXPECTED),
                      GetGattErrorCallback(Call::EXPECTED));
}

// This test should only be enabled for platforms that uses the
// BluetoothAdapter#RemoveOutdatedDevices function to purge outdated
// devices.
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_EnsureUpdatedTimestamps EnsureUpdatedTimestamps
#else
#define MAYBE_EnsureUpdatedTimestamps DISABLED_EnsureUpdatedTimestamps
#endif
TEST_F(BluetoothTest, MAYBE_EnsureUpdatedTimestamps) {
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  // Test that the timestamp of a device is updated during multiple
  // discovery sessions.
  StartLowEnergyDiscoverySession();
  BluetoothDevice* device = SimulateLowEnergyDevice(1);

  EXPECT_EQ(1, observer.device_added_count());
  EXPECT_EQ(1u, adapter_->GetDevices().size());
  base::Time first_timestamp = device->GetLastUpdateTime();

  // Do a new discovery and check that the timestamp is updated.
  observer.Reset();
  StartLowEnergyDiscoverySession();
  SimulateLowEnergyDevice(1);
  EXPECT_EQ(0, observer.device_added_count());
  EXPECT_EQ(1u, adapter_->GetDevices().size());
  base::Time second_timestamp = device->GetLastUpdateTime();
  EXPECT_TRUE(second_timestamp > first_timestamp);

  // Check that timestamp doesn't change when there is no discovery.
  base::Time third_timestamp = device->GetLastUpdateTime();
  EXPECT_TRUE(second_timestamp == third_timestamp);
}

// This test should only be enabled for platforms that uses the
// BluetoothAdapter#RemoveOutdatedDevices function to purge outdated
// devices.
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_RemoveOutdatedDevices RemoveOutdatedDevices
#else
#define MAYBE_RemoveOutdatedDevices DISABLED_RemoveOutdatedDevices
#endif
TEST_F(BluetoothTest, MAYBE_RemoveOutdatedDevices) {
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);
  StartLowEnergyDiscoverySession();
  BluetoothDevice* device1 = SimulateLowEnergyDevice(1);
  BluetoothDevice* device2 = SimulateLowEnergyDevice(4);

  EXPECT_EQ(2u, adapter_->GetDevices().size());
  device1->SetAsExpiredForTesting();

  // Check that the outdated device is removed.
  RemoveTimedOutDevices();
  EXPECT_EQ(1, observer.device_removed_count());
  EXPECT_EQ(1u, adapter_->GetDevices().size());
  EXPECT_EQ(adapter_->GetDevices()[0]->GetAddress(), device2->GetAddress());
}

// This test should only be enabled for platforms that uses the
// BluetoothAdapter#RemoveOutdatedDevices function to purge outdated
// devices.
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_RemoveOutdatedDeviceGattConnect RemoveOutdatedDeviceGattConnect
#else
#define MAYBE_RemoveOutdatedDeviceGattConnect \
  DISABLED_RemoveOutdatedDeviceGattConnect
#endif
TEST_F(BluetoothTest, MAYBE_RemoveOutdatedDeviceGattConnect) {
  // Test that a device with GATT connection isn't removed.
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);
  StartLowEnergyDiscoverySession();
  BluetoothDevice* device = SimulateLowEnergyDevice(1);
  device->SetAsExpiredForTesting();
  device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
                               GetConnectErrorCallback(Call::NOT_EXPECTED));
  SimulateGattConnection(device);
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1u, adapter_->GetDevices().size());
  RemoveTimedOutDevices();
  EXPECT_EQ(0, observer.device_removed_count());
  EXPECT_EQ(1u, adapter_->GetDevices().size());
}

#if defined(OS_MACOSX)
// Simulate two devices being connected before calling
// RetrieveGattConnectedDevicesWithDiscoveryFilter() with no service filter.
TEST_F(BluetoothTest, DiscoverConnectedLowEnergyDeviceWithNoFilter) {
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  SimulateConnectedLowEnergyDevice(ConnectedDeviceType::GENERIC_DEVICE);
  SimulateConnectedLowEnergyDevice(ConnectedDeviceType::HEART_RATE_DEVICE);
  BluetoothDiscoveryFilter discovery_filter(BLUETOOTH_TRANSPORT_LE);
  std::unordered_map<BluetoothDevice*, BluetoothDevice::UUIDSet> result =
      adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter(
          discovery_filter);

  EXPECT_EQ(2u, result.size());
  for (const auto& pair : result) {
    EXPECT_TRUE(adapter_->GetDevice(pair.first->GetAddress()));
    EXPECT_TRUE(pair.second.empty());
  }
  EXPECT_EQ(BluetoothDevice::UUIDSet({BluetoothUUID(kTestUUIDGenericAccess)}),
            RetrieveConnectedPeripheralServiceUUIDs());
  EXPECT_EQ(2, observer.device_added_count());
  EXPECT_EQ(2u, adapter_->GetDevices().size());
}
#endif  // defined(OS_MACOSX)

#if defined(OS_MACOSX)
// Simulate two devices being connected before calling
// RetrieveGattConnectedDevicesWithDiscoveryFilter() with one service filter.
TEST_F(BluetoothTest, DiscoverConnectedLowEnergyDeviceWithFilter) {
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  SimulateConnectedLowEnergyDevice(ConnectedDeviceType::GENERIC_DEVICE);
  SimulateConnectedLowEnergyDevice(ConnectedDeviceType::HEART_RATE_DEVICE);
  BluetoothDiscoveryFilter discovery_filter(BLUETOOTH_TRANSPORT_LE);
  device::BluetoothUUID heart_service_uuid =
      device::BluetoothUUID(kTestUUIDHeartRate);
  discovery_filter.AddUUID(heart_service_uuid);
  std::unordered_map<BluetoothDevice*, BluetoothDevice::UUIDSet> result =
      adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter(
          discovery_filter);

  EXPECT_EQ(1u, result.size());
  for (const auto& pair : result) {
    EXPECT_EQ(kTestDeviceAddress2, pair.first->GetAddress());
    EXPECT_TRUE(adapter_->GetDevice(pair.first->GetAddress()));
    EXPECT_EQ(BluetoothDevice::UUIDSet({heart_service_uuid}), pair.second);
  }
  EXPECT_EQ(BluetoothDevice::UUIDSet({heart_service_uuid}),
            RetrieveConnectedPeripheralServiceUUIDs());
  EXPECT_EQ(1, observer.device_added_count());
  EXPECT_EQ(1u, adapter_->GetDevices().size());
}
#endif  // defined(OS_MACOSX)

#if defined(OS_MACOSX)
// Simulate two devices being connected before calling
// RetrieveGattConnectedDevicesWithDiscoveryFilter() with one service filter
// that doesn't match.
TEST_F(BluetoothTest, DiscoverConnectedLowEnergyDeviceWithWrongFilter) {
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  SimulateConnectedLowEnergyDevice(ConnectedDeviceType::GENERIC_DEVICE);
  SimulateConnectedLowEnergyDevice(ConnectedDeviceType::HEART_RATE_DEVICE);
  BluetoothDiscoveryFilter discovery_filter(BLUETOOTH_TRANSPORT_LE);
  discovery_filter.AddUUID(device::BluetoothUUID(kTestUUIDLinkLoss));
  std::unordered_map<BluetoothDevice*, BluetoothDevice::UUIDSet> result =
      adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter(
          discovery_filter);

  EXPECT_TRUE(result.empty());
  EXPECT_EQ(
      BluetoothDevice::UUIDSet({device::BluetoothUUID(kTestUUIDLinkLoss)}),
      RetrieveConnectedPeripheralServiceUUIDs());
  EXPECT_EQ(0, observer.device_added_count());
  EXPECT_EQ(0u, adapter_->GetDevices().size());
}
#endif  // defined(OS_MACOSX)

#if defined(OS_MACOSX)
// Simulate two devices being connected before calling
// RetrieveGattConnectedDevicesWithDiscoveryFilter() with two service filters
// that both match.
TEST_F(BluetoothTest, DiscoverConnectedLowEnergyDeviceWithTwoFilters) {
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  SimulateConnectedLowEnergyDevice(ConnectedDeviceType::GENERIC_DEVICE);
  SimulateConnectedLowEnergyDevice(ConnectedDeviceType::HEART_RATE_DEVICE);
  BluetoothDiscoveryFilter discovery_filter(BLUETOOTH_TRANSPORT_LE);
  device::BluetoothUUID heart_service_uuid =
      device::BluetoothUUID(kTestUUIDHeartRate);
  discovery_filter.AddUUID(heart_service_uuid);
  device::BluetoothUUID generic_service_uuid =
      device::BluetoothUUID(kTestUUIDGenericAccess);
  discovery_filter.AddUUID(generic_service_uuid);
  std::unordered_map<BluetoothDevice*, BluetoothDevice::UUIDSet> result =
      adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter(
          discovery_filter);

  EXPECT_EQ(2u, result.size());
  for (const auto& pair : result) {
    EXPECT_TRUE(adapter_->GetDevice(pair.first->GetAddress()));
    if (pair.first->GetAddress() == kTestDeviceAddress2) {
      EXPECT_EQ(
          BluetoothDevice::UUIDSet({heart_service_uuid, generic_service_uuid}),
          pair.second);
    } else if (pair.first->GetAddress() == kTestDeviceAddress1) {
      EXPECT_EQ(BluetoothDevice::UUIDSet({generic_service_uuid}), pair.second);
    } else {
      // Unknown device.
      EXPECT_TRUE(false);
    }
  }
  EXPECT_EQ(
      BluetoothDevice::UUIDSet({generic_service_uuid, heart_service_uuid}),
      RetrieveConnectedPeripheralServiceUUIDs());
  EXPECT_EQ(2, observer.device_added_count());
  EXPECT_EQ(2u, adapter_->GetDevices().size());
}
#endif  // defined(OS_MACOSX)

#if defined(OS_MACOSX)
// Simulate two devices being connected before calling
// RetrieveGattConnectedDevicesWithDiscoveryFilter() with one service filter
// that one match device, and then
// RetrieveGattConnectedDevicesWithDiscoveryFilter() again.
TEST_F(BluetoothTest, DiscoverConnectedLowEnergyDeviceTwice) {
  if (!PlatformSupportsLowEnergy()) {
    LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
    return;
  }
  InitWithFakeAdapter();
  TestBluetoothAdapterObserver observer(adapter_);

  SimulateConnectedLowEnergyDevice(ConnectedDeviceType::GENERIC_DEVICE);
  SimulateConnectedLowEnergyDevice(ConnectedDeviceType::HEART_RATE_DEVICE);
  BluetoothDiscoveryFilter discovery_filter(BLUETOOTH_TRANSPORT_LE);
  device::BluetoothUUID heart_service_uuid =
      device::BluetoothUUID(kTestUUIDHeartRate);
  discovery_filter.AddUUID(heart_service_uuid);
  std::unordered_map<BluetoothDevice*, BluetoothDevice::UUIDSet> result =
      adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter(
          discovery_filter);

  EXPECT_EQ(1u, result.size());
  for (const auto& pair : result) {
    EXPECT_EQ(kTestDeviceAddress2, pair.first->GetAddress());
    EXPECT_TRUE(adapter_->GetDevice(pair.first->GetAddress()));
    EXPECT_EQ(BluetoothDevice::UUIDSet({heart_service_uuid}), pair.second);
  }
  EXPECT_EQ(BluetoothDevice::UUIDSet({heart_service_uuid}),
            RetrieveConnectedPeripheralServiceUUIDs());
  EXPECT_EQ(1, observer.device_added_count());
  EXPECT_EQ(1u, adapter_->GetDevices().size());

  observer.Reset();
  ResetRetrieveConnectedPeripheralServiceUUIDs();
  result = adapter_->RetrieveGattConnectedDevicesWithDiscoveryFilter(
      discovery_filter);

  EXPECT_EQ(1u, result.size());
  for (const auto& pair : result) {
    EXPECT_EQ(kTestDeviceAddress2, pair.first->GetAddress());
    EXPECT_TRUE(adapter_->GetDevice(pair.first->GetAddress()));
    EXPECT_EQ(BluetoothDevice::UUIDSet({heart_service_uuid}), pair.second);
  }
  EXPECT_EQ(BluetoothDevice::UUIDSet({heart_service_uuid}),
            RetrieveConnectedPeripheralServiceUUIDs());

  EXPECT_EQ(0, observer.device_added_count());
  EXPECT_EQ(1u, adapter_->GetDevices().size());
}
#endif  // defined(OS_MACOSX)

#if defined(OS_WIN)
INSTANTIATE_TEST_CASE_P(
    /* no prefix */,
    BluetoothTestWinrt,
    ::testing::Bool());

INSTANTIATE_TEST_CASE_P(
    /* no prefix */,
    BluetoothTestWinrtOnly,
    ::testing::Values(true));
#endif  // defined(OS_WIN)

}  // namespace device
