#include "device/bluetooth/bluetooth_pairing_winrt.h"
#include <utility>
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/win/com_init_util.h"
#include "base/win/post_async_results.h"
#include "base/win/scoped_hstring.h"
#include "device/base/features.h"
#include "device/bluetooth/bluetooth_device_winrt.h"
#include "device/bluetooth/event_utils_winrt.h"
namespace device {
namespace {
using ABI::Windows::Devices::Enumeration::DevicePairingKinds;
using ABI::Windows::Devices::Enumeration::DevicePairingKinds_ConfirmOnly;
using ABI::Windows::Devices::Enumeration::DevicePairingKinds_ConfirmPinMatch;
using ABI::Windows::Devices::Enumeration::DevicePairingKinds_DisplayPin;
using ABI::Windows::Devices::Enumeration::DevicePairingKinds_ProvidePin;
using ABI::Windows::Devices::Enumeration::DevicePairingResult;
using ABI::Windows::Devices::Enumeration::DevicePairingResultStatus;
using ABI::Windows::Devices::Enumeration::
DevicePairingResultStatus_AlreadyPaired;
using ABI::Windows::Devices::Enumeration::
DevicePairingResultStatus_AuthenticationFailure;
using ABI::Windows::Devices::Enumeration::
DevicePairingResultStatus_AuthenticationTimeout;
using ABI::Windows::Devices::Enumeration::
DevicePairingResultStatus_ConnectionRejected;
using ABI::Windows::Devices::Enumeration::DevicePairingResultStatus_Failed;
using ABI::Windows::Devices::Enumeration::
DevicePairingResultStatus_OperationAlreadyInProgress;
using ABI::Windows::Devices::Enumeration::DevicePairingResultStatus_Paired;
using ABI::Windows::Devices::Enumeration::
DevicePairingResultStatus_PairingCanceled;
using ABI::Windows::Devices::Enumeration::
DevicePairingResultStatus_RejectedByHandler;
using CompletionCallback = base::OnceCallback<void(HRESULT hr)>;
using ConnectErrorCode = BluetoothDevice::ConnectErrorCode;
using ABI::Windows::Devices::Enumeration::IDeviceInformationCustomPairing;
using ABI::Windows::Devices::Enumeration::IDevicePairingRequestedEventArgs;
using ABI::Windows::Devices::Enumeration::IDevicePairingResult;
using ABI::Windows::Foundation::IAsyncOperation;
using Microsoft::WRL::ComPtr;
void PostTask(BluetoothPairingWinrt::ConnectCallback callback,
absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), error_code));
}
HRESULT CompleteDeferral(
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IDeferral> deferral) {
base::win::AssertComApartmentType(base::win::ComApartmentType::MTA);
return deferral->Complete();
}
bool HstringToUint32(HSTRING in, uint32_t& out) {
if (!in) {
DVLOG(2) << "HstringToUint32: HSTRING PIN is NULL.";
return false;
}
base::win::ScopedHString scoped_hstring{in};
std::string str = scoped_hstring.GetAsUTF8();
if (str.length() > 6) {
DVLOG(2) << "HstringToUint32: PIN code = " << str
<< " which is more than 6 digits.";
return false;
}
str.erase(0, str.find_first_not_of('0'));
if (base::StringToUint(str, &out)) {
return true;
} else {
DVLOG(2) << "HstringToUint32: failed to convert pin = " << str
<< " into uint32_t";
return false;
}
}
}
BluetoothPairingWinrt::BluetoothPairingWinrt(
BluetoothDeviceWinrt* device,
BluetoothDevice::PairingDelegate* pairing_delegate,
ComPtr<IDeviceInformationCustomPairing> custom_pairing,
ConnectCallback callback)
: device_(device),
pairing_delegate_(pairing_delegate),
custom_pairing_(std::move(custom_pairing)),
callback_(std::move(callback)) {
DCHECK(device_);
DCHECK(pairing_delegate_);
DCHECK(custom_pairing_);
}
BluetoothPairingWinrt::~BluetoothPairingWinrt() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!pairing_requested_token_)
return;
HRESULT hr =
custom_pairing_->remove_PairingRequested(*pairing_requested_token_);
if (FAILED(hr)) {
DVLOG(2) << "Removing PairingRequested Handler failed: "
<< logging::SystemErrorCodeToString(hr);
}
}
void BluetoothPairingWinrt::StartPairing() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pairing_requested_token_ = AddTypedEventHandler(
custom_pairing_.Get(),
&IDeviceInformationCustomPairing::add_PairingRequested,
base::BindRepeating(&BluetoothPairingWinrt::OnPairingRequested,
weak_ptr_factory_.GetWeakPtr()));
if (!pairing_requested_token_) {
PostTask(std::move(callback_),
BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
ComPtr<IAsyncOperation<DevicePairingResult*>> pair_op;
HRESULT hr = custom_pairing_->PairAsync(
DevicePairingKinds_ConfirmOnly | DevicePairingKinds_ProvidePin |
DevicePairingKinds_ConfirmPinMatch,
&pair_op);
if (FAILED(hr)) {
DVLOG(2) << "DeviceInformationCustomPairing::PairAsync() failed: "
<< logging::SystemErrorCodeToString(hr);
PostTask(std::move(callback_),
BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
hr = base::win::PostAsyncResults(
std::move(pair_op), base::BindOnce(&BluetoothPairingWinrt::OnPair,
weak_ptr_factory_.GetWeakPtr()));
if (FAILED(hr)) {
DVLOG(2) << "PostAsyncResults failed: "
<< logging::SystemErrorCodeToString(hr);
PostTask(std::move(callback_),
BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
}
bool BluetoothPairingWinrt::ExpectingPinCode() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return expecting_pin_code_;
}
void BluetoothPairingWinrt::OnSetPinCodeDeferralCompletion(HRESULT hr) {
if (FAILED(hr)) {
DVLOG(2) << "Completing Deferred Pairing Request failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
}
}
void BluetoothPairingWinrt::OnConfirmPairingDeferralCompletion(HRESULT hr) {
if (FAILED(hr)) {
DVLOG(2) << "Completing Deferred Pairing Request failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
}
}
void BluetoothPairingWinrt::SetPinCode(base::StringPiece pin_code) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "BluetoothPairingWinrt::SetPinCode(" << pin_code << ")";
auto pin_hstring = base::win::ScopedHString::Create(pin_code);
DCHECK(expecting_pin_code_);
expecting_pin_code_ = false;
DCHECK(pairing_requested_);
HRESULT hr = pairing_requested_->AcceptWithPin(pin_hstring.get());
if (FAILED(hr)) {
DVLOG(2) << "Accepting Pairing Request With Pin failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
DCHECK(pairing_deferral_);
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&CompleteDeferral, std::move(pairing_deferral_)),
base::BindOnce(&BluetoothPairingWinrt::OnSetPinCodeDeferralCompletion,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothPairingWinrt::ConfirmPairing() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "BluetoothPairingWinrt::ConfirmPairing() is called";
DCHECK(pairing_requested_);
HRESULT hr = pairing_requested_->Accept();
if (FAILED(hr)) {
DVLOG(2) << "Accepting Pairing Request in ConfirmPairing failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
DCHECK(pairing_deferral_);
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&CompleteDeferral, std::move(pairing_deferral_)),
base::BindOnce(&BluetoothPairingWinrt::OnConfirmPairingDeferralCompletion,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothPairingWinrt::OnRejectPairing(HRESULT hr) {
if (FAILED(hr)) {
DVLOG(2) << "Completing Deferred Pairing Request failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
std::move(callback_).Run(
BluetoothDevice::ConnectErrorCode::ERROR_AUTH_REJECTED);
}
void BluetoothPairingWinrt::RejectPairing() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "BluetoothPairingWinrt::RejectPairing()";
DCHECK(pairing_deferral_);
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&CompleteDeferral, std::move(pairing_deferral_)),
base::BindOnce(&BluetoothPairingWinrt::OnRejectPairing,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothPairingWinrt::OnCancelPairing(HRESULT hr) {
if (FAILED(hr)) {
DVLOG(2) << "Completing Deferred Pairing Request failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
std::move(callback_).Run(
BluetoothDevice::ConnectErrorCode::ERROR_AUTH_CANCELED);
}
void BluetoothPairingWinrt::CancelPairing() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "BluetoothPairingWinrt::CancelPairing()";
DCHECK(pairing_deferral_);
was_cancelled_ = true;
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&CompleteDeferral, std::move(pairing_deferral_)),
base::BindOnce(&BluetoothPairingWinrt::OnCancelPairing,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothPairingWinrt::OnPairingRequested(
IDeviceInformationCustomPairing* custom_pairing,
IDevicePairingRequestedEventArgs* pairing_requested) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "BluetoothPairingWinrt::OnPairingRequested()";
DevicePairingKinds pairing_kind;
HRESULT hr = pairing_requested->get_PairingKind(&pairing_kind);
if (FAILED(hr)) {
DVLOG(2) << "Getting Pairing Kind failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
DVLOG(2) << "DevicePairingKind: " << static_cast<int>(pairing_kind);
hr = pairing_requested->GetDeferral(&pairing_deferral_);
if (FAILED(hr)) {
DVLOG(2) << "Getting Pairing Deferral failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
switch (pairing_kind) {
case DevicePairingKinds_ProvidePin:
pairing_requested_ = pairing_requested;
expecting_pin_code_ = true;
pairing_delegate_->RequestPinCode(device_);
return;
case DevicePairingKinds_ConfirmOnly:
if (base::FeatureList::IsEnabled(
features::kWebBluetoothConfirmPairingSupport)) {
pairing_requested_ = pairing_requested;
pairing_delegate_->AuthorizePairing(device_);
return;
} else {
DVLOG(2) << "DevicePairingKind = " << static_cast<int>(pairing_kind)
<< " is not enabled by "
"enable-web-bluetooth-confirm-pairing-support";
}
break;
case DevicePairingKinds_ConfirmPinMatch:
if (base::FeatureList::IsEnabled(
features::kWebBluetoothConfirmPairingSupport)) {
pairing_requested_ = pairing_requested;
HSTRING hstring_pin;
pairing_requested->get_Pin(&hstring_pin);
uint32_t pin;
if (HstringToUint32(hstring_pin, pin)) {
pairing_delegate_->ConfirmPasskey(device_, pin);
return;
} else {
DVLOG(2) << "DevicePairingKind = " << static_cast<int>(pairing_kind)
<< " has invalid PIN to display, cancel pairing procedure.";
}
} else {
DVLOG(2) << "DevicePairingKind = " << static_cast<int>(pairing_kind)
<< " is not enabled by "
"enable-web-bluetooth-confirm-pairing-support";
}
break;
default:
DVLOG(2) << "Unsupported DevicePairingKind = "
<< static_cast<int>(pairing_kind);
break;
}
std::move(callback_).Run(
BluetoothDevice::ConnectErrorCode::ERROR_AUTH_FAILED);
}
void BluetoothPairingWinrt::OnPair(
ComPtr<IDevicePairingResult> pairing_result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DevicePairingResultStatus status;
HRESULT hr = pairing_result->get_Status(&status);
if (FAILED(hr)) {
DVLOG(2) << "Getting Pairing Result Status failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
if (was_cancelled_ && status == DevicePairingResultStatus_RejectedByHandler) {
status = DevicePairingResultStatus_PairingCanceled;
}
DVLOG(2) << "Pairing Result Status: " << static_cast<int>(status);
switch (status) {
case DevicePairingResultStatus_AlreadyPaired:
case DevicePairingResultStatus_Paired:
std::move(callback_).Run(absl::nullopt);
return;
case DevicePairingResultStatus_PairingCanceled:
std::move(callback_).Run(
BluetoothDevice::ConnectErrorCode::ERROR_AUTH_CANCELED);
return;
case DevicePairingResultStatus_AuthenticationFailure:
std::move(callback_).Run(
BluetoothDevice::ConnectErrorCode::ERROR_AUTH_FAILED);
return;
case DevicePairingResultStatus_ConnectionRejected:
case DevicePairingResultStatus_RejectedByHandler:
std::move(callback_).Run(
BluetoothDevice::ConnectErrorCode::ERROR_AUTH_REJECTED);
return;
case DevicePairingResultStatus_AuthenticationTimeout:
std::move(callback_).Run(
BluetoothDevice::ConnectErrorCode::ERROR_AUTH_TIMEOUT);
return;
case DevicePairingResultStatus_Failed:
std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
case DevicePairingResultStatus_OperationAlreadyInProgress:
std::move(callback_).Run(
BluetoothDevice::ConnectErrorCode::ERROR_INPROGRESS);
return;
default:
std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
}
}