123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- // Copyright 2018 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "device/bluetooth/bluetooth_pairing_winrt.h"
- #include <utility>
- #include "base/bind.h"
- #include "base/feature_list.h"
- #include "base/logging.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/string_piece.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::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback), error_code));
- }
- HRESULT CompleteDeferral(
- Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IDeferral> deferral) {
- // Apparently deferrals may be created (aka obtained) on the main thread
- // initialized for STA, but must be completed on a thread with COM initialized
- // for MTA. If the deferral is completed on the main thread then the
- // Complete() call will succeed, i.e return S_OK, but the Windows Device
- // Association Service will be hung and all Bluetooth association changes
- // (system wide) will fail.
- base::win::AssertComApartmentType(base::win::ComApartmentType::MTA);
- return deferral->Complete();
- }
- // TODO: https://crbug.com/1345471, once we refactor the
- // BluetoothDevice::ConfirmPasskey() to use std::u16string instead of uint32_t
- // we can then get rid of HstringToUint32()
- 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();
- // PIN has to be <= 6 digits
- if (str.length() > 6) {
- DVLOG(2) << "HstringToUint32: PIN code = " << str
- << " which is more than 6 digits.";
- return false;
- }
- // Remove leading '0' before being converted into uint32_t
- str.erase(0, str.find_first_not_of('0'));
- // If we failed to convert str into unsigned int we cancel pairing by return
- // false
- if (base::StringToUint(str, &out)) {
- return true;
- } else {
- DVLOG(2) << "HstringToUint32: failed to convert pin = " << str
- << " into uint32_t";
- return false;
- }
- }
- } // namespace
- 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) {
- // This method is normally never called. Usually when CancelPairing() is
- // invoked the deferral is completed, which immediately calls OnPair(), which
- // runs |callback_| and destroys this object before this method can be
- // executed. However, if the deferral fails to complete, this will be run.
- 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_);
- // There is no way to explicitly cancel an in-progress pairing 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;
- 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) {
- // See comment in CancelPairing() for explanation of why was_cancelled_
- // is used.
- status = DevicePairingResultStatus_PairingCanceled;
- }
- DVLOG(2) << "Pairing Result Status: " << static_cast<int>(status);
- switch (status) {
- case DevicePairingResultStatus_AlreadyPaired:
- case DevicePairingResultStatus_Paired:
- std::move(callback_).Run(/*error_code=*/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;
- }
- }
- } // namespace device
|