diff --git a/internal/platform/bluetooth_classic.h b/internal/platform/bluetooth_classic.h index 319d14f5..eee85f8c 100644 --- a/internal/platform/bluetooth_classic.h +++ b/internal/platform/bluetooth_classic.h @@ -16,7 +16,9 @@ #define PLATFORM_PUBLIC_BLUETOOTH_CLASSIC_H_ #include +#include #include +#include #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" @@ -126,6 +128,36 @@ class BluetoothServerSocket final { std::shared_ptr impl_; }; +// Opaque wrapper for a BluetoothPairing. +class BluetoothPairing final { + public: + explicit BluetoothPairing( + std::unique_ptr bluetooth_pairing) + : impl_(std::move(bluetooth_pairing)) {} + + bool InitiatePairing(api::BluetoothPairingCallback pairing_cb) { + return impl_->InitiatePairing(std::move(pairing_cb)); + } + + bool FinishPairing(std::optional pin_code) { + return impl_->FinishPairing(pin_code); + } + + bool CancelPairing() { return impl_->CancelPairing(); } + + bool Unpair() { return impl_->Unpair(); } + + bool IsPaired() { return impl_->IsPaired(); } + + // Returns reference to platform implementation. + // This is used to communicate with platform code, and for debugging + // purposes. + api::BluetoothPairing* GetImpl() { return impl_.get(); } + + private: + std::unique_ptr impl_; +}; + // Container of operations that can be performed over the Bluetooth Classic // medium. class BluetoothClassicMedium final @@ -147,6 +179,7 @@ class BluetoothClassicMedium final absl::AnyInvocable device_lost_cb = DefaultCallback(); }; + struct DeviceDiscoveryInfo { BluetoothDevice device; }; @@ -239,6 +272,15 @@ class BluetoothClassicMedium final impl_->ListenForService(service_name, service_uuid)); } + // Return a Bluetooth pairing instance to handle the pairing process with the + // remote device. + std::unique_ptr CreatePairing( + BluetoothDevice& remote_device) { + std::unique_ptr bluetooth_pairing = + impl_->CreatePairing(remote_device.GetImpl()); + return std::make_unique(std::move(bluetooth_pairing)); + } + bool IsValid() const { return impl_ != nullptr; } api::BluetoothClassicMedium& GetImpl() { return *impl_; } diff --git a/internal/platform/implementation/bluetooth_classic.h b/internal/platform/implementation/bluetooth_classic.h index 7f8f9cf3..ae8da9f7 100644 --- a/internal/platform/implementation/bluetooth_classic.h +++ b/internal/platform/implementation/bluetooth_classic.h @@ -16,8 +16,11 @@ #define PLATFORM_API_BLUETOOTH_CLASSIC_H_ #include +#include #include +#include "absl/functional/any_invocable.h" +#include "absl/strings/string_view.h" #include "internal/platform/byte_array.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/exception.h" @@ -89,6 +92,105 @@ class BluetoothServerSocket { virtual Exception Close() = 0; }; +// https://developer.android.com/reference/com/google/android/things/bluetooth/PairingParams +// +// Encapsulates the data for a particular pairing attempt. +// The caller can use it to determine the pairing approach and choose a suitable +// way to obtain user consent to conclude the pairing process. +struct PairingParams { + // Pairing type for this pairing attempt. + // The Pairing type is based on the User Interface capabilities of both + // the pairing devices, and determines the process of pairing. + // `kConstent`: the user is expected to consent to the pairing process. + // `kDisplayPasskey`: the user is notified of a pairing passkey. + // `kDisplayPin`: same as kDisplayPasskey, but different pairing key format. + // `kConfirmPasskey`: the user must confirm pairing after verifying a passkey. + // `kRequestPin`: the user is supposed to enter a pin to confirm pairing. + enum class PairingType { + kUnknown = 0, + kConsent = 1, + kDisplayPasskey = 2, + kDisplayPin = 3, + kConfirmPasskey = 4, + kRequestPin = 5, + kLast, + }; + PairingType pairing_type; + + // Pairing pin to notify the user for the pairing process. + // If not relevant to the current pairing process, it's empty. + std::string passkey; +}; + +// https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothPairingCallback +// +// This callback is invoked during the Bluetooth pairing process and +// contains all the relevant pairing information required for pairing. +struct BluetoothPairingCallback { + // https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothPairingCallback.PairingError + enum class PairingError { + kUnknown = 0, + kAuthCanceled = 1, /* failed because we canceled the pairing process. */ + kAuthFailed = 2, /* failed with pins did not match, or no response. */ + kAuthRejected = 3, /* failed with the remote device rejected pairing. */ + kAuthTimeout = 4, /* failed with authentication timeout. */ + kFailed = 5, /* failed with no explicit reason. */ + kRepeatedAttempts = 6, /* failed with many repeated attempts. */ + kLast, + }; + + // Invoked when successfully paired with a device. + absl::AnyInvocable on_paired_cb = DefaultCallback<>(); + + // Invoked when pairing with a device is canceled or fails. + absl::AnyInvocable + on_pairing_error_cb = + DefaultCallback(); + + // Invoked when the pairing process has been initiated with a remote + // Bluetooth device. + absl::AnyInvocable + on_pairing_initiated_cb = DefaultCallback(); +}; + +// This class is responsible for handling Bluetooth pairing with a remote +// BluetoothDevice. +// DCHECK_CALLED_ON_VALID_SEQUENCE +class BluetoothPairing { + public: + virtual ~BluetoothPairing() = default; + + // https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothConnectionManager#initiatepairing + // https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothConnectionManager#registerpairingcallback + // + // Initiate Bluetooth pairing process with a remote device. + // Register a BluetoothPairingCallback to listen for Bluetooth pairing events + // Such as incoming pairing request, devices paired etc. + virtual bool InitiatePairing(BluetoothPairingCallback pairing_cb) = 0; + + // https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothConnectionManager#finishpairing + // + // Invoke this function to finish the pairing process with the remote device. + // Should be called only after receiving a callback from onPairingInitiated. + // Pin is needed for PairingType::kRequestPin + virtual bool FinishPairing(std::optional pin_code) = 0; + + // https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothConnectionManager#cancelpairing + // + // Cancel an ongoing pairing process with a remote device. + virtual bool CancelPairing() = 0; + + // https://developer.android.com/reference/com/google/android/things/bluetooth/BluetoothConnectionManager#unpair + // + // Destroys the existing pairing/bond with the remote device. + virtual bool Unpair() = 0; + + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getBondState() + // + // Get the pairing state of the remote device. + virtual bool IsPaired() = 0; +}; + // Container of operations that can be performed over the Bluetooth Classic // medium. class BluetoothClassicMedium { @@ -174,6 +276,14 @@ class BluetoothClassicMedium { virtual std::unique_ptr ListenForService( const std::string& service_name, const std::string& service_uuid) = 0; + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createBond() + // + // Start the bonding (pairing) process with the remote device. + // Return a Bluetooth pairing instance to handle the pairing process with the + // remote device. + virtual std::unique_ptr CreatePairing( + BluetoothDevice& remote_device) = 0; + virtual BluetoothDevice* GetRemoteDevice(const std::string& mac_address) = 0; virtual void AddObserver(Observer* observer) = 0; diff --git a/internal/platform/implementation/g3/bluetooth_classic.cc b/internal/platform/implementation/g3/bluetooth_classic.cc index b8544aba..b7732b5e 100644 --- a/internal/platform/implementation/g3/bluetooth_classic.cc +++ b/internal/platform/implementation/g3/bluetooth_classic.cc @@ -271,6 +271,12 @@ BluetoothClassicMedium::ListenForService(const std::string& service_name, return socket; } +std::unique_ptr BluetoothClassicMedium::CreatePairing( + api::BluetoothDevice& remote_device) { + // TODO(b/279964840): Add g3 implementation for BluetoothPairing. + return nullptr; +} + api::BluetoothDevice* BluetoothClassicMedium::GetRemoteDevice( const std::string& mac_address) { auto& env = MediumEnvironment::Instance(); diff --git a/internal/platform/implementation/g3/bluetooth_classic.h b/internal/platform/implementation/g3/bluetooth_classic.h index 1db7af17..966615cd 100644 --- a/internal/platform/implementation/g3/bluetooth_classic.h +++ b/internal/platform/implementation/g3/bluetooth_classic.h @@ -233,6 +233,11 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium { const std::string& service_name, const std::string& service_uuid) override ABSL_LOCKS_EXCLUDED(mutex_); + // Return a Bluetooth pairing instance to handle the pairing process with the + // remote device. + std::unique_ptr CreatePairing( + api::BluetoothDevice& remote_device) override; + api::BluetoothDevice* GetRemoteDevice( const std::string& mac_address) override; diff --git a/internal/platform/implementation/windows/bluetooth_classic_medium.cc b/internal/platform/implementation/windows/bluetooth_classic_medium.cc index 563df49c..6f914208 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_medium.cc +++ b/internal/platform/implementation/windows/bluetooth_classic_medium.cc @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -29,10 +30,12 @@ #include "internal/platform/cancellation_flag.h" #include "internal/platform/cancellation_flag_listener.h" #include "internal/platform/exception.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" #include "internal/platform/implementation/windows/bluetooth_classic_server_socket.h" #include "internal/platform/implementation/windows/bluetooth_classic_socket.h" +#include "internal/platform/implementation/windows/bluetooth_pairing.h" #include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Bluetooth.Rfcomm.h" #include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Bluetooth.h" #include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Enumeration.h" @@ -45,7 +48,6 @@ namespace nearby { namespace windows { namespace { - using winrt::Windows::Foundation::IInspectable; using winrt::Windows::Foundation::Collections::IMapView; @@ -336,6 +338,37 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( } } +std::unique_ptr BluetoothClassicMedium::CreatePairing( + api::BluetoothDevice& remote_device) { + NEARBY_LOGS(VERBOSE) << __func__ << ": Start to createPairing with device: " + << remote_device.GetMacAddress(); + try { + winrt::Windows::Devices::Bluetooth::BluetoothDevice bluetooth_device = + winrt::Windows::Devices::Bluetooth::BluetoothDevice:: + FromBluetoothAddressAsync( + mac_address_string_to_uint64(remote_device.GetMacAddress())) + .get(); + winrt::Windows::Devices::Enumeration::DeviceInformationCustomPairing + custom_pairing = + bluetooth_device.DeviceInformation().Pairing().Custom(); + if (custom_pairing) { + return std::make_unique(bluetooth_device, + custom_pairing); + } + NEARBY_LOGS(VERBOSE) << __func__ + << ": Failed to get DeviceInformationCustomPairing."; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << " : Failed to create pairing. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to create pairing. WinRT exception: " + << error.code() << ": " + << winrt::to_string(error.message()); + } + return nullptr; +} + bool BluetoothClassicMedium::HaveAccess(winrt::hstring device_id) { if (device_id.empty()) { return false; diff --git a/internal/platform/implementation/windows/bluetooth_classic_medium.h b/internal/platform/implementation/windows/bluetooth_classic_medium.h index 40d1f933..bfaa00a6 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_medium.h +++ b/internal/platform/implementation/windows/bluetooth_classic_medium.h @@ -146,6 +146,11 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium { api::BluetoothDevice* GetRemoteDevice( const std::string& mac_address) override; + // Return a Bluetooth pairing instance to handle the pairing process with the + // remote device. + std::unique_ptr CreatePairing( + api::BluetoothDevice& remote_device) override; + void AddObserver(Observer* observer) override { // TODO(b/269521993): Implement. } diff --git a/internal/platform/implementation/windows/bluetooth_pairing.cc b/internal/platform/implementation/windows/bluetooth_pairing.cc index f038fa4d..f43d1193 100644 --- a/internal/platform/implementation/windows/bluetooth_pairing.cc +++ b/internal/platform/implementation/windows/bluetooth_pairing.cc @@ -14,7 +14,20 @@ #include "internal/platform/implementation/windows/bluetooth_pairing.h" +#include + +#include +#include +#include +#include + +#include "absl/log/check.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "internal/platform/implementation/bluetooth_classic.h" +#include "internal/platform/implementation/windows/generated/winrt/impl/Windows.Devices.Enumeration.0.h" #include "internal/platform/logging.h" +#include "winrt/Windows.Devices.Bluetooth.h" #include "winrt/Windows.Devices.Enumeration.h" #include "winrt/Windows.Foundation.Collections.h" #include "winrt/base.h" @@ -23,93 +36,294 @@ namespace nearby { namespace windows { namespace { +using ::winrt::Windows::Devices::Bluetooth::BluetoothDevice; using ::winrt::Windows::Devices::Enumeration::DeviceInformationCustomPairing; using ::winrt::Windows::Devices::Enumeration::DevicePairingKinds; using ::winrt::Windows::Devices::Enumeration::DevicePairingProtectionLevel; using ::winrt::Windows::Devices::Enumeration::DevicePairingRequestedEventArgs; using ::winrt::Windows::Devices::Enumeration::DevicePairingResult; using ::winrt::Windows::Devices::Enumeration::DevicePairingResultStatus; +using ::winrt::Windows::Devices::Enumeration::DeviceUnpairingResult; +using ::winrt::Windows::Devices::Enumeration::DeviceUnpairingResultStatus; using ::winrt::Windows::Foundation::IAsyncOperation; +using PairingError = ::nearby::api::BluetoothPairingCallback::PairingError; +using PairingType = ::nearby::api::PairingParams::PairingType; } // namespace BluetoothPairing::BluetoothPairing( - DeviceInformationCustomPairing& custom_pairing) - : custom_pairing_(custom_pairing) {} + BluetoothDevice bluetooth_device, + DeviceInformationCustomPairing custom_pairing) + : bluetooth_device_(bluetooth_device), custom_pairing_(custom_pairing) { + NEARBY_LOGS(VERBOSE) << __func__ + << ": BluetoothPairing is created for device."; +} -BluetoothPairing::~BluetoothPairing() = default; - -void BluetoothPairing::StartPairing() { - NEARBY_LOGS(VERBOSE) << "Bluetooth_pairing start pairing"; - pairing_requested_token_ = custom_pairing_.PairingRequested( - {this, &BluetoothPairing::OnPairingRequested}); - IAsyncOperation pairing_operation = - custom_pairing_.PairAsync(DevicePairingKinds::ConfirmOnly | - DevicePairingKinds::ProvidePin | - DevicePairingKinds::ConfirmPinMatch, - DevicePairingProtectionLevel::None); - DevicePairingResult pairing_result = pairing_operation.get(); - if (pairing_result != nullptr) { - OnPair(pairing_result); +BluetoothPairing::~BluetoothPairing() { + if (pairing_requested_token_) { + custom_pairing_.PairingRequested( + std::exchange(pairing_requested_token_, {})); } + CancelPairing(); + NEARBY_LOGS(VERBOSE) << __func__ + << ": BluetoothPairing is destroyed for device."; +} + +bool BluetoothPairing::InitiatePairing( + api::BluetoothPairingCallback pairing_cb) { + NEARBY_LOGS(VERBOSE) << __func__ << ": Start to initiate pairing process."; + try { + pairing_requested_token_ = custom_pairing_.PairingRequested( + {this, &BluetoothPairing::OnPairingRequested}); + if (!pairing_requested_token_) { + NEARBY_LOGS(VERBOSE) << __func__ + << " Failed to registered pairing callback."; + return false; + } + pairing_callback_ = std::move(pairing_cb); + DevicePairingResult pairing_result = + custom_pairing_ + .PairAsync(DevicePairingKinds::ConfirmOnly | + DevicePairingKinds::ProvidePin | + DevicePairingKinds::ConfirmPinMatch | + DevicePairingKinds::DisplayPin, + DevicePairingProtectionLevel::None) + .get(); + OnPair(pairing_result); + return true; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to initiate pairing. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to initiate pairing. WinRT exception: " + << error.code() << ": " + << winrt::to_string(error.message()); + } + return false; +} + +bool BluetoothPairing::FinishPairing( + std::optional pin_code) { + NEARBY_LOGS(VERBOSE) << __func__ << "Start to finish pairing."; + try { + if (!pairing_requested_) { + NEARBY_LOGS(VERBOSE) << __func__ << "No pairing requested."; + return false; + } + if (!pairing_deferral_) { + NEARBY_LOGS(VERBOSE) << __func__ << "No ongoing pairing process."; + return false; + } + if (expecting_pin_code_) { + if (!pin_code.has_value()) { + NEARBY_LOGS(INFO) << __func__ << " Failed to get pin code"; + return false; + } + expecting_pin_code_ = false; + auto pin_hstring = winrt::to_hstring(std::string(pin_code.value())); + pairing_requested_.Accept(pin_hstring); + } else { + pairing_requested_.Accept(); + } + pairing_deferral_.Complete(); + NEARBY_LOGS(VERBOSE) << "Successfully finished pairing."; + return true; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to finish pairing. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to finish pairing. WinRT exception: " + << error.code() << ": " + << winrt::to_string(error.message()); + } + return false; +} + +bool BluetoothPairing::CancelPairing() { + NEARBY_LOGS(VERBOSE) << __func__ + << "Start to cancel ongoing pairing process."; + try { + if (!pairing_deferral_) { + NEARBY_LOGS(VERBOSE) << __func__ << "No ongoing pairing process."; + return true; + } + // There is no way to explicitly cancel an in-progress pairing on Windows as + // DevicePairingRequestedEventArgs has no Cancel() method. + // Our approach is to complete the deferral, without accepting, + // which results in a RejectedByHandler result status. + // |was_cancelled_| is set so that OnPair(), which is called when the + // deferral is completed, will know that cancellation was the actual result. + was_cancelled_ = true; + pairing_deferral_.Complete(); + NEARBY_LOGS(VERBOSE) << __func__ << "Canceled ongoing pairing process."; + return true; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to cancel ongoing pairing " + << "process. exception: " << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to cancel ongoing pairing process. " + << "WinRT exception: " << error.code() << ": " + << winrt::to_string(error.message()); + } + return false; +} + +bool BluetoothPairing::Unpair() { + NEARBY_LOGS(VERBOSE) << __func__ << ": Start to unpair with remote device."; + try { + if (!IsPaired()) { + NEARBY_LOGS(VERBOSE) << __func__ << " : Remote device Was not paired."; + return true; + } + DeviceUnpairingResult unpairing_result = + bluetooth_device_.DeviceInformation().Pairing().UnpairAsync().get(); + if (unpairing_result.Status() == DeviceUnpairingResultStatus::Unpaired) { + NEARBY_LOGS(VERBOSE) << __func__ << ": Unpaired with remote device."; + return true; + } + NEARBY_LOGS(VERBOSE) << __func__ + << ": Failed to unpaired with remote device."; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to unpaired with device. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to unpaired with device. WinRT exception: " + << error.code() << ": " + << winrt::to_string(error.message()); + } + return false; +} + +bool BluetoothPairing::IsPaired() { + try { + bool is_paired = bluetooth_device_.DeviceInformation().Pairing().IsPaired(); + NEARBY_LOGS(INFO) << __func__ << (is_paired ? "True" : "False"); + return is_paired; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to get IsPaired. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to get IsPaired. WinRT exception: " + << error.code() << ": " + << winrt::to_string(error.message()); + } + return false; } void BluetoothPairing::OnPairingRequested( DeviceInformationCustomPairing custom_pairing, DevicePairingRequestedEventArgs pairing_requested) { - NEARBY_LOGS(INFO) << "BluetoothPairing::OnPairingRequested()"; - DevicePairingKinds pairing_kind = pairing_requested.PairingKind(); - switch (pairing_kind) { - case DevicePairingKinds::ProvidePin: - NEARBY_LOGS(INFO) << "DevicePairingKind: RequestPinCode."; - pairing_requested.Accept(); - return; - case DevicePairingKinds::ConfirmOnly: - NEARBY_LOGS(INFO) << "DevicePairingKind: ConfirmOnly."; - pairing_requested.Accept(); - break; - case DevicePairingKinds::ConfirmPinMatch: - NEARBY_LOGS(INFO) << "DevicePairingKind: Confirm Pin Match: " - << pairing_requested.Pin().c_str(); - pairing_requested.Accept(); - break; - default: - NEARBY_LOGS(INFO) << "Unsupported DevicePairingKind = " - << static_cast(pairing_kind); - break; + NEARBY_LOGS(VERBOSE) << __func__ << "Requested to pair."; + try { + DevicePairingKinds pairing_kind = pairing_requested.PairingKind(); + pairing_requested_ = pairing_requested; + pairing_deferral_ = pairing_requested.GetDeferral(); + api::PairingParams params; + switch (pairing_kind) { + case DevicePairingKinds::ProvidePin: + NEARBY_LOGS(INFO) << __func__ << "DevicePairingKind: RequestPinCode."; + expecting_pin_code_ = true; + params.pairing_type = PairingType::kRequestPin; + pairing_callback_.on_pairing_initiated_cb(params); + return; + case DevicePairingKinds::ConfirmOnly: + NEARBY_LOGS(INFO) << __func__ << "DevicePairingKind: ConfirmOnly."; + params.pairing_type = PairingType::kConsent; + pairing_callback_.on_pairing_initiated_cb(params); + return; + case DevicePairingKinds::ConfirmPinMatch: + NEARBY_LOGS(INFO) << __func__ + << "DevicePairingKind: Confirm Pin Match."; + params.pairing_type = PairingType::kConfirmPasskey; + params.passkey = winrt::to_string(pairing_requested.Pin()); + pairing_callback_.on_pairing_initiated_cb(params); + return; + default: + params.pairing_type = PairingType::kUnknown; + NEARBY_LOGS(INFO) << __func__ << "Unsupported DevicePairingKind:" + << static_cast(pairing_kind); + break; + } + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to request to pair with device. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) + << __func__ + << ": Failed to request to pair with device. WinRT exception: " + << error.code() << ": " << winrt::to_string(error.message()); } + pairing_callback_.on_pairing_error_cb(PairingError::kFailed); } void BluetoothPairing::OnPair(DevicePairingResult& pairing_result) { - DevicePairingResultStatus status = pairing_result.Status(); - - switch (status) { - case DevicePairingResultStatus::AlreadyPaired: - case DevicePairingResultStatus::Paired: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Paired."; - return; - case DevicePairingResultStatus::PairingCanceled: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Pairing Canceled."; - return; - case DevicePairingResultStatus::AuthenticationFailure: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Authentication Failure."; - return; - case DevicePairingResultStatus::ConnectionRejected: - case DevicePairingResultStatus::RejectedByHandler: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Authentication Rejected."; - return; - case DevicePairingResultStatus::AuthenticationTimeout: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Authentication Timeout."; - return; - case DevicePairingResultStatus::Failed: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Failed."; - return; - case DevicePairingResultStatus::OperationAlreadyInProgress: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Operatio In Progress."; - return; - default: - NEARBY_LOGS(ERROR) << "Pairing Result Status: Failed."; - return; + try { + DevicePairingResultStatus status = pairing_result.Status(); + NEARBY_LOGS(INFO) << __func__ + << "Pairing Result Status: " << static_cast(status); + if (was_cancelled_ && + status == DevicePairingResultStatus::RejectedByHandler) { + // See comment in CancelPairing() for explanation of why was_cancelled_ + // is used. + status = DevicePairingResultStatus::PairingCanceled; + } + switch (status) { + case DevicePairingResultStatus::AlreadyPaired: + case DevicePairingResultStatus::Paired: + NEARBY_LOGS(ERROR) << __func__ << "Pairing Result Status: Paired."; + pairing_callback_.on_paired_cb(); + return; + case DevicePairingResultStatus::PairingCanceled: + NEARBY_LOGS(ERROR) << __func__ + << "Pairing Result Status: Pairing Canceled."; + pairing_callback_.on_pairing_error_cb(PairingError::kAuthCanceled); + return; + case DevicePairingResultStatus::AuthenticationFailure: + NEARBY_LOGS(ERROR) << __func__ + << "Pairing Result Status: Authentication Failure."; + pairing_callback_.on_pairing_error_cb(PairingError::kAuthFailed); + return; + case DevicePairingResultStatus::ConnectionRejected: + case DevicePairingResultStatus::RejectedByHandler: + NEARBY_LOGS(ERROR) << __func__ + << "Pairing Result Status: Authentication Rejected."; + pairing_callback_.on_pairing_error_cb(PairingError::kAuthRejected); + return; + case DevicePairingResultStatus::AuthenticationTimeout: + NEARBY_LOGS(ERROR) << __func__ + << "Pairing Result Status: Authentication Timeout."; + pairing_callback_.on_pairing_error_cb(PairingError::kAuthTimeout); + return; + case DevicePairingResultStatus::Failed: + NEARBY_LOGS(ERROR) << __func__ << "Pairing Result Status: Failed."; + pairing_callback_.on_pairing_error_cb(PairingError::kFailed); + return; + case DevicePairingResultStatus::OperationAlreadyInProgress: + NEARBY_LOGS(ERROR) << __func__ + << "Pairing Result Status: Operation In Progress."; + pairing_callback_.on_pairing_error_cb(PairingError::kRepeatedAttempts); + return; + default: + break; + } + NEARBY_LOGS(ERROR) << __func__ << "Pairing Result Status: Failed."; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to get Pairing Result Status. exception: " + << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to get Pairing Result Status." + << " WinRT exception: " << error.code() << ": " + << winrt::to_string(error.message()); } + pairing_callback_.on_pairing_error_cb(PairingError::kFailed); } } // namespace windows diff --git a/internal/platform/implementation/windows/bluetooth_pairing.h b/internal/platform/implementation/windows/bluetooth_pairing.h index 05cf5c75..4ffd8d04 100644 --- a/internal/platform/implementation/windows/bluetooth_pairing.h +++ b/internal/platform/implementation/windows/bluetooth_pairing.h @@ -15,27 +15,35 @@ #ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLUETOOTH_PAIRING_H_ #define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLUETOOTH_PAIRING_H_ -#include +#include -#include +#include +#include "absl/strings/string_view.h" +#include "internal/platform/implementation/bluetooth_classic.h" +#include "winrt/Windows.Devices.Bluetooth.h" #include "winrt/Windows.Devices.Enumeration.h" +#include "winrt/Windows.Foundation.Collections.h" +#include "winrt/base.h" namespace nearby { namespace windows { -class BluetoothPairing { +class BluetoothPairing : public api::BluetoothPairing { public: explicit BluetoothPairing( - ::winrt::Windows::Devices::Enumeration::DeviceInformationCustomPairing& + ::winrt::Windows::Devices::Bluetooth::BluetoothDevice bluetooth_device, + ::winrt::Windows::Devices::Enumeration::DeviceInformationCustomPairing custom_pairing); BluetoothPairing(const BluetoothPairing&) = default; BluetoothPairing& operator=(const BluetoothPairing&) = default; + ~BluetoothPairing() override; - ~BluetoothPairing(); - - // Initiates the pairing procedure. - void StartPairing(); + bool InitiatePairing(api::BluetoothPairingCallback pairing_cb) override; + bool FinishPairing(std::optional pin_code) override; + bool CancelPairing() override; + bool Unpair() override; + bool IsPaired() override; private: void OnPairingRequested( @@ -48,9 +56,20 @@ class BluetoothPairing { pairing_result); // WinRT objects + ::winrt::Windows::Devices::Bluetooth::BluetoothDevice bluetooth_device_; ::winrt::Windows::Devices::Enumeration::DeviceInformationCustomPairing custom_pairing_; + ::winrt::event_token pairing_requested_token_; + ::winrt::Windows::Devices::Enumeration::DevicePairingRequestedEventArgs + pairing_requested_ = nullptr; + ::winrt::Windows::Foundation::Deferral pairing_deferral_ = nullptr; + api::BluetoothPairingCallback pairing_callback_; + + // Boolean indicating whether the device is currently pairing and expecting a + // PIN Code to be returned. + bool expecting_pin_code_ = false; + bool was_cancelled_ = false; }; } // namespace windows