diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index 334c01a7..e0e41c86 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -38,7 +38,7 @@ jobs: - name: Build Presence run: CC=clang CXX=clang++ bazel build --features=-layering_check //presence --spawn_strategy=standalone - name: Build Sharing - run: CC=clang CXX=clang++ bazel build --features=-layering_check //sharing/proto:all //sharing/internal/public:nearby_context //sharing/common:all //sharing/scheduling:scheduling --spawn_strategy=standalone + run: CC=clang CXX=clang++ bazel build --features=-layering_check //sharing/proto:all //sharing/internal/public:nearby_context //sharing/common:all //sharing/scheduling:scheduling //sharing/fast_initiation:nearby_fast_initiation --spawn_strategy=standalone build-rust-linux: name: Build Rust on Linux diff --git a/sharing/fast_initiation/BUILD b/sharing/fast_initiation/BUILD new file mode 100644 index 00000000..3f67af3c --- /dev/null +++ b/sharing/fast_initiation/BUILD @@ -0,0 +1,69 @@ +# Copyright 2024 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. + +licenses(["notice"]) + +cc_library( + name = "nearby_fast_initiation", + srcs = [ + "nearby_fast_initiation_impl.cc", + ], + hdrs = [ + "nearby_fast_initiation.h", + "nearby_fast_initiation_impl.h", + ], + visibility = ["//visibility:public"], + deps = [ + "//internal/base", + "//sharing/internal/api:platform", + "//sharing/internal/public:logging", + "//sharing/internal/public:types", + ], +) + +cc_library( + name = "test_support", + testonly = True, + srcs = [ + "fake_nearby_fast_initiation.cc", + ], + hdrs = [ + "fake_nearby_fast_initiation.h", + "fake_nearby_fast_initiation_observer.h", + ], + visibility = ["//visibility:public"], + deps = [ + ":nearby_fast_initiation", + "//internal/base", + "//sharing/internal/public:logging", + "//sharing/internal/public:types", + "@com_google_absl//absl/memory", + ], +) + +cc_test( + name = "nearby_fast_initiation_test", + srcs = [ + "nearby_fast_initiation_impl_test.cc", + ], + deps = [ + ":nearby_fast_initiation", + ":test_support", + "//internal/platform/implementation/g3", # fixdeps: keep + "//sharing/internal/api:platform", + "//sharing/internal/test:nearby_test", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/sharing/fast_initiation/fake_nearby_fast_initiation.cc b/sharing/fast_initiation/fake_nearby_fast_initiation.cc new file mode 100644 index 00000000..0c54a7c8 --- /dev/null +++ b/sharing/fast_initiation/fake_nearby_fast_initiation.cc @@ -0,0 +1,202 @@ +// Copyright 2022 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 "sharing/fast_initiation/fake_nearby_fast_initiation.h" + +#include +#include +#include + +#include "absl/memory/memory.h" +#include "sharing/fast_initiation/nearby_fast_initiation.h" +#include "sharing/internal/public/context.h" +#include "sharing/internal/public/logging.h" + +namespace nearby { +namespace sharing { + +void FakeNearbyFastInitiation::Factory::SetStartScanningError( + bool is_start_scanning_error) { + is_start_scanning_error_ = is_start_scanning_error; +} + +void FakeNearbyFastInitiation::Factory::SetStartAdvertisingError( + bool is_start_advertising_error) { + is_start_advertising_error_ = is_start_advertising_error; +} + +void FakeNearbyFastInitiation::Factory::SetLowEnergySupported( + bool is_low_energy_supported) { + is_low_energy_supported_ = is_low_energy_supported; +} + +void FakeNearbyFastInitiation::Factory::SetScanOffloadSupported( + bool is_scan_offload_supported) { + is_scan_offload_supported_ = is_scan_offload_supported; +} + +void FakeNearbyFastInitiation::Factory::SetAdvertisementOffloadSupported( + bool is_advertisement_offload_supported) { + is_advertisement_offload_supported_ = is_advertisement_offload_supported; +} + +FakeNearbyFastInitiation* +FakeNearbyFastInitiation::Factory::GetNearbyFastInitiation() { + return fake_nearby_fast_initiation_; +} + +std::unique_ptr +FakeNearbyFastInitiation::Factory::CreateInstance(Context* context) { + fake_nearby_fast_initiation_ = new FakeNearbyFastInitiation(context); + fake_nearby_fast_initiation_->SetStartScanningError(is_start_scanning_error_); + fake_nearby_fast_initiation_->SetStartAdvertisingError( + is_start_advertising_error_); + fake_nearby_fast_initiation_->SetLowEnergySupported(is_low_energy_supported_); + fake_nearby_fast_initiation_->SetScanOffloadSupported( + is_scan_offload_supported_); + fake_nearby_fast_initiation_->SetAdvertisementOffloadSupported( + is_advertisement_offload_supported_); + return absl::WrapUnique(fake_nearby_fast_initiation_); +} + +FakeNearbyFastInitiation::FakeNearbyFastInitiation(Context* context) + : context_(context) { + NL_DCHECK(context_); +} + +bool FakeNearbyFastInitiation::IsLowEnergySupported() { + return is_low_energy_supported_; +} + +bool FakeNearbyFastInitiation::IsScanOffloadSupported() { + return is_scan_offload_supported_; +} + +bool FakeNearbyFastInitiation::IsAdvertisementOffloadSupported() { + return is_advertisement_offload_supported_; +} + +void FakeNearbyFastInitiation::StartScanning( + std::function devices_discovered_callback, + std::function devices_not_discovered_callback, + std::function error_callback) { + ++start_scanning_call_count_; + if (is_start_scanning_error_) { + error_callback(); + return; + } + + is_scanning_ = true; + scanning_devices_discovered_callback_ = + std::move(devices_discovered_callback); + scanning_devices_not_discovered_callback_ = + std::move(devices_not_discovered_callback); +} + +void FakeNearbyFastInitiation::StopScanning(std::function callback) { + ++stop_scanning_call_count_; + is_scanning_ = false; + callback(); +} + +void FakeNearbyFastInitiation::StartAdvertising( + FastInitType type, std::function callback, + std::function error_callback) { + ++start_advertising_call_count_; + if (is_start_advertising_error_) { + error_callback(); + } else { + is_advertising_ = true; + callback(); + } +} + +void FakeNearbyFastInitiation::StopAdvertising(std::function callback) { + ++stop_advertising_call_count_; + is_advertising_ = false; + callback(); +} + +void FakeNearbyFastInitiation::AddObserver(Observer* observer) { + observer_list_.AddObserver(observer); +} +void FakeNearbyFastInitiation::RemoveObserver(Observer* observer) { + observer_list_.RemoveObserver(observer); +} +bool FakeNearbyFastInitiation::HasObserver(Observer* observer) { + return observer_list_.HasObserver(observer); +} + +// Fake methods +void FakeNearbyFastInitiation::SetStartScanningError( + bool is_start_scanning_error) { + is_start_scanning_error_ = is_start_scanning_error; + for (Observer* observer : observer_list_.GetObservers()) { + if (observer != nullptr) { + observer->HardwareErrorReported(this); + } + } +} + +void FakeNearbyFastInitiation::SetStartAdvertisingError( + bool is_start_advertising_error) { + is_start_advertising_error_ = is_start_advertising_error; + for (Observer* observer : observer_list_.GetObservers()) { + if (observer != nullptr) { + observer->HardwareErrorReported(this); + } + } +} + +void FakeNearbyFastInitiation::SetLowEnergySupported( + bool is_low_energy_supported) { + is_low_energy_supported_ = is_low_energy_supported; +} + +void FakeNearbyFastInitiation::SetScanOffloadSupported( + bool is_scan_offload_supported) { + is_scan_offload_supported_ = is_scan_offload_supported; +} + +void FakeNearbyFastInitiation::SetAdvertisementOffloadSupported( + bool is_advertisement_offload_supported) { + is_advertisement_offload_supported_ = is_advertisement_offload_supported; +} + +int FakeNearbyFastInitiation::StartScanningCount() const { + return start_scanning_call_count_; +} + +int FakeNearbyFastInitiation::StopScanningCount() const { + return stop_scanning_call_count_; +} + +int FakeNearbyFastInitiation::StartAdvertisingCount() const { + return start_advertising_call_count_; +} + +int FakeNearbyFastInitiation::StopAdvertisingCount() const { + return stop_advertising_call_count_; +} + +void FakeNearbyFastInitiation::FireDevicesDetected() { + scanning_devices_discovered_callback_(); +} + +void FakeNearbyFastInitiation::FireDevicesNotDetected() { + scanning_devices_not_discovered_callback_(); +} + +} // namespace sharing +} // namespace nearby diff --git a/sharing/fast_initiation/fake_nearby_fast_initiation.h b/sharing/fast_initiation/fake_nearby_fast_initiation.h new file mode 100644 index 00000000..719342ba --- /dev/null +++ b/sharing/fast_initiation/fake_nearby_fast_initiation.h @@ -0,0 +1,128 @@ +// Copyright 2022 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_SHARING_FAST_INITIATION_FAKE_NEARBY_FAST_INITIATION_H_ +#define THIRD_PARTY_NEARBY_SHARING_FAST_INITIATION_FAKE_NEARBY_FAST_INITIATION_H_ + +#include +#include + +#include "internal/base/observer_list.h" +#include "sharing/fast_initiation/nearby_fast_initiation.h" +#include "sharing/fast_initiation/nearby_fast_initiation_impl.h" +#include "sharing/internal/public/context.h" + +namespace nearby { +namespace sharing { + +// FakeNearbyFastInitiation is a fake implementation of NearbyFastInitiation. +// In the implementation, developers can simulate different output by calling +// faked methods, such as setting offload supports on scanning or advertising. +class FakeNearbyFastInitiation : public NearbyFastInitiation { + public: + class Factory : public NearbyFastInitiationImpl::Factory { + public: + Factory() = default; + ~Factory() override = default; + + void SetStartScanningError(bool is_start_scanning_error); + + void SetStartAdvertisingError(bool is_start_advertising_error); + + void SetLowEnergySupported(bool is_low_energy_supported); + + void SetScanOffloadSupported(bool is_scan_offload_supported); + + void SetAdvertisementOffloadSupported( + bool is_advertisement_offload_supported); + + FakeNearbyFastInitiation* GetNearbyFastInitiation(); + + private: + std::unique_ptr CreateInstance( + Context* context) override; + + bool is_start_scanning_error_ = false; + bool is_start_advertising_error_ = false; + bool is_low_energy_supported_ = true; + bool is_scan_offload_supported_ = true; + bool is_advertisement_offload_supported_ = true; + FakeNearbyFastInitiation* fake_nearby_fast_initiation_ = nullptr; + }; + + explicit FakeNearbyFastInitiation(Context* context); + ~FakeNearbyFastInitiation() override = default; + + bool IsLowEnergySupported() override; + bool IsScanOffloadSupported() override; + bool IsAdvertisementOffloadSupported() override; + + void StartScanning(std::function devices_discovered_callback, + std::function devices_not_discovered_callback, + std::function error_callback) override; + + void StopScanning(std::function callback) override; + + void StartAdvertising(FastInitType type, std::function callback, + std::function error_callback) override; + + void StopAdvertising(std::function callback) override; + + bool IsScanning() const override { return is_scanning_; } + bool IsAdvertising() const override { return is_advertising_; } + + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + bool HasObserver(Observer* observer) override; + + // Fake methods + void SetStartScanningError(bool is_start_scanning_error); + void SetStartAdvertisingError(bool is_start_advertising_error); + void SetLowEnergySupported(bool is_low_energy_supported); + void SetScanOffloadSupported(bool is_scan_offload_supported); + void SetAdvertisementOffloadSupported( + bool is_advertisement_offload_supported); + + int StartScanningCount() const; + int StopScanningCount() const; + int StartAdvertisingCount() const; + int StopAdvertisingCount() const; + + void FireDevicesDetected(); + void FireDevicesNotDetected(); + + private: + Context* context_; + bool is_scanning_ = false; + bool is_advertising_ = false; + bool is_start_scanning_error_ = false; + bool is_start_advertising_error_ = false; + bool is_low_energy_supported_ = true; + bool is_scan_offload_supported_ = true; + bool is_advertisement_offload_supported_ = true; + int start_scanning_call_count_ = 0; + int stop_scanning_call_count_ = 0; + int start_advertising_call_count_ = 0; + int stop_advertising_call_count_ = 0; + + std::function scanning_devices_discovered_callback_ = nullptr; + std::function scanning_devices_not_discovered_callback_ = nullptr; + + ObserverList observer_list_; +}; + +} // namespace sharing +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_SHARING_FAST_INITIATION_FAKE_NEARBY_FAST_INITIATION_H_ diff --git a/sharing/fast_initiation/fake_nearby_fast_initiation_observer.h b/sharing/fast_initiation/fake_nearby_fast_initiation_observer.h new file mode 100644 index 00000000..22658998 --- /dev/null +++ b/sharing/fast_initiation/fake_nearby_fast_initiation_observer.h @@ -0,0 +1,46 @@ +// Copyright 2024 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_SHARING_FAST_INITIATION_FAKE_NEARBY_FAST_INITIATION_OBSERVER_H_ +#define THIRD_PARTY_NEARBY_SHARING_FAST_INITIATION_FAKE_NEARBY_FAST_INITIATION_OBSERVER_H_ + +#include "sharing/fast_initiation/nearby_fast_initiation.h" + +namespace nearby { +namespace sharing { + +class FakeNearbyFastInitiationObserver : public NearbyFastInitiation::Observer { + public: + explicit FakeNearbyFastInitiationObserver( + NearbyFastInitiation* fast_init_manager) { + fast_init_manager_ = fast_init_manager; + num_hardware_error_reported_ = 0; + } + + void HardwareErrorReported(NearbyFastInitiation* fast_init_manager) override { + if (fast_init_manager_ == fast_init_manager) { + num_hardware_error_reported_ += 1; + } + } + + int GetNumHardwareErrorReported() { return num_hardware_error_reported_; } + + private: + NearbyFastInitiation* fast_init_manager_; + int num_hardware_error_reported_; +}; + +} // namespace sharing +} // namespace nearby +#endif // THIRD_PARTY_NEARBY_SHARING_FAST_INITIATION_FAKE_NEARBY_FAST_INITIATION_OBSERVER_H_ diff --git a/sharing/fast_initiation/nearby_fast_initiation.h b/sharing/fast_initiation/nearby_fast_initiation.h new file mode 100644 index 00000000..6b4e9d31 --- /dev/null +++ b/sharing/fast_initiation/nearby_fast_initiation.h @@ -0,0 +1,101 @@ +// Copyright 2022 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_SHARING_FAST_INITIATION_NEARBY_FAST_INITIATION_H_ +#define THIRD_PARTY_NEARBY_SHARING_FAST_INITIATION_NEARBY_FAST_INITIATION_H_ + +#include + +#include + +namespace nearby { +namespace sharing { + +class NearbyFastInitiation { + public: + enum class Version { kV1 = 0 }; + + // Fast initialization type impacts on the metadata information in payload of + // advertising. + // kNotify = The sender is in the foreground, actively trying to send a file + // to another device. Receivers in the room should be alerted that they may + // need to enter Everyone mode to receive the file. + // kSilent = The phone is passively looking for nearby receivers, but the user + // has not explicitly tried to enter Nearby Share to send the file. The phone + // may cache the receivers nearby so that discovery is faster, or it may + // present these users on ambient surfaces + enum class FastInitType : uint8_t { + kNotify = 0, + kSilent = 1, + }; + + class Observer { + public: + virtual ~Observer() = default; + + // Called when hardware error reported that requires PC restart + virtual void HardwareErrorReported(NearbyFastInitiation* fast_init) {} + }; + + virtual ~NearbyFastInitiation() = default; + + virtual bool IsLowEnergySupported() = 0; + virtual bool IsScanOffloadSupported() = 0; + virtual bool IsAdvertisementOffloadSupported() = 0; + + // Scans nearby devices using BLE. + // |devices_discovered_callback| is called when discovered devices from + // 0 to more. |devices_not_discovered_callback| is called when discovered + // devices become to 0 from more than one. |error_callback| is called + // when error happened. + virtual void StartScanning( + std::function devices_discovered_callback, + std::function devices_not_discovered_callback, + std::function error_callback) = 0; + + // Stops Fast Initialization scanning. |callback| is called + // when scanning is stopped. + virtual void StopScanning(std::function callback) = 0; + + // Begins broadcasting Fast Initiation advertisement. |callback| is called + // when advertising is started. |error_callback| is called if start + // advertising with errors. + // Note: FastInitType is currently hardcoded to kNotify under the hood. To + // support changing the FastInitType after initialization, additional APIs + // need to be added/refactored to Context to reinitialize/reset the + // FastInitiationManager with the desired type. + virtual void StartAdvertising(FastInitType type, + std::function callback, + std::function error_callback) = 0; + + // Stop broadcasting Fast Initiation advertisements. |callback| + // is called when advertising is stopped. + virtual void StopAdvertising(std::function callback) = 0; + + // Check the scanning status. + virtual bool IsScanning() const = 0; + + // Check the advertising status. + virtual bool IsAdvertising() const = 0; + + // Adds and removes observers for hardware error events. + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + virtual bool HasObserver(Observer* observer) = 0; +}; + +} // namespace sharing +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_SHARING_FAST_INITIATION_NEARBY_FAST_INITIATION_H_ diff --git a/sharing/fast_initiation/nearby_fast_initiation_impl.cc b/sharing/fast_initiation/nearby_fast_initiation_impl.cc new file mode 100644 index 00000000..59c4f020 --- /dev/null +++ b/sharing/fast_initiation/nearby_fast_initiation_impl.cc @@ -0,0 +1,299 @@ +// Copyright 2022 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 "sharing/fast_initiation/nearby_fast_initiation_impl.h" + +#include +#include +#include +#include + +#include "sharing/fast_initiation/nearby_fast_initiation.h" +#include "sharing/internal/api/bluetooth_adapter.h" +#include "sharing/internal/api/fast_init_ble_beacon.h" +#include "sharing/internal/api/fast_initiation_manager.h" +#include "sharing/internal/public/context.h" +#include "sharing/internal/public/logging.h" + +namespace nearby { +namespace sharing { +using ::nearby::api::FastInitiationManager; + +NearbyFastInitiationImpl::Factory* + NearbyFastInitiationImpl::Factory::test_factory_ = nullptr; + +std::unique_ptr NearbyFastInitiationImpl::Factory::Create( + Context* context) { + NL_DCHECK(context); + if (test_factory_) { + return test_factory_->CreateInstance(context); + } + + return std::make_unique(context); +} + +void NearbyFastInitiationImpl::Factory::SetFactoryForTesting( + Factory* test_factory) { + test_factory_ = test_factory; +} + +NearbyFastInitiationImpl::NearbyFastInitiationImpl(Context* context) + : context_(context) { + NL_DCHECK(context); +} + +bool NearbyFastInitiationImpl::IsLowEnergySupported() { + return context_->GetBluetoothAdapter().IsLowEnergySupported(); +} + +bool NearbyFastInitiationImpl::IsScanOffloadSupported() { + return context_->GetBluetoothAdapter().IsScanOffloadSupported(); +} + +bool NearbyFastInitiationImpl::IsAdvertisementOffloadSupported() { + return context_->GetBluetoothAdapter().IsAdvertisementOffloadSupported(); +} + +bool NearbyFastInitiationImpl::IsScanning() const { + return context_->GetFastInitiationManager().IsScanning(); +} + +bool NearbyFastInitiationImpl::IsAdvertising() const { + return context_->GetFastInitiationManager().IsAdvertising(); +} + +void NearbyFastInitiationImpl::StartScanning( + std::function devices_discovered_callback, + std::function devices_not_discovered_callback, + std::function error_callback) { + if (IsScanning()) { + NL_LOG(WARNING) << __func__ + << ": FastInit BLE scanning was started already."; + error_callback(); + return; + } + + context_->GetFastInitiationManager().StartScanning( + std::move(devices_discovered_callback), + std::move(devices_not_discovered_callback), + [&, error_callback = + std::move(error_callback)](FastInitiationManager::Error error) { + ScanningErrorCodeCallbackHandler(error); + error_callback(); + }); +} + +void NearbyFastInitiationImpl::StopScanning(std::function callback) { + if (!IsScanning()) { + NL_LOG(WARNING) << __func__ << ": FastInit BLE scanning is not running."; + callback(); + return; + } + context_->GetFastInitiationManager().StopScanning( + [&, callback = std::move(callback)]() { callback(); }); +} + +void NearbyFastInitiationImpl::StartAdvertising( + FastInitType type, std::function callback, + std::function error_callback) { + if (IsAdvertising()) { + NL_LOG(WARNING) << __func__ + << ": FastInit BLE advertising was started already."; + error_callback(); + return; + } + + context_->GetFastInitiationManager().StartAdvertising( + ::nearby::api::FastInitBleBeacon::FastInitType(type), + [&, callback = std::move(callback)]() { callback(); }, + [&, error_callback = + std::move(error_callback)](FastInitiationManager::Error error) { + AdvertisingErrorCodeCallbackHandler(error); + error_callback(); + }); +} + +void NearbyFastInitiationImpl::StopAdvertising(std::function callback) { + if (!IsAdvertising()) { + NL_LOG(WARNING) << __func__ << ": FastInit BLE advertising is not running."; + callback(); + return; + } + + context_->GetFastInitiationManager().StopAdvertising( + [&, callback = std::move(callback)]() { callback(); }); +} + +void NearbyFastInitiationImpl::ScanningErrorCodeCallbackHandler( + FastInitiationManager::Error error) { + switch (error) { + case FastInitiationManager::Error::kBluetoothRadioUnavailable: + for (Observer* observer : observer_list_.GetObservers()) { + if (observer != nullptr) { + observer->HardwareErrorReported(this); + } + } + NL_LOG(ERROR) << __func__ + << ": FastInit BLE scanning failed due to bluetooth radio " + "unavailable."; + break; + case FastInitiationManager::Error::kResourceInUse: + for (Observer* observer : observer_list_.GetObservers()) { + if (observer != nullptr) { + observer->HardwareErrorReported(this); + } + } + NL_LOG(ERROR) + << __func__ + << ": FastInit BLE scanning failed due to bluetooth resources are " + "in use/at full capacity."; + break; + case FastInitiationManager::Error::kDisabledByPolicy: + NL_LOG(ERROR) + << __func__ + << ": FastInit BLE scanning failed due to being disabled by policy."; + break; + case FastInitiationManager::Error::kDisabledByUser: + NL_LOG(ERROR) + << __func__ + << ": FastInit BLE scanning failed due to being disabled by user."; + break; + case FastInitiationManager::Error::kHardwareNotSupported: + for (Observer* observer : observer_list_.GetObservers()) { + if (observer != nullptr) { + observer->HardwareErrorReported(this); + } + } + NL_LOG(ERROR) + << __func__ + << ": FastInit BLE scanning failed due to hardware not supported."; + break; + case FastInitiationManager::Error::kTransportNotSupported: + for (Observer* observer : observer_list_.GetObservers()) { + if (observer != nullptr) { + observer->HardwareErrorReported(this); + } + } + NL_LOG(ERROR) + << __func__ + << ": FastInit BLE scanning failed due to transport not supported."; + break; + case FastInitiationManager::Error::kConsentRequired: + NL_LOG(ERROR) + << __func__ + << ": FastInit BLE scanning failed due to consent required."; + break; + case FastInitiationManager::Error::kUnknown: + for (Observer* observer : observer_list_.GetObservers()) { + if (observer != nullptr) { + observer->HardwareErrorReported(this); + } + } + NL_LOG(ERROR) << __func__ + << ": FastInit BLE scanning failed due to unknown reasons."; + break; + default: + break; + } +} + +void NearbyFastInitiationImpl::AdvertisingErrorCodeCallbackHandler( + FastInitiationManager::Error error) { + switch (error) { + case FastInitiationManager::Error::kBluetoothRadioUnavailable: + for (Observer* observer : observer_list_.GetObservers()) { + if (observer != nullptr) { + observer->HardwareErrorReported(this); + } + } + NL_LOG(ERROR) + << __func__ + << ": FastInit BLE advertising failed due to bluetooth radio " + "unavailable."; + break; + case FastInitiationManager::Error::kResourceInUse: + for (Observer* observer : observer_list_.GetObservers()) { + if (observer != nullptr) { + observer->HardwareErrorReported(this); + } + } + NL_LOG(ERROR) + << __func__ + << ": FastInit BLE advertising failed due to bluetooth resources are " + "in use/at full capacity."; + break; + case FastInitiationManager::Error::kDisabledByPolicy: + NL_LOG(ERROR) << __func__ + << ": FastInit BLE advertising failed due to being " + "disabled by policy."; + break; + case FastInitiationManager::Error::kDisabledByUser: + NL_LOG(ERROR) + << __func__ + << ": FastInit BLE advertising failed due to being disabled by user."; + break; + case FastInitiationManager::Error::kHardwareNotSupported: + for (Observer* observer : observer_list_.GetObservers()) { + if (observer != nullptr) { + observer->HardwareErrorReported(this); + } + } + NL_LOG(ERROR) + << __func__ + << ": FastInit BLE advertising failed due to hardware not supported."; + break; + case FastInitiationManager::Error::kTransportNotSupported: + for (Observer* observer : observer_list_.GetObservers()) { + if (observer != nullptr) { + observer->HardwareErrorReported(this); + } + } + NL_LOG(ERROR) << __func__ + << ": FastInit BLE advertising failed due to transport not " + "supported."; + break; + case FastInitiationManager::Error::kConsentRequired: + NL_LOG(ERROR) + << __func__ + << ": FastInit BLE advertising failed due to consent required."; + break; + case FastInitiationManager::Error::kUnknown: + for (Observer* observer : observer_list_.GetObservers()) { + if (observer != nullptr) { + observer->HardwareErrorReported(this); + } + } + NL_LOG(ERROR) + << __func__ + << ": FastInit BLE advertising failed due to unknown reasons."; + break; + default: + break; + } +} + +void NearbyFastInitiationImpl::AddObserver(Observer* observer) { + observer_list_.AddObserver(observer); + NL_LOG(INFO) << __func__ << ": Fast Initiation observer added."; +} +void NearbyFastInitiationImpl::RemoveObserver(Observer* observer) { + observer_list_.RemoveObserver(observer); + NL_LOG(INFO) << __func__ << ": Fast Initiation observer removed."; +} +bool NearbyFastInitiationImpl::HasObserver(Observer* observer) { + return observer_list_.HasObserver(observer); +} + +} // namespace sharing +} // namespace nearby diff --git a/sharing/fast_initiation/nearby_fast_initiation_impl.h b/sharing/fast_initiation/nearby_fast_initiation_impl.h new file mode 100644 index 00000000..4a21447b --- /dev/null +++ b/sharing/fast_initiation/nearby_fast_initiation_impl.h @@ -0,0 +1,86 @@ +// Copyright 2022 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_SHARING_FAST_INITIATION_NEARBY_FAST_INITIATION_IMPL_H_ +#define THIRD_PARTY_NEARBY_SHARING_FAST_INITIATION_NEARBY_FAST_INITIATION_IMPL_H_ + +#include +#include + +#include "internal/base/observer_list.h" +#include "sharing/fast_initiation/nearby_fast_initiation.h" +#include "sharing/internal/api/fast_initiation_manager.h" +#include "sharing/internal/public/context.h" + +namespace nearby { +namespace sharing { + +class NearbyFastInitiationImpl : public NearbyFastInitiation { + public: + class Factory { + public: + static std::unique_ptr Create(Context* context); + static void SetFactoryForTesting(Factory* test_factory); + + protected: + virtual ~Factory() = default; + virtual std::unique_ptr CreateInstance( + Context* context) = 0; + + private: + static Factory* test_factory_; + }; + + explicit NearbyFastInitiationImpl(Context* context); + + bool IsLowEnergySupported() override; + bool IsScanOffloadSupported() override; + bool IsAdvertisementOffloadSupported() override; + + void StartScanning(std::function devices_discovered_callback, + std::function devices_not_discovered_callback, + std::function error_callback) override; + + void StopScanning(std::function callback) override; + + void StartAdvertising(FastInitType type, std::function callback, + std::function error_callback) override; + + void StopAdvertising(std::function callback) override; + + bool IsScanning() const override; + + bool IsAdvertising() const override; + + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + bool HasObserver(Observer* observer) override; + + private: + // Handle the scanning error codes and print out to logs + void ScanningErrorCodeCallbackHandler( + nearby::api::FastInitiationManager::Error error); + + // Handle the advertising error codes and print out to logs + void AdvertisingErrorCodeCallbackHandler( + nearby::api::FastInitiationManager::Error error); + + Context* const context_; + ObserverList observer_list_; +}; + +} // namespace sharing +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_SHARING_FAST_INITIATION_NEARBY_FAST_INITIATION_IMPL_H_ diff --git a/sharing/fast_initiation/nearby_fast_initiation_impl_test.cc b/sharing/fast_initiation/nearby_fast_initiation_impl_test.cc new file mode 100644 index 00000000..0f14f7d2 --- /dev/null +++ b/sharing/fast_initiation/nearby_fast_initiation_impl_test.cc @@ -0,0 +1,240 @@ +// Copyright 2022 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 "sharing/fast_initiation/nearby_fast_initiation_impl.h" + +#include +#include +#include + +#include "gtest/gtest.h" +#include "sharing/fast_initiation/fake_nearby_fast_initiation_observer.h" +#include "sharing/fast_initiation/nearby_fast_initiation.h" +#include "sharing/internal/api/fast_initiation_manager.h" +#include "sharing/internal/test/fake_context.h" +#include "sharing/internal/test/fake_fast_initiation_manager.h" + +namespace nearby { +namespace sharing { +namespace { + +TEST(NearbyFastInitiationImpl, IsLowEnergySupported) { + auto fake_context = std::make_unique(); + NearbyFastInitiationImpl nearby_fast_initiation_impl(fake_context.get()); + EXPECT_TRUE(nearby_fast_initiation_impl.IsLowEnergySupported()); +} + +TEST(NearbyFastInitiationImpl, IsScanOffloadSupported) { + auto fake_context = std::make_unique(); + NearbyFastInitiationImpl nearby_fast_initiation_impl(fake_context.get()); + EXPECT_TRUE(nearby_fast_initiation_impl.IsScanOffloadSupported()); +} + +TEST(NearbyFastInitiationImpl, IsAdvertisementOffloadSupported) { + auto fake_context = std::make_unique(); + NearbyFastInitiationImpl nearby_fast_initiation_impl(fake_context.get()); + EXPECT_TRUE(nearby_fast_initiation_impl.IsAdvertisementOffloadSupported()); +} + +TEST(NearbyFastInitiationImpl, HasObserverReturnsFalseAfterRemovingObserver) { + auto fake_context = std::make_unique(); + NearbyFastInitiationImpl nearby_fast_initiation_impl(fake_context.get()); + FakeNearbyFastInitiationObserver fake_observer(&nearby_fast_initiation_impl); + + nearby_fast_initiation_impl.AddObserver(&fake_observer); + EXPECT_TRUE(nearby_fast_initiation_impl.HasObserver(&fake_observer)); + + nearby_fast_initiation_impl.RemoveObserver(&fake_observer); + EXPECT_FALSE(nearby_fast_initiation_impl.HasObserver(&fake_observer)); +} + +TEST(NearbyFastInitiationImpl, HasObserver) { + auto fake_context = std::make_unique(); + NearbyFastInitiationImpl nearby_fast_initiation_impl(fake_context.get()); + FakeNearbyFastInitiationObserver fake_observer(&nearby_fast_initiation_impl); + + FakeNearbyFastInitiationObserver fake_observer_1( + &nearby_fast_initiation_impl); + FakeNearbyFastInitiationObserver fake_observer_2( + &nearby_fast_initiation_impl); + + nearby_fast_initiation_impl.AddObserver(&fake_observer_1); + + EXPECT_TRUE(nearby_fast_initiation_impl.HasObserver(&fake_observer_1)); + EXPECT_FALSE(nearby_fast_initiation_impl.HasObserver(&fake_observer_2)); +} + +TEST(NearbyFastInitiationImpl, StartAdvertising) { + bool success_callback_called = false; + std::function success_callback = [&]() { + success_callback_called = true; + }; + std::function error_callback = []() {}; + + auto fake_context = std::make_unique(); + NearbyFastInitiationImpl nearby_fast_initiation_impl(fake_context.get()); + nearby_fast_initiation_impl.StartAdvertising( + NearbyFastInitiation::FastInitType::kNotify, success_callback, + error_callback); + FakeFastInitiationManager& fake_fast_initiation_manager = + dynamic_cast( + fake_context->GetFastInitiationManager()); + fake_fast_initiation_manager.SetAdvertisingStarted(); + EXPECT_TRUE(success_callback_called); +} + +TEST(NearbyFastInitiationImpl, StartAdvertisingAndGetHardwareError) { + bool error_callback_called = false; + std::function success_callback = []() {}; + std::function error_callback = [&]() { + error_callback_called = true; + }; + + auto fake_context = std::make_unique(); + NearbyFastInitiationImpl nearby_fast_initiation_impl(fake_context.get()); + + FakeNearbyFastInitiationObserver fake_observer(&nearby_fast_initiation_impl); + + nearby_fast_initiation_impl.AddObserver(&fake_observer); + + nearby_fast_initiation_impl.StartAdvertising( + NearbyFastInitiation::FastInitType::kNotify, success_callback, + error_callback); + + FakeFastInitiationManager& fake_fast_initiation_manager = + dynamic_cast( + fake_context->GetFastInitiationManager()); + + // Mocking OS hardware error event on Windows + fake_fast_initiation_manager.SetAdvertisingError( + nearby::api::FastInitiationManager::Error::kBluetoothRadioUnavailable); + + EXPECT_TRUE(error_callback_called); + EXPECT_EQ(fake_observer.GetNumHardwareErrorReported(), 1); +} + +TEST(NearbyFastInitiationImpl, StopAdvertisingNotStart) { + bool success_callback_called = false; + std::function success_callback = [&]() { + success_callback_called = true; + }; + + auto fake_context = std::make_unique(); + NearbyFastInitiationImpl nearby_fast_initiation_impl(fake_context.get()); + nearby_fast_initiation_impl.StopAdvertising(success_callback); + EXPECT_TRUE(success_callback_called); +} + +TEST(NearbyFastInitiationImpl, StopStartedAdvertising) { + bool start_callback_called = false; + bool stop_callback_called = false; + auto fake_context = std::make_unique(); + FakeFastInitiationManager& fake_fast_initiation_manager = + dynamic_cast( + fake_context->GetFastInitiationManager()); + + NearbyFastInitiationImpl nearby_fast_initiation_impl(fake_context.get()); + nearby_fast_initiation_impl.StartAdvertising( + NearbyFastInitiation::FastInitType::kNotify, + [&]() { start_callback_called = true; }, [&]() {}); + fake_fast_initiation_manager.SetAdvertisingStarted(); + EXPECT_TRUE(start_callback_called); + EXPECT_TRUE(nearby_fast_initiation_impl.IsAdvertising()); + nearby_fast_initiation_impl.StopAdvertising( + [&]() { stop_callback_called = true; }); + + fake_fast_initiation_manager.SetAdvertisingStopped(); + EXPECT_TRUE(stop_callback_called); +} + +TEST(NearbyFastInitiationImpl, StartScanningSucceed) { + bool devices_discovered = false; + std::function devices_discovered_callback = [&devices_discovered]() { + devices_discovered = true; + }; + std::function devices_not_discovered_callback = []() {}; + std::function error_callback = []() {}; + + auto fake_context = std::make_unique(); + NearbyFastInitiationImpl nearby_fast_initiation_impl(fake_context.get()); + nearby_fast_initiation_impl.StartScanning(devices_discovered_callback, + devices_not_discovered_callback, + error_callback); + FakeFastInitiationManager& fake_fast_initiation_manager = + dynamic_cast( + fake_context->GetFastInitiationManager()); + fake_fast_initiation_manager.SetScanningDiscovered(); + EXPECT_TRUE(devices_discovered); +} + +TEST(NearbyFastInitiationImpl, StartScanningAndGetHardwareError) { + bool error_callback_called = false; + std::function devices_discovered_callback = []() {}; + std::function devices_not_discovered_callback = []() {}; + std::function error_callback = [&]() { + error_callback_called = true; + }; + + auto fake_context = std::make_unique(); + NearbyFastInitiationImpl nearby_fast_initiation_impl(fake_context.get()); + + FakeNearbyFastInitiationObserver fake_observer(&nearby_fast_initiation_impl); + + nearby_fast_initiation_impl.AddObserver(&fake_observer); + + nearby_fast_initiation_impl.StartScanning(devices_discovered_callback, + devices_not_discovered_callback, + error_callback); + + FakeFastInitiationManager& fake_fast_initiation_manager = + dynamic_cast( + fake_context->GetFastInitiationManager()); + + // Mocking OS hardware error event on Windows + fake_fast_initiation_manager.SetScanningError( + nearby::api::FastInitiationManager::Error::kBluetoothRadioUnavailable); + + EXPECT_TRUE(error_callback_called); + EXPECT_EQ(fake_observer.GetNumHardwareErrorReported(), 1); +} + +TEST(NearbyFastInitiationImpl, StopScanningNotStart) { + bool stop_callback_called = false; + std::function success_callback = [&]() { + stop_callback_called = true; + }; + + auto fake_context = std::make_unique(); + NearbyFastInitiationImpl nearby_fast_initiation_impl(fake_context.get()); + nearby_fast_initiation_impl.StopScanning(success_callback); + EXPECT_TRUE(stop_callback_called); +} + +TEST(NearbyFastInitiationImpl, StopStartedScanning) { + bool stop_callback_called = false; + auto fake_context = std::make_unique(); + NearbyFastInitiationImpl nearby_fast_initiation_impl(fake_context.get()); + FakeFastInitiationManager& fake_fast_initiation_manager = + dynamic_cast( + fake_context->GetFastInitiationManager()); + nearby_fast_initiation_impl.StartScanning([]() {}, []() {}, []() {}); + nearby_fast_initiation_impl.StopScanning( + [&]() { stop_callback_called = true; }); + fake_fast_initiation_manager.SetScanningStopped(); + EXPECT_TRUE(stop_callback_called); +} + +} // namespace +} // namespace sharing +} // namespace nearby