0

[Bluetooth][Win/Linux] Add confirm pairing mode support runtime flag

When reading characteristic which required secured connection, it need
to firstly complete pairing. Confirm-only is one of pairing kind and its
idea is to have a prompt for user to consent andproceed pairing.  This
CL includes: 1. runtime flag enable confirm pairing support  2. device
change to support confirm-only pairing and unit tests for confirm-only
change 3. pairing_manager and bluetooth_service_impl change for
showing prompt and handling prompt result and unit tests.

Bug: 1309715
Change-Id: I75d56b1243ca3f523b0b686c7468386f5da5607a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3688827
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Commit-Queue: Alvin Ji <alvinji@chromium.org>
Reviewed-by: Matt Reynolds <mattreynolds@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1015492}
This commit is contained in:
Alvin Ji
2022-06-17 19:57:47 +00:00
committed by Chromium LUCI CQ
parent 5d8d86656f
commit 3443b9b9f3
26 changed files with 421 additions and 40 deletions

@ -8779,6 +8779,14 @@ const FeatureEntry kFeatureEntries[] = {
autofill::features::
kAutofillEnableGetDetailsForEnrollParsingInUploadCardResponse)},
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
{"enable-web-bluetooth-confirm-pairing-support",
flag_descriptions::kWebBluetoothConfirmPairingSupportName,
flag_descriptions::kWebBluetoothConfirmPairingSupportDescription,
kOsDesktop,
FEATURE_VALUE_TYPE(device::features::kWebBluetoothConfirmPairingSupport)},
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
// NOTE: Adding a new flag requires adding a corresponding entry to enum
// "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
// Histograms" in tools/metrics/histograms/README.md (run the

