mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 22:56:12 -04:00
327 lines
13 KiB
C++
327 lines
13 KiB
C++
// 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 "internal/platform/implementation/windows/bluetooth_pairing.h"
|
|
|
|
#include <windows.h>
|
|
|
|
#include <exception>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#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"
|
|
|
|
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(
|
|
BluetoothDevice bluetooth_device,
|
|
DeviceInformationCustomPairing custom_pairing)
|
|
: bluetooth_device_(bluetooth_device), custom_pairing_(custom_pairing) {
|
|
VLOG(1) << __func__ << ": BluetoothPairing is created for device.";
|
|
}
|
|
|
|
BluetoothPairing::~BluetoothPairing() {
|
|
if (pairing_requested_token_) {
|
|
custom_pairing_.PairingRequested(
|
|
std::exchange(pairing_requested_token_, {}));
|
|
}
|
|
CancelPairing();
|
|
VLOG(1) << __func__ << ": BluetoothPairing is destroyed for device.";
|
|
}
|
|
|
|
bool BluetoothPairing::InitiatePairing(
|
|
api::BluetoothPairingCallback pairing_cb) {
|
|
VLOG(1) << __func__ << ": Start to initiate pairing process.";
|
|
try {
|
|
pairing_requested_token_ = custom_pairing_.PairingRequested(
|
|
{this, &BluetoothPairing::OnPairingRequested});
|
|
if (!pairing_requested_token_) {
|
|
VLOG(1) << __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) {
|
|
LOG(ERROR) << __func__ << ": Failed to initiate pairing. exception: "
|
|
<< exception.what();
|
|
} catch (const winrt::hresult_error& error) {
|
|
LOG(ERROR) << __func__ << ": Failed to initiate pairing. WinRT exception: "
|
|
<< error.code() << ": " << winrt::to_string(error.message());
|
|
} catch (...) {
|
|
LOG(ERROR) << __func__ << ": Unknown exception.";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BluetoothPairing::FinishPairing(
|
|
std::optional<absl::string_view> pin_code) {
|
|
VLOG(1) << __func__ << "Start to finish pairing.";
|
|
try {
|
|
if (!pairing_requested_) {
|
|
VLOG(1) << __func__ << "No pairing requested.";
|
|
return false;
|
|
}
|
|
if (!pairing_deferral_) {
|
|
VLOG(1) << __func__ << "No ongoing pairing process.";
|
|
return false;
|
|
}
|
|
if (expecting_pin_code_) {
|
|
if (!pin_code.has_value()) {
|
|
LOG(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();
|
|
VLOG(1) << "Successfully finished pairing.";
|
|
return true;
|
|
} catch (std::exception exception) {
|
|
LOG(ERROR) << __func__
|
|
<< ": Failed to finish pairing. exception: " << exception.what();
|
|
} catch (const winrt::hresult_error& error) {
|
|
LOG(ERROR) << __func__ << ": Failed to finish pairing. WinRT exception: "
|
|
<< error.code() << ": " << winrt::to_string(error.message());
|
|
} catch (...) {
|
|
LOG(ERROR) << __func__ << ": Unknown exception.";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BluetoothPairing::CancelPairing() {
|
|
VLOG(1) << __func__ << " Start to cancel ongoing pairing process.";
|
|
try {
|
|
if (!pairing_deferral_) {
|
|
VLOG(1) << __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_.Close();
|
|
VLOG(1) << __func__ << "Canceled ongoing pairing process.";
|
|
return true;
|
|
} catch (std::exception exception) {
|
|
LOG(ERROR) << __func__ << ": Failed to cancel ongoing pairing "
|
|
<< "process. exception: " << exception.what();
|
|
} catch (const winrt::hresult_error& error) {
|
|
LOG(ERROR) << __func__ << ": Failed to cancel ongoing pairing process. "
|
|
<< "WinRT exception: " << error.code() << ": "
|
|
<< winrt::to_string(error.message());
|
|
} catch (...) {
|
|
LOG(ERROR) << __func__ << ": Unknown exception.";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BluetoothPairing::Unpair() {
|
|
VLOG(1) << __func__ << ": Start to unpair with remote device.";
|
|
try {
|
|
if (!IsPaired()) {
|
|
VLOG(1) << __func__ << " : Remote device Was not paired.";
|
|
return true;
|
|
}
|
|
DeviceUnpairingResult unpairing_result =
|
|
bluetooth_device_.DeviceInformation().Pairing().UnpairAsync().get();
|
|
if (unpairing_result.Status() == DeviceUnpairingResultStatus::Unpaired) {
|
|
VLOG(1) << __func__ << ": Unpaired with remote device.";
|
|
return true;
|
|
}
|
|
VLOG(1) << __func__ << ": Failed to unpaired with remote device.";
|
|
} catch (std::exception exception) {
|
|
LOG(ERROR) << __func__ << ": Failed to unpaired with device. exception: "
|
|
<< exception.what();
|
|
} catch (const winrt::hresult_error& error) {
|
|
LOG(ERROR) << __func__
|
|
<< ": Failed to unpaired with device. WinRT exception: "
|
|
<< error.code() << ": " << winrt::to_string(error.message());
|
|
} catch (...) {
|
|
LOG(ERROR) << __func__ << ": Unknown exception.";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BluetoothPairing::IsPaired() {
|
|
try {
|
|
bool is_paired = bluetooth_device_.DeviceInformation().Pairing().IsPaired();
|
|
LOG(INFO) << __func__ << (is_paired ? " True" : " False");
|
|
return is_paired;
|
|
} catch (std::exception exception) {
|
|
LOG(ERROR) << __func__
|
|
<< ": Failed to get IsPaired. exception: " << exception.what();
|
|
} catch (const winrt::hresult_error& error) {
|
|
LOG(ERROR) << __func__
|
|
<< ": Failed to get IsPaired. WinRT exception: " << error.code()
|
|
<< ": " << winrt::to_string(error.message());
|
|
} catch (...) {
|
|
LOG(ERROR) << __func__ << ": Unknown exception.";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void BluetoothPairing::OnPairingRequested(
|
|
DeviceInformationCustomPairing custom_pairing,
|
|
DevicePairingRequestedEventArgs pairing_requested) {
|
|
VLOG(1) << __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:
|
|
LOG(INFO) << __func__ << "DevicePairingKind: RequestPinCode.";
|
|
expecting_pin_code_ = true;
|
|
params.pairing_type = PairingType::kRequestPin;
|
|
pairing_callback_.on_pairing_initiated_cb(params);
|
|
return;
|
|
case DevicePairingKinds::ConfirmOnly:
|
|
LOG(INFO) << __func__ << "DevicePairingKind: ConfirmOnly.";
|
|
params.pairing_type = PairingType::kConsent;
|
|
pairing_callback_.on_pairing_initiated_cb(params);
|
|
return;
|
|
case DevicePairingKinds::ConfirmPinMatch:
|
|
LOG(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;
|
|
LOG(INFO) << __func__ << "Unsupported DevicePairingKind:"
|
|
<< static_cast<int>(pairing_kind);
|
|
break;
|
|
}
|
|
} catch (std::exception exception) {
|
|
LOG(ERROR) << __func__
|
|
<< ": Failed to request to pair with device. exception: "
|
|
<< exception.what();
|
|
} catch (const winrt::hresult_error& error) {
|
|
LOG(ERROR) << __func__
|
|
<< ": Failed to request to pair with device. WinRT exception: "
|
|
<< error.code() << ": " << winrt::to_string(error.message());
|
|
} catch (...) {
|
|
LOG(ERROR) << __func__ << ": Unknown exception.";
|
|
}
|
|
pairing_callback_.on_pairing_error_cb(PairingError::kFailed);
|
|
}
|
|
|
|
void BluetoothPairing::OnPair(DevicePairingResult& pairing_result) {
|
|
try {
|
|
DevicePairingResultStatus status = pairing_result.Status();
|
|
LOG(INFO) << __func__
|
|
<< "Pairing Result Status: " << static_cast<int>(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:
|
|
LOG(ERROR) << __func__ << "Pairing Result Status: Paired.";
|
|
pairing_callback_.on_paired_cb();
|
|
return;
|
|
case DevicePairingResultStatus::PairingCanceled:
|
|
LOG(ERROR) << __func__ << "Pairing Result Status: Pairing Canceled.";
|
|
pairing_callback_.on_pairing_error_cb(PairingError::kAuthCanceled);
|
|
return;
|
|
case DevicePairingResultStatus::AuthenticationFailure:
|
|
LOG(ERROR) << __func__
|
|
<< "Pairing Result Status: Authentication Failure.";
|
|
pairing_callback_.on_pairing_error_cb(PairingError::kAuthFailed);
|
|
return;
|
|
case DevicePairingResultStatus::ConnectionRejected:
|
|
case DevicePairingResultStatus::RejectedByHandler:
|
|
LOG(ERROR) << __func__
|
|
<< "Pairing Result Status: Authentication Rejected.";
|
|
pairing_callback_.on_pairing_error_cb(PairingError::kAuthRejected);
|
|
return;
|
|
case DevicePairingResultStatus::AuthenticationTimeout:
|
|
LOG(ERROR) << __func__
|
|
<< "Pairing Result Status: Authentication Timeout.";
|
|
pairing_callback_.on_pairing_error_cb(PairingError::kAuthTimeout);
|
|
return;
|
|
case DevicePairingResultStatus::Failed:
|
|
LOG(ERROR) << __func__ << "Pairing Result Status: Failed.";
|
|
pairing_callback_.on_pairing_error_cb(PairingError::kFailed);
|
|
return;
|
|
case DevicePairingResultStatus::OperationAlreadyInProgress:
|
|
LOG(ERROR) << __func__
|
|
<< "Pairing Result Status: Operation In Progress.";
|
|
pairing_callback_.on_pairing_error_cb(PairingError::kRepeatedAttempts);
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
LOG(ERROR) << __func__ << "Pairing Result Status: Failed.";
|
|
} catch (std::exception exception) {
|
|
LOG(ERROR) << __func__
|
|
<< ": Failed to get Pairing Result Status. exception: "
|
|
<< exception.what();
|
|
} catch (const winrt::hresult_error& error) {
|
|
LOG(ERROR) << __func__ << ": Failed to get Pairing Result Status."
|
|
<< " WinRT exception: " << error.code() << ": "
|
|
<< winrt::to_string(error.message());
|
|
} catch (...) {
|
|
LOG(ERROR) << __func__ << ": Unknown exception.";
|
|
}
|
|
pairing_callback_.on_pairing_error_cb(PairingError::kFailed);
|
|
}
|
|
|
|
} // namespace windows
|
|
} // namespace nearby
|