diff --git a/fastpair/internal/mediums/BUILD b/fastpair/internal/mediums/BUILD index 680b0061..3b43b962 100644 --- a/fastpair/internal/mediums/BUILD +++ b/fastpair/internal/mediums/BUILD @@ -119,9 +119,9 @@ cc_test( ":mediums", "//internal/platform:comm", "//internal/platform:test_util", + "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], ) diff --git a/fastpair/internal/mediums/bluetooth_classic.cc b/fastpair/internal/mediums/bluetooth_classic.cc index 3ad15854..a80e10be 100644 --- a/fastpair/internal/mediums/bluetooth_classic.cc +++ b/fastpair/internal/mediums/bluetooth_classic.cc @@ -34,6 +34,57 @@ bool BluetoothClassic::IsAvailableLocked() const { return medium_.IsValid() && adapter_.IsValid() && adapter_.IsEnabled(); } +void BluetoothClassic::AddObserver(BluetoothClassicMedium::Observer* observer) { + MutexLock lock(&mutex_); + medium_.AddObserver(observer); +} + +void BluetoothClassic::RemoveObserver( + BluetoothClassicMedium::Observer* observer) { + MutexLock lock(&mutex_); + medium_.RemoveObserver(observer); +} + +bool BluetoothClassic::StartDiscovery() { + MutexLock lock(&mutex_); + if (!radio_.IsEnabled()) { + NEARBY_LOGS(INFO) << "Can't discover BT devices because BT isn't enabled."; + return false; + } + + if (!IsAvailableLocked()) { + NEARBY_LOGS(INFO) + << "Can't discover BT devices because BT isn't available."; + return false; + } + + if (!medium_.StartDiscovery({})) { + NEARBY_LOGS(INFO) << "Failed to start discovery of BT devices."; + return false; + } + // Mark the fact that we're currently performing a Bluetooth scan. + is_scanning = true; + return true; +} + +bool BluetoothClassic::StopDiscovery() { + MutexLock lock(&mutex_); + if (!IsDiscovering()) { + NEARBY_LOGS(INFO) + << "Can't stop discovery of BT devices because it never started."; + return false; + } + + if (!medium_.StopDiscovery()) { + NEARBY_LOGS(INFO) << "Failed to stop discovery of Bluetooth devices."; + return false; + } + is_scanning = false; + return true; +} + +bool BluetoothClassic::IsDiscovering() const { return is_scanning; } + std::unique_ptr BluetoothClassic::CreatePairing( absl::string_view public_address) { MutexLock lock(&mutex_); diff --git a/fastpair/internal/mediums/bluetooth_classic.h b/fastpair/internal/mediums/bluetooth_classic.h index f04aa308..1256354c 100644 --- a/fastpair/internal/mediums/bluetooth_classic.h +++ b/fastpair/internal/mediums/bluetooth_classic.h @@ -32,6 +32,17 @@ class BluetoothClassic { // Returns true, if BT communications are supported by a platform. bool IsAvailable() const ABSL_LOCKS_EXCLUDED(mutex_); + void AddObserver(BluetoothClassicMedium::Observer* observer); + void RemoveObserver(BluetoothClassicMedium::Observer* observer); + + // Enables BT discovery mode to observer any discoverable device in range. + // Returns true, if discovery mode was enabled, false otherwise. + bool StartDiscovery() ABSL_LOCKS_EXCLUDED(mutex_); + + // Disables BT discovery mode. + // Returns true, if discovery mode was previously enabled, false otherwise. + bool StopDiscovery() ABSL_LOCKS_EXCLUDED(mutex_); + // Returns a new BluetoothPairing instance to handle the pairing process // with the remote device. std::unique_ptr CreatePairing( @@ -43,11 +54,15 @@ class BluetoothClassic { // Same as IsAvailable(), but must be called with mutex_ held. bool IsAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + // Returns true if device is currently in discovery mode. + bool IsDiscovering() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + mutable Mutex mutex_; BluetoothRadio& radio_ ABSL_GUARDED_BY(mutex_); BluetoothAdapter& adapter_ ABSL_GUARDED_BY(mutex_){ radio_.GetBluetoothAdapter()}; BluetoothClassicMedium medium_ ABSL_GUARDED_BY(mutex_){adapter_}; + bool is_scanning ABSL_GUARDED_BY(mutex_) = false; }; } // namespace fastpair diff --git a/fastpair/internal/mediums/bluetooth_classic_test.cc b/fastpair/internal/mediums/bluetooth_classic_test.cc index fc95ec22..307ed470 100644 --- a/fastpair/internal/mediums/bluetooth_classic_test.cc +++ b/fastpair/internal/mediums/bluetooth_classic_test.cc @@ -16,11 +16,74 @@ #include "gtest/gtest.h" #include "internal/platform/bluetooth_adapter.h" +#include "internal/platform/count_down_latch.h" #include "internal/platform/medium_environment.h" namespace nearby { namespace fastpair { namespace { +class BluetoothClassicMediumObserver + : public BluetoothClassicMedium ::Observer { + public: + explicit BluetoothClassicMediumObserver( + BluetoothClassic* bluetooth_classic, CountDownLatch* device_added_latch, + CountDownLatch* device_removed_latch, + CountDownLatch* device_paired_changed_latch) + : bluetooth_classic_(bluetooth_classic), + device_added_latch_(device_added_latch), + device_removed_latch_(device_removed_latch), + device_paired_changed_latch_(device_paired_changed_latch) { + bluetooth_classic_->AddObserver(this); + } + + ~BluetoothClassicMediumObserver() override { + bluetooth_classic_->RemoveObserver(this); + } + + void DeviceAdded(BluetoothDevice& device) override { + if (!device_added_latch_) return; + device_added_latch_->CountDown(); + } + + void DeviceRemoved(BluetoothDevice& device) override { + if (!device_removed_latch_) return; + device_removed_latch_->CountDown(); + } + + void DevicePairedChanged(BluetoothDevice& device, + bool new_paired_status) override { + if (!device_paired_changed_latch_) return; + device_paired_changed_latch_->CountDown(); + } + + BluetoothClassic* bluetooth_classic_; + CountDownLatch* device_added_latch_; + CountDownLatch* device_removed_latch_; + CountDownLatch* device_paired_changed_latch_; +}; + +TEST(BluetoothClassicTest, CanStartAndStopDiscovery) { + MediumEnvironment::Instance().Start(); + BluetoothRadio radio; + BluetoothClassic bluetooth_classic(radio); + BluetoothAdapter provider_adapter; + provider_adapter.SetStatus(BluetoothAdapter::Status::kEnabled); + provider_adapter.SetScanMode( + BluetoothAdapter::ScanMode::kConnectableDiscoverable); + BluetoothClassicMedium bt_provider(provider_adapter); + CountDownLatch device_added_latch(1); + CountDownLatch device_removed_latch(1); + BluetoothClassicMediumObserver observer( + &bluetooth_classic, &device_added_latch, &device_removed_latch, nullptr); + + EXPECT_TRUE(bluetooth_classic.StartDiscovery()); + device_added_latch.Await(); + provider_adapter.SetStatus(BluetoothAdapter::Status::kDisabled); + device_removed_latch.Await(); + + EXPECT_TRUE(bluetooth_classic.StopDiscovery()); + MediumEnvironment::Instance().Stop(); +} TEST(BluetoothClassicTest, CanCreatePairing) { MediumEnvironment::Instance().Start(); @@ -36,7 +99,7 @@ TEST(BluetoothClassicTest, CanCreatePairing) { MediumEnvironment::Instance().Stop(); } -TEST(BluetoothClassicTest, RemoteDeviceNotFound) { +TEST(BluetoothClassicTest, FailedToCreatePairingDueToRemoteDeviceNotFound) { MediumEnvironment::Instance().Start(); BluetoothRadio radio; BluetoothClassic bluetooth_classic(radio); @@ -60,6 +123,8 @@ TEST(BluetoothClassicTest, RadioDisable) { EXPECT_FALSE(bluetooth_classic.IsAvailable()); EXPECT_FALSE( bluetooth_classic.CreatePairing(provider_adapter.GetMacAddress())); + EXPECT_FALSE(bluetooth_classic.StartDiscovery()); + EXPECT_FALSE(bluetooth_classic.StopDiscovery()); } TEST(BluetoothClassicTest, BluetoothAdapterDisable) { @@ -73,6 +138,8 @@ TEST(BluetoothClassicTest, BluetoothAdapterDisable) { EXPECT_FALSE(bluetooth_classic.IsAvailable()); EXPECT_FALSE( bluetooth_classic.CreatePairing(adapter_provider.GetMacAddress())); + EXPECT_FALSE(bluetooth_classic.StartDiscovery()); + EXPECT_FALSE(bluetooth_classic.StopDiscovery()); } TEST(BluetoothClassicTest, GetMedium) { diff --git a/fastpair/retroactive/BUILD b/fastpair/retroactive/BUILD index a22396cf..fe9fe81a 100644 --- a/fastpair/retroactive/BUILD +++ b/fastpair/retroactive/BUILD @@ -18,9 +18,12 @@ cc_library( name = "retroactive", srcs = [ "retroactive.cc", + "retroactive_pairing_detector_impl.cc", ], hdrs = [ "retroactive.h", + "retroactive_pairing_detector.h", + "retroactive_pairing_detector_impl.h", ], compatible_with = ["//buildenv/target:non_prod"], visibility = [ @@ -30,10 +33,14 @@ cc_library( deps = [ "//fastpair:fast_pair_controller", "//fastpair/common", + "//fastpair/internal/mediums", "//fastpair/message_stream", + "//fastpair/pairing", + "//internal/base", "//internal/platform:comm", "//internal/platform:types", "//third_party/magic_enum", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", ], diff --git a/fastpair/retroactive/retroactive_pairing_detector.h b/fastpair/retroactive/retroactive_pairing_detector.h new file mode 100644 index 00000000..ad8d2485 --- /dev/null +++ b/fastpair/retroactive/retroactive_pairing_detector.h @@ -0,0 +1,41 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_RETROACTIVE_RETROACTIVE_PAIRING_DETECTOR_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_RETROACTIVE_RETROACTIVE_PAIRING_DETECTOR_H_ + +#include "fastpair/common/fast_pair_device.h" +namespace nearby { +namespace fastpair { +// A RetroactivePairingDetector instance is responsible for detecting Fast Pair +// devices that can be paired retroactively, and notifying observers of this +// device. +class RetroactivePairingDetector { + public: + class Observer { + public: + virtual ~Observer() = default; + virtual void OnRetroactivePairFound(FastPairDevice& device) = 0; + }; + + virtual ~RetroactivePairingDetector() = default; + + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; +}; + +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_RETROACTIVE_RETROACTIVE_PAIRING_DETECTOR_H_ diff --git a/fastpair/retroactive/retroactive_pairing_detector_impl.cc b/fastpair/retroactive/retroactive_pairing_detector_impl.cc new file mode 100644 index 00000000..1464fe12 --- /dev/null +++ b/fastpair/retroactive/retroactive_pairing_detector_impl.cc @@ -0,0 +1,105 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fastpair/retroactive/retroactive_pairing_detector_impl.h" + +#include + +#include "fastpair/internal/mediums/mediums.h" +#include "fastpair/pairing/pairer_broker.h" + +namespace nearby { +namespace fastpair { + +RetroactivePairingDetectorImpl::RetroactivePairingDetectorImpl( + Mediums& mediums, PairerBroker* pairer_broker) + : mediums_(mediums) { + pairer_broker->AddObserver(this); + mediums_.GetBluetoothClassic().AddObserver(this); + mediums_.GetBluetoothClassic().StartDiscovery(); +} + +RetroactivePairingDetectorImpl::~RetroactivePairingDetectorImpl() { + mediums_.GetBluetoothClassic().RemoveObserver(this); + mediums_.GetBluetoothClassic().StopDiscovery(); +} + +void RetroactivePairingDetectorImpl::AddObserver( + RetroactivePairingDetector::Observer* observer) { + observers_.AddObserver(observer); +} + +void RetroactivePairingDetectorImpl::RemoveObserver( + RetroactivePairingDetector::Observer* observer) { + observers_.RemoveObserver(observer); +} + +void RetroactivePairingDetectorImpl::OnDevicePaired(FastPairDevice& device) { + // The classic address is assigned to the Device during the + // initial Fast Pair pairing protocol and if it doesn't exist, + // then it wasn't properly paired during initial Fast Pair + // pairing. + if (!device.GetPublicAddress().has_value()) { + return; + } + + // The Bluetooth Adapter system event `DevicePairedChanged` fires before + // Fast Pair's `OnDevicePaired`, and a Fast Pair pairing is expected to have + // both events. If a device is Fast Paired, it is already inserted in the + // |potential_retroactive_addresses_| in `DevicePairedChanged`; we need to + // remove it to prevent a false positive. + if (potential_retroactive_addresses_.contains( + device.GetPublicAddress().value())) { + NEARBY_LOGS(INFO) + << __func__ + << ": paired with initial pairing, removing device at address = " + << device.GetPublicAddress().value(); + potential_retroactive_addresses_.erase(device.GetPublicAddress().value()); + } +} + +void RetroactivePairingDetectorImpl::DevicePairedChanged( + BluetoothDevice& device, bool new_paired_status) { + NEARBY_LOGS(INFO) << __func__ + << " Device paired changed, name = " << device.GetName() + << " address = " << device.GetMacAddress() + << " new_paired_status = " << std::boolalpha + << new_paired_status; + // This event fires whenever a device pairing has changed with + // the BluetoothClassicMedium. + // If the |new_paired_status| is false, it means a device was unpaired, + // so we early return since it would not be a device to retroactively pair to. + if (!new_paired_status) { + return; + } + + // Both classic paired and Fast paired devices call this function, so we + // have to add the device to |potential_retroactive_addresses_|. We expect + // devices paired via Fast Pair to always call `OnDevicePaired` after calling + // this function, which will remove the device from + // |potential_retroactive_addresses_|. + potential_retroactive_addresses_.insert(device.GetMacAddress()); + + // In order to confirm that this device is a retroactive pairing, we need to + // first check if it has already been saved to the user's account. If it has + // already been saved, we don't want to prompt the user to save a device + // again. + // TODO(b/285047010): check if device has already been saved to the user's + // account + + // TODO(Janusz) Add implementation for AttemptRetroactivePairing +} + +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/retroactive/retroactive_pairing_detector_impl.h b/fastpair/retroactive/retroactive_pairing_detector_impl.h new file mode 100644 index 00000000..e4baef38 --- /dev/null +++ b/fastpair/retroactive/retroactive_pairing_detector_impl.h @@ -0,0 +1,60 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_RETROACTIVE_RETROACTIVE_PAIRING_DETECTOR_IMPL_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_RETROACTIVE_RETROACTIVE_PAIRING_DETECTOR_IMPL_H_ +#include + +#include "absl/container/flat_hash_set.h" +#include "fastpair/internal/mediums/mediums.h" +#include "fastpair/pairing/pairer_broker.h" +#include "fastpair/retroactive/retroactive_pairing_detector.h" +#include "internal/base/observer_list.h" +#include "internal/platform/bluetooth_classic.h" + +namespace nearby { +namespace fastpair { + +class RetroactivePairingDetectorImpl : public RetroactivePairingDetector, + public BluetoothClassicMedium ::Observer, + public PairerBroker::Observer { + public: + RetroactivePairingDetectorImpl(Mediums& mediums, PairerBroker* pairer_broker); + RetroactivePairingDetectorImpl(const RetroactivePairingDetectorImpl&) = + delete; + RetroactivePairingDetectorImpl& operator=( + const RetroactivePairingDetectorImpl&) = delete; + ~RetroactivePairingDetectorImpl() override; + + // RetroactivePairingDetector: + void AddObserver(RetroactivePairingDetector::Observer* observer) override; + void RemoveObserver(RetroactivePairingDetector::Observer* observer) override; + + // BluetoothClassicMedium :: Observer + void DevicePairedChanged(BluetoothDevice& device, + bool new_paired_status) override; + + // PairerBroker::Observer + void OnDevicePaired(FastPairDevice& device) override; + + private: + Mediums& mediums_; + ObserverList observers_; + absl::flat_hash_set potential_retroactive_addresses_; +}; + +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_RETROACTIVE_RETROACTIVE_PAIRING_DETECTOR_IMPL_H_ diff --git a/internal/platform/BUILD b/internal/platform/BUILD index ff5d406f..1de62125 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -226,6 +226,7 @@ cc_library( ":logging", ":types", ":uuid", + "//internal/base", "//internal/platform/implementation:comm", "//internal/test", "@com_google_absl//absl/container:flat_hash_map", diff --git a/internal/platform/bluetooth_classic_test.cc b/internal/platform/bluetooth_classic_test.cc index 1d14dfa9..12d97989 100644 --- a/internal/platform/bluetooth_classic_test.cc +++ b/internal/platform/bluetooth_classic_test.cc @@ -45,6 +45,39 @@ constexpr FeatureFlags kTestCases[] = { }, }; +class BluetoothClassicMediumObserver + : public BluetoothClassicMedium ::Observer { + public: + explicit BluetoothClassicMediumObserver( + CountDownLatch* device_added_latch, CountDownLatch* device_removed_latch, + CountDownLatch* device_paired_changed_latch) + : device_added_latch_(device_added_latch), + device_removed_latch_(device_removed_latch), + device_paired_changed_latch_(device_paired_changed_latch) {} + + void DeviceAdded(BluetoothDevice& device) override { + if (!device_added_latch_) return; + device_added_latch_->CountDown(); + } + + void DeviceRemoved(BluetoothDevice& device) override { + if (!device_removed_latch_) return; + device_removed_latch_->CountDown(); + } + + void DevicePairedChanged(BluetoothDevice& device, + bool new_paired_status) override { + if (!device_paired_changed_latch_) return; + paired_status_ = new_paired_status; + device_paired_changed_latch_->CountDown(); + } + + CountDownLatch* device_added_latch_; + CountDownLatch* device_removed_latch_; + CountDownLatch* device_paired_changed_latch_; + bool paired_status_ = false; +}; + class BluetoothClassicMediumTest : public ::testing::TestWithParam { protected: @@ -304,6 +337,12 @@ TEST_F(BluetoothClassicMediumTest, CanStartDiscovery) { adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable); CountDownLatch found_latch(1); CountDownLatch lost_latch(1); + CountDownLatch device_added_latch(1); + CountDownLatch device_removed_latch(1); + BluetoothClassicMediumObserver observer(&device_added_latch, + &device_removed_latch, nullptr); + bt_a_->AddObserver(&observer); + bt_a_->StartDiscovery(DiscoveryCallback{ .device_discovered_cb = [this, &found_latch](BluetoothDevice& device) { @@ -322,9 +361,11 @@ TEST_F(BluetoothClassicMediumTest, CanStartDiscovery) { EXPECT_EQ(adapter_b_->GetScanMode(), BluetoothAdapter::ScanMode::kConnectableDiscoverable); EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); + EXPECT_TRUE(device_added_latch.Await(absl::Milliseconds(1000)).result()); adapter_b_->SetStatus(BluetoothAdapter::Status::kDisabled); EXPECT_FALSE(adapter_b_->IsEnabled()); EXPECT_TRUE(lost_latch.Await(absl::Milliseconds(1000)).result()); + EXPECT_TRUE(device_removed_latch.Await(absl::Milliseconds(1000)).result()); } TEST_F(BluetoothClassicMediumTest, CanStopDiscovery) { @@ -417,6 +458,10 @@ TEST_F(BluetoothClassicMediumTest, BluetoothPairingSuccess) { CountDownLatch paired_latch(1); CountDownLatch initiated_latch(1); CountDownLatch error_latch(1); + CountDownLatch device_paired_latch(1); + BluetoothClassicMediumObserver observer(nullptr, nullptr, + &device_paired_latch); + bt_a_->AddObserver(&observer); EXPECT_TRUE(bluetooth_pairing->InitiatePairing({ .on_paired_cb = [&]() { paired_latch.CountDown(); }, .on_pairing_error_cb = @@ -442,6 +487,8 @@ TEST_F(BluetoothClassicMediumTest, BluetoothPairingSuccess) { // Finishes pairing with remote device. EXPECT_TRUE(bluetooth_pairing->FinishPairing(received_passkey)); paired_latch.Await(); + device_paired_latch.Await(); + EXPECT_TRUE(observer.paired_status_); EXPECT_TRUE(bluetooth_pairing->IsPaired()); // Unpairs with remote device. diff --git a/internal/platform/implementation/g3/bluetooth_classic.cc b/internal/platform/implementation/g3/bluetooth_classic.cc index dc102594..26eb4d17 100644 --- a/internal/platform/implementation/g3/bluetooth_classic.cc +++ b/internal/platform/implementation/g3/bluetooth_classic.cc @@ -314,11 +314,12 @@ api::BluetoothDevice* BluetoothClassicMedium::GetRemoteDevice( void BluetoothClassicMedium::AddObserver( api::BluetoothClassicMedium::Observer* observer) { - // TODO(b/269521993): Implement observer callbacks. + MediumEnvironment::Instance().AddObserver(observer); } + void BluetoothClassicMedium::RemoveObserver( api::BluetoothClassicMedium::Observer* observer) { - // TODO(b/269521993): Implement observer callbacks. + MediumEnvironment::Instance().RemoveObserver(observer); } } // namespace g3 diff --git a/internal/platform/implementation/windows/bluetooth_classic_medium.cc b/internal/platform/implementation/windows/bluetooth_classic_medium.cc index 6f914208..7d69bb77 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_medium.cc +++ b/internal/platform/implementation/windows/bluetooth_classic_medium.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -560,11 +561,6 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Added( return winrt::fire_and_forget(); } - // Represents a Bluetooth device. - // https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothdevice?view=winrt-20348 - std::unique_ptr - windowsBluetoothDevice; - // Create an iterator for the internal list std::map>::const_iterator it = discovered_devices_by_id_.find(deviceInfo.Id()); @@ -587,11 +583,14 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Added( discovered_devices_by_id_[deviceInfo.Id()] = std::move(bluetoothDeviceP); + NEARBY_LOGS(INFO) << __func__ << ": Notifying bluetooth device added"; if (discovery_callback_.device_discovered_cb != nullptr) { discovery_callback_.device_discovered_cb( *discovered_devices_by_id_[deviceInfo.Id()]); } - + for (auto& observer : observers_.GetObservers()) { + observer->DeviceAdded(*discovered_devices_by_id_[deviceInfo.Id()]); + } return winrt::fire_and_forget(); } @@ -619,6 +618,7 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Updated( return winrt::fire_and_forget(); } + // https://learn.microsoft.com/en-us/windows/uwp/devices-sensors/device-information-properties#associationendpoint-properties if (properties.HasKey(L"System.ItemNameDisplay")) { // we need to really change the name of the bluetooth device std::string new_device_name = InspectableReader::ReadString( @@ -627,17 +627,29 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Updated( if (it->second->GetName() == new_device_name) { NEARBY_LOGS(INFO) << "Device name is same as old name, ignore the update."; - return {}; + } else { + it->second->SetName(new_device_name); + + NEARBY_LOGS(INFO) + << "Updated device name:" + << discovered_devices_by_id_[deviceInfoUpdate.Id()]->GetName(); + + discovery_callback_.device_name_changed_cb( + *discovered_devices_by_id_[deviceInfoUpdate.Id()]); } + } - it->second->SetName(new_device_name); - - NEARBY_LOGS(INFO) - << "Updated device name:" - << discovered_devices_by_id_[deviceInfoUpdate.Id()]->GetName(); - - discovery_callback_.device_name_changed_cb( - *discovered_devices_by_id_[deviceInfoUpdate.Id()]); + // Indicates if the device is currently paired. + if (properties.HasKey(L"System.Devices.Aep.IsPaired")) { + bool new_paired_status = InspectableReader::ReadBoolean( + properties.Lookup(L"System.Devices.Aep.IsPaired")); + NEARBY_LOGS(INFO) << __func__ + << ": Notifying device paired changed: " << std::boolalpha + << new_paired_status; + for (auto& observer : observers_.GetObservers()) { + observer->DevicePairedChanged( + *discovered_devices_by_id_[deviceInfoUpdate.Id()], new_paired_status); + } } return winrt::fire_and_forget(); @@ -662,11 +674,16 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Removed( return winrt::fire_and_forget(); } + NEARBY_LOGS(INFO) << __func__ << ": Notifying bluetooth device removed"; if (discovery_callback_.device_lost_cb != nullptr) { discovery_callback_.device_lost_cb( *discovered_devices_by_id_[deviceInfo.Id()]); } + for (auto& observer : observers_.GetObservers()) { + observer->DeviceRemoved(*discovered_devices_by_id_[deviceInfo.Id()]); + } + discovered_devices_by_id_.erase(deviceInfo.Id()); return winrt::fire_and_forget(); diff --git a/internal/platform/implementation/windows/bluetooth_classic_medium.h b/internal/platform/implementation/windows/bluetooth_classic_medium.h index bfaa00a6..44f62d06 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_medium.h +++ b/internal/platform/implementation/windows/bluetooth_classic_medium.h @@ -19,6 +19,7 @@ #include #include +#include "internal/base/observer_list.h" #include "internal/platform/implementation/bluetooth_classic.h" #include "internal/platform/implementation/windows/bluetooth_adapter.h" #include "internal/platform/implementation/windows/bluetooth_classic_device.h" @@ -152,12 +153,12 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium { api::BluetoothDevice& remote_device) override; void AddObserver(Observer* observer) override { - // TODO(b/269521993): Implement. + observers_.AddObserver(observer); } // Removes an observer. It's OK to remove an unregistered observer. void RemoveObserver(Observer* observer) override { - // TODO(b/269521993): Implement. + observers_.RemoveObserver(observer); } private: @@ -218,6 +219,7 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium { std::unique_ptr server_socket_ = nullptr; BluetoothServerSocket* raw_server_socket_ = nullptr; bool is_radio_discoverable_ = false; + ObserverList observers_; }; } // namespace windows diff --git a/internal/platform/implementation/windows/condition_variable.h b/internal/platform/implementation/windows/condition_variable.h index 992b8176..edd0dc6d 100644 --- a/internal/platform/implementation/windows/condition_variable.h +++ b/internal/platform/implementation/windows/condition_variable.h @@ -29,7 +29,7 @@ namespace windows { class ConditionVariable : public api::ConditionVariable { public: explicit ConditionVariable(api::Mutex* mutex) - : mutex_(&(static_cast(mutex))->mutex_) {} + : mutex_(&(static_cast(mutex))->GetMutex()) {} ~ConditionVariable() override = default; Exception Wait() override { diff --git a/internal/platform/medium_environment.cc b/internal/platform/medium_environment.cc index b465c97b..72a5d62d 100644 --- a/internal/platform/medium_environment.cc +++ b/internal/platform/medium_environment.cc @@ -167,8 +167,12 @@ void MediumEnvironment::OnBluetoothDeviceStateChanged( // Store device name, and report it as discovered. info.devices.emplace(&device, name); if (enable_notifications_) { - RunOnMediumEnvironmentThread( - [&info, &device]() { info.callback.device_discovered_cb(device); }); + RunOnMediumEnvironmentThread([&]() { + info.callback.device_discovered_cb(device); + for (auto& observer : observers_.GetObservers()) { + observer->DeviceAdded(device); + } + }); } } } else { @@ -190,8 +194,11 @@ void MediumEnvironment::OnBluetoothDeviceStateChanged( } else { // Device is in discovery mode, so we are reporting it anyway. if (enable_notifications_) { - RunOnMediumEnvironmentThread([&info, &device]() { + RunOnMediumEnvironmentThread([&]() { info.callback.device_discovered_cb(device); + for (auto& observer : observers_.GetObservers()) { + observer->DeviceAdded(device); + } }); } } @@ -200,8 +207,12 @@ void MediumEnvironment::OnBluetoothDeviceStateChanged( // Known device is turned off. // Erase it from the map, and report as lost. if (enable_notifications_) { - RunOnMediumEnvironmentThread( - [&info, &device]() { info.callback.device_lost_cb(device); }); + RunOnMediumEnvironmentThread([&]() { + info.callback.device_lost_cb(device); + for (auto& observer : observers_.GetObservers()) { + observer->DeviceRemoved(device); + } + }); } info.devices.erase(item); } @@ -1199,6 +1210,11 @@ bool MediumEnvironment::SetPairingState(api::BluetoothDevice* device, latch.CountDown(); }); latch.Await(); + if (enable_notifications_) { + for (auto& observer : observers_.GetObservers()) { + observer->DevicePairedChanged(*device, true); + } + } return updated; } @@ -1262,6 +1278,11 @@ bool MediumEnvironment::FinishPairing(api::BluetoothDevice* device) { pairing_context->pairing_error.value()); } else { pairing_context->is_paired = true; + if (enable_notifications_) { + for (auto& observer : observers_.GetObservers()) { + observer->DevicePairedChanged(*device, true); + } + } pairing_context->pairing_callback.on_paired_cb(); } return finshed; @@ -1306,4 +1327,17 @@ void MediumEnvironment::ClearBluetoothDevicesForPairing() { if (!enabled_) return; RunOnMediumEnvironmentThread([&]() { devices_pairing_contexts_.clear(); }); } + +void MediumEnvironment::AddObserver( + api::BluetoothClassicMedium::Observer* observer) { + if (!enabled_) return; + observers_.AddObserver(observer); +} + +void MediumEnvironment::RemoveObserver( + api::BluetoothClassicMedium::Observer* observer) { + if (!enabled_) return; + observers_.RemoveObserver(observer); +} + } // namespace nearby diff --git a/internal/platform/medium_environment.h b/internal/platform/medium_environment.h index 0677e8eb..60595ac8 100644 --- a/internal/platform/medium_environment.h +++ b/internal/platform/medium_environment.h @@ -26,6 +26,7 @@ #include "absl/container/flat_hash_set.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" +#include "internal/base/observer_list.h" #include "internal/platform/borrowable.h" #include "internal/platform/implementation/ble.h" #include "internal/platform/implementation/ble_v2.h" @@ -384,6 +385,9 @@ class MediumEnvironment { // Clears the map `devices_pairing_contexts_`. void ClearBluetoothDevicesForPairing(); + void AddObserver(api::BluetoothClassicMedium::Observer* observer); + void RemoveObserver(api::BluetoothClassicMedium::Observer* observer); + private: struct BluetoothMediumContext { BluetoothDiscoveryCallback callback; @@ -514,6 +518,7 @@ class MediumEnvironment { bool use_valid_peer_connection_ = true; absl::Duration peer_connection_latency_ = absl::ZeroDuration(); std::unique_ptr simulated_clock_ ABSL_GUARDED_BY(mutex_); + ObserverList observers_; }; } // namespace nearby diff --git a/proto/connections_enums.proto b/proto/connections_enums.proto index c82b55bb..3d312a10 100644 --- a/proto/connections_enums.proto +++ b/proto/connections_enums.proto @@ -418,4 +418,4 @@ enum PowerLevel { } // LINT.ThenChange( // //depot/google3/java/com/google/android/gmscore/integ/client/nearby/src/com/google/android/gms/nearby/connection/PowerLevel.java -// ) \ No newline at end of file +// )