@ -2946,6 +2946,11 @@
"owners": [ "web-bluetooth@google.com" ],
"expiry_milestone": 110
},
{
"name": "enable-web-bluetooth-confirm-pairing-support",
"owners": [ "web-bluetooth@google.com" ],
"expiry_milestone": 116
},
{
"name": "enable-web-bluetooth-new-permissions-backend",
"owners": [ "web-bluetooth@google.com" ],

@ -5906,6 +5906,14 @@ const char kOzonePlatformHintDescription[] =
"\"X11\". \"Auto\" selects Wayland if possible, X11 otherwise. ";
#endif // BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
const char kWebBluetoothConfirmPairingSupportName[] =
"Web Bluetooth confirm pairing support";
const char kWebBluetoothConfirmPairingSupportDescription[] =
"Enable confirm-only and confirm-pin pairing mode support for Web "
"Bluetooth";
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
#if BUILDFLAG(IS_LINUX)
const char kCleanUndecryptablePasswordsLinuxName[] =
"Cleanup local undecryptable passwords during initial sync flow";

@ -3386,6 +3386,11 @@ extern const char kWebShareName[];
extern const char kWebShareDescription[];
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
extern const char kWebBluetoothConfirmPairingSupportName[];
extern const char kWebBluetoothConfirmPairingSupportDescription[];
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
#if BUILDFLAG(IS_LINUX)
extern const char kOzonePlatformHintChoiceDefault[];
extern const char kOzonePlatformHintChoiceAuto[];

@ -20,8 +20,9 @@ namespace content {
// separated into a separate interface for readability and testing purposes.
class WebBluetoothPairingManagerDelegate {
public:
enum class CredentialPromptResult {
// User entered text and pressed OK (or equiv.) button.
enum class PairPromptResult {
// User entered valid pin text or pressed OK (or equiv.) button for pairing
// confirmation.
kSuccess,
// User cancelled, or agent cancelled on their behalf.
kCancelled,
@ -30,8 +31,11 @@ class WebBluetoothPairingManagerDelegate {
// Callback for bluetooth auth credential (PIN, Passkey) prompts.
// |result| is only valid when status is SUCCESS.
using BluetoothCredentialsCallback =
base::OnceCallback<void(CredentialPromptResult,
const std::string& result)>;
base::OnceCallback<void(PairPromptResult, const std::string& result)>;
// Callback for bluetooth pair confirm prompts.
using BluetoothPairConfirmCallback =
base::OnceCallback<void(PairPromptResult)>;
// Return the cached device ID for the given characteric instance ID.
// The returned device ID may be invalid - check before use.
@ -64,6 +68,9 @@ class WebBluetoothPairingManagerDelegate {
virtual void SetPinCode(const blink::WebBluetoothDeviceId& device_id,
const std::string& pincode) = 0;
// The user consented to pairing with the Bluetooth device.
virtual void PairConfirmed(const blink::WebBluetoothDeviceId& device_id) = 0;
// Reads the value for the characteristic identified by
// |characteristic_instance_id|. If the value is successfully read the
// callback will be run with WebBluetoothResult::SUCCESS and the
@ -121,6 +128,14 @@ class WebBluetoothPairingManagerDelegate {
virtual void PromptForBluetoothCredentials(
const std::u16string& device_identifier,
BluetoothCredentialsCallback callback) = 0;
// Display a dialog to prompt for user to confirm to pair with Bluetooth
// device. |device_identifier| is any string the caller wants to display to
// the user to identify the device (MAC address, name, etc.). |callback| will
// be called with the dialog result.
virtual void PromptForBluetoothPairConfirm(
const std::u16string& device_identifier,
BluetoothPairConfirmCallback callback) = 0;
};
} // namespace content

@ -267,18 +267,31 @@ void WebBluetoothPairingManagerImpl::RequestPinCode(BluetoothDevice* device) {
void WebBluetoothPairingManagerImpl::OnPinCodeResult(
blink::WebBluetoothDeviceId device_id,
WebBluetoothPairingManagerDelegate::CredentialPromptResult status,
WebBluetoothPairingManagerDelegate::PairPromptResult status,
const std::string& result) {
switch (status) {
case WebBluetoothPairingManagerDelegate::CredentialPromptResult::kCancelled:
case WebBluetoothPairingManagerDelegate::PairPromptResult::kCancelled:
pairing_manager_delegate_->CancelPairing(device_id);
break;
case WebBluetoothPairingManagerDelegate::CredentialPromptResult::kSuccess:
case WebBluetoothPairingManagerDelegate::PairPromptResult::kSuccess:
pairing_manager_delegate_->SetPinCode(device_id, result);
break;
}
}
void WebBluetoothPairingManagerImpl::OnPairConfirmResult(
blink::WebBluetoothDeviceId device_id,
WebBluetoothPairingManagerDelegate::PairPromptResult status) {
switch (status) {
case WebBluetoothPairingManagerDelegate::PairPromptResult::kCancelled:
pairing_manager_delegate_->CancelPairing(device_id);
break;
case WebBluetoothPairingManagerDelegate::PairPromptResult::kSuccess:
pairing_manager_delegate_->PairConfirmed(device_id);
break;
}
}
void WebBluetoothPairingManagerImpl::RequestPasskey(BluetoothDevice* device) {
device->CancelPairing();
NOTIMPLEMENTED() << "Passkey pairing not supported.";
@ -310,8 +323,12 @@ void WebBluetoothPairingManagerImpl::ConfirmPasskey(BluetoothDevice* device,
}
void WebBluetoothPairingManagerImpl::AuthorizePairing(BluetoothDevice* device) {
device->CancelPairing();
NOTIMPLEMENTED();
blink::WebBluetoothDeviceId device_id =
pairing_manager_delegate_->GetWebBluetoothDeviceId(device->GetAddress());
pairing_manager_delegate_->PromptForBluetoothPairConfirm(
device->GetNameForDisplay(),
base::BindOnce(&WebBluetoothPairingManagerImpl::OnPairConfirmResult,
weak_ptr_factory_.GetWeakPtr(), device_id));
}
} // namespace content

@ -72,6 +72,10 @@ class CONTENT_EXPORT WebBluetoothPairingManagerImpl
CredentialPromptPINCancelled);
FRIEND_TEST_ALL_PREFIXES(BluetoothPairingManagerTest,
CredentialPromptPasskeyCancelled);
FRIEND_TEST_ALL_PREFIXES(BluetoothPairingManagerTest,
PairConfirmPromptSuccess);
FRIEND_TEST_ALL_PREFIXES(BluetoothPairingManagerTest,
PairConfirmPromptCancelled);
// Pair the Bluetooth device identified by |device_id|. |num_pair_attempts|
// represents the number of pairing attempts for the specified device which
@ -93,9 +97,13 @@ class CONTENT_EXPORT WebBluetoothPairingManagerImpl
void OnPinCodeResult(
blink::WebBluetoothDeviceId device_id,
WebBluetoothPairingManagerDelegate::CredentialPromptResult status,
WebBluetoothPairingManagerDelegate::PairPromptResult status,
const std::string& result);
void OnPairConfirmResult(
blink::WebBluetoothDeviceId device_id,
WebBluetoothPairingManagerDelegate::PairPromptResult status);
// device::BluetoothDevice::PairingDelegate implementation:
void RequestPinCode(device::BluetoothDevice* device) override;
void RequestPasskey(device::BluetoothDevice* device) override;

@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/bluetooth/web_bluetooth_pairing_manager.h"
#include <string>
#include <utility>
#include <vector>
#include "base/notreached.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "content/browser/bluetooth/web_bluetooth_pairing_manager.h"
#include "content/browser/bluetooth/web_bluetooth_pairing_manager_delegate.h"
#include "content/browser/bluetooth/web_bluetooth_pairing_manager_impl.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
@ -21,6 +22,7 @@ namespace content {
namespace {
using ::base::test::SingleThreadTaskEnvironment;
using ::base::test::TestFuture;
using ::blink::WebBluetoothDeviceId;
using ::blink::mojom::WebBluetoothResult;
using ::blink::mojom::WebBluetoothService;
@ -170,6 +172,12 @@ class BluetoothPairingManagerTest : public testing::Test,
std::move(pair_callback_).Run(/*error_code=*/absl::nullopt);
}
void PairConfirmed(const blink::WebBluetoothDeviceId& device_id) override {
ASSERT_TRUE(device_id.IsValid());
EXPECT_EQ(device_id, kValidTestData.device_id);
std::move(pair_callback_).Run(/*error_code=*/absl::nullopt);
}
void ResumeSuspendedPairingWithSuccess() {
device_paired_ = true;
EXPECT_FALSE(pair_callback_.is_null());
@ -275,25 +283,35 @@ class BluetoothPairingManagerTest : public testing::Test,
const std::u16string& device_identifier,
BluetoothCredentialsCallback callback) override {
switch (prompt_result_) {
case CredentialPromptResult::kSuccess:
case PairPromptResult::kSuccess:
std::move(callback).Run(prompt_result_, kValidTestData.pincode);
break;
case CredentialPromptResult::kCancelled:
case PairPromptResult::kCancelled:
std::move(callback).Run(prompt_result_, "");
break;
}
}
void PromptForBluetoothPairConfirm(
const std::u16string& device_identifier,
BluetoothPairConfirmCallback callback) override {
std::move(callback).Run(confirm_only_prompt_result_);
}
void SetAuthBehavior(AuthBehavior auth_behavior) {
auth_behavior_ = auth_behavior;
}
int num_pair_attempts() const { return num_pair_attempts_; }
void SetCredentialPromptResult(CredentialPromptResult result) {
void SetCredentialPromptResult(PairPromptResult result) {
prompt_result_ = result;
}
void SetPairConfirmPromptResult(PairPromptResult result) {
confirm_only_prompt_result_ = result;
}
const std::vector<uint8_t>& characteristic_value() const {
return characteristic_value_;
}
@ -317,7 +335,8 @@ class BluetoothPairingManagerTest : public testing::Test,
int num_pair_attempts_ = 0;
bool device_paired_ = false;
AuthBehavior auth_behavior_ = AuthBehavior::kUnspecified;
CredentialPromptResult prompt_result_ = CredentialPromptResult::kSuccess;
PairPromptResult prompt_result_ = PairPromptResult::kSuccess;
PairPromptResult confirm_only_prompt_result_ = PairPromptResult::kSuccess;
std::unique_ptr<WebBluetoothPairingManagerImpl> pairing_manager_;
SingleThreadTaskEnvironment single_threaded_task_environment_;
};
@ -870,7 +889,7 @@ TEST_F(BluetoothPairingManagerTest, WriteDescriptorDoublePair) {
}
TEST_F(BluetoothPairingManagerTest, CredentialPromptPINSuccess) {
SetCredentialPromptResult(CredentialPromptResult::kSuccess);
SetCredentialPromptResult(PairPromptResult::kSuccess);
MockBluetoothDevice device(/*adapter=*/nullptr,
/*bluetooth_class=*/0,
@ -894,7 +913,7 @@ TEST_F(BluetoothPairingManagerTest, CredentialPromptPINSuccess) {
}
TEST_F(BluetoothPairingManagerTest, CredentialPromptPINCancelled) {
SetCredentialPromptResult(CredentialPromptResult::kCancelled);
SetCredentialPromptResult(PairPromptResult::kCancelled);
MockBluetoothDevice device(/*adapter=*/nullptr,
/*bluetooth_class=*/0,
@ -931,6 +950,44 @@ TEST_F(BluetoothPairingManagerTest, CredentialPromptPasskeyCancelled) {
pairing_manager()->RequestPasskey(&device);
}
TEST_F(BluetoothPairingManagerTest, PairConfirmPromptSuccess) {
SetPairConfirmPromptResult(PairPromptResult::kSuccess);
MockBluetoothDevice device(/*adapter=*/nullptr,
/*bluetooth_class=*/0,
kValidTestData.device_name.c_str(),
kValidTestData.device_address,
/*initially_paired=*/false,
/*connected=*/true);
EXPECT_CALL(device, GetAddress());
EXPECT_CALL(device, GetNameForDisplay());
TestFuture<absl::optional<BluetoothDevice::ConnectErrorCode>> future;
pair_callback_ = future.GetCallback();
pairing_manager()->AuthorizePairing(&device);
EXPECT_FALSE(future.Get());
}
TEST_F(BluetoothPairingManagerTest, PairConfirmPromptCancelled) {
SetPairConfirmPromptResult(PairPromptResult::kCancelled);
MockBluetoothDevice device(/*adapter=*/nullptr,
/*bluetooth_class=*/0,
kValidTestData.device_name.c_str(),
kValidTestData.device_address,
/*initially_paired=*/false,
/*connected=*/true);
EXPECT_CALL(device, GetAddress());
EXPECT_CALL(device, GetNameForDisplay());
TestFuture<absl::optional<BluetoothDevice::ConnectErrorCode>> future;
pair_callback_ = future.GetCallback();
pairing_manager()->AuthorizePairing(&device);
EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, future.Get());
}
TEST_F(BluetoothPairingManagerTest, StartNotificationsAllAuthsSuccess) {
SetAuthBehavior(AuthBehavior::kSucceedFirst);

@ -180,21 +180,39 @@ void BluetoothDelegateCredentialsCallback(
WebBluetoothPairingManagerDelegate::BluetoothCredentialsCallback callback,
BluetoothDelegate::DeviceCredentialsPromptResult status,
const std::u16string& result) {
WebBluetoothPairingManagerDelegate::CredentialPromptResult delegate_result;
WebBluetoothPairingManagerDelegate::PairPromptResult delegate_result;
switch (status) {
case BluetoothDelegate::DeviceCredentialsPromptResult::kSuccess:
delegate_result =
WebBluetoothPairingManagerDelegate::CredentialPromptResult::kSuccess;
WebBluetoothPairingManagerDelegate::PairPromptResult::kSuccess;
break;
case BluetoothDelegate::DeviceCredentialsPromptResult::kCancelled:
delegate_result = WebBluetoothPairingManagerDelegate::
CredentialPromptResult::kCancelled;
delegate_result =
WebBluetoothPairingManagerDelegate::PairPromptResult::kCancelled;
break;
}
std::move(callback).Run(delegate_result, base::UTF16ToUTF8(result));
}
void BluetoothDelegatePairConfirmCallback(
WebBluetoothPairingManagerDelegate::BluetoothPairConfirmCallback callback,
BluetoothDelegate::DevicePairConfirmPromptResult status) {
WebBluetoothPairingManagerDelegate::PairPromptResult delegate_result;
switch (status) {
case BluetoothDelegate::DevicePairConfirmPromptResult::kSuccess:
delegate_result =
WebBluetoothPairingManagerDelegate::PairPromptResult::kSuccess;
break;
case BluetoothDelegate::DevicePairConfirmPromptResult::kCancelled:
delegate_result =
WebBluetoothPairingManagerDelegate::PairPromptResult::kCancelled;
break;
}
std::move(callback).Run(delegate_result);
}
bool& ShouldIgnoreVisibilityRequirementsForTesting() {
static bool should_ignore_visibility_requirements = false;
return should_ignore_visibility_requirements;
@ -2072,7 +2090,8 @@ void WebBluetoothServiceImpl::OnCharacteristicReadValue(
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (error_code.has_value()) {
#if PAIR_BLUETOOTH_ON_DEMAND()
if (error_code.value() == GattErrorCode::GATT_ERROR_NOT_AUTHORIZED) {
if (error_code.value() == GattErrorCode::GATT_ERROR_NOT_AUTHORIZED ||
error_code.value() == GattErrorCode::GATT_ERROR_NOT_PAIRED) {
BluetoothDevice* device = GetCachedDevice(
GetCharacteristicDeviceID(characteristic_instance_id));
if (device && !device->IsPaired()) {
@ -2641,6 +2660,17 @@ void WebBluetoothServiceImpl::SetPinCode(
device->SetPinCode(pincode);
}
void WebBluetoothServiceImpl::PairConfirmed(
const blink::WebBluetoothDeviceId& device_id) {
DCHECK(device_id.IsValid());
BluetoothDevice* device = GetCachedDevice(device_id);
if (!device)
return;
device->ConfirmPairing();
}
void WebBluetoothServiceImpl::PromptForBluetoothCredentials(
const std::u16string& device_identifier,
BluetoothCredentialsCallback callback) {
@ -2650,8 +2680,7 @@ void WebBluetoothServiceImpl::PromptForBluetoothCredentials(
GetContentClient()->browser()->GetBluetoothDelegate();
if (!delegate) {
std::move(callback).Run(
WebBluetoothPairingManagerDelegate::CredentialPromptResult::kCancelled,
"");
WebBluetoothPairingManagerDelegate::PairPromptResult::kCancelled, "");
return;
}
delegate->ShowDeviceCredentialsPrompt(
@ -2660,6 +2689,24 @@ void WebBluetoothServiceImpl::PromptForBluetoothCredentials(
std::move(callback)));
}
void WebBluetoothServiceImpl::PromptForBluetoothPairConfirm(
const std::u16string& device_identifier,
BluetoothPairConfirmCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BluetoothDelegate* delegate =
GetContentClient()->browser()->GetBluetoothDelegate();
if (!delegate) {
std::move(callback).Run(
WebBluetoothPairingManagerDelegate::PairPromptResult::kCancelled);
return;
}
delegate->ShowDevicePairConfirmPrompt(
render_frame_host(), device_identifier,
base::BindOnce(&BluetoothDelegatePairConfirmCallback,
std::move(callback)));
}
#if PAIR_BLUETOOTH_ON_DEMAND()
void WebBluetoothServiceImpl::SetPairingManagerForTesting(
std::unique_ptr<WebBluetoothPairingManager> pairing_manager) {

@ -465,6 +465,11 @@ class CONTENT_EXPORT WebBluetoothServiceImpl
const std::u16string& device_identifier,
BluetoothCredentialsCallback callback) override;
void PromptForBluetoothPairConfirm(
const std::u16string& device_identifier,
BluetoothPairConfirmCallback callback) override;
void PairConfirmed(const blink::WebBluetoothDeviceId& device_id) override;
// Used to open a BluetoothChooser and start a device discovery session.
std::unique_ptr<BluetoothDeviceChooserController> device_chooser_controller_;

@ -67,5 +67,12 @@ const base::Feature kWebXrHitTest{"WebXRHitTest",
// Enables access to experimental WebXR features.
const base::Feature kWebXrIncubations{"WebXRIncubations",
base::FEATURE_DISABLED_BY_DEFAULT};
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
// Controls whether Web Bluetooth should support confirm-only and confirm-PIN
// pairing mode on Win/Linux
const base::Feature kWebBluetoothConfirmPairingSupport{
"WebBluetoothConfirmPairingSupport", base::FEATURE_DISABLED_BY_DEFAULT};
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
} // namespace features
} // namespace device

@ -38,6 +38,11 @@ DEVICE_BASE_EXPORT extern const base::Feature kWebXrHandInput;
DEVICE_BASE_EXPORT extern const base::Feature kWebXrHitTest;
DEVICE_BASE_EXPORT extern const base::Feature kWebXrIncubations;
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
DEVICE_BASE_EXPORT extern const base::Feature
kWebBluetoothConfirmPairingSupport;
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
} // namespace features
} // namespace device

@ -12,6 +12,7 @@
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/test_future.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "build/build_config.h"
#include "device/bluetooth/bluetooth_remote_gatt_service.h"
@ -299,6 +300,68 @@ TEST_P(BluetoothTestWinrtOnly, DevicePairRequestPinCodeCancelPairing) {
EXPECT_FALSE(device->IsPaired());
EXPECT_FALSE(device->ExpectingPinCode());
}
TEST_P(BluetoothTestWinrtOnly, DevicePairRequestConfirmOnlyAccept) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(1);
ASSERT_TRUE(ConnectGatt(device));
EXPECT_FALSE(device->IsPaired());
SimulatePairingKind(
device,
ABI::Windows::Devices::Enumeration::DevicePairingKinds_ConfirmOnly);
StrictMock<MockPairingDelegate> pairing_delegate;
EXPECT_CALL(pairing_delegate, AuthorizePairing)
.WillOnce([](BluetoothDevice* device) {
ASSERT_NE(device, nullptr);
device->ConfirmPairing();
});
base::test::TestFuture<absl::optional<BluetoothDevice::ConnectErrorCode>>
error_code_future;
device->Pair(&pairing_delegate, error_code_future.GetCallback());
EXPECT_FALSE(error_code_future.Get().has_value());
EXPECT_TRUE(device->IsPaired());
}
TEST_P(BluetoothTestWinrtOnly, DevicePairRequestConfirmOnlyCancel) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(1);
ASSERT_TRUE(ConnectGatt(device));
EXPECT_FALSE(device->IsPaired());
SimulatePairingKind(
device,
ABI::Windows::Devices::Enumeration::DevicePairingKinds_ConfirmOnly);
StrictMock<MockPairingDelegate> pairing_delegate;
EXPECT_CALL(pairing_delegate, AuthorizePairing)
.WillOnce([](BluetoothDevice* device) {
ASSERT_NE(device, nullptr);
ScheduleAsynchronousCancelPairing(device);
});
base::test::TestFuture<absl::optional<BluetoothDevice::ConnectErrorCode>>
error_code_future;
device->Pair(&pairing_delegate, error_code_future.GetCallback());
EXPECT_EQ(error_code_future.Get(), BluetoothDevice::ERROR_AUTH_CANCELED);
EXPECT_FALSE(device->IsPaired());
}
#endif // BUILDFLAG(IS_WIN)
// Verifies basic device properties, e.g. GetAddress, GetName, ...

@ -390,7 +390,8 @@ void BluetoothDeviceWinrt::SetPasskey(uint32_t passkey) {
}
void BluetoothDeviceWinrt::ConfirmPairing() {
NOTIMPLEMENTED();
if (pairing_)
pairing_->ConfirmPairing();
}
void BluetoothDeviceWinrt::RejectPairing() {

@ -7,12 +7,14 @@
#include <utility>
#include "base/bind.h"
#include "base/feature_list.h"
#include "base/logging.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"
@ -150,6 +152,14 @@ void BluetoothPairingWinrt::OnSetPinCodeDeferralCompletion(HRESULT hr) {
}
}
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 << ")";
@ -173,6 +183,26 @@ void BluetoothPairingWinrt::SetPinCode(base::StringPiece pin_code) {
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: "
@ -245,11 +275,6 @@ void BluetoothPairingWinrt::OnPairingRequested(
}
DVLOG(2) << "DevicePairingKind: " << static_cast<int>(pairing_kind);
if (pairing_kind != DevicePairingKinds_ProvidePin) {
DVLOG(2) << "Unexpected DevicePairingKind.";
std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
hr = pairing_requested->GetDeferral(&pairing_deferral_);
if (FAILED(hr)) {
@ -259,9 +284,32 @@ void BluetoothPairingWinrt::OnPairingRequested(
return;
}
pairing_requested_ = pairing_requested;
expecting_pin_code_ = true;
pairing_delegate_->RequestPinCode(device_);
switch (pairing_kind) {
case DevicePairingKinds_ProvidePin:
pairing_requested_ = pairing_requested;
expecting_pin_code_ = true;
pairing_delegate_->RequestPinCode(device_);
break;
case DevicePairingKinds_ConfirmOnly:
if (base::FeatureList::IsEnabled(
features::kWebBluetoothConfirmPairingSupport)) {
pairing_requested_ = pairing_requested;
pairing_delegate_->AuthorizePairing(device_);
} else {
DVLOG(2) << "DevicePairingKind = " << static_cast<int>(pairing_kind)
<< " is not enabled by "
"enable-web-bluetooth-confirm-pairing-support";
std::move(callback_).Run(
BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
break;
default:
DVLOG(2) << "Unsupported DevicePairingKind = "
<< static_cast<int>(pairing_kind);
std::move(callback_).Run(BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
return;
}
}
void BluetoothPairingWinrt::OnPair(

@ -51,6 +51,9 @@ class BluetoothPairingWinrt {
// Sends the PIN code |pin_code| to the remote device during pairing.
void SetPinCode(base::StringPiece pin_code);
// User consented to continue pairing the remote device.
void ConfirmPairing();
// Rejects a pairing or connection request from a remote device.
void RejectPairing();
@ -69,6 +72,7 @@ class BluetoothPairingWinrt {
pairing_result);
void OnSetPinCodeDeferralCompletion(HRESULT hr);
void OnConfirmPairingDeferralCompletion(HRESULT hr);
void OnRejectPairing(HRESULT hr);
void OnCancelPairing(HRESULT hr);

@ -89,6 +89,7 @@ using ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEAdvertisementWatcher;
using ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEManufacturerDataFactory;
using ABI::Windows::Devices::Enumeration::DevicePairingKinds;
using ABI::Windows::Devices::Enumeration::IDeviceInformation;
using ABI::Windows::Devices::Enumeration::IDeviceInformationStatics;
using ABI::Windows::Devices::Radios::IRadioStatics;
@ -703,6 +704,9 @@ BluetoothTestWinrt::BluetoothTestWinrt() {
} else {
disabled.push_back(kNewBLEGattSessionHandling);
}
// TODO(crbug.com/1335586): Remove once `kWebBluetoothConfirmPairingSupport`
// is enabled by default.
enabled.push_back(features::kWebBluetoothConfirmPairingSupport);
scoped_feature_list_.InitWithFeatures(enabled, disabled);
}
@ -881,6 +885,14 @@ void BluetoothTestWinrt::SimulatePairingPinCode(BluetoothDevice* device,
ble_device->SimulatePairingPinCode(std::move(pin_code));
}
void BluetoothTestWinrt::SimulatePairingKind(BluetoothDevice* device,
DevicePairingKinds pairing_kind) {
auto* const ble_device =
static_cast<TestBluetoothDeviceWinrt*>(device)->ble_device();
DCHECK(ble_device);
ble_device->SimulatePairingKind(pairing_kind);
}
void BluetoothTestWinrt::SimulateAdvertisementStarted(
BluetoothAdvertisement* advertisement) {
static_cast<FakeBluetoothLEAdvertisementPublisherWinrt*>(

@ -5,12 +5,14 @@
#ifndef DEVICE_BLUETOOTH_TEST_BLUETOOTH_TEST_WIN_H_
#define DEVICE_BLUETOOTH_TEST_BLUETOOTH_TEST_WIN_H_
#include "base/memory/raw_ptr.h"
#include "device/bluetooth/test/bluetooth_test.h"
#include <Windows.Devices.Enumeration.h>
#include <string>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_pending_task.h"
@ -191,6 +193,12 @@ class BluetoothTestWinrt
void SimulateDevicePaired(BluetoothDevice* device, bool is_paired) override;
void SimulatePairingPinCode(BluetoothDevice* device,
std::string pin_code) override;
// Currently only Win derived class has this function for create pairing_kind
// tests. If in future we find that other platform need to test for
// pairing_kind we should promote this function as virtual
void SimulatePairingKind(
BluetoothDevice* device,
ABI::Windows::Devices::Enumeration::DevicePairingKinds pairing_kind);
void SimulateAdvertisementStarted(
BluetoothAdvertisement* advertisement) override;
void SimulateAdvertisementStopped(

@ -259,6 +259,12 @@ void FakeBluetoothLEDeviceWinrt::SimulatePairingPinCode(std::string pin_code) {
Make<FakeDeviceInformationPairingWinrt>(std::move(pin_code)));
}
void FakeBluetoothLEDeviceWinrt::SimulatePairingKind(
ABI::Windows::Devices::Enumeration::DevicePairingKinds pairing_kind) {
device_information_ = Make<FakeDeviceInformationWinrt>(
Make<FakeDeviceInformationPairingWinrt>(pairing_kind));
}
absl::optional<BluetoothUUID> FakeBluetoothLEDeviceWinrt::GetTargetGattService()
const {
if (!service_uuid_)

@ -6,6 +6,7 @@
#define DEVICE_BLUETOOTH_TEST_FAKE_BLUETOOTH_LE_DEVICE_WINRT_H_
#include <windows.devices.bluetooth.h>
#include <windows.devices.enumeration.h>
#include <windows.foundation.h>
#include <wrl/client.h>
#include <wrl/implements.h>
@ -132,6 +133,8 @@ class FakeBluetoothLEDeviceWinrt
void SimulateDevicePaired(bool is_paired);
void SimulatePairingPinCode(std::string pin_code);
void SimulatePairingKind(
ABI::Windows::Devices::Enumeration::DevicePairingKinds pairing_kind);
absl::optional<BluetoothUUID> GetTargetGattService() const;
void SimulateGattConnection();
void SimulateGattConnectionError(

@ -4,6 +4,7 @@
#include "device/bluetooth/test/fake_device_information_custom_pairing_winrt.h"
#include <Windows.Devices.Enumeration.h>
#include <windows.foundation.h>
#include <utility>
@ -21,6 +22,8 @@ namespace {
using ABI::Windows::Devices::Enumeration::DeviceInformationCustomPairing;
using ABI::Windows::Devices::Enumeration::DevicePairingKinds;
using ABI::Windows::Devices::Enumeration::DevicePairingKinds_ConfirmOnly;
using ABI::Windows::Devices::Enumeration::DevicePairingKinds_ProvidePin;
using ABI::Windows::Devices::Enumeration::DevicePairingProtectionLevel;
using ABI::Windows::Devices::Enumeration::DevicePairingRequestedEventArgs;
using ABI::Windows::Devices::Enumeration::DevicePairingResult;
@ -35,12 +38,20 @@ using Microsoft::WRL::Make;
} // namespace
// This ctor used only by ProvidePin pairing kind
FakeDeviceInformationCustomPairingWinrt::
FakeDeviceInformationCustomPairingWinrt(
Microsoft::WRL::ComPtr<FakeDeviceInformationPairingWinrt> pairing,
std::string pin)
: pairing_(std::move(pairing)), pin_(std::move(pin)) {}
// This ctor used by ConfirmOnly (or ConfirmPinMatch in future) pairing kind
FakeDeviceInformationCustomPairingWinrt::
FakeDeviceInformationCustomPairingWinrt(
Microsoft::WRL::ComPtr<FakeDeviceInformationPairingWinrt> pairing,
DevicePairingKinds pairing_kind)
: pairing_(std::move(pairing)), pairing_kind_(pairing_kind) {}
FakeDeviceInformationCustomPairingWinrt::
~FakeDeviceInformationCustomPairingWinrt() = default;
@ -98,6 +109,18 @@ void FakeDeviceInformationCustomPairingWinrt::AcceptWithPin(std::string pin) {
}
void FakeDeviceInformationCustomPairingWinrt::Complete() {
bool is_paired = false;
switch (pairing_kind_) {
case DevicePairingKinds_ProvidePin:
is_paired = pin_ == accepted_pin_;
break;
case DevicePairingKinds_ConfirmOnly:
is_paired = confirmed_;
break;
default:
break;
}
pair_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
@ -111,7 +134,7 @@ void FakeDeviceInformationCustomPairingWinrt::Complete() {
: DevicePairingResultStatus_Failed));
pairing->set_paired(is_paired);
},
std::move(pair_callback_), pairing_, pin_ == accepted_pin_));
std::move(pair_callback_), pairing_, is_paired));
}
} // namespace device

@ -14,6 +14,7 @@
#include "base/callback.h"
#include "base/memory/scoped_refptr.h"
#include "device/bluetooth/test/fake_device_information_pairing_winrt.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace base {
class SequencedTaskRunner;
@ -31,6 +32,10 @@ class FakeDeviceInformationCustomPairingWinrt
Microsoft::WRL::ComPtr<FakeDeviceInformationPairingWinrt> pairing,
std::string pin);
FakeDeviceInformationCustomPairingWinrt(
Microsoft::WRL::ComPtr<FakeDeviceInformationPairingWinrt> pairing,
ABI::Windows::Devices::Enumeration::DevicePairingKinds pairing_kind);
FakeDeviceInformationCustomPairingWinrt(
const FakeDeviceInformationCustomPairingWinrt&) = delete;
FakeDeviceInformationCustomPairingWinrt& operator=(
@ -74,10 +79,19 @@ class FakeDeviceInformationCustomPairingWinrt
void AcceptWithPin(std::string pin);
void Complete();
ABI::Windows::Devices::Enumeration::DevicePairingKinds pairing_kind() const {
return pairing_kind_;
};
void SetConfirmed() { confirmed_ = true; }
private:
Microsoft::WRL::ComPtr<FakeDeviceInformationPairingWinrt> pairing_;
std::string pin_;
const absl::optional<std::string> pin_;
std::string accepted_pin_;
bool confirmed_ = false;
ABI::Windows::Devices::Enumeration::DevicePairingKinds pairing_kind_ =
ABI::Windows::Devices::Enumeration::DevicePairingKinds_ProvidePin;
base::OnceCallback<void(
Microsoft::WRL::ComPtr<

@ -12,6 +12,8 @@ namespace device {
namespace {
using ABI::Windows::Devices::Enumeration::DevicePairingKinds;
using ABI::Windows::Devices::Enumeration::DevicePairingKinds_ProvidePin;
using ABI::Windows::Devices::Enumeration::DevicePairingProtectionLevel;
using ABI::Windows::Devices::Enumeration::DevicePairingResult;
using ABI::Windows::Devices::Enumeration::DeviceUnpairingResult;
@ -31,6 +33,11 @@ FakeDeviceInformationPairingWinrt::FakeDeviceInformationPairingWinrt(
: custom_(Make<FakeDeviceInformationCustomPairingWinrt>(this,
std::move(pin))) {}
FakeDeviceInformationPairingWinrt::FakeDeviceInformationPairingWinrt(
DevicePairingKinds pairing_kind)
: custom_(
Make<FakeDeviceInformationCustomPairingWinrt>(this, pairing_kind)) {}
FakeDeviceInformationPairingWinrt::~FakeDeviceInformationPairingWinrt() =
default;

@ -5,7 +5,7 @@
#ifndef DEVICE_BLUETOOTH_TEST_FAKE_DEVICE_INFORMATION_PAIRING_WINRT_H_
#define DEVICE_BLUETOOTH_TEST_FAKE_DEVICE_INFORMATION_PAIRING_WINRT_H_
#include <windows.devices.enumeration.h>
#include <Windows.Devices.Enumeration.h>
#include <wrl/client.h>
#include <wrl/implements.h>
@ -22,6 +22,8 @@ class FakeDeviceInformationPairingWinrt
public:
explicit FakeDeviceInformationPairingWinrt(bool is_paired);
explicit FakeDeviceInformationPairingWinrt(std::string pin);
explicit FakeDeviceInformationPairingWinrt(
ABI::Windows::Devices::Enumeration::DevicePairingKinds pairing_kind);
FakeDeviceInformationPairingWinrt(const FakeDeviceInformationPairingWinrt&) =
delete;

@ -63,7 +63,7 @@ HRESULT FakeDevicePairingRequestedEventArgsWinrt::get_DeviceInformation(
HRESULT FakeDevicePairingRequestedEventArgsWinrt::get_PairingKind(
DevicePairingKinds* value) {
*value = DevicePairingKinds_ProvidePin;
*value = custom_pairing_->pairing_kind();
return S_OK;
}
@ -72,7 +72,8 @@ HRESULT FakeDevicePairingRequestedEventArgsWinrt::get_Pin(HSTRING* value) {
}
HRESULT FakeDevicePairingRequestedEventArgsWinrt::Accept() {
return E_NOTIMPL;
custom_pairing_->SetConfirmed();
return S_OK;
}
HRESULT FakeDevicePairingRequestedEventArgsWinrt::AcceptWithPin(HSTRING pin) {

@ -58732,6 +58732,7 @@ from previous Chrome versions.
<int value="346406287" label="EnableWebAppUninstallFromOsSettings:disabled"/>
<int value="346430505" label="SidePanel:enabled"/>
<int value="346711293" label="enable-save-password-bubble"/>
<int value="347577245" label="WebBluetoothConfirmPairingSupport:enabled"/>
<int value="347948074" label="WebFeedSort:disabled"/>
<int value="347981012" label="TabToGTSAnimation:disabled"/>
<int value="348449023"
@ -58894,6 +58895,7 @@ from previous Chrome versions.
label="AutofillEnableStickyManualFallbackForCards:enabled"/>
<int value="439267320" label="ScreenTime:disabled"/>
<int value="439525862" label="GlobalMediaControlsForCast:disabled"/>
<int value="441096714" label="WebBluetoothConfirmPairingSupport:disabled"/>
<int value="442561299" label="FeatureNotificationGuide:disabled"/>
<int value="444411390" label="enable-incognito-shortcut-on-desktop"/>
<int value="444754854" label="LegacyTLSWarnings:disabled"/>