blob: 78ed69c40e27e9144b73aad6b5ee3609c6b4e210 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2016 The Chromium Authors
ortunoad6b0fea2016-03-31 18:49:112// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
dougt4f2237c2017-01-14 04:14:135// ID Not In Map Note: A service, characteristic, or descriptor ID not in the
6// corresponding WebBluetoothServiceImpl map [service_id_to_device_address_,
7// characteristic_id_to_service_id_, descriptor_id_to_characteristic_id_]
8// implies a hostile renderer because a renderer obtains the corresponding ID
9// from this class and it will be added to the map at that time.
ortunob0fb6a182016-04-27 01:45:2610
ortunoad6b0fea2016-03-31 18:49:1111#include "content/browser/bluetooth/web_bluetooth_service_impl.h"
12
Peter Kasting1557e5f2025-01-28 01:14:0813#include <algorithm>
Peter Boströmdd7e40ec2021-04-05 20:40:1014#include <memory>
Henrique Ferreirodb47b3e2019-09-11 14:29:3915#include <utility>
Andrew Rayskiyf65990362024-02-27 18:43:2416#include <vector>
ortuno67acd832016-04-30 00:13:2217
Lei Zhangd4f2c7ad2021-05-13 20:10:1218#include "base/containers/contains.h"
Chris Mumforde6fa96a42021-07-20 00:15:1419#include "base/containers/queue.h"
Reilly Grantc81cdeb12024-11-09 02:20:4220#include "base/containers/to_vector.h"
Avi Drissmanadac21992023-01-11 23:46:3921#include "base/functional/bind.h"
22#include "base/functional/callback_helpers.h"
Keishi Hattori0e45c022021-11-27 09:25:5223#include "base/memory/raw_ptr.h"
Doug Turnera3a45412019-01-11 04:41:0824#include "base/strings/string_util.h"
ortunob6374dd82016-05-27 03:04:0725#include "base/strings/utf_string_conversions.h"
Sean Maher5b9af51f2022-11-21 15:32:4726#include "base/task/single_thread_task_runner.h"
Alvin Jif4c234d412023-08-23 16:58:5427#include "content/browser/bluetooth/advertisement_client.h"
Reilly Grant454ad802020-08-26 18:28:3928#include "content/browser/bluetooth/bluetooth_adapter_factory_wrapper.h"
Lei Zhang5874c7e2021-07-16 01:39:4929#include "content/browser/bluetooth/bluetooth_allowed_devices.h"
Lei Zhang60a43f582021-07-26 18:36:5130#include "content/browser/bluetooth/bluetooth_allowed_devices_map.h"
beaufort.francois01135bf2016-11-23 14:37:3631#include "content/browser/bluetooth/bluetooth_blocklist.h"
ortunob6374dd82016-05-27 03:04:0732#include "content/browser/bluetooth/bluetooth_device_chooser_controller.h"
Jun Cai149002e2019-05-09 23:13:0733#include "content/browser/bluetooth/bluetooth_device_scanning_prompt_controller.h"
ortunob6374dd82016-05-27 03:04:0734#include "content/browser/bluetooth/bluetooth_metrics.h"
Jun Cai732a05e32019-05-29 19:34:1935#include "content/browser/bluetooth/bluetooth_util.h"
ortunob6c45d4f2016-05-07 04:19:4236#include "content/browser/bluetooth/frame_connected_bluetooth_devices.h"
Chris Mumford4d1cf30782021-10-05 23:02:4737#include "content/browser/bluetooth/web_bluetooth_pairing_manager_impl.h"
juncaif70c51172017-02-10 23:49:1738#include "content/browser/storage_partition_impl.h"
Ovidio Henriquez76696f62020-07-08 03:06:5939#include "content/browser/web_contents/web_contents_impl.h"
Ovidio Henriquez3d729f62020-02-07 00:43:2940#include "content/public/browser/bluetooth_delegate.h"
juncaif70c51172017-02-10 23:49:1741#include "content/public/browser/browser_context.h"
ortunob6374dd82016-05-27 03:04:0742#include "content/public/browser/browser_thread.h"
Daniel Chengd6978b062023-11-16 00:11:3843#include "content/public/browser/document_user_data.h"
ortunobc3bce12016-04-15 21:22:5544#include "content/public/browser/navigation_handle.h"
ortunoad6b0fea2016-03-31 18:49:1145#include "content/public/browser/render_frame_host.h"
ortunobc3bce12016-04-15 21:22:5546#include "content/public/browser/web_contents.h"
Hans Wennborg5ffd1392019-10-16 11:00:0247#include "content/public/common/content_client.h"
Ovidio Henriquez3d729f62020-02-07 00:43:2948#include "content/public/common/content_features.h"
Lei Zhang5874c7e2021-07-16 01:39:4949#include "device/bluetooth/bluetooth_discovery_session.h"
50#include "device/bluetooth/bluetooth_gatt_characteristic.h"
51#include "device/bluetooth/bluetooth_gatt_connection.h"
52#include "device/bluetooth/bluetooth_gatt_notify_session.h"
rkc122239752016-04-20 23:59:0853#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
dougt4f2237c2017-01-14 04:14:1354#include "device/bluetooth/bluetooth_remote_gatt_descriptor.h"
Lei Zhang5874c7e2021-07-16 01:39:4955#include "device/bluetooth/bluetooth_remote_gatt_service.h"
Matt Reynolds8cca57d52025-02-20 18:20:5956#include "device/bluetooth/public/cpp/bluetooth_features.h"
Lei Zhang5874c7e2021-07-16 01:39:4957#include "device/bluetooth/public/cpp/bluetooth_uuid.h"
Henrique Ferreirodb47b3e2019-09-11 14:29:3958#include "mojo/public/cpp/bindings/associated_remote.h"
59#include "mojo/public/cpp/bindings/pending_associated_remote.h"
60#include "mojo/public/cpp/bindings/pending_receiver.h"
61#include "mojo/public/cpp/bindings/receiver.h"
Sandor «Alex» Majorc41217f2025-02-14 23:33:1362#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom.h"
Ovidio Henriquez0e8ab7072019-05-31 21:38:0763#include "third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h"
Henrique Ferreirodb47b3e2019-09-11 14:29:3964#include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom.h"
ortunoad6b0fea2016-03-31 18:49:1165
ortunoad6b0fea2016-03-31 18:49:1166namespace content {
67
68namespace {
69
Chris Mumforda0e55f372021-09-10 22:47:2670using ::device::BluetoothAdapter;
71using ::device::BluetoothDevice;
72using ::device::BluetoothDiscoverySession;
73using ::device::BluetoothGattCharacteristic;
74using ::device::BluetoothGattConnection;
75using ::device::BluetoothGattNotifySession;
76using ::device::BluetoothGattService;
77using ::device::BluetoothRemoteGattCharacteristic;
78using ::device::BluetoothRemoteGattDescriptor;
79using ::device::BluetoothRemoteGattService;
80using ::device::BluetoothUUID;
81using GattErrorCode = ::device::BluetoothGattService::GattErrorCode;
82
Sonny Sasaka8b22f472021-08-17 21:17:3583// Client names for logging in BLE scanning.
84constexpr char kScanClientNameWatchAdvertisements[] =
85 "Web Bluetooth watchAdvertisements()";
86constexpr char kScanClientNameRequestLeScan[] = "Web Bluetooth requestLeScan()";
87
Claudio DeSouzade4a42d2021-12-03 21:42:5388// The renderer performs its own checks so a request that gets to the browser
89// process indicates some failure to check for fenced frames.
90const char kFencedFrameError[] =
Sina Firoozabadi36a29bc2023-03-08 02:29:3691 "Use of Web Bluetooth API is blocked in a <fencedframe> tree.";
Claudio DeSouzade4a42d2021-12-03 21:42:5392
Chris Mumford40cd29352021-06-16 19:13:3393blink::mojom::WebBluetoothResult TranslateGATTErrorAndRecord(
Chris Mumforda0e55f372021-09-10 22:47:2694 GattErrorCode error_code,
Chris Mumford40cd29352021-06-16 19:13:3395 UMAGATTOperation operation) {
ortunoad6b0fea2016-03-31 18:49:1196 switch (error_code) {
Lei Zhang494bc8b2022-09-20 16:52:4197 case device::BluetoothRemoteGattService::GattErrorCode::kUnknown:
Chris Mumford40cd29352021-06-16 19:13:3398 RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::kUnknown);
dougt4c6a5772016-10-13 01:03:2799 return blink::mojom::WebBluetoothResult::GATT_UNKNOWN_ERROR;
Lei Zhang494bc8b2022-09-20 16:52:41100 case device::BluetoothRemoteGattService::GattErrorCode::kFailed:
Chris Mumford40cd29352021-06-16 19:13:33101 RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::kFailed);
dougt4c6a5772016-10-13 01:03:27102 return blink::mojom::WebBluetoothResult::GATT_UNKNOWN_FAILURE;
Lei Zhang494bc8b2022-09-20 16:52:41103 case device::BluetoothRemoteGattService::GattErrorCode::kInProgress:
Chris Mumford40cd29352021-06-16 19:13:33104 RecordGATTOperationOutcome(operation,
105 UMAGATTOperationOutcome::kInProgress);
dougt4c6a5772016-10-13 01:03:27106 return blink::mojom::WebBluetoothResult::GATT_OPERATION_IN_PROGRESS;
Lei Zhang494bc8b2022-09-20 16:52:41107 case device::BluetoothRemoteGattService::GattErrorCode::kInvalidLength:
Chris Mumford40cd29352021-06-16 19:13:33108 RecordGATTOperationOutcome(operation,
109 UMAGATTOperationOutcome::kInvalidLength);
dougt4c6a5772016-10-13 01:03:27110 return blink::mojom::WebBluetoothResult::GATT_INVALID_ATTRIBUTE_LENGTH;
Lei Zhang494bc8b2022-09-20 16:52:41111 case device::BluetoothRemoteGattService::GattErrorCode::kNotPermitted:
Chris Mumford40cd29352021-06-16 19:13:33112 RecordGATTOperationOutcome(operation,
113 UMAGATTOperationOutcome::kNotPermitted);
dougt4c6a5772016-10-13 01:03:27114 return blink::mojom::WebBluetoothResult::GATT_NOT_PERMITTED;
Lei Zhang494bc8b2022-09-20 16:52:41115 case device::BluetoothRemoteGattService::GattErrorCode::kNotAuthorized:
Chris Mumford40cd29352021-06-16 19:13:33116 RecordGATTOperationOutcome(operation,
117 UMAGATTOperationOutcome::kNotAuthorized);
dougt4c6a5772016-10-13 01:03:27118 return blink::mojom::WebBluetoothResult::GATT_NOT_AUTHORIZED;
Lei Zhang494bc8b2022-09-20 16:52:41119 case device::BluetoothRemoteGattService::GattErrorCode::kNotPaired:
Chris Mumford40cd29352021-06-16 19:13:33120 RecordGATTOperationOutcome(operation,
121 UMAGATTOperationOutcome::kNotPaired);
dougt4c6a5772016-10-13 01:03:27122 return blink::mojom::WebBluetoothResult::GATT_NOT_PAIRED;
Lei Zhang494bc8b2022-09-20 16:52:41123 case device::BluetoothRemoteGattService::GattErrorCode::kNotSupported:
Chris Mumford40cd29352021-06-16 19:13:33124 RecordGATTOperationOutcome(operation,
125 UMAGATTOperationOutcome::kNotSupported);
dougt4c6a5772016-10-13 01:03:27126 return blink::mojom::WebBluetoothResult::GATT_NOT_SUPPORTED;
ortunoad6b0fea2016-03-31 18:49:11127 }
Peter Boströmfc7ddc182024-10-31 19:37:21128 NOTREACHED();
ortunoad6b0fea2016-03-31 18:49:11129}
Doug Turner9b931de2019-01-08 18:22:37130
131// Max length of device name in filter. Bluetooth 5.0 3.C.3.2.2.3 states that
132// the maximum device name length is 248 bytes (UTF-8 encoded).
133constexpr size_t kMaxLengthForDeviceName = 248;
134
Md. Hasanur Rashid28c84952020-01-05 11:36:36135bool IsValidFilter(const blink::mojom::WebBluetoothLeScanFilterPtr& filter) {
Doug Turner9b931de2019-01-08 18:22:37136 // At least one member needs to be present.
François Beaufort33578ed2021-05-05 10:02:22137 if (!filter->name && !filter->name_prefix && !filter->services &&
138 !filter->manufacturer_data) {
139 return false;
140 }
141
142 // The |services| should not be empty.
143 if (filter->services && filter->services->empty())
Md. Hasanur Rashid28c84952020-01-05 11:36:36144 return false;
Doug Turner9b931de2019-01-08 18:22:37145
146 // The renderer will never send a |name| or a |name_prefix| longer than
147 // kMaxLengthForDeviceName.
148 if (filter->name && filter->name->size() > kMaxLengthForDeviceName)
Md. Hasanur Rashid28c84952020-01-05 11:36:36149 return false;
Doug Turner9b931de2019-01-08 18:22:37150
151 if (filter->name_prefix &&
152 filter->name_prefix->size() > kMaxLengthForDeviceName)
Md. Hasanur Rashid28c84952020-01-05 11:36:36153 return false;
Doug Turner9b931de2019-01-08 18:22:37154
François Beaufort33578ed2021-05-05 10:02:22155 // The |name_prefix| should not be empty.
Doug Turner9b931de2019-01-08 18:22:37156 if (filter->name_prefix && filter->name_prefix->empty())
Md. Hasanur Rashid28c84952020-01-05 11:36:36157 return false;
Doug Turner9b931de2019-01-08 18:22:37158
François Beaufort33578ed2021-05-05 10:02:22159 // The |manufacturer_data| should not be empty.
160 if (filter->manufacturer_data && filter->manufacturer_data->empty())
161 return false;
162
Md. Hasanur Rashid28c84952020-01-05 11:36:36163 return true;
Doug Turner9b931de2019-01-08 18:22:37164}
165
Md. Hasanur Rashid28c84952020-01-05 11:36:36166bool IsValidRequestDeviceOptions(
Doug Turner9b931de2019-01-08 18:22:37167 const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options) {
168 if (options->accept_all_devices)
Md. Hasanur Rashid28c84952020-01-05 11:36:36169 return !options->filters.has_value();
Doug Turner9b931de2019-01-08 18:22:37170
François Beaufortad82f972023-04-04 09:13:33171 if (!HasValidFilter(options->filters)) {
172 return false;
173 }
174
175 if (options->exclusion_filters.has_value()) {
176 return HasValidFilter(options->exclusion_filters);
177 }
178
179 return true;
Doug Turner9b931de2019-01-08 18:22:37180}
181
Md. Hasanur Rashid28c84952020-01-05 11:36:36182bool IsValidRequestScanOptions(
Doug Turner9b931de2019-01-08 18:22:37183 const blink::mojom::WebBluetoothRequestLEScanOptionsPtr& options) {
184 if (options->accept_all_advertisements)
Md. Hasanur Rashid28c84952020-01-05 11:36:36185 return !options->filters.has_value();
Doug Turner9b931de2019-01-08 18:22:37186
Md. Hasanur Rashid28c84952020-01-05 11:36:36187 return HasValidFilter(options->filters);
Doug Turner9b931de2019-01-08 18:22:37188}
189
Gabriel Britobf12a4e2022-02-03 21:46:33190bool& ShouldIgnoreVisibilityRequirementsForTesting() {
191 static bool should_ignore_visibility_requirements = false;
192 return should_ignore_visibility_requirements;
193}
194
ortunob0fb6a182016-04-27 01:45:26195} // namespace
ortunoad6b0fea2016-03-31 18:49:11196
Chris Mumforde6fa96a42021-07-20 00:15:14197// Parameters for a call to RemoteCharacteristicStartNotifications. Used to
Chris Mumford2b2708f2021-10-01 20:25:57198// defer notification starts when one is currently running for the same
Chris Mumforde6fa96a42021-07-20 00:15:14199// characteristic instance.
200struct WebBluetoothServiceImpl::DeferredStartNotificationData {
201 DeferredStartNotificationData(
202 mojo::PendingAssociatedRemote<
203 blink::mojom::WebBluetoothCharacteristicClient> client,
204 RemoteCharacteristicStartNotificationsCallback callback)
205 : client(std::move(client)), callback(std::move(callback)) {}
206
207 ~DeferredStartNotificationData() = default;
208
209 DeferredStartNotificationData& operator=(
210 const DeferredStartNotificationData&) = delete;
211
212 mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothCharacteristicClient>
213 client;
214 RemoteCharacteristicStartNotificationsCallback callback;
215};
216
Chris Mumford02de39b2021-05-06 15:28:59217// static
218blink::mojom::WebBluetoothResult
219WebBluetoothServiceImpl::TranslateConnectErrorAndRecord(
220 BluetoothDevice::ConnectErrorCode error_code) {
221 switch (error_code) {
222 case BluetoothDevice::ERROR_UNKNOWN:
Daniel Chengafaabacf2024-09-20 01:23:00223 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kUnknown);
Chris Mumford02de39b2021-05-06 15:28:59224 return blink::mojom::WebBluetoothResult::CONNECT_UNKNOWN_ERROR;
225 case BluetoothDevice::ERROR_INPROGRESS:
Daniel Chengafaabacf2024-09-20 01:23:00226 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kInProgress);
Chris Mumford02de39b2021-05-06 15:28:59227 return blink::mojom::WebBluetoothResult::CONNECT_ALREADY_IN_PROGRESS;
228 case BluetoothDevice::ERROR_FAILED:
Daniel Chengafaabacf2024-09-20 01:23:00229 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kFailed);
Sarvesh23e05c82022-12-07 18:47:54230 return blink::mojom::WebBluetoothResult::CONNECT_CONN_FAILED;
Chris Mumford02de39b2021-05-06 15:28:59231 case BluetoothDevice::ERROR_AUTH_FAILED:
Daniel Chengafaabacf2024-09-20 01:23:00232 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kAuthFailed);
Chris Mumford02de39b2021-05-06 15:28:59233 return blink::mojom::WebBluetoothResult::CONNECT_AUTH_FAILED;
234 case BluetoothDevice::ERROR_AUTH_CANCELED:
Daniel Chengafaabacf2024-09-20 01:23:00235 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kAuthCanceled);
Chris Mumford02de39b2021-05-06 15:28:59236 return blink::mojom::WebBluetoothResult::CONNECT_AUTH_CANCELED;
237 case BluetoothDevice::ERROR_AUTH_REJECTED:
Daniel Chengafaabacf2024-09-20 01:23:00238 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kAuthRejected);
Chris Mumford02de39b2021-05-06 15:28:59239 return blink::mojom::WebBluetoothResult::CONNECT_AUTH_REJECTED;
240 case BluetoothDevice::ERROR_AUTH_TIMEOUT:
Daniel Chengafaabacf2024-09-20 01:23:00241 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kAuthTimeout);
Chris Mumford02de39b2021-05-06 15:28:59242 return blink::mojom::WebBluetoothResult::CONNECT_AUTH_TIMEOUT;
243 case BluetoothDevice::ERROR_UNSUPPORTED_DEVICE:
Daniel Chengafaabacf2024-09-20 01:23:00244 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kUnsupportedDevice);
Chris Mumford02de39b2021-05-06 15:28:59245 return blink::mojom::WebBluetoothResult::CONNECT_UNSUPPORTED_DEVICE;
Sarvesh23e05c82022-12-07 18:47:54246 case BluetoothDevice::ERROR_DEVICE_NOT_READY:
Daniel Chengafaabacf2024-09-20 01:23:00247 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kNotReady);
Sarvesh23e05c82022-12-07 18:47:54248 return blink::mojom::WebBluetoothResult::CONNECT_NOT_READY;
249 case BluetoothDevice::ERROR_ALREADY_CONNECTED:
Daniel Chengafaabacf2024-09-20 01:23:00250 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kAlreadyConnected);
Sarvesh23e05c82022-12-07 18:47:54251 return blink::mojom::WebBluetoothResult::CONNECT_ALREADY_CONNECTED;
252 case BluetoothDevice::ERROR_DEVICE_ALREADY_EXISTS:
Daniel Chengafaabacf2024-09-20 01:23:00253 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kAlreadyExists);
Sarvesh23e05c82022-12-07 18:47:54254 return blink::mojom::WebBluetoothResult::CONNECT_ALREADY_EXISTS;
255 case BluetoothDevice::ERROR_DEVICE_UNCONNECTED:
Daniel Chengafaabacf2024-09-20 01:23:00256 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kNotConnected);
Sarvesh23e05c82022-12-07 18:47:54257 return blink::mojom::WebBluetoothResult::CONNECT_NOT_CONNECTED;
258 case BluetoothDevice::ERROR_DOES_NOT_EXIST:
Daniel Chengafaabacf2024-09-20 01:23:00259 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kDoesNotExist);
Sarvesh23e05c82022-12-07 18:47:54260 return blink::mojom::WebBluetoothResult::CONNECT_DOES_NOT_EXIST;
261 case BluetoothDevice::ERROR_INVALID_ARGS:
Daniel Chengafaabacf2024-09-20 01:23:00262 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kInvalidArgs);
Sarvesh23e05c82022-12-07 18:47:54263 return blink::mojom::WebBluetoothResult::CONNECT_INVALID_ARGS;
Gordon Setodb8bda5a2024-04-13 01:38:29264 case BluetoothDevice::ERROR_NON_AUTH_TIMEOUT:
Daniel Chengafaabacf2024-09-20 01:23:00265 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kNonAuthTimeout);
Gordon Setodb8bda5a2024-04-13 01:38:29266 return blink::mojom::WebBluetoothResult::CONNECT_NON_AUTH_TIMEOUT;
Gordon Seto08749dd2024-06-05 15:46:55267 case device::BluetoothDevice::ERROR_NO_MEMORY:
Daniel Chengafaabacf2024-09-20 01:23:00268 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kNoMemory);
Gordon Seto08749dd2024-06-05 15:46:55269 return blink::mojom::WebBluetoothResult::CONNECT_NO_MEMORY;
270 case device::BluetoothDevice::ERROR_JNI_ENVIRONMENT:
Daniel Chengafaabacf2024-09-20 01:23:00271 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kJniEnvironment);
Gordon Seto08749dd2024-06-05 15:46:55272 return blink::mojom::WebBluetoothResult::CONNECT_JNI_ENVIRONMENT;
273 case device::BluetoothDevice::ERROR_JNI_THREAD_ATTACH:
Daniel Chengafaabacf2024-09-20 01:23:00274 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kJniThreadAttach);
Gordon Seto08749dd2024-06-05 15:46:55275 return blink::mojom::WebBluetoothResult::CONNECT_JNI_THREAD_ATTACH;
276 case device::BluetoothDevice::ERROR_WAKELOCK:
Daniel Chengafaabacf2024-09-20 01:23:00277 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kWakelock);
Gordon Seto08749dd2024-06-05 15:46:55278 return blink::mojom::WebBluetoothResult::CONNECT_WAKELOCK;
Sarvesh Kalwit978e0aa42024-08-28 19:38:20279 case device::BluetoothDevice::ERROR_UNEXPECTED_STATE:
Daniel Chengafaabacf2024-09-20 01:23:00280 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kUnexpectedState);
Sarvesh Kalwit978e0aa42024-08-28 19:38:20281 return blink::mojom::WebBluetoothResult::CONNECT_UNEXPECTED_STATE;
282 case device::BluetoothDevice::ERROR_SOCKET:
Daniel Chengafaabacf2024-09-20 01:23:00283 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kSocketError);
Sarvesh Kalwit978e0aa42024-08-28 19:38:20284 return blink::mojom::WebBluetoothResult::CONNECT_SOCKET_ERROR;
Chris Mumford02de39b2021-05-06 15:28:59285 case BluetoothDevice::NUM_CONNECT_ERROR_CODES:
Peter Boströmfc7ddc182024-10-31 19:37:21286 NOTREACHED();
Chris Mumford02de39b2021-05-06 15:28:59287 }
Peter Boströmfc7ddc182024-10-31 19:37:21288 NOTREACHED();
Chris Mumford02de39b2021-05-06 15:28:59289}
290
Gabriel Britobf12a4e2022-02-03 21:46:33291// static
292void WebBluetoothServiceImpl::IgnoreVisibilityRequirementsForTesting() {
293 ShouldIgnoreVisibilityRequirementsForTesting() = true;
294}
295
Md. Hasanur Rashid28c84952020-01-05 11:36:36296bool HasValidFilter(
Arthur Sonzognic686e8f2024-01-11 08:36:37297 const std::optional<std::vector<blink::mojom::WebBluetoothLeScanFilterPtr>>&
298 filters) {
Doug Turner9b931de2019-01-08 18:22:37299 if (!filters) {
Md. Hasanur Rashid28c84952020-01-05 11:36:36300 return false;
Doug Turner9b931de2019-01-08 18:22:37301 }
302
Peter Kasting1557e5f2025-01-28 01:14:08303 return !filters->empty() && std::ranges::all_of(*filters, IsValidFilter);
Doug Turner9b931de2019-01-08 18:22:37304}
305
juncai00441ebd2017-01-09 19:14:51306// Struct that holds the result of a cache query.
307struct CacheQueryResult {
Daniel Chengafaabacf2024-09-20 01:23:00308 CacheQueryResult() : outcome(CacheQueryOutcome::kSuccess) {}
juncai00441ebd2017-01-09 19:14:51309
310 explicit CacheQueryResult(CacheQueryOutcome outcome) : outcome(outcome) {}
311
312 ~CacheQueryResult() {}
313
314 blink::mojom::WebBluetoothResult GetWebResult() const {
315 switch (outcome) {
Daniel Chengafaabacf2024-09-20 01:23:00316 case CacheQueryOutcome::kSuccess:
317 case CacheQueryOutcome::kBadRenderer:
Peter Boströmfc7ddc182024-10-31 19:37:21318 NOTREACHED();
Daniel Chengafaabacf2024-09-20 01:23:00319 case CacheQueryOutcome::kNoDevice:
juncai00441ebd2017-01-09 19:14:51320 return blink::mojom::WebBluetoothResult::DEVICE_NO_LONGER_IN_RANGE;
Daniel Chengafaabacf2024-09-20 01:23:00321 case CacheQueryOutcome::kNoService:
juncai00441ebd2017-01-09 19:14:51322 return blink::mojom::WebBluetoothResult::SERVICE_NO_LONGER_EXISTS;
Daniel Chengafaabacf2024-09-20 01:23:00323 case CacheQueryOutcome::kNoCharacteristic:
juncai00441ebd2017-01-09 19:14:51324 return blink::mojom::WebBluetoothResult::
325 CHARACTERISTIC_NO_LONGER_EXISTS;
Daniel Chengafaabacf2024-09-20 01:23:00326 case CacheQueryOutcome::kNoDescriptor:
dougta2fe055212017-01-27 05:35:30327 return blink::mojom::WebBluetoothResult::DESCRIPTOR_NO_LONGER_EXISTS;
juncai00441ebd2017-01-09 19:14:51328 }
Peter Boströmfc7ddc182024-10-31 19:37:21329 NOTREACHED();
juncai00441ebd2017-01-09 19:14:51330 }
331
Keishi Hattoric1b00232022-11-22 09:04:26332 raw_ptr<BluetoothDevice> device = nullptr;
333 raw_ptr<BluetoothRemoteGattService> service = nullptr;
334 raw_ptr<BluetoothRemoteGattCharacteristic> characteristic = nullptr;
335 raw_ptr<BluetoothRemoteGattDescriptor> descriptor = nullptr;
juncai00441ebd2017-01-09 19:14:51336 CacheQueryOutcome outcome;
337};
338
juncai5fbf7e62017-03-23 21:21:56339struct GATTNotifySessionAndCharacteristicClient {
340 GATTNotifySessionAndCharacteristicClient(
Henrique Ferreirodb47b3e2019-09-11 14:29:39341 mojo::AssociatedRemote<blink::mojom::WebBluetoothCharacteristicClient>
342 client)
Chris Mumforde6fa96a42021-07-20 00:15:14343 : characteristic_client(std::move(client)) {}
juncai5fbf7e62017-03-23 21:21:56344
Chris Mumford2f49c612021-04-21 17:54:20345 std::unique_ptr<BluetoothGattNotifySession> gatt_notify_session;
Henrique Ferreirodb47b3e2019-09-11 14:29:39346 mojo::AssociatedRemote<blink::mojom::WebBluetoothCharacteristicClient>
juncai5fbf7e62017-03-23 21:21:56347 characteristic_client;
348};
349
Claudio DeSouza3b4ad052021-11-23 02:32:43350// static
Daniel Chengd6978b062023-11-16 00:11:38351void WebBluetoothServiceImpl::BindIfAllowed(
Claudio DeSouza3b4ad052021-11-23 02:32:43352 RenderFrameHost* render_frame_host,
353 mojo::PendingReceiver<blink::mojom::WebBluetoothService> receiver) {
354 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
danakjc70aec1f2022-07-07 15:48:19355 CHECK(render_frame_host);
Claudio DeSouzade4a42d2021-12-03 21:42:53356
357 if (render_frame_host->IsNestedWithinFencedFrame()) {
358 // The renderer is supposed to disallow the use of web bluetooth when inside
359 // a fenced frame. Anything getting past the renderer checks must be marked
360 // as a bad request.
361 mojo::ReportBadMessage(kFencedFrameError);
Daniel Chengd6978b062023-11-16 00:11:38362 return;
Claudio DeSouzade4a42d2021-12-03 21:42:53363 }
364
Sina Firoozabadi36a29bc2023-03-08 02:29:36365 if (render_frame_host->GetOutermostMainFrame()
366 ->GetLastCommittedOrigin()
367 .opaque()) {
368 mojo::ReportBadMessage(
369 "Web Bluetooth is not allowed from an opaque origin.");
Daniel Chengd6978b062023-11-16 00:11:38370 return;
Sina Firoozabadi36a29bc2023-03-08 02:29:36371 }
372
Daniel Chengd6978b062023-11-16 00:11:38373 auto* impl = GetOrCreateForCurrentDocument(render_frame_host);
374 if (!impl->Bind(std::move(receiver))) {
375 // The renderer should only ever try to bind one instance of this service
376 // per document.
377 mojo::ReportBadMessage("Web Bluetooth already bound for current document.");
378 }
Claudio DeSouza3b4ad052021-11-23 02:32:43379}
380
Daniel Chengd6978b062023-11-16 00:11:38381WebBluetoothServiceImpl* WebBluetoothServiceImpl::CreateForTesting(
382 RenderFrameHost* render_frame_host,
383 mojo::PendingReceiver<blink::mojom::WebBluetoothService> receiver) {
384 WebBluetoothServiceImpl::BindIfAllowed(render_frame_host,
385 std::move(receiver));
386 return WebBluetoothServiceImpl::GetForCurrentDocument(render_frame_host);
387}
388
389DOCUMENT_USER_DATA_KEY_IMPL(WebBluetoothServiceImpl);
390
ortunoad6b0fea2016-03-31 18:49:11391WebBluetoothServiceImpl::WebBluetoothServiceImpl(
Daniel Chengd6978b062023-11-16 00:11:38392 RenderFrameHost* render_frame_host)
393 : DocumentUserData(render_frame_host),
394 WebContentsObserver(WebContents::FromRenderFrameHost(render_frame_host)),
395 receiver_(this),
396 connected_devices_(new FrameConnectedBluetoothDevices(*render_frame_host))
Chris Mumford4d1cf30782021-10-05 23:02:47397#if PAIR_BLUETOOTH_ON_DEMAND()
Claudio DeSouza3b4ad052021-11-23 02:32:43398 ,
399 pairing_manager_(std::make_unique<WebBluetoothPairingManagerImpl>(this))
Chris Mumford4d1cf30782021-10-05 23:02:47400#endif
Claudio DeSouza3b4ad052021-11-23 02:32:43401{
ortunoad6b0fea2016-03-31 18:49:11402 DCHECK_CURRENTLY_ON(BrowserThread::UI);
ortunobc3bce12016-04-15 21:22:55403 CHECK(web_contents());
Andy Paicu1d7fc172021-01-20 18:29:45404
Matt Reynolds8cca57d52025-02-20 18:20:59405 if (!base::FeatureList::IsEnabled(
406 features::kWebBluetoothAllowGetAvailabilityWithBfcache)) {
407 PreventBackForwardCache();
408 }
Daniel Chengd6978b062023-11-16 00:11:38409
Andy Paicu1d7fc172021-01-20 18:29:45410 if (base::FeatureList::IsEnabled(
411 features::kWebBluetoothNewPermissionsBackend)) {
412 BluetoothDelegate* delegate =
413 GetContentClient()->browser()->GetBluetoothDelegate();
414 if (delegate) {
415 observer_.Observe(delegate);
416 }
417 }
ortunoad6b0fea2016-03-31 18:49:11418}
419
Daniel Chengd6978b062023-11-16 00:11:38420bool WebBluetoothServiceImpl::Bind(
421 mojo::PendingReceiver<blink::mojom::WebBluetoothService> receiver) {
422 if (receiver_.is_bound()) {
423 return false;
424 }
425 receiver_.Bind(std::move(receiver));
426 return true;
427}
428
ortunobc3bce12016-04-15 21:22:55429WebBluetoothServiceImpl::~WebBluetoothServiceImpl() {
430 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Reilly Grant12e53d92022-01-13 00:58:37431
Reilly Grant12e53d92022-01-13 00:58:37432#if PAIR_BLUETOOTH_ON_DEMAND()
433 // Destroy the pairing manager before releasing the adapter to give it an
434 // opportunity to cancel pairing operations that are in progress.
435 pairing_manager_.reset();
436#endif
437
Claudio DeSouza3b4ad052021-11-23 02:32:43438 BluetoothAdapterFactoryWrapper::Get().ReleaseAdapter(this);
Jose Dapena Paz0b14aaaf2022-03-17 08:50:39439
440 // Force destructor of device_scanning_prompt_controller_ happening before
441 // members destruction stage to prevent use-after-free accessing
442 // scanning_clients_.empty().
443 device_scanning_prompt_controller_.reset();
ortunobc3bce12016-04-15 21:22:55444}
445
Ovidio Henriquez62154472019-08-21 14:45:11446blink::mojom::WebBluetoothResult
447WebBluetoothServiceImpl::GetBluetoothAllowed() {
danakjc70aec1f2022-07-07 15:48:19448 // The use of render_frame_host().GetMainFrame() below is safe as fenced
Claudio DeSouzade4a42d2021-12-03 21:42:53449 // frames are disallowed.
danakjc70aec1f2022-07-07 15:48:19450 DCHECK(!render_frame_host().IsNestedWithinFencedFrame());
Claudio DeSouzade4a42d2021-12-03 21:42:53451
Zelin Liu44c16582024-09-12 09:37:31452 BluetoothDelegate* delegate =
453 GetContentClient()->browser()->GetBluetoothDelegate();
454 if (!delegate || !delegate->MayUseBluetooth(&render_frame_host())) {
455 return blink::mojom::WebBluetoothResult::WEB_BLUETOOTH_NOT_SUPPORTED;
456 }
457
Gabriel Britoaa269ba2022-05-17 00:42:09458 // Check if Web Bluetooth is allowed by Permissions Policy.
danakjc70aec1f2022-07-07 15:48:19459 if (!render_frame_host().IsFeatureEnabled(
Sandor «Alex» Majore9545a72025-01-31 20:40:46460 network::mojom::PermissionsPolicyFeature::kBluetooth)) {
Gabriel Britoaa269ba2022-05-17 00:42:09461 return blink::mojom::WebBluetoothResult::PERMISSIONS_POLICY_VIOLATION;
462 }
463
Claudio DeSouza3b4ad052021-11-23 02:32:43464 const url::Origin& requesting_origin = origin();
Ovidio Henriquez62154472019-08-21 14:45:11465 const url::Origin& embedding_origin =
danakjc70aec1f2022-07-07 15:48:19466 render_frame_host().GetMainFrame()->GetLastCommittedOrigin();
Ovidio Henriquez62154472019-08-21 14:45:11467
Ovidio Henriquez62154472019-08-21 14:45:11468 // Some embedders that don't support Web Bluetooth indicate this by not
469 // returning a chooser.
Alison Gale923a33e2024-04-22 23:34:28470 // TODO(crbug.com/41476036): Perform this check once there is a way to
Ovidio Henriquez62154472019-08-21 14:45:11471 // check if a platform is capable of producing a chooser and return a
472 // |blink::mojom::WebBluetoothResult::WEB_BLUETOOTH_NOT_SUPPORTED| error.
473 switch (GetContentClient()->browser()->AllowWebBluetooth(
474 web_contents()->GetBrowserContext(), requesting_origin,
475 embedding_origin)) {
476 case ContentBrowserClient::AllowWebBluetoothResult::BLOCK_POLICY:
477 return blink::mojom::WebBluetoothResult::
478 CHOOSER_NOT_SHOWN_API_LOCALLY_DISABLED;
479 case ContentBrowserClient::AllowWebBluetoothResult::BLOCK_GLOBALLY_DISABLED:
480 return blink::mojom::WebBluetoothResult::
481 CHOOSER_NOT_SHOWN_API_GLOBALLY_DISABLED;
482 case ContentBrowserClient::AllowWebBluetoothResult::ALLOW:
483 return blink::mojom::WebBluetoothResult::SUCCESS;
484 }
485}
486
juncai87d09292016-09-15 04:02:53487bool WebBluetoothServiceImpl::IsDevicePaired(
488 const std::string& device_address) {
Chris Mumford4d1cf30782021-10-05 23:02:47489 return GetWebBluetoothDeviceId(device_address).IsValid();
juncai87d09292016-09-15 04:02:53490}
491
Jun Cai149002e2019-05-09 23:13:07492void WebBluetoothServiceImpl::OnBluetoothScanningPromptEvent(
493 BluetoothScanningPrompt::Event event,
494 BluetoothDeviceScanningPromptController* prompt_controller) {
danakjc70aec1f2022-07-07 15:48:19495 // The use of render_frame_host().GetMainFrame() below is safe as fenced
Claudio DeSouzade4a42d2021-12-03 21:42:53496 // frames are disallowed.
danakjc70aec1f2022-07-07 15:48:19497 DCHECK(!render_frame_host().IsNestedWithinFencedFrame());
Claudio DeSouzade4a42d2021-12-03 21:42:53498
Ovidio Henriquez405c0142019-11-14 23:04:47499 // It is possible for |scanning_clients_| to be empty if a Mojo connection
500 // error has occurred before this method was called.
501 if (scanning_clients_.empty())
502 return;
Jun Cai149002e2019-05-09 23:13:07503
Ovidio Henriquez405c0142019-11-14 23:04:47504 auto& client = scanning_clients_.back();
Jun Cai149002e2019-05-09 23:13:07505
Ovidio Henriquez405c0142019-11-14 23:04:47506 DCHECK(client->prompt_controller() == prompt_controller);
Jun Cai149002e2019-05-09 23:13:07507
508 auto result = blink::mojom::WebBluetoothResult::SUCCESS;
Jun Cai732a05e32019-05-29 19:34:19509 if (event == BluetoothScanningPrompt::Event::kAllow) {
Jun Cai149002e2019-05-09 23:13:07510 result = blink::mojom::WebBluetoothResult::SUCCESS;
Ovidio Henriquez405c0142019-11-14 23:04:47511 StoreAllowedScanOptions(client->scan_options());
Jun Cai732a05e32019-05-29 19:34:19512 } else if (event == BluetoothScanningPrompt::Event::kBlock) {
Jun Cai149002e2019-05-09 23:13:07513 result = blink::mojom::WebBluetoothResult::SCANNING_BLOCKED;
Claudio DeSouza3b4ad052021-11-23 02:32:43514 const url::Origin requesting_origin = origin();
Jun Cai732a05e32019-05-29 19:34:19515 const url::Origin embedding_origin =
danakjc70aec1f2022-07-07 15:48:19516 render_frame_host().GetMainFrame()->GetLastCommittedOrigin();
Jun Cai732a05e32019-05-29 19:34:19517 GetContentClient()->browser()->BlockBluetoothScanning(
518 web_contents()->GetBrowserContext(), requesting_origin,
519 embedding_origin);
520 } else if (event == BluetoothScanningPrompt::Event::kCanceled) {
Jun Cai149002e2019-05-09 23:13:07521 result = blink::mojom::WebBluetoothResult::PROMPT_CANCELED;
Jun Cai732a05e32019-05-29 19:34:19522 } else {
Peter Boströmfc7ddc182024-10-31 19:37:21523 NOTREACHED();
Jun Cai732a05e32019-05-29 19:34:19524 }
Jun Cai149002e2019-05-09 23:13:07525
Alvin Jif4c234d412023-08-23 16:58:54526 client->RunCallback(std::move(result));
Ovidio Henriquez405c0142019-11-14 23:04:47527 client->set_prompt_controller(nullptr);
Jun Caia1f8bf25d92019-06-07 19:06:53528 if (event == BluetoothScanningPrompt::Event::kAllow) {
Ovidio Henriquez405c0142019-11-14 23:04:47529 client->set_allow_send_event(true);
Jun Caia1f8bf25d92019-06-07 19:06:53530 } else if (event == BluetoothScanningPrompt::Event::kBlock) {
531 // Here because user explicitly blocks the permission to do Bluetooth
532 // scanning in one request, it can be interpreted as user wants the current
533 // and all previous scanning to be blocked, so remove all existing scanning
534 // clients.
535 scanning_clients_.clear();
536 allowed_scan_filters_.clear();
537 accept_all_advertisements_ = false;
538 } else if (event == BluetoothScanningPrompt::Event::kCanceled) {
Ovidio Henriquez405c0142019-11-14 23:04:47539 scanning_clients_.pop_back();
Jun Caia1f8bf25d92019-06-07 19:06:53540 } else {
Peter Boströmfc7ddc182024-10-31 19:37:21541 NOTREACHED();
Jun Caia1f8bf25d92019-06-07 19:06:53542 }
Jun Cai149002e2019-05-09 23:13:07543}
544
Andy Paicu13f3f462021-03-09 12:14:56545void WebBluetoothServiceImpl::OnPermissionRevoked(const url::Origin& origin) {
danakjc70aec1f2022-07-07 15:48:19546 // The use of render_frame_host().GetMainFrame() below is safe as fenced
Claudio DeSouzade4a42d2021-12-03 21:42:53547 // frames are disallowed.
danakjc70aec1f2022-07-07 15:48:19548 DCHECK(!render_frame_host().IsNestedWithinFencedFrame());
Claudio DeSouzade4a42d2021-12-03 21:42:53549
danakjc70aec1f2022-07-07 15:48:19550 if (render_frame_host().GetMainFrame()->GetLastCommittedOrigin() != origin) {
Andy Paicu1d7fc172021-01-20 18:29:45551 return;
552 }
553
554 BluetoothDelegate* delegate =
555 GetContentClient()->browser()->GetBluetoothDelegate();
556 if (!delegate)
557 return;
558
559 std::set<blink::WebBluetoothDeviceId> permitted_ids;
danakjc70aec1f2022-07-07 15:48:19560 for (const auto& device : delegate->GetPermittedDevices(&render_frame_host()))
Andy Paicu1d7fc172021-01-20 18:29:45561 permitted_ids.insert(device->id);
562
563 connected_devices_->CloseConnectionsToDevicesNotInList(permitted_ids);
564
Andrew Rayskiyf65990362024-02-27 18:43:24565 std::erase_if(watch_advertisements_clients_,
Andy Paicu1d7fc172021-01-20 18:29:45566 [&](const std::unique_ptr<WatchAdvertisementsClient>& client) {
567 return !base::Contains(permitted_ids, client->device_id());
568 });
569
570 MaybeStopDiscovery();
571}
572
573content::RenderFrameHost* WebBluetoothServiceImpl::GetRenderFrameHost() {
danakjc70aec1f2022-07-07 15:48:19574 return &render_frame_host();
ortunoe2d1eb72016-04-20 00:32:58575}
576
Jun Caie27db6b2019-06-05 03:52:15577void WebBluetoothServiceImpl::OnVisibilityChanged(Visibility visibility) {
Gabriel Britobf12a4e2022-02-03 21:46:33578 if (ShouldIgnoreVisibilityRequirementsForTesting()) {
579 return;
580 }
581
Jun Caie27db6b2019-06-05 03:52:15582 if (visibility == content::Visibility::HIDDEN ||
583 visibility == content::Visibility::OCCLUDED) {
Ovidio Henriquez887bb132020-06-04 21:45:55584 ClearAdvertisementClients();
Jun Caie27db6b2019-06-05 03:52:15585 }
586}
587
Ovidio Henriquez5bfc18162020-03-24 16:17:27588void WebBluetoothServiceImpl::OnWebContentsLostFocus(
589 RenderWidgetHost* render_widget_host) {
Gabriel Britobf12a4e2022-02-03 21:46:33590 if (ShouldIgnoreVisibilityRequirementsForTesting()) {
591 return;
592 }
593
Ovidio Henriquez887bb132020-06-04 21:45:55594 ClearAdvertisementClients();
Ovidio Henriquez5bfc18162020-03-24 16:17:27595}
596
Chris Mumford2f49c612021-04-21 17:54:20597void WebBluetoothServiceImpl::AdapterPoweredChanged(BluetoothAdapter* adapter,
598 bool powered) {
ortunob6374dd82016-05-27 03:04:07599 DCHECK_CURRENTLY_ON(BrowserThread::UI);
600 if (device_chooser_controller_.get()) {
601 device_chooser_controller_->AdapterPoweredChanged(powered);
602 }
603}
604
Chris Mumford2f49c612021-04-21 17:54:20605void WebBluetoothServiceImpl::DeviceAdded(BluetoothAdapter* adapter,
606 BluetoothDevice* device) {
ortunob6374dd82016-05-27 03:04:07607 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Doug Turner23a658b92018-12-20 01:57:54608
ortunob6374dd82016-05-27 03:04:07609 if (device_chooser_controller_.get()) {
ortunob6374dd82016-05-27 03:04:07610 device_chooser_controller_->AddFilteredDevice(*device);
ortunobc3bce12016-04-15 21:22:55611 }
612}
613
Chris Mumford2f49c612021-04-21 17:54:20614void WebBluetoothServiceImpl::DeviceChanged(BluetoothAdapter* adapter,
615 BluetoothDevice* device) {
ortunob6c45d4f2016-05-07 04:19:42616 DCHECK_CURRENTLY_ON(BrowserThread::UI);
ortuno79923512016-08-10 06:41:32617
618 if (device_chooser_controller_.get()) {
619 device_chooser_controller_->AddFilteredDevice(*device);
620 }
621
ortunobb0a85c2016-05-31 18:43:58622 if (!device->IsGattConnected()) {
Arthur Sonzognic686e8f2024-01-11 08:36:37623 std::optional<blink::WebBluetoothDeviceId> device_id =
ortunob6c45d4f2016-05-07 04:19:42624 connected_devices_->CloseConnectionToDeviceWithAddress(
625 device->GetAddress());
Giovanni Ortuño Urquidi8688a0d2017-05-20 06:12:32626
627 // Since the device disconnected we need to send an error for pending
628 // primary services requests.
629 RunPendingPrimaryServicesRequests(device);
ortunob6c45d4f2016-05-07 04:19:42630 }
631}
632
Doug Turner23a658b92018-12-20 01:57:54633void WebBluetoothServiceImpl::DeviceAdvertisementReceived(
Doug Turner10051df2019-01-17 02:21:55634 const std::string& device_address,
Arthur Sonzognic686e8f2024-01-11 08:36:37635 const std::optional<std::string>& device_name,
636 const std::optional<std::string>& advertisement_name,
637 std::optional<int8_t> rssi,
638 std::optional<int8_t> tx_power,
639 std::optional<uint16_t> appearance,
Chris Mumford2f49c612021-04-21 17:54:20640 const BluetoothDevice::UUIDList& advertised_uuids,
641 const BluetoothDevice::ServiceDataMap& service_data_map,
642 const BluetoothDevice::ManufacturerDataMap& manufacturer_data_map) {
Doug Turner23a658b92018-12-20 01:57:54643 DCHECK_CURRENTLY_ON(BrowserThread::UI);
644
Ovidio Henriquez887bb132020-06-04 21:45:55645 if (!HasActiveDiscoverySession())
Doug Turner23a658b92018-12-20 01:57:54646 return;
647
Ovidio Henriquez887bb132020-06-04 21:45:55648 // Construct the WebBluetoothAdvertisingEvent.
649 auto device = blink::mojom::WebBluetoothDevice::New();
650 if (base::FeatureList::IsEnabled(
651 features::kWebBluetoothNewPermissionsBackend)) {
652 BluetoothDelegate* delegate =
653 GetContentClient()->browser()->GetBluetoothDelegate();
654 if (!delegate)
655 return;
Claudio DeSouza3b4ad052021-11-23 02:32:43656 device->id =
danakjc70aec1f2022-07-07 15:48:19657 delegate->AddScannedDevice(&render_frame_host(), device_address);
Ovidio Henriquez887bb132020-06-04 21:45:55658 } else {
659 device->id = allowed_devices().AddDevice(device_address);
Doug Turner9b931de2019-01-08 18:22:37660 }
Ovidio Henriquez887bb132020-06-04 21:45:55661 device->name = device_name;
Doug Turner23a658b92018-12-20 01:57:54662
Ovidio Henriquez887bb132020-06-04 21:45:55663 auto result = blink::mojom::WebBluetoothAdvertisingEvent::New();
664 result->device = std::move(device);
665
666 result->name = advertisement_name;
667
668 // Note about the default value for these optional types. On the other side of
669 // this IPC, the receiver will be checking to see if |*_is_set| is true before
670 // using the value. Here we chose reasonable defaults in case the other side
671 // does something incorrect. We have to do this manual serialization because
672 // mojo does not support optional primitive types.
673 result->appearance_is_set = appearance.has_value();
674 result->appearance = appearance.value_or(/*not present=*/0xffc0);
675
676 result->rssi_is_set = rssi.has_value();
677 result->rssi = rssi.value_or(/*invalid value=*/128);
678
679 result->tx_power_is_set = tx_power.has_value();
680 result->tx_power = tx_power.value_or(/*invalid value=*/128);
681
Chris Mumford2f49c612021-04-21 17:54:20682 std::vector<BluetoothUUID> uuids;
Ovidio Henriquez887bb132020-06-04 21:45:55683 for (auto& uuid : advertised_uuids)
Chris Mumford2f49c612021-04-21 17:54:20684 uuids.push_back(BluetoothUUID(uuid.canonical_value()));
Ovidio Henriquez887bb132020-06-04 21:45:55685 result->uuids = std::move(uuids);
686
687 auto& manufacturer_data = result->manufacturer_data;
Alvin Ji0d5a45bd62024-09-04 19:55:05688 for (const auto& entry : manufacturer_data_map) {
689 manufacturer_data.emplace(
690 blink::mojom::WebBluetoothCompany::New(entry.first), entry.second);
691 }
Ovidio Henriquez887bb132020-06-04 21:45:55692
Ovidio Henriquezbbc7853c2020-09-17 22:36:56693 auto& service_data = result->service_data;
694 service_data.insert(service_data_map.begin(), service_data_map.end());
Ovidio Henriquez887bb132020-06-04 21:45:55695
Alison Gale770f3fc2024-04-27 00:39:58696 // TODO(crbug.com/40132791): These two classes can potentially be
Ovidio Henriquez887bb132020-06-04 21:45:55697 // combined into the same container.
698 for (const auto& scanning_client : scanning_clients_)
699 scanning_client->SendEvent(*result);
700
701 for (const auto& watch_advertisements_client : watch_advertisements_clients_)
702 watch_advertisements_client->SendEvent(*result);
703
704 MaybeStopDiscovery();
Doug Turner23a658b92018-12-20 01:57:54705}
706
Chris Mumford2f49c612021-04-21 17:54:20707void WebBluetoothServiceImpl::GattServicesDiscovered(BluetoothAdapter* adapter,
708 BluetoothDevice* device) {
Giovanni Ortuño Urquidi8688a0d2017-05-20 06:12:32709 DCHECK_CURRENTLY_ON(BrowserThread::UI);
710
711 DVLOG(1) << "Services discovered for device: " << device->GetAddress();
712
ortuno79923512016-08-10 06:41:32713 if (device_chooser_controller_.get()) {
714 device_chooser_controller_->AddFilteredDevice(*device);
715 }
716
Giovanni Ortuño Urquidi8688a0d2017-05-20 06:12:32717 RunPendingPrimaryServicesRequests(device);
ortuno67acd832016-04-30 00:13:22718}
719
ortunobc3bce12016-04-15 21:22:55720void WebBluetoothServiceImpl::GattCharacteristicValueChanged(
Chris Mumford2f49c612021-04-21 17:54:20721 BluetoothAdapter* adapter,
722 BluetoothRemoteGattCharacteristic* characteristic,
ortunobc3bce12016-04-15 21:22:55723 const std::vector<uint8_t>& value) {
ortunob0fb6a182016-04-27 01:45:26724 // Don't notify of characteristics that we haven't returned.
Jan Wilken Dörrie77c581a2019-06-07 16:25:06725 if (!base::Contains(characteristic_id_to_service_id_,
726 characteristic->GetIdentifier())) {
ortunobc3bce12016-04-15 21:22:55727 return;
728 }
729
730 // On Chrome OS and Linux, GattCharacteristicValueChanged is called before the
731 // success callback for ReadRemoteCharacteristic is called, which could result
732 // in an event being fired before the readValue promise is resolved.
Sean Maher5b9af51f2022-11-21 15:32:47733 if (!base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
ortunobc3bce12016-04-15 21:22:55734 FROM_HERE,
tzik4fea24af2017-08-23 11:41:47735 base::BindOnce(
736 &WebBluetoothServiceImpl::NotifyCharacteristicValueChanged,
737 weak_ptr_factory_.GetWeakPtr(), characteristic->GetIdentifier(),
738 value))) {
ortunobc3bce12016-04-15 21:22:55739 LOG(WARNING) << "No TaskRunner.";
740 }
741}
742
743void WebBluetoothServiceImpl::NotifyCharacteristicValueChanged(
744 const std::string& characteristic_instance_id,
juncai1ef7dd42016-11-29 04:28:21745 const std::vector<uint8_t>& value) {
juncai5fbf7e62017-03-23 21:21:56746 auto iter =
747 characteristic_id_to_notify_session_.find(characteristic_instance_id);
748 if (iter != characteristic_id_to_notify_session_.end()) {
749 iter->second->characteristic_client->RemoteCharacteristicValueChanged(
750 value);
ortunobc3bce12016-04-15 21:22:55751 }
752}
753
Ovidio Henriquez62154472019-08-21 14:45:11754void WebBluetoothServiceImpl::GetAvailability(
755 GetAvailabilityCallback callback) {
756 if (GetBluetoothAllowed() != blink::mojom::WebBluetoothResult::SUCCESS) {
757 std::move(callback).Run(/*result=*/false);
758 return;
759 }
760
761 if (!BluetoothAdapterFactoryWrapper::Get().IsLowEnergySupported()) {
762 std::move(callback).Run(/*result=*/false);
763 return;
764 }
765
766 auto get_availability_impl = base::BindOnce(
Reilly Granta255d1be2019-11-14 07:01:05767 [](GetAvailabilityCallback callback,
Chris Mumford2f49c612021-04-21 17:54:20768 scoped_refptr<BluetoothAdapter> adapter) {
Ovidio Henriquez62154472019-08-21 14:45:11769 std::move(callback).Run(adapter->IsPresent());
770 },
771 std::move(callback));
772
773 auto* adapter = GetAdapter();
774 if (adapter) {
775 std::move(get_availability_impl).Run(adapter);
776 return;
777 }
778
779 BluetoothAdapterFactoryWrapper::Get().AcquireAdapter(
780 this, std::move(get_availability_impl));
781}
782
ortunob6374dd82016-05-27 03:04:07783void WebBluetoothServiceImpl::RequestDevice(
784 blink::mojom::WebBluetoothRequestDeviceOptionsPtr options,
tzikcf7bcd652017-06-15 04:19:30785 RequestDeviceCallback callback) {
Matt Reynolds8cca57d52025-02-20 18:20:59786 if (base::FeatureList::IsEnabled(
787 features::kWebBluetoothAllowGetAvailabilityWithBfcache)) {
788 PreventBackForwardCache();
789 }
790
ortunob6374dd82016-05-27 03:04:07791 RecordRequestDeviceOptions(options);
792
793 if (!GetAdapter()) {
ortuno57ed4542017-04-20 01:16:28794 if (BluetoothAdapterFactoryWrapper::Get().IsLowEnergySupported()) {
ortuno189e9582016-07-08 20:06:45795 BluetoothAdapterFactoryWrapper::Get().AcquireAdapter(
Reilly Grantd833b3072019-01-08 01:39:02796 this, base::BindOnce(&WebBluetoothServiceImpl::RequestDeviceImpl,
797 weak_ptr_factory_.GetWeakPtr(),
798 std::move(options), std::move(callback)));
ortunob6374dd82016-05-27 03:04:07799 return;
800 }
tzikcf7bcd652017-06-15 04:19:30801 std::move(callback).Run(
dougt4c6a5772016-10-13 01:03:27802 blink::mojom::WebBluetoothResult::BLUETOOTH_LOW_ENERGY_NOT_AVAILABLE,
beaufort.francois308464872016-10-05 14:35:35803 nullptr /* device */);
ortunob6374dd82016-05-27 03:04:07804 return;
805 }
tzikcf7bcd652017-06-15 04:19:30806 RequestDeviceImpl(std::move(options), std::move(callback), GetAdapter());
ortunob6374dd82016-05-27 03:04:07807}
808
Ovidio Henriquez278801c22020-03-10 21:52:01809void WebBluetoothServiceImpl::GetDevices(GetDevicesCallback callback) {
Matt Reynolds8cca57d52025-02-20 18:20:59810 if (base::FeatureList::IsEnabled(
811 features::kWebBluetoothAllowGetAvailabilityWithBfcache)) {
812 PreventBackForwardCache();
813 }
814
Ovidio Henriquez278801c22020-03-10 21:52:01815 if (GetBluetoothAllowed() != blink::mojom::WebBluetoothResult::SUCCESS ||
816 !BluetoothAdapterFactoryWrapper::Get().IsLowEnergySupported()) {
817 std::move(callback).Run({});
818 return;
819 }
820
821 auto* adapter = GetAdapter();
822 if (adapter) {
823 GetDevicesImpl(std::move(callback), adapter);
824 return;
825 }
826
827 BluetoothAdapterFactoryWrapper::Get().AcquireAdapter(
828 this,
829 base::BindOnce(&WebBluetoothServiceImpl::GetDevicesImpl,
830 weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
831}
832
François Beaufortf100253a82022-03-10 11:26:31833void WebBluetoothServiceImpl::ForgetDevice(
834 const blink::WebBluetoothDeviceId& device_id,
835 ForgetDeviceCallback callback) {
Matt Reynolds8cca57d52025-02-20 18:20:59836 CHECK(back_forward_cache_feature_handle_.IsValid());
François Beaufortf100253a82022-03-10 11:26:31837 if (!base::FeatureList::IsEnabled(
838 features::kWebBluetoothNewPermissionsBackend)) {
839 auto device_address = allowed_devices().GetDeviceAddress(device_id);
Jack Hsieh4862e4d2023-01-09 19:45:58840 // allowed_devices().RemoveDevice() expects a valid |device_address|.
841 if (!device_address.empty()) {
842 allowed_devices().RemoveDevice(device_address);
843 }
François Beaufortf100253a82022-03-10 11:26:31844 std::move(callback).Run();
845 return;
846 }
847
848 BluetoothDelegate* delegate =
849 GetContentClient()->browser()->GetBluetoothDelegate();
850 if (delegate &&
danakjc70aec1f2022-07-07 15:48:19851 delegate->HasDevicePermission(&render_frame_host(), device_id)) {
852 delegate->RevokeDevicePermissionWebInitiated(&render_frame_host(),
François Beaufortf100253a82022-03-10 11:26:31853 device_id);
854 }
855 std::move(callback).Run();
856}
857
ortunob6c45d4f2016-05-07 04:19:42858void WebBluetoothServiceImpl::RemoteServerConnect(
Ovidio Henriquez0e8ab7072019-05-31 21:38:07859 const blink::WebBluetoothDeviceId& device_id,
Henrique Ferreirodb47b3e2019-09-11 14:29:39860 mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothServerClient>
861 client,
tzikcf7bcd652017-06-15 04:19:30862 RemoteServerConnectCallback callback) {
ortunob6c45d4f2016-05-07 04:19:42863 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Matt Reynolds8cca57d52025-02-20 18:20:59864 CHECK(back_forward_cache_feature_handle_.IsValid());
ortunob6c45d4f2016-05-07 04:19:42865
Ovidio Henriquezc3adec272020-03-03 19:40:59866 bool is_connect_allowed = false;
867 if (base::FeatureList::IsEnabled(
868 features::kWebBluetoothNewPermissionsBackend)) {
869 BluetoothDelegate* delegate =
870 GetContentClient()->browser()->GetBluetoothDelegate();
871 if (delegate) {
872 is_connect_allowed =
danakjc70aec1f2022-07-07 15:48:19873 delegate->HasDevicePermission(&render_frame_host(), device_id);
Ovidio Henriquezc3adec272020-03-03 19:40:59874 }
Ovidio Henriquez3d729f62020-02-07 00:43:29875 } else {
876 is_connect_allowed = allowed_devices().IsAllowedToGATTConnect(device_id);
877 }
878 if (!is_connect_allowed) {
Doug Turner10051df2019-01-17 02:21:55879 std::move(callback).Run(
880 blink::mojom::WebBluetoothResult::GATT_NOT_AUTHORIZED);
881 return;
882 }
883
ortunob6374dd82016-05-27 03:04:07884 const CacheQueryResult query_result = QueryCacheForDevice(device_id);
ortunob6c45d4f2016-05-07 04:19:42885
Daniel Chengafaabacf2024-09-20 01:23:00886 if (query_result.outcome != CacheQueryOutcome::kSuccess) {
ortunob6c45d4f2016-05-07 04:19:42887 RecordConnectGATTOutcome(query_result.outcome);
tzikcf7bcd652017-06-15 04:19:30888 std::move(callback).Run(query_result.GetWebResult());
ortunob6c45d4f2016-05-07 04:19:42889 return;
890 }
891
892 if (connected_devices_->IsConnectedToDeviceWithId(device_id)) {
dougt16af7e82017-01-13 23:20:08893 DVLOG(1) << "Already connected.";
tzikcf7bcd652017-06-15 04:19:30894 std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS);
ortunob6c45d4f2016-05-07 04:19:42895 return;
896 }
897
898 // It's possible for WebBluetoothServiceImpl to issue two successive
899 // connection requests for which it would get two successive responses
900 // and consequently try to insert two BluetoothGattConnections for the
901 // same device. WebBluetoothServiceImpl should reject or queue connection
902 // requests if there is a pending connection already, but the platform
903 // abstraction doesn't currently support checking for pending connections.
904 // TODO(ortuno): CHECK that this never happens once the platform
905 // abstraction allows to check for pending connections.
906 // http://crbug.com/583544
Henrique Ferreirodb47b3e2019-09-11 14:29:39907 mojo::AssociatedRemote<blink::mojom::WebBluetoothServerClient>
908 web_bluetooth_server_client(std::move(client));
tzikcf7bcd652017-06-15 04:19:30909
Chengwei Hsieh3de031c32025-08-01 23:32:02910 if (base::FeatureList::IsEnabled(
911 blink::features::kWebBluetoothCancelConnect)) {
912 pending_connection_device_ids_.insert(device_id);
913 }
Chris Mumfordf59d16b2021-06-23 00:32:11914 query_result.device->CreateGattConnection(base::BindOnce(
915 &WebBluetoothServiceImpl::OnCreateGATTConnection,
Reilly Grantf319be982022-11-17 03:33:42916 weak_ptr_factory_.GetWeakPtr(), device_id,
Chris Mumfordf59d16b2021-06-23 00:32:11917 std::move(web_bluetooth_server_client), std::move(callback)));
ortunob6c45d4f2016-05-07 04:19:42918}
919
920void WebBluetoothServiceImpl::RemoteServerDisconnect(
Ovidio Henriquez0e8ab7072019-05-31 21:38:07921 const blink::WebBluetoothDeviceId& device_id) {
ortunob6c45d4f2016-05-07 04:19:42922 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Matt Reynolds8cca57d52025-02-20 18:20:59923 CHECK(back_forward_cache_feature_handle_.IsValid());
ortunob6c45d4f2016-05-07 04:19:42924
Chengwei Hsieh3de031c32025-08-01 23:32:02925 if (base::FeatureList::IsEnabled(
926 blink::features::kWebBluetoothCancelConnect)) {
927 auto connecting_iter = pending_connection_device_ids_.find(device_id);
928 if (connecting_iter != pending_connection_device_ids_.end()) {
929 pending_connection_device_ids_.erase(connecting_iter);
930 const CacheQueryResult query_result = QueryCacheForDevice(device_id);
931 if (query_result.outcome != CacheQueryOutcome::kSuccess) {
932 return;
933 }
934 query_result.device->DisconnectGatt();
935 return;
936 }
937 }
938
ortunob6c45d4f2016-05-07 04:19:42939 if (connected_devices_->IsConnectedToDeviceWithId(device_id)) {
dougt16af7e82017-01-13 23:20:08940 DVLOG(1) << "Disconnecting device: " << device_id.str();
ortunob6c45d4f2016-05-07 04:19:42941 connected_devices_->CloseConnectionToDeviceWithId(device_id);
942 }
943}
944
beaufort.francois7952f002016-06-14 16:44:09945void WebBluetoothServiceImpl::RemoteServerGetPrimaryServices(
Ovidio Henriquez0e8ab7072019-05-31 21:38:07946 const blink::WebBluetoothDeviceId& device_id,
beaufort.francois7952f002016-06-14 16:44:09947 blink::mojom::WebBluetoothGATTQueryQuantity quantity,
Arthur Sonzognic686e8f2024-01-11 08:36:37948 const std::optional<BluetoothUUID>& services_uuid,
tzikcf7bcd652017-06-15 04:19:30949 RemoteServerGetPrimaryServicesCallback callback) {
ortuno67acd832016-04-30 00:13:22950 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Matt Reynolds8cca57d52025-02-20 18:20:59951 CHECK(back_forward_cache_feature_handle_.IsValid());
952
beaufort.francois7952f002016-06-14 16:44:09953 RecordGetPrimaryServicesServices(quantity, services_uuid);
ortunoe4914832016-06-10 20:15:07954
Ovidio Henriquez3d729f62020-02-07 00:43:29955 if (!IsAllowedToAccessAtLeastOneService(device_id)) {
tzikcf7bcd652017-06-15 04:19:30956 std::move(callback).Run(
beaufort.francois08303d62016-11-15 15:53:14957 blink::mojom::WebBluetoothResult::NOT_ALLOWED_TO_ACCESS_ANY_SERVICE,
Arthur Sonzognic686e8f2024-01-11 08:36:37958 /*service=*/std::nullopt);
beaufort.francois08303d62016-11-15 15:53:14959 return;
960 }
961
Ovidio Henriquez3d729f62020-02-07 00:43:29962 if (services_uuid &&
963 !IsAllowedToAccessService(device_id, services_uuid.value())) {
tzikcf7bcd652017-06-15 04:19:30964 std::move(callback).Run(
dougt4c6a5772016-10-13 01:03:27965 blink::mojom::WebBluetoothResult::NOT_ALLOWED_TO_ACCESS_SERVICE,
Arthur Sonzognic686e8f2024-01-11 08:36:37966 /*service=*/std::nullopt);
ortuno67acd832016-04-30 00:13:22967 return;
968 }
969
ortunob6374dd82016-05-27 03:04:07970 const CacheQueryResult query_result = QueryCacheForDevice(device_id);
ortuno67acd832016-04-30 00:13:22971
Daniel Chengafaabacf2024-09-20 01:23:00972 if (query_result.outcome == CacheQueryOutcome::kBadRenderer) {
ortuno67acd832016-04-30 00:13:22973 return;
974 }
975
Daniel Chengafaabacf2024-09-20 01:23:00976 if (query_result.outcome != CacheQueryOutcome::kSuccess) {
tzikcf7bcd652017-06-15 04:19:30977 std::move(callback).Run(query_result.GetWebResult(),
Arthur Sonzognic686e8f2024-01-11 08:36:37978 std::nullopt /* service */);
ortuno67acd832016-04-30 00:13:22979 return;
980 }
981
Jack Hsiehadebefb2022-02-15 18:57:53982 RemoteServerGetPrimaryServicesImpl(device_id, quantity, services_uuid,
983 std::move(callback), query_result.device);
ortuno67acd832016-04-30 00:13:22984}
985
ortunob0fb6a182016-04-27 01:45:26986void WebBluetoothServiceImpl::RemoteServiceGetCharacteristics(
juncai1ef7dd42016-11-29 04:28:21987 const std::string& service_instance_id,
ortunob0fb6a182016-04-27 01:45:26988 blink::mojom::WebBluetoothGATTQueryQuantity quantity,
Arthur Sonzognic686e8f2024-01-11 08:36:37989 const std::optional<BluetoothUUID>& characteristics_uuid,
tzikcf7bcd652017-06-15 04:19:30990 RemoteServiceGetCharacteristicsCallback callback) {
ortunob0fb6a182016-04-27 01:45:26991 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Matt Reynolds8cca57d52025-02-20 18:20:59992 CHECK(back_forward_cache_feature_handle_.IsValid());
ortunoe4914832016-06-10 20:15:07993
ortunob0fb6a182016-04-27 01:45:26994 RecordGetCharacteristicsCharacteristic(quantity, characteristics_uuid);
995
ortunoe4914832016-06-10 20:15:07996 if (characteristics_uuid &&
beaufort.francois01135bf2016-11-23 14:37:36997 BluetoothBlocklist::Get().IsExcluded(characteristics_uuid.value())) {
tzikcf7bcd652017-06-15 04:19:30998 std::move(callback).Run(
beaufort.francois01135bf2016-11-23 14:37:36999 blink::mojom::WebBluetoothResult::BLOCKLISTED_CHARACTERISTIC_UUID,
Arthur Sonzognic686e8f2024-01-11 08:36:371000 std::nullopt /* characteristics */);
ortunob0fb6a182016-04-27 01:45:261001 return;
1002 }
1003
1004 const CacheQueryResult query_result =
ortuno67acd832016-04-30 00:13:221005 QueryCacheForService(service_instance_id);
ortunob0fb6a182016-04-27 01:45:261006
Daniel Chengafaabacf2024-09-20 01:23:001007 if (query_result.outcome == CacheQueryOutcome::kBadRenderer) {
ortunob0fb6a182016-04-27 01:45:261008 return;
1009 }
1010
Daniel Chengafaabacf2024-09-20 01:23:001011 if (query_result.outcome != CacheQueryOutcome::kSuccess) {
tzikcf7bcd652017-06-15 04:19:301012 std::move(callback).Run(query_result.GetWebResult(),
Arthur Sonzognic686e8f2024-01-11 08:36:371013 std::nullopt /* characteristics */);
ortunob0fb6a182016-04-27 01:45:261014 return;
1015 }
1016
Chris Mumford2f49c612021-04-21 17:54:201017 std::vector<BluetoothRemoteGattCharacteristic*> characteristics =
juncaib594d332017-04-05 19:13:571018 characteristics_uuid ? query_result.service->GetCharacteristicsByUUID(
1019 characteristics_uuid.value())
1020 : query_result.service->GetCharacteristics();
ortunob0fb6a182016-04-27 01:45:261021
juncai1ef7dd42016-11-29 04:28:211022 std::vector<blink::mojom::WebBluetoothRemoteGATTCharacteristicPtr>
ortunob0fb6a182016-04-27 01:45:261023 response_characteristics;
Chris Mumford2f49c612021-04-21 17:54:201024 for (BluetoothRemoteGattCharacteristic* characteristic : characteristics) {
beaufort.francois01135bf2016-11-23 14:37:361025 if (BluetoothBlocklist::Get().IsExcluded(characteristic->GetUUID())) {
ortunob0fb6a182016-04-27 01:45:261026 continue;
1027 }
1028 std::string characteristic_instance_id = characteristic->GetIdentifier();
1029 auto insert_result = characteristic_id_to_service_id_.insert(
1030 std::make_pair(characteristic_instance_id, service_instance_id));
1031 // If value is already in map, DCHECK it's valid.
1032 if (!insert_result.second)
1033 DCHECK(insert_result.first->second == service_instance_id);
1034
1035 blink::mojom::WebBluetoothRemoteGATTCharacteristicPtr characteristic_ptr =
1036 blink::mojom::WebBluetoothRemoteGATTCharacteristic::New();
ortunoe30623502016-05-03 21:45:091037 characteristic_ptr->instance_id = characteristic_instance_id;
scheib69022ecc2017-01-18 00:19:411038 characteristic_ptr->uuid = characteristic->GetUUID();
ortunob0fb6a182016-04-27 01:45:261039 characteristic_ptr->properties =
1040 static_cast<uint32_t>(characteristic->GetProperties());
1041 response_characteristics.push_back(std::move(characteristic_ptr));
1042
1043 if (quantity == blink::mojom::WebBluetoothGATTQueryQuantity::SINGLE) {
1044 break;
1045 }
1046 }
1047
1048 if (!response_characteristics.empty()) {
tzikcf7bcd652017-06-15 04:19:301049 std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS,
1050 std::move(response_characteristics));
ortunob0fb6a182016-04-27 01:45:261051 return;
1052 }
ortunoe4914832016-06-10 20:15:071053
tzikcf7bcd652017-06-15 04:19:301054 std::move(callback).Run(
1055 characteristics_uuid
1056 ? blink::mojom::WebBluetoothResult::CHARACTERISTIC_NOT_FOUND
1057 : blink::mojom::WebBluetoothResult::NO_CHARACTERISTICS_FOUND,
Arthur Sonzognic686e8f2024-01-11 08:36:371058 std::nullopt /* characteristics */);
ortunob0fb6a182016-04-27 01:45:261059}
1060
dougt4f2237c2017-01-14 04:14:131061void WebBluetoothServiceImpl::RemoteCharacteristicGetDescriptors(
1062 const std::string& characteristic_instance_id,
1063 blink::mojom::WebBluetoothGATTQueryQuantity quantity,
Arthur Sonzognic686e8f2024-01-11 08:36:371064 const std::optional<BluetoothUUID>& descriptors_uuid,
tzikcf7bcd652017-06-15 04:19:301065 RemoteCharacteristicGetDescriptorsCallback callback) {
dougt4f2237c2017-01-14 04:14:131066 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Matt Reynolds8cca57d52025-02-20 18:20:591067 CHECK(back_forward_cache_feature_handle_.IsValid());
dougt819a8622017-02-14 15:05:431068
dougt4f2237c2017-01-14 04:14:131069 if (descriptors_uuid &&
1070 BluetoothBlocklist::Get().IsExcluded(descriptors_uuid.value())) {
tzikcf7bcd652017-06-15 04:19:301071 std::move(callback).Run(
1072 blink::mojom::WebBluetoothResult::BLOCKLISTED_DESCRIPTOR_UUID,
Arthur Sonzognic686e8f2024-01-11 08:36:371073 std::nullopt /* descriptor */);
dougt4f2237c2017-01-14 04:14:131074 return;
1075 }
1076
1077 const CacheQueryResult query_result =
1078 QueryCacheForCharacteristic(characteristic_instance_id);
1079
Daniel Chengafaabacf2024-09-20 01:23:001080 if (query_result.outcome == CacheQueryOutcome::kBadRenderer) {
dougt4f2237c2017-01-14 04:14:131081 return;
1082 }
1083
Daniel Chengafaabacf2024-09-20 01:23:001084 if (query_result.outcome != CacheQueryOutcome::kSuccess) {
tzikcf7bcd652017-06-15 04:19:301085 std::move(callback).Run(query_result.GetWebResult(),
Arthur Sonzognic686e8f2024-01-11 08:36:371086 std::nullopt /* descriptor */);
dougt4f2237c2017-01-14 04:14:131087 return;
1088 }
1089
juncaib594d332017-04-05 19:13:571090 auto descriptors = descriptors_uuid
1091 ? query_result.characteristic->GetDescriptorsByUUID(
1092 descriptors_uuid.value())
1093 : query_result.characteristic->GetDescriptors();
dougt4f2237c2017-01-14 04:14:131094
1095 std::vector<blink::mojom::WebBluetoothRemoteGATTDescriptorPtr>
1096 response_descriptors;
Chris Mumford2f49c612021-04-21 17:54:201097 for (BluetoothRemoteGattDescriptor* descriptor : descriptors) {
dougt4f2237c2017-01-14 04:14:131098 if (BluetoothBlocklist::Get().IsExcluded(descriptor->GetUUID())) {
1099 continue;
1100 }
1101 std::string descriptor_instance_id = descriptor->GetIdentifier();
1102 auto insert_result = descriptor_id_to_characteristic_id_.insert(
1103 {descriptor_instance_id, characteristic_instance_id});
1104 // If value is already in map, DCHECK it's valid.
1105 if (!insert_result.second)
1106 DCHECK(insert_result.first->second == characteristic_instance_id);
1107
1108 auto descriptor_ptr(blink::mojom::WebBluetoothRemoteGATTDescriptor::New());
1109 descriptor_ptr->instance_id = descriptor_instance_id;
scheib69022ecc2017-01-18 00:19:411110 descriptor_ptr->uuid = descriptor->GetUUID();
dougt4f2237c2017-01-14 04:14:131111 response_descriptors.push_back(std::move(descriptor_ptr));
1112
1113 if (quantity == blink::mojom::WebBluetoothGATTQueryQuantity::SINGLE) {
1114 break;
1115 }
1116 }
1117
1118 if (!response_descriptors.empty()) {
tzikcf7bcd652017-06-15 04:19:301119 std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS,
1120 std::move(response_descriptors));
dougt4f2237c2017-01-14 04:14:131121 return;
1122 }
tzikcf7bcd652017-06-15 04:19:301123 std::move(callback).Run(
1124 descriptors_uuid ? blink::mojom::WebBluetoothResult::DESCRIPTOR_NOT_FOUND
1125 : blink::mojom::WebBluetoothResult::NO_DESCRIPTORS_FOUND,
Arthur Sonzognic686e8f2024-01-11 08:36:371126 std::nullopt /* descriptors */);
dougt4f2237c2017-01-14 04:14:131127}
1128
ortuno12e91f072016-04-15 22:57:331129void WebBluetoothServiceImpl::RemoteCharacteristicReadValue(
juncai1ef7dd42016-11-29 04:28:211130 const std::string& characteristic_instance_id,
tzikcf7bcd652017-06-15 04:19:301131 RemoteCharacteristicReadValueCallback callback) {
ortuno12e91f072016-04-15 22:57:331132 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Matt Reynolds8cca57d52025-02-20 18:20:591133 CHECK(back_forward_cache_feature_handle_.IsValid());
ortuno12e91f072016-04-15 22:57:331134
1135 const CacheQueryResult query_result =
ortunob0fb6a182016-04-27 01:45:261136 QueryCacheForCharacteristic(characteristic_instance_id);
ortuno12e91f072016-04-15 22:57:331137
Daniel Chengafaabacf2024-09-20 01:23:001138 if (query_result.outcome == CacheQueryOutcome::kBadRenderer) {
ortuno12e91f072016-04-15 22:57:331139 return;
1140 }
1141
Daniel Chengafaabacf2024-09-20 01:23:001142 if (query_result.outcome != CacheQueryOutcome::kSuccess) {
Chris Mumford40cd29352021-06-16 19:13:331143 RecordCharacteristicReadValueOutcome(query_result.outcome);
tzikcf7bcd652017-06-15 04:19:301144 std::move(callback).Run(query_result.GetWebResult(),
Reilly Grant8002d42f2024-10-23 22:40:311145 /*value=*/{});
ortuno12e91f072016-04-15 22:57:331146 return;
1147 }
1148
beaufort.francois01135bf2016-11-23 14:37:361149 if (BluetoothBlocklist::Get().IsExcludedFromReads(
ortuno12e91f072016-04-15 22:57:331150 query_result.characteristic->GetUUID())) {
Chris Mumford40cd29352021-06-16 19:13:331151 RecordCharacteristicReadValueOutcome(UMAGATTOperationOutcome::kBlocklisted);
tzikcf7bcd652017-06-15 04:19:301152 std::move(callback).Run(blink::mojom::WebBluetoothResult::BLOCKLISTED_READ,
Reilly Grant8002d42f2024-10-23 22:40:311153 /*value=*/{});
ortuno12e91f072016-04-15 22:57:331154 return;
1155 }
1156
1157 query_result.characteristic->ReadRemoteCharacteristic(
Chris Mumford6627ae42021-05-03 18:54:401158 base::BindOnce(&WebBluetoothServiceImpl::OnCharacteristicReadValue,
Chris Mumford4d1cf30782021-10-05 23:02:471159 weak_ptr_factory_.GetWeakPtr(), characteristic_instance_id,
1160 std::move(callback)));
ortuno12e91f072016-04-15 22:57:331161}
1162
ortunoad6b0fea2016-03-31 18:49:111163void WebBluetoothServiceImpl::RemoteCharacteristicWriteValue(
juncai1ef7dd42016-11-29 04:28:211164 const std::string& characteristic_instance_id,
Reilly Grant8002d42f2024-10-23 22:40:311165 base::span<const uint8_t> value,
David Lechner2e7a43a2020-05-26 20:29:591166 blink::mojom::WebBluetoothWriteType write_type,
tzikcf7bcd652017-06-15 04:19:301167 RemoteCharacteristicWriteValueCallback callback) {
ortunoad6b0fea2016-03-31 18:49:111168 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Matt Reynolds8cca57d52025-02-20 18:20:591169 CHECK(back_forward_cache_feature_handle_.IsValid());
ortunoad6b0fea2016-03-31 18:49:111170
1171 // We perform the length check on the renderer side. So if we
1172 // get a value with length > 512, we can assume it's a hostile
1173 // renderer and kill it.
1174 if (value.size() > 512) {
Daniel Chengd6978b062023-11-16 00:11:381175 ReceivedBadMessage(bad_message::BDH_INVALID_WRITE_VALUE_LENGTH);
ortunoad6b0fea2016-03-31 18:49:111176 return;
1177 }
1178
1179 const CacheQueryResult query_result =
ortunob0fb6a182016-04-27 01:45:261180 QueryCacheForCharacteristic(characteristic_instance_id);
ortunoad6b0fea2016-03-31 18:49:111181
Daniel Chengafaabacf2024-09-20 01:23:001182 if (query_result.outcome == CacheQueryOutcome::kBadRenderer) {
ortunoad6b0fea2016-03-31 18:49:111183 return;
1184 }
1185
Daniel Chengafaabacf2024-09-20 01:23:001186 if (query_result.outcome != CacheQueryOutcome::kSuccess) {
Chris Mumford40cd29352021-06-16 19:13:331187 RecordCharacteristicWriteValueOutcome(query_result.outcome);
tzikcf7bcd652017-06-15 04:19:301188 std::move(callback).Run(query_result.GetWebResult());
ortunoad6b0fea2016-03-31 18:49:111189 return;
1190 }
1191
beaufort.francois01135bf2016-11-23 14:37:361192 if (BluetoothBlocklist::Get().IsExcludedFromWrites(
ortunoad6b0fea2016-03-31 18:49:111193 query_result.characteristic->GetUUID())) {
Chris Mumford40cd29352021-06-16 19:13:331194 RecordCharacteristicWriteValueOutcome(
1195 UMAGATTOperationOutcome::kBlocklisted);
tzikcf7bcd652017-06-15 04:19:301196 std::move(callback).Run(
1197 blink::mojom::WebBluetoothResult::BLOCKLISTED_WRITE);
ortunoad6b0fea2016-03-31 18:49:111198 return;
1199 }
1200
Alison Gale770f3fc2024-04-27 00:39:581201 // TODO(crbug.com/40524549): Remove SplitOnceCallback() by updating
tzikcf7bcd652017-06-15 04:19:301202 // the callee interface.
danakj6e9d9b7a2021-05-13 19:38:321203 auto split_callback = base::SplitOnceCallback(std::move(callback));
David Lechner2e7a43a2020-05-26 20:29:591204 base::OnceClosure write_callback = base::BindOnce(
1205 &WebBluetoothServiceImpl::OnCharacteristicWriteValueSuccess,
danakj6e9d9b7a2021-05-13 19:38:321206 weak_ptr_factory_.GetWeakPtr(), std::move(split_callback.first));
Chris Mumford2f49c612021-04-21 17:54:201207 BluetoothGattCharacteristic::ErrorCallback write_error_callback =
danakjd3bb8002019-12-04 18:46:511208 base::BindOnce(&WebBluetoothServiceImpl::OnCharacteristicWriteValueFailed,
Chris Mumford4d1cf30782021-10-05 23:02:471209 weak_ptr_factory_.GetWeakPtr(), characteristic_instance_id,
Reilly Grantc81cdeb12024-11-09 02:20:421210 base::ToVector(value), write_type,
1211 std::move(split_callback.second));
David Lechner2e7a43a2020-05-26 20:29:591212 using WebBluetoothWriteType = blink::mojom::WebBluetoothWriteType;
Chris Mumford2f49c612021-04-21 17:54:201213 using WriteType = BluetoothRemoteGattCharacteristic::WriteType;
David Lechner2e7a43a2020-05-26 20:29:591214 switch (write_type) {
1215 case WebBluetoothWriteType::kWriteDefaultDeprecated:
1216 query_result.characteristic->DeprecatedWriteRemoteCharacteristic(
Reilly Grantc81cdeb12024-11-09 02:20:421217 value, std::move(write_callback), std::move(write_error_callback));
David Lechner2e7a43a2020-05-26 20:29:591218 break;
1219 case WebBluetoothWriteType::kWriteWithResponse:
1220 query_result.characteristic->WriteRemoteCharacteristic(
Reilly Grantc81cdeb12024-11-09 02:20:421221 value, WriteType::kWithResponse, std::move(write_callback),
David Lechner2e7a43a2020-05-26 20:29:591222 std::move(write_error_callback));
1223 break;
1224 case WebBluetoothWriteType::kWriteWithoutResponse:
1225 query_result.characteristic->WriteRemoteCharacteristic(
Reilly Grantc81cdeb12024-11-09 02:20:421226 value, WriteType::kWithoutResponse, std::move(write_callback),
David Lechner2e7a43a2020-05-26 20:29:591227 std::move(write_error_callback));
1228 break;
1229 }
ortunoad6b0fea2016-03-31 18:49:111230}
1231
Chris Mumford4d1cf30782021-10-05 23:02:471232void WebBluetoothServiceImpl::RemoteCharacteristicStartNotificationsInternal(
1233 const std::string& characteristic_instance_id,
1234 mojo::AssociatedRemote<blink::mojom::WebBluetoothCharacteristicClient>
1235 client,
1236 RemoteCharacteristicStartNotificationsCallback callback) {
1237 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1238
1239 const CacheQueryResult query_result =
1240 QueryCacheForCharacteristic(characteristic_instance_id);
Daniel Chengafaabacf2024-09-20 01:23:001241 if (query_result.outcome != CacheQueryOutcome::kSuccess) {
Chris Mumford4d1cf30782021-10-05 23:02:471242 RecordStartNotificationsOutcome(UMAGATTOperationOutcome::kNotSupported);
1243 std::move(callback).Run(query_result.GetWebResult());
1244 return;
1245 }
1246
1247 characteristic_id_to_notify_session_[characteristic_instance_id] =
1248 std::make_unique<GATTNotifySessionAndCharacteristicClient>(
1249 std::move(client));
1250
Alison Gale770f3fc2024-04-27 00:39:581251 // TODO(crbug.com/40524549): Remove SplitOnceCallback() by updating
Chris Mumford4d1cf30782021-10-05 23:02:471252 // the callee interface.
1253 auto split_callback = base::SplitOnceCallback(std::move(callback));
1254 query_result.characteristic->StartNotifySession(
1255 base::BindOnce(&WebBluetoothServiceImpl::OnStartNotifySessionSuccess,
1256 weak_ptr_factory_.GetWeakPtr(),
1257 std::move(split_callback.first)),
1258 base::BindOnce(&WebBluetoothServiceImpl::OnStartNotifySessionFailed,
1259 weak_ptr_factory_.GetWeakPtr(),
1260 std::move(split_callback.second),
1261 characteristic_instance_id));
1262}
1263
ortunobc3bce12016-04-15 21:22:551264void WebBluetoothServiceImpl::RemoteCharacteristicStartNotifications(
juncai1ef7dd42016-11-29 04:28:211265 const std::string& characteristic_instance_id,
Henrique Ferreirodb47b3e2019-09-11 14:29:391266 mojo::PendingAssociatedRemote<
1267 blink::mojom::WebBluetoothCharacteristicClient> client,
tzikcf7bcd652017-06-15 04:19:301268 RemoteCharacteristicStartNotificationsCallback callback) {
ortunobc3bce12016-04-15 21:22:551269 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Matt Reynolds8cca57d52025-02-20 18:20:591270 CHECK(back_forward_cache_feature_handle_.IsValid());
ortunobc3bce12016-04-15 21:22:551271
1272 auto iter =
1273 characteristic_id_to_notify_session_.find(characteristic_instance_id);
Chris Mumforde6fa96a42021-07-20 00:15:141274 if (iter != characteristic_id_to_notify_session_.end()) {
1275 const auto& notification_client = iter->second;
1276 if (!notification_client->gatt_notify_session) {
1277 // There is an in-flight startNotification being processed which is
1278 // awaiting a notify session. Defer this start, and continue once the
1279 // in-flight start has completed.
1280 characteristic_id_to_deferred_start_[characteristic_instance_id].emplace(
1281 std::make_unique<DeferredStartNotificationData>(std::move(client),
1282 std::move(callback)));
1283 return;
1284 }
1285 if (notification_client->gatt_notify_session->IsActive()) {
1286 // If the frame has already started notifications and the notifications
1287 // are active we return SUCCESS.
1288 std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS);
1289 return;
1290 }
ortunobc3bce12016-04-15 21:22:551291 }
1292
1293 const CacheQueryResult query_result =
ortunob0fb6a182016-04-27 01:45:261294 QueryCacheForCharacteristic(characteristic_instance_id);
ortunobc3bce12016-04-15 21:22:551295
Daniel Chengafaabacf2024-09-20 01:23:001296 if (query_result.outcome == CacheQueryOutcome::kBadRenderer) {
ortunobc3bce12016-04-15 21:22:551297 return;
1298 }
1299
Daniel Chengafaabacf2024-09-20 01:23:001300 if (query_result.outcome != CacheQueryOutcome::kSuccess) {
Chris Mumford40cd29352021-06-16 19:13:331301 RecordStartNotificationsOutcome(UMAGATTOperationOutcome::kNotSupported);
tzikcf7bcd652017-06-15 04:19:301302 std::move(callback).Run(query_result.GetWebResult());
ortunobc3bce12016-04-15 21:22:551303 return;
1304 }
1305
Chris Mumford2f49c612021-04-21 17:54:201306 BluetoothRemoteGattCharacteristic::Properties notify_or_indicate =
ortunobc3bce12016-04-15 21:22:551307 query_result.characteristic->GetProperties() &
Chris Mumford2f49c612021-04-21 17:54:201308 (BluetoothRemoteGattCharacteristic::PROPERTY_NOTIFY |
1309 BluetoothRemoteGattCharacteristic::PROPERTY_INDICATE);
ortunobc3bce12016-04-15 21:22:551310 if (!notify_or_indicate) {
tzikcf7bcd652017-06-15 04:19:301311 std::move(callback).Run(
1312 blink::mojom::WebBluetoothResult::GATT_NOT_SUPPORTED);
ortunobc3bce12016-04-15 21:22:551313 return;
1314 }
1315
Chris Mumforde6fa96a42021-07-20 00:15:141316 // Create entry in the notify session map - even before the notification
1317 // is successfully registered. This allows clients to send value change
1318 // notifications during the notification registration process.
Henrique Ferreirodb47b3e2019-09-11 14:29:391319 mojo::AssociatedRemote<blink::mojom::WebBluetoothCharacteristicClient>
1320 characteristic_client(std::move(client));
juncai5fbf7e62017-03-23 21:21:561321
Chris Mumford4d1cf30782021-10-05 23:02:471322 RemoteCharacteristicStartNotificationsInternal(
1323 characteristic_instance_id, std::move(characteristic_client),
1324 std::move(callback));
ortunobc3bce12016-04-15 21:22:551325}
1326
1327void WebBluetoothServiceImpl::RemoteCharacteristicStopNotifications(
juncai1ef7dd42016-11-29 04:28:211328 const std::string& characteristic_instance_id,
tzikcf7bcd652017-06-15 04:19:301329 RemoteCharacteristicStopNotificationsCallback callback) {
ortunobc3bce12016-04-15 21:22:551330 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Matt Reynolds8cca57d52025-02-20 18:20:591331 CHECK(back_forward_cache_feature_handle_.IsValid());
ortunobc3bce12016-04-15 21:22:551332
1333 const CacheQueryResult query_result =
ortunob0fb6a182016-04-27 01:45:261334 QueryCacheForCharacteristic(characteristic_instance_id);
ortunobc3bce12016-04-15 21:22:551335
Daniel Chengafaabacf2024-09-20 01:23:001336 if (query_result.outcome == CacheQueryOutcome::kBadRenderer) {
ortunobc3bce12016-04-15 21:22:551337 return;
1338 }
1339
1340 auto notify_session_iter =
1341 characteristic_id_to_notify_session_.find(characteristic_instance_id);
1342 if (notify_session_iter == characteristic_id_to_notify_session_.end()) {
1343 // If the frame hasn't subscribed to notifications before we just
1344 // run the callback.
tzikcf7bcd652017-06-15 04:19:301345 std::move(callback).Run();
ortunobc3bce12016-04-15 21:22:551346 return;
1347 }
tzikcf7bcd652017-06-15 04:19:301348 notify_session_iter->second->gatt_notify_session->Stop(
danakjd3bb8002019-12-04 18:46:511349 base::BindOnce(&WebBluetoothServiceImpl::OnStopNotifySessionComplete,
1350 weak_ptr_factory_.GetWeakPtr(), characteristic_instance_id,
Jan Wilken Dörrie1494205b2020-03-26 09:32:531351 std::move(callback)));
ortunobc3bce12016-04-15 21:22:551352}
1353
dougta2fe055212017-01-27 05:35:301354void WebBluetoothServiceImpl::RemoteDescriptorReadValue(
1355 const std::string& descriptor_instance_id,
tzikcf7bcd652017-06-15 04:19:301356 RemoteDescriptorReadValueCallback callback) {
dougta2fe055212017-01-27 05:35:301357 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Matt Reynolds8cca57d52025-02-20 18:20:591358 CHECK(back_forward_cache_feature_handle_.IsValid());
dougta2fe055212017-01-27 05:35:301359
1360 const CacheQueryResult query_result =
1361 QueryCacheForDescriptor(descriptor_instance_id);
1362
Daniel Chengafaabacf2024-09-20 01:23:001363 if (query_result.outcome == CacheQueryOutcome::kBadRenderer) {
dougta2fe055212017-01-27 05:35:301364 return;
1365 }
1366
Daniel Chengafaabacf2024-09-20 01:23:001367 if (query_result.outcome != CacheQueryOutcome::kSuccess) {
tzikcf7bcd652017-06-15 04:19:301368 std::move(callback).Run(query_result.GetWebResult(),
Reilly Grant8002d42f2024-10-23 22:40:311369 /*value=*/{});
dougta2fe055212017-01-27 05:35:301370 return;
1371 }
1372
1373 if (BluetoothBlocklist::Get().IsExcludedFromReads(
1374 query_result.descriptor->GetUUID())) {
tzikcf7bcd652017-06-15 04:19:301375 std::move(callback).Run(blink::mojom::WebBluetoothResult::BLOCKLISTED_READ,
Reilly Grant8002d42f2024-10-23 22:40:311376 /*value=*/{});
dougta2fe055212017-01-27 05:35:301377 return;
1378 }
1379
1380 query_result.descriptor->ReadRemoteDescriptor(
Chris Mumford6627ae42021-05-03 18:54:401381 base::BindOnce(&WebBluetoothServiceImpl::OnDescriptorReadValue,
Chris Mumford4d1cf30782021-10-05 23:02:471382 weak_ptr_factory_.GetWeakPtr(), descriptor_instance_id,
1383 std::move(callback)));
dougta2fe055212017-01-27 05:35:301384}
1385
dougtbe62e9d2017-02-01 16:13:551386void WebBluetoothServiceImpl::RemoteDescriptorWriteValue(
1387 const std::string& descriptor_instance_id,
Reilly Grant8002d42f2024-10-23 22:40:311388 base::span<const uint8_t> value,
tzikcf7bcd652017-06-15 04:19:301389 RemoteDescriptorWriteValueCallback callback) {
dougtbe62e9d2017-02-01 16:13:551390 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Matt Reynolds8cca57d52025-02-20 18:20:591391 CHECK(back_forward_cache_feature_handle_.IsValid());
dougtbe62e9d2017-02-01 16:13:551392
1393 // We perform the length check on the renderer side. So if we
1394 // get a value with length > 512, we can assume it's a hostile
1395 // renderer and kill it.
1396 if (value.size() > 512) {
Daniel Chengd6978b062023-11-16 00:11:381397 ReceivedBadMessage(bad_message::BDH_INVALID_WRITE_VALUE_LENGTH);
dougtbe62e9d2017-02-01 16:13:551398 return;
1399 }
1400
1401 const CacheQueryResult query_result =
1402 QueryCacheForDescriptor(descriptor_instance_id);
1403
Daniel Chengafaabacf2024-09-20 01:23:001404 if (query_result.outcome == CacheQueryOutcome::kBadRenderer) {
dougtbe62e9d2017-02-01 16:13:551405 return;
1406 }
1407
Daniel Chengafaabacf2024-09-20 01:23:001408 if (query_result.outcome != CacheQueryOutcome::kSuccess) {
tzikcf7bcd652017-06-15 04:19:301409 std::move(callback).Run(query_result.GetWebResult());
dougtbe62e9d2017-02-01 16:13:551410 return;
1411 }
1412
1413 if (BluetoothBlocklist::Get().IsExcludedFromWrites(
1414 query_result.descriptor->GetUUID())) {
tzikcf7bcd652017-06-15 04:19:301415 std::move(callback).Run(
1416 blink::mojom::WebBluetoothResult::BLOCKLISTED_WRITE);
dougtbe62e9d2017-02-01 16:13:551417 return;
1418 }
1419
Alison Gale770f3fc2024-04-27 00:39:581420 // TODO(crbug.com/40524549): Remove SplitOnceCallback() by updating
tzikcf7bcd652017-06-15 04:19:301421 // the callee interface.
danakj6e9d9b7a2021-05-13 19:38:321422 auto split_callback = base::SplitOnceCallback(std::move(callback));
dougtbe62e9d2017-02-01 16:13:551423 query_result.descriptor->WriteRemoteDescriptor(
Reilly Grantc81cdeb12024-11-09 02:20:421424 value,
danakjd3bb8002019-12-04 18:46:511425 base::BindOnce(&WebBluetoothServiceImpl::OnDescriptorWriteValueSuccess,
danakj6e9d9b7a2021-05-13 19:38:321426 weak_ptr_factory_.GetWeakPtr(),
1427 std::move(split_callback.first)),
danakjd3bb8002019-12-04 18:46:511428 base::BindOnce(&WebBluetoothServiceImpl::OnDescriptorWriteValueFailed,
Chris Mumford4d1cf30782021-10-05 23:02:471429 weak_ptr_factory_.GetWeakPtr(), descriptor_instance_id,
Reilly Grantc81cdeb12024-11-09 02:20:421430 base::ToVector(value), std::move(split_callback.second)));
dougtbe62e9d2017-02-01 16:13:551431}
1432
Doug Turner23a658b92018-12-20 01:57:541433void WebBluetoothServiceImpl::RequestScanningStart(
Ovidio Henriquez3dee626c2020-05-05 19:37:101434 mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
Alvin Jic0c16c72023-08-25 18:41:571435 client_remote,
Doug Turner9b931de2019-01-08 18:22:371436 blink::mojom::WebBluetoothRequestLEScanOptionsPtr options,
Doug Turner23a658b92018-12-20 01:57:541437 RequestScanningStartCallback callback) {
1438 DCHECK_CURRENTLY_ON(BrowserThread::UI);
danakjc70aec1f2022-07-07 15:48:191439 // The use of render_frame_host().GetMainFrame() below is safe as fenced
Claudio DeSouzade4a42d2021-12-03 21:42:531440 // frames are disallowed.
danakjc70aec1f2022-07-07 15:48:191441 DCHECK(!render_frame_host().IsNestedWithinFencedFrame());
Doug Turner23a658b92018-12-20 01:57:541442
Matt Reynolds8cca57d52025-02-20 18:20:591443 if (base::FeatureList::IsEnabled(
1444 features::kWebBluetoothAllowGetAvailabilityWithBfcache)) {
1445 PreventBackForwardCache();
1446 }
1447
Claudio DeSouza3b4ad052021-11-23 02:32:431448 const url::Origin requesting_origin = origin();
Ovidio Henriquez887bb132020-06-04 21:45:551449 const url::Origin embedding_origin =
danakjc70aec1f2022-07-07 15:48:191450 render_frame_host().GetMainFrame()->GetLastCommittedOrigin();
Ovidio Henriquez887bb132020-06-04 21:45:551451
1452 bool blocked = GetContentClient()->browser()->IsBluetoothScanningBlocked(
1453 web_contents()->GetBrowserContext(), requesting_origin, embedding_origin);
1454 if (blocked) {
1455 std::move(callback).Run(blink::mojom::WebBluetoothResult::SCANNING_BLOCKED);
1456 return;
1457 }
1458
1459 // The renderer should never send invalid options.
1460 if (!IsValidRequestScanOptions(options)) {
Daniel Chengd6978b062023-11-16 00:11:381461 ReceivedBadMessage(bad_message::BDH_INVALID_OPTIONS);
Ovidio Henriquez887bb132020-06-04 21:45:551462 return;
1463 }
Doug Turner23a658b92018-12-20 01:57:541464
1465 if (!GetAdapter()) {
1466 if (BluetoothAdapterFactoryWrapper::Get().IsLowEnergySupported()) {
1467 BluetoothAdapterFactoryWrapper::Get().AcquireAdapter(
Alvin Jic0c16c72023-08-25 18:41:571468 this, base::BindOnce(
1469 &WebBluetoothServiceImpl::RequestScanningStartImpl,
1470 weak_ptr_factory_.GetWeakPtr(), std::move(client_remote),
1471 std::move(options), std::move(callback)));
Doug Turner23a658b92018-12-20 01:57:541472 return;
1473 }
Ovidio Henriquez3dee626c2020-05-05 19:37:101474 std::move(callback).Run(
Doug Turner23a658b92018-12-20 01:57:541475 blink::mojom::WebBluetoothResult::BLUETOOTH_LOW_ENERGY_NOT_AVAILABLE);
1476 return;
1477 }
1478
Alvin Jic0c16c72023-08-25 18:41:571479 RequestScanningStartImpl(std::move(client_remote), std::move(options),
Doug Turner9b931de2019-01-08 18:22:371480 std::move(callback), GetAdapter());
Doug Turner23a658b92018-12-20 01:57:541481}
1482
Ovidio Henriquez887bb132020-06-04 21:45:551483void WebBluetoothServiceImpl::WatchAdvertisementsForDevice(
1484 const blink::WebBluetoothDeviceId& device_id,
1485 mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
Alvin Jic0c16c72023-08-25 18:41:571486 client_remote,
Ovidio Henriquez887bb132020-06-04 21:45:551487 WatchAdvertisementsForDeviceCallback callback) {
1488 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Matt Reynolds8cca57d52025-02-20 18:20:591489 CHECK(back_forward_cache_feature_handle_.IsValid());
Ovidio Henriquez887bb132020-06-04 21:45:551490
1491 blink::mojom::WebBluetoothResult allowed_result = GetBluetoothAllowed();
1492 if (allowed_result != blink::mojom::WebBluetoothResult::SUCCESS) {
1493 std::move(callback).Run(allowed_result);
1494 return;
1495 }
1496
1497 // The renderer should never send an invalid |device_id|.
1498 if (!device_id.IsValid()) {
Daniel Chengd6978b062023-11-16 00:11:381499 ReceivedBadMessage(bad_message::BDH_INVALID_OPTIONS);
Ovidio Henriquez887bb132020-06-04 21:45:551500 return;
1501 }
1502
1503 if (!GetAdapter()) {
1504 if (BluetoothAdapterFactoryWrapper::Get().IsLowEnergySupported()) {
1505 BluetoothAdapterFactoryWrapper::Get().AcquireAdapter(
1506 this, base::BindOnce(
1507 &WebBluetoothServiceImpl::WatchAdvertisementsForDeviceImpl,
1508 weak_ptr_factory_.GetWeakPtr(), device_id,
Alvin Jic0c16c72023-08-25 18:41:571509 std::move(client_remote), std::move(callback)));
Ovidio Henriquez887bb132020-06-04 21:45:551510 return;
1511 }
1512 std::move(callback).Run(
1513 blink::mojom::WebBluetoothResult::BLUETOOTH_LOW_ENERGY_NOT_AVAILABLE);
1514 return;
1515 }
1516
Alvin Jic0c16c72023-08-25 18:41:571517 WatchAdvertisementsForDeviceImpl(std::move(device_id),
1518 std::move(client_remote),
Ovidio Henriquez887bb132020-06-04 21:45:551519 std::move(callback), GetAdapter());
1520}
1521
1522void WebBluetoothServiceImpl::RemoveDisconnectedClients() {
1523 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1524
Alison Gale770f3fc2024-04-27 00:39:581525 // TODO(crbug.com/40132791): These two classes can potentially be
Ovidio Henriquez887bb132020-06-04 21:45:551526 // combined into the same container.
Andrew Rayskiyf65990362024-02-27 18:43:241527 std::erase_if(scanning_clients_,
Ovidio Henriquez887bb132020-06-04 21:45:551528 [](const std::unique_ptr<ScanningClient>& client) {
1529 return !client->is_connected();
1530 });
Andrew Rayskiyf65990362024-02-27 18:43:241531 std::erase_if(watch_advertisements_clients_,
Ovidio Henriquez887bb132020-06-04 21:45:551532 [](const std::unique_ptr<WatchAdvertisementsClient>& client) {
1533 return !client->is_connected();
1534 });
1535 MaybeStopDiscovery();
1536}
1537
1538void WebBluetoothServiceImpl::MaybeStopDiscovery() {
1539 if (scanning_clients_.empty())
1540 ble_scan_discovery_session_.reset();
1541
1542 if (watch_advertisements_clients_.empty())
1543 watch_advertisements_discovery_session_.reset();
1544}
1545
Doug Turner23a658b92018-12-20 01:57:541546void WebBluetoothServiceImpl::RequestScanningStartImpl(
Ovidio Henriquez887bb132020-06-04 21:45:551547 mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
Alvin Jic0c16c72023-08-25 18:41:571548 client_remote,
Doug Turner9b931de2019-01-08 18:22:371549 blink::mojom::WebBluetoothRequestLEScanOptionsPtr options,
Doug Turner23a658b92018-12-20 01:57:541550 RequestScanningStartCallback callback,
Chris Mumford2f49c612021-04-21 17:54:201551 scoped_refptr<BluetoothAdapter> adapter) {
Doug Turner23a658b92018-12-20 01:57:541552 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1553
1554 if (!adapter) {
Ovidio Henriquez3dee626c2020-05-05 19:37:101555 std::move(callback).Run(
Doug Turner23a658b92018-12-20 01:57:541556 blink::mojom::WebBluetoothResult::BLUETOOTH_LOW_ENERGY_NOT_AVAILABLE);
1557 return;
1558 }
1559
Ovidio Henriquez887bb132020-06-04 21:45:551560 if (request_scanning_start_callback_) {
Ovidio Henriquez3dee626c2020-05-05 19:37:101561 std::move(callback).Run(blink::mojom::WebBluetoothResult::PROMPT_CANCELED);
Doug Turner23a658b92018-12-20 01:57:541562 return;
1563 }
1564
Ovidio Henriquez887bb132020-06-04 21:45:551565 if (ble_scan_discovery_session_) {
1566 auto scanning_client = std::make_unique<ScanningClient>(
Alvin Jic0c16c72023-08-25 18:41:571567 /*service=*/this, std::move(client_remote), std::move(options),
Ovidio Henriquez887bb132020-06-04 21:45:551568 std::move(callback));
1569
1570 if (AreScanFiltersAllowed(scanning_client->scan_options().filters)) {
Alvin Jif4c234d412023-08-23 16:58:541571 scanning_client->RunCallback(blink::mojom::WebBluetoothResult::SUCCESS);
Jun Cai732a05e32019-05-29 19:34:191572 scanning_client->set_allow_send_event(true);
1573 scanning_clients_.push_back(std::move(scanning_client));
1574 return;
1575 }
1576
Jun Cai149002e2019-05-09 23:13:071577 // By resetting |device_scanning_prompt_controller_|, it returns an error if
1578 // there are duplicate calls to RequestScanningStart().
1579 device_scanning_prompt_controller_ =
1580 std::make_unique<BluetoothDeviceScanningPromptController>(
Claudio DeSouza3b4ad052021-11-23 02:32:431581 this, render_frame_host());
Ovidio Henriquez887bb132020-06-04 21:45:551582 scanning_client->SetPromptController(
1583 device_scanning_prompt_controller_.get());
1584 scanning_clients_.push_back(std::move(scanning_client));
Jun Cai149002e2019-05-09 23:13:071585 device_scanning_prompt_controller_->ShowPermissionPrompt();
Doug Turner23a658b92018-12-20 01:57:541586 return;
Jun Cai149002e2019-05-09 23:13:071587 }
Doug Turner23a658b92018-12-20 01:57:541588
Ovidio Henriquez887bb132020-06-04 21:45:551589 request_scanning_start_callback_ = std::move(callback);
Jun Cai149002e2019-05-09 23:13:071590
Alison Gale770f3fc2024-04-27 00:39:581591 // TODO(crbug.com/40630111): Since scanning without a filter wastes
Jun Caia1f8bf25d92019-06-07 19:06:531592 // resources, we need use StartDiscoverySessionWithFilter() instead of
1593 // StartDiscoverySession() here.
Doug Turner23a658b92018-12-20 01:57:541594 adapter->StartDiscoverySession(
Sonny Sasaka8b22f472021-08-17 21:17:351595 kScanClientNameRequestLeScan,
Ovidio Henriquez887bb132020-06-04 21:45:551596 base::BindOnce(
1597 &WebBluetoothServiceImpl::OnStartDiscoverySessionForScanning,
Alvin Jic0c16c72023-08-25 18:41:571598 weak_ptr_factory_.GetWeakPtr(), std::move(client_remote),
Ovidio Henriquez887bb132020-06-04 21:45:551599 std::move(options)),
1600 base::BindOnce(
1601 &WebBluetoothServiceImpl::OnDiscoverySessionErrorForScanning,
1602 weak_ptr_factory_.GetWeakPtr()));
Doug Turner23a658b92018-12-20 01:57:541603}
1604
Ovidio Henriquez887bb132020-06-04 21:45:551605void WebBluetoothServiceImpl::OnStartDiscoverySessionForScanning(
1606 mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
Alvin Jic0c16c72023-08-25 18:41:571607 client_remote,
Doug Turner9b931de2019-01-08 18:22:371608 blink::mojom::WebBluetoothRequestLEScanOptionsPtr options,
Chris Mumford2f49c612021-04-21 17:54:201609 std::unique_ptr<BluetoothDiscoverySession> session) {
Doug Turner23a658b92018-12-20 01:57:541610 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Ovidio Henriquez887bb132020-06-04 21:45:551611 DCHECK(!ble_scan_discovery_session_);
Doug Turner23a658b92018-12-20 01:57:541612
Ovidio Henriquez887bb132020-06-04 21:45:551613 ble_scan_discovery_session_ = std::move(session);
Jun Cai732a05e32019-05-29 19:34:191614
Ovidio Henriquez887bb132020-06-04 21:45:551615 auto scanning_client = std::make_unique<ScanningClient>(
Alvin Jic0c16c72023-08-25 18:41:571616 /*service=*/this, std::move(client_remote), std::move(options),
Ovidio Henriquez887bb132020-06-04 21:45:551617 std::move(request_scanning_start_callback_));
1618
1619 if (AreScanFiltersAllowed(scanning_client->scan_options().filters)) {
Alvin Jif4c234d412023-08-23 16:58:541620 scanning_client->RunCallback(blink::mojom::WebBluetoothResult::SUCCESS);
Jun Cai732a05e32019-05-29 19:34:191621 scanning_client->set_allow_send_event(true);
1622 scanning_clients_.push_back(std::move(scanning_client));
1623 return;
1624 }
1625
Jun Cai149002e2019-05-09 23:13:071626 device_scanning_prompt_controller_ =
1627 std::make_unique<BluetoothDeviceScanningPromptController>(
Claudio DeSouza3b4ad052021-11-23 02:32:431628 this, render_frame_host());
Ovidio Henriquez887bb132020-06-04 21:45:551629 scanning_client->SetPromptController(
1630 device_scanning_prompt_controller_.get());
1631 scanning_clients_.push_back(std::move(scanning_client));
Jun Cai149002e2019-05-09 23:13:071632 device_scanning_prompt_controller_->ShowPermissionPrompt();
Doug Turner23a658b92018-12-20 01:57:541633}
1634
Ovidio Henriquez887bb132020-06-04 21:45:551635void WebBluetoothServiceImpl::OnDiscoverySessionErrorForScanning() {
Doug Turner23a658b92018-12-20 01:57:541636 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Doug Turner23a658b92018-12-20 01:57:541637
Jun Cai149002e2019-05-09 23:13:071638 device_scanning_prompt_controller_.reset();
Doug Turner23a658b92018-12-20 01:57:541639
Ovidio Henriquez887bb132020-06-04 21:45:551640 std::move(request_scanning_start_callback_)
Ovidio Henriquez3dee626c2020-05-05 19:37:101641 .Run(blink::mojom::WebBluetoothResult::NO_BLUETOOTH_ADAPTER);
Ovidio Henriquez887bb132020-06-04 21:45:551642 ClearAdvertisementClients();
Doug Turner23a658b92018-12-20 01:57:541643}
1644
ortunob6374dd82016-05-27 03:04:071645void WebBluetoothServiceImpl::RequestDeviceImpl(
1646 blink::mojom::WebBluetoothRequestDeviceOptionsPtr options,
tzikcf7bcd652017-06-15 04:19:301647 RequestDeviceCallback callback,
Chris Mumford2f49c612021-04-21 17:54:201648 scoped_refptr<BluetoothAdapter> adapter) {
Doug Turner9b931de2019-01-08 18:22:371649 // The renderer should never send invalid options.
Md. Hasanur Rashid28c84952020-01-05 11:36:361650 if (!IsValidRequestDeviceOptions(options)) {
Daniel Chengd6978b062023-11-16 00:11:381651 ReceivedBadMessage(bad_message::BDH_INVALID_OPTIONS);
Doug Turner9b931de2019-01-08 18:22:371652 return;
1653 }
1654
Mustaq Ahmedc4dfbae2018-04-06 14:01:021655 // Calls to requestDevice() require user activation (user gestures). We
Ovidio Henriquez887bb132020-06-04 21:45:551656 // should close any opened chooser when a duplicate requestDevice call is
1657 // made with the same user activation or when any gesture occurs outside
1658 // of the opened chooser. This does not happen on all platforms so we
1659 // don't DCHECK that the old one is closed. We destroy the old chooser
1660 // before constructing the new one to make sure they can't conflict.
ortuno8eba1a92016-06-15 04:18:521661 device_chooser_controller_.reset();
ortunob6374dd82016-05-27 03:04:071662
Peter Boströmdd7e40ec2021-04-05 20:40:101663 device_chooser_controller_ =
1664 std::make_unique<BluetoothDeviceChooserController>(
Claudio DeSouza3b4ad052021-11-23 02:32:431665 this, render_frame_host(), std::move(adapter));
ortunob6374dd82016-05-27 03:04:071666 device_chooser_controller_->GetDevice(
1667 std::move(options),
Reilly Grantb936f9e2021-04-29 21:04:431668 base::BindOnce(&WebBluetoothServiceImpl::OnGetDevice,
1669 weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
ortunob6374dd82016-05-27 03:04:071670}
1671
Ovidio Henriquez278801c22020-03-10 21:52:011672void WebBluetoothServiceImpl::GetDevicesImpl(
1673 GetDevicesCallback callback,
Chris Mumford2f49c612021-04-21 17:54:201674 scoped_refptr<BluetoothAdapter> adapter) {
Ovidio Henriquez278801c22020-03-10 21:52:011675 if (base::FeatureList::IsEnabled(
1676 features::kWebBluetoothNewPermissionsBackend)) {
1677 BluetoothDelegate* delegate =
1678 GetContentClient()->browser()->GetBluetoothDelegate();
1679 if (!delegate) {
1680 std::move(callback).Run({});
1681 return;
1682 }
1683
danakjc70aec1f2022-07-07 15:48:191684 std::move(callback).Run(
1685 delegate->GetPermittedDevices(&render_frame_host()));
Ovidio Henriquez278801c22020-03-10 21:52:011686 return;
1687 }
1688
Ovidio Henriquez887bb132020-06-04 21:45:551689 // BluetoothAllowedDevices does not provide a way to get all of the
1690 // permitted devices, so instead return all of the allowed devices that
1691 // are currently known to the system.
Ovidio Henriquez278801c22020-03-10 21:52:011692 std::vector<blink::mojom::WebBluetoothDevicePtr> web_bluetooth_devices;
1693 for (const auto* device : adapter->GetDevices()) {
1694 const blink::WebBluetoothDeviceId* device_id =
1695 allowed_devices().GetDeviceId(device->GetAddress());
1696 if (!device_id || !allowed_devices().IsAllowedToGATTConnect(*device_id))
1697 continue;
1698
1699 web_bluetooth_devices.push_back(
1700 blink::mojom::WebBluetoothDevice::New(*device_id, device->GetName()));
1701 }
1702 std::move(callback).Run(std::move(web_bluetooth_devices));
1703}
1704
Ovidio Henriquez887bb132020-06-04 21:45:551705void WebBluetoothServiceImpl::WatchAdvertisementsForDeviceImpl(
1706 const blink::WebBluetoothDeviceId& device_id,
1707 mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
Alvin Jic0c16c72023-08-25 18:41:571708 client_remote,
Ovidio Henriquez887bb132020-06-04 21:45:551709 WatchAdvertisementsForDeviceCallback callback,
Chris Mumford2f49c612021-04-21 17:54:201710 scoped_refptr<BluetoothAdapter> adapter) {
Ovidio Henriquez887bb132020-06-04 21:45:551711 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1712
1713 if (!adapter) {
1714 std::move(callback).Run(
1715 blink::mojom::WebBluetoothResult::BLUETOOTH_LOW_ENERGY_NOT_AVAILABLE);
1716 return;
1717 }
1718
Alvin Jif4c234d412023-08-23 16:58:541719 auto pending_client = std::make_unique<WatchAdvertisementsClient>(
Alvin Jic0c16c72023-08-25 18:41:571720 /*service=*/this, std::move(client_remote), std::move(device_id),
Alvin Jif4c234d412023-08-23 16:58:541721 std::move(callback));
Ovidio Henriquez887bb132020-06-04 21:45:551722 if (watch_advertisements_discovery_session_) {
Alvin Jif4c234d412023-08-23 16:58:541723 pending_client->RunCallback(blink::mojom::WebBluetoothResult::SUCCESS);
1724 watch_advertisements_clients_.push_back(std::move(pending_client));
Ovidio Henriquez887bb132020-06-04 21:45:551725 return;
1726 }
1727
Alvin Jif4c234d412023-08-23 16:58:541728 // If |watch_advertisements_pending_clients_| has more than one client,
Ovidio Henriquez887bb132020-06-04 21:45:551729 // then it means that a previous watch advertisements operation has already
Alvin Jif4c234d412023-08-23 16:58:541730 // started a discovery session, so the |pending_client| for this
Ovidio Henriquez887bb132020-06-04 21:45:551731 // operation needs to be stored until the start discovery operation is
1732 // complete.
Alvin Jif4c234d412023-08-23 16:58:541733 watch_advertisements_pending_clients_.push_back(std::move(pending_client));
1734 if (watch_advertisements_pending_clients_.size() > 1) {
Ovidio Henriquez887bb132020-06-04 21:45:551735 return;
Alvin Jif4c234d412023-08-23 16:58:541736 }
Ovidio Henriquez887bb132020-06-04 21:45:551737
1738 // Not all platforms support filtering by address.
Alison Gale770f3fc2024-04-27 00:39:581739 // TODO(crbug.com/40630111): Use StartDiscoverySessionWithFilter() to
Ovidio Henriquez887bb132020-06-04 21:45:551740 // filter out by MAC address when platforms provide this capability.
1741 adapter->StartDiscoverySession(
Sonny Sasaka8b22f472021-08-17 21:17:351742 kScanClientNameWatchAdvertisements,
Ovidio Henriquez887bb132020-06-04 21:45:551743 base::BindOnce(&WebBluetoothServiceImpl::
1744 OnStartDiscoverySessionForWatchAdvertisements,
1745 weak_ptr_factory_.GetWeakPtr()),
1746 base::BindOnce(&WebBluetoothServiceImpl::
1747 OnDiscoverySessionErrorForWatchAdvertisements,
1748 weak_ptr_factory_.GetWeakPtr()));
1749}
1750
1751void WebBluetoothServiceImpl::OnStartDiscoverySessionForWatchAdvertisements(
Chris Mumford2f49c612021-04-21 17:54:201752 std::unique_ptr<BluetoothDiscoverySession> session) {
Ovidio Henriquez887bb132020-06-04 21:45:551753 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1754 DCHECK(!watch_advertisements_discovery_session_);
1755 watch_advertisements_discovery_session_ = std::move(session);
1756
Andy Paicu1d7fc172021-01-20 18:29:451757 BluetoothDelegate* delegate =
1758 GetContentClient()->browser()->GetBluetoothDelegate();
1759
Alvin Jif4c234d412023-08-23 16:58:541760 for (auto& pending_client : watch_advertisements_pending_clients_) {
1761 // Check if |pending_client| is still alive.
1762 if (!pending_client->is_connected()) {
1763 pending_client->RunCallback(
1764 blink::mojom::WebBluetoothResult::WATCH_ADVERTISEMENTS_ABORTED);
Ovidio Henriquez887bb132020-06-04 21:45:551765 continue;
1766 }
1767
Andy Paicu1d7fc172021-01-20 18:29:451768 // If the new permissions backend is enabled, verify the permission using
1769 // the delegate.
1770 if (base::FeatureList::IsEnabled(
1771 features::kWebBluetoothNewPermissionsBackend) &&
Alvin Jif4c234d412023-08-23 16:58:541772 (!delegate || !delegate->HasDevicePermission(
1773 &render_frame_host(), pending_client->device_id()))) {
1774 pending_client->RunCallback(
1775 blink::mojom::WebBluetoothResult::NOT_ALLOWED_TO_ACCESS_ANY_SERVICE);
Andy Paicu1d7fc172021-01-20 18:29:451776 continue;
1777 }
1778
1779 // Otherwise verify it via |allowed_devices|.
1780 if (!base::FeatureList::IsEnabled(
1781 features::kWebBluetoothNewPermissionsBackend) &&
1782 !allowed_devices().IsAllowedToGATTConnect(
Alvin Jif4c234d412023-08-23 16:58:541783 pending_client->device_id())) {
1784 pending_client->RunCallback(
1785 blink::mojom::WebBluetoothResult::NOT_ALLOWED_TO_ACCESS_ANY_SERVICE);
Andy Paicu1d7fc172021-01-20 18:29:451786 continue;
1787 }
1788
Alvin Jif4c234d412023-08-23 16:58:541789 // Here we already make sure that pending_client is still alive and have
1790 // permissions. Add it to |watch_advertisements_clients_|.
1791 pending_client->RunCallback(blink::mojom::WebBluetoothResult::SUCCESS);
1792 watch_advertisements_clients_.push_back(std::move(pending_client));
Ovidio Henriquez887bb132020-06-04 21:45:551793 }
1794
Alvin Jif4c234d412023-08-23 16:58:541795 watch_advertisements_pending_clients_.clear();
Ovidio Henriquez887bb132020-06-04 21:45:551796
Chris Mumford2b2708f2021-10-01 20:25:571797 // If a client was disconnected while a discovery session was being started,
Ovidio Henriquez887bb132020-06-04 21:45:551798 // then there may not be any valid clients, so discovery should be stopped.
1799 MaybeStopDiscovery();
1800}
1801
1802void WebBluetoothServiceImpl::OnDiscoverySessionErrorForWatchAdvertisements() {
1803 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1804
Alvin Jif4c234d412023-08-23 16:58:541805 for (auto& pending_client : watch_advertisements_pending_clients_) {
1806 pending_client->RunCallback(
1807 blink::mojom::WebBluetoothResult::NO_BLUETOOTH_ADAPTER);
Ovidio Henriquez887bb132020-06-04 21:45:551808 }
Alvin Jif4c234d412023-08-23 16:58:541809 watch_advertisements_pending_clients_.clear();
Ovidio Henriquez887bb132020-06-04 21:45:551810 ClearAdvertisementClients();
1811}
1812
beaufort.francois7952f002016-06-14 16:44:091813void WebBluetoothServiceImpl::RemoteServerGetPrimaryServicesImpl(
Ovidio Henriquez0e8ab7072019-05-31 21:38:071814 const blink::WebBluetoothDeviceId& device_id,
beaufort.francois7952f002016-06-14 16:44:091815 blink::mojom::WebBluetoothGATTQueryQuantity quantity,
Arthur Sonzognic686e8f2024-01-11 08:36:371816 const std::optional<BluetoothUUID>& services_uuid,
tzikcf7bcd652017-06-15 04:19:301817 RemoteServerGetPrimaryServicesCallback callback,
Chris Mumford2f49c612021-04-21 17:54:201818 BluetoothDevice* device) {
ortuno67acd832016-04-30 00:13:221819 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Giovanni Ortuño Urquidi8688a0d2017-05-20 06:12:321820
1821 if (!device->IsGattConnected()) {
1822 // The device disconnected while discovery was pending. The returned error
1823 // does not matter because the renderer ignores the error if the device
1824 // disconnected.
tzikcf7bcd652017-06-15 04:19:301825 std::move(callback).Run(blink::mojom::WebBluetoothResult::NO_SERVICES_FOUND,
Arthur Sonzognic686e8f2024-01-11 08:36:371826 std::nullopt /* services */);
Giovanni Ortuño Urquidi8688a0d2017-05-20 06:12:321827 return;
1828 }
1829
Jack Hsiehadebefb2022-02-15 18:57:531830 // We can't know if a service is present or not until GATT service discovery
1831 // is complete for the device.
1832 if (!device->IsGattServicesDiscoveryComplete()) {
1833 DVLOG(1) << "Services not yet discovered.";
1834 pending_primary_services_requests_[device->GetAddress()].push_back(
1835 base::BindOnce(
1836 &WebBluetoothServiceImpl::RemoteServerGetPrimaryServicesImpl,
1837 base::Unretained(this), device_id, quantity, services_uuid,
1838 std::move(callback)));
1839 return;
1840 }
1841
ortuno67acd832016-04-30 00:13:221842 DCHECK(device->IsGattServicesDiscoveryComplete());
1843
Chris Mumford2f49c612021-04-21 17:54:201844 std::vector<BluetoothRemoteGattService*> services =
juncaied9dda42017-01-09 23:15:371845 services_uuid ? device->GetPrimaryServicesByUUID(services_uuid.value())
1846 : device->GetPrimaryServices();
ortuno67acd832016-04-30 00:13:221847
juncai1ef7dd42016-11-29 04:28:211848 std::vector<blink::mojom::WebBluetoothRemoteGATTServicePtr> response_services;
Chris Mumford2f49c612021-04-21 17:54:201849 for (BluetoothRemoteGattService* service : services) {
Ovidio Henriquez3d729f62020-02-07 00:43:291850 if (!IsAllowedToAccessService(device_id, service->GetUUID()))
beaufort.francois7952f002016-06-14 16:44:091851 continue;
Ovidio Henriquez3d729f62020-02-07 00:43:291852
beaufort.francois7952f002016-06-14 16:44:091853 std::string service_instance_id = service->GetIdentifier();
1854 const std::string& device_address = device->GetAddress();
1855 auto insert_result = service_id_to_device_address_.insert(
1856 make_pair(service_instance_id, device_address));
1857 // If value is already in map, DCHECK it's valid.
1858 if (!insert_result.second)
1859 DCHECK_EQ(insert_result.first->second, device_address);
1860
1861 blink::mojom::WebBluetoothRemoteGATTServicePtr service_ptr =
1862 blink::mojom::WebBluetoothRemoteGATTService::New();
1863 service_ptr->instance_id = service_instance_id;
scheib69022ecc2017-01-18 00:19:411864 service_ptr->uuid = service->GetUUID();
beaufort.francois7952f002016-06-14 16:44:091865 response_services.push_back(std::move(service_ptr));
1866
1867 if (quantity == blink::mojom::WebBluetoothGATTQueryQuantity::SINGLE) {
1868 break;
1869 }
1870 }
1871
1872 if (!response_services.empty()) {
dougt16af7e82017-01-13 23:20:081873 DVLOG(1) << "Services found in device.";
tzikcf7bcd652017-06-15 04:19:301874 std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS,
1875 std::move(response_services));
ortuno67acd832016-04-30 00:13:221876 return;
1877 }
1878
dougt16af7e82017-01-13 23:20:081879 DVLOG(1) << "Services not found in device.";
tzikcf7bcd652017-06-15 04:19:301880 std::move(callback).Run(
1881 services_uuid ? blink::mojom::WebBluetoothResult::SERVICE_NOT_FOUND
1882 : blink::mojom::WebBluetoothResult::NO_SERVICES_FOUND,
Arthur Sonzognic686e8f2024-01-11 08:36:371883 std::nullopt /* services */);
ortuno67acd832016-04-30 00:13:221884}
1885
Reilly Grantb936f9e2021-04-29 21:04:431886void WebBluetoothServiceImpl::OnGetDevice(
tzikcf7bcd652017-06-15 04:19:301887 RequestDeviceCallback callback,
Reilly Grantb936f9e2021-04-29 21:04:431888 blink::mojom::WebBluetoothResult result,
ortunob6374dd82016-05-27 03:04:071889 blink::mojom::WebBluetoothRequestDeviceOptionsPtr options,
1890 const std::string& device_address) {
1891 device_chooser_controller_.reset();
1892
Reilly Grantb936f9e2021-04-29 21:04:431893 if (result != blink::mojom::WebBluetoothResult::SUCCESS) {
1894 // Errors are recorded by |device_chooser_controller_|.
1895 std::move(callback).Run(result, /*device=*/nullptr);
1896 return;
1897 }
1898
Chris Mumford2f49c612021-04-21 17:54:201899 const BluetoothDevice* const device = GetAdapter()->GetDevice(device_address);
ortunob6374dd82016-05-27 03:04:071900 if (device == nullptr) {
dougt16af7e82017-01-13 23:20:081901 DVLOG(1) << "Device " << device_address << " no longer in adapter";
tzikcf7bcd652017-06-15 04:19:301902 std::move(callback).Run(
1903 blink::mojom::WebBluetoothResult::CHOSEN_DEVICE_VANISHED,
1904 nullptr /* device */);
ortunob6374dd82016-05-27 03:04:071905 return;
1906 }
1907
dougt16af7e82017-01-13 23:20:081908 DVLOG(1) << "Device: " << device->GetNameForDisplay();
ortunob6374dd82016-05-27 03:04:071909
Doug Turner23a658b92018-12-20 01:57:541910 auto web_bluetooth_device = blink::mojom::WebBluetoothDevice::New();
Ovidio Henriquezc3adec272020-03-03 19:40:591911 if (base::FeatureList::IsEnabled(
1912 features::kWebBluetoothNewPermissionsBackend)) {
1913 BluetoothDelegate* delegate =
1914 GetContentClient()->browser()->GetBluetoothDelegate();
1915 if (!delegate) {
1916 std::move(callback).Run(
1917 blink::mojom::WebBluetoothResult::WEB_BLUETOOTH_NOT_SUPPORTED,
1918 /*device=*/nullptr);
1919 return;
1920 }
Ovidio Henriquez3d729f62020-02-07 00:43:291921 web_bluetooth_device->id = delegate->GrantServiceAccessPermission(
danakjc70aec1f2022-07-07 15:48:191922 &render_frame_host(), device, options.get());
Ovidio Henriquez3d729f62020-02-07 00:43:291923 } else {
1924 web_bluetooth_device->id =
1925 allowed_devices().AddDevice(device_address, options);
1926 }
Doug Turner23a658b92018-12-20 01:57:541927 web_bluetooth_device->name = device->GetName();
ortunob6374dd82016-05-27 03:04:071928
tzikcf7bcd652017-06-15 04:19:301929 std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS,
Doug Turner23a658b92018-12-20 01:57:541930 std::move(web_bluetooth_device));
ortunob6374dd82016-05-27 03:04:071931}
1932
Chris Mumfordf59d16b2021-06-23 00:32:111933void WebBluetoothServiceImpl::OnCreateGATTConnection(
Ovidio Henriquez0e8ab7072019-05-31 21:38:071934 const blink::WebBluetoothDeviceId& device_id,
Henrique Ferreirodb47b3e2019-09-11 14:29:391935 mojo::AssociatedRemote<blink::mojom::WebBluetoothServerClient> client,
tzikcf7bcd652017-06-15 04:19:301936 RemoteServerConnectCallback callback,
Chris Mumfordf59d16b2021-06-23 00:32:111937 std::unique_ptr<BluetoothGattConnection> connection,
Arthur Sonzognic686e8f2024-01-11 08:36:371938 std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
ortunob6c45d4f2016-05-07 04:19:421939 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Chengwei Hsieh3de031c32025-08-01 23:32:021940
1941 if (base::FeatureList::IsEnabled(
1942 blink::features::kWebBluetoothCancelConnect)) {
1943 pending_connection_device_ids_.erase(device_id);
1944 }
Chris Mumfordf59d16b2021-06-23 00:32:111945 if (error_code.has_value()) {
Chris Mumfordf59d16b2021-06-23 00:32:111946 std::move(callback).Run(TranslateConnectErrorAndRecord(error_code.value()));
1947 return;
1948 }
Daniel Chengafaabacf2024-09-20 01:23:001949 RecordConnectGATTOutcome(UMAConnectGATTOutcome::kSuccess);
ortunob6c45d4f2016-05-07 04:19:421950
juncai5fbf7e62017-03-23 21:21:561951 if (connected_devices_->IsConnectedToDeviceWithId(device_id)) {
1952 DVLOG(1) << "Already connected.";
tzikcf7bcd652017-06-15 04:19:301953 std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS);
juncai5fbf7e62017-03-23 21:21:561954 return;
1955 }
1956
tzikcf7bcd652017-06-15 04:19:301957 std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS);
juncai5fbf7e62017-03-23 21:21:561958 connected_devices_->Insert(device_id, std::move(connection),
1959 std::move(client));
ortunob6c45d4f2016-05-07 04:19:421960}
1961
Chris Mumford6627ae42021-05-03 18:54:401962void WebBluetoothServiceImpl::OnCharacteristicReadValue(
Chris Mumford4d1cf30782021-10-05 23:02:471963 const std::string& characteristic_instance_id,
tzikcf7bcd652017-06-15 04:19:301964 RemoteCharacteristicReadValueCallback callback,
Arthur Sonzognic686e8f2024-01-11 08:36:371965 std::optional<GattErrorCode> error_code,
ortuno12e91f072016-04-15 22:57:331966 const std::vector<uint8_t>& value) {
1967 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Chris Mumford6627ae42021-05-03 18:54:401968 if (error_code.has_value()) {
Chris Mumford4d1cf30782021-10-05 23:02:471969#if PAIR_BLUETOOTH_ON_DEMAND()
Lei Zhang494bc8b2022-09-20 16:52:411970 if (error_code.value() == GattErrorCode::kNotAuthorized ||
1971 error_code.value() == GattErrorCode::kNotPaired) {
Chris Mumford4d1cf30782021-10-05 23:02:471972 BluetoothDevice* device = GetCachedDevice(
1973 GetCharacteristicDeviceID(characteristic_instance_id));
1974 if (device && !device->IsPaired()) {
1975 // Initiate pairing. See (Secure Characteristics) in README.md for more
1976 // information.
1977 pairing_manager_->PairForCharacteristicReadValue(
1978 characteristic_instance_id, std::move(callback));
1979 return;
1980 }
1981 }
1982#endif // PAIR_BLUETOOTH_ON_DEMAND()
Chris Mumford40cd29352021-06-16 19:13:331983 std::move(callback).Run(
1984 TranslateGATTErrorAndRecord(error_code.value(),
1985 UMAGATTOperation::kCharacteristicRead),
Reilly Grant8002d42f2024-10-23 22:40:311986 /*value=*/{});
Chris Mumford6627ae42021-05-03 18:54:401987 return;
1988 }
Chris Mumford40cd29352021-06-16 19:13:331989 RecordCharacteristicReadValueOutcome(UMAGATTOperationOutcome::kSuccess);
tzikcf7bcd652017-06-15 04:19:301990 std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS, value);
ortuno12e91f072016-04-15 22:57:331991}
1992
dougt4f2237c2017-01-14 04:14:131993void WebBluetoothServiceImpl::OnCharacteristicWriteValueSuccess(
tzikcf7bcd652017-06-15 04:19:301994 RemoteCharacteristicWriteValueCallback callback) {
ortunoad6b0fea2016-03-31 18:49:111995 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Chris Mumford40cd29352021-06-16 19:13:331996 RecordCharacteristicWriteValueOutcome(UMAGATTOperationOutcome::kSuccess);
tzikcf7bcd652017-06-15 04:19:301997 std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS);
ortunoad6b0fea2016-03-31 18:49:111998}
1999
dougt4f2237c2017-01-14 04:14:132000void WebBluetoothServiceImpl::OnCharacteristicWriteValueFailed(
Chris Mumford4d1cf30782021-10-05 23:02:472001 const std::string& characteristic_instance_id,
2002 const std::vector<uint8_t>& value,
2003 blink::mojom::WebBluetoothWriteType write_type,
tzikcf7bcd652017-06-15 04:19:302004 RemoteCharacteristicWriteValueCallback callback,
Chris Mumforda0e55f372021-09-10 22:47:262005 GattErrorCode error_code) {
ortunoad6b0fea2016-03-31 18:49:112006 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Chris Mumford4d1cf30782021-10-05 23:02:472007
2008#if PAIR_BLUETOOTH_ON_DEMAND()
Lei Zhang494bc8b2022-09-20 16:52:412009 if (error_code == GattErrorCode::kNotAuthorized) {
Chris Mumford4d1cf30782021-10-05 23:02:472010 BluetoothDevice* device =
2011 GetCachedDevice(GetCharacteristicDeviceID(characteristic_instance_id));
2012 if (device && !device->IsPaired()) {
2013 // Initiate pairing. See (Secure Characteristics) in README.md for more
2014 // information.
2015 pairing_manager_->PairForCharacteristicWriteValue(
2016 characteristic_instance_id, value, write_type, std::move(callback));
2017 return;
2018 }
2019 }
2020#endif // PAIR_BLUETOOTH_ON_DEMAND()
2021
Chris Mumford40cd29352021-06-16 19:13:332022 std::move(callback).Run(TranslateGATTErrorAndRecord(
2023 error_code, UMAGATTOperation::kCharacteristicWrite));
ortunoad6b0fea2016-03-31 18:49:112024}
2025
ortunobc3bce12016-04-15 21:22:552026void WebBluetoothServiceImpl::OnStartNotifySessionSuccess(
tzikcf7bcd652017-06-15 04:19:302027 RemoteCharacteristicStartNotificationsCallback callback,
Chris Mumford2f49c612021-04-21 17:54:202028 std::unique_ptr<BluetoothGattNotifySession> notify_session) {
ortunobc3bce12016-04-15 21:22:552029 DCHECK_CURRENTLY_ON(BrowserThread::UI);
tzikcf7bcd652017-06-15 04:19:302030 std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS);
Chris Mumforde6fa96a42021-07-20 00:15:142031 std::string characteristic_id = notify_session->GetCharacteristicIdentifier();
2032 auto iter = characteristic_id_to_notify_session_.find(characteristic_id);
2033
2034 if (iter == characteristic_id_to_notify_session_.end())
2035 return;
juncai5fbf7e62017-03-23 21:21:562036 // Saving the BluetoothGattNotifySession keeps notifications active.
Chris Mumforde6fa96a42021-07-20 00:15:142037 iter->second->gatt_notify_session = std::move(notify_session);
2038
2039 // Continue any deferred notification starts.
2040 auto deferred_iter =
2041 characteristic_id_to_deferred_start_.find(characteristic_id);
2042 if (deferred_iter != characteristic_id_to_deferred_start_.end()) {
2043 base::queue<std::unique_ptr<DeferredStartNotificationData>> deferral_queue =
2044 std::move(deferred_iter->second);
2045 characteristic_id_to_deferred_start_.erase(deferred_iter);
2046 while (!deferral_queue.empty()) {
2047 RemoteCharacteristicStartNotifications(
2048 characteristic_id, std::move(deferral_queue.front()->client),
2049 std::move(deferral_queue.front()->callback));
2050 deferral_queue.pop();
2051 }
2052 }
ortunobc3bce12016-04-15 21:22:552053}
2054
2055void WebBluetoothServiceImpl::OnStartNotifySessionFailed(
tzikcf7bcd652017-06-15 04:19:302056 RemoteCharacteristicStartNotificationsCallback callback,
Chris Mumforde6fa96a42021-07-20 00:15:142057 const std::string& characteristic_instance_id,
Chris Mumforda0e55f372021-09-10 22:47:262058 GattErrorCode error_code) {
ortunobc3bce12016-04-15 21:22:552059 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Chris Mumforde6fa96a42021-07-20 00:15:142060 auto iter =
2061 characteristic_id_to_notify_session_.find(characteristic_instance_id);
Chris Mumford4d1cf30782021-10-05 23:02:472062 mojo::AssociatedRemote<blink::mojom::WebBluetoothCharacteristicClient> client;
2063 if (iter != characteristic_id_to_notify_session_.end()) {
2064 client = std::move(iter->second->characteristic_client);
Chris Mumforde6fa96a42021-07-20 00:15:142065 characteristic_id_to_notify_session_.erase(iter);
Chris Mumford4d1cf30782021-10-05 23:02:472066 }
2067
2068#if PAIR_BLUETOOTH_ON_DEMAND()
Lei Zhang494bc8b2022-09-20 16:52:412069 if (error_code == GattErrorCode::kNotAuthorized && client) {
Chris Mumford4d1cf30782021-10-05 23:02:472070 BluetoothDevice* device =
2071 GetCachedDevice(GetCharacteristicDeviceID(characteristic_instance_id));
2072 if (device && !device->IsPaired()) {
2073 // Initiate pairing. See (Secure Characteristics) in README.md for more
2074 // information.
2075 pairing_manager_->PairForCharacteristicStartNotifications(
2076 characteristic_instance_id, std::move(client), std::move(callback));
2077 return;
2078 }
2079 }
2080#endif // PAIR_BLUETOOTH_ON_DEMAND()
2081
Chris Mumford40cd29352021-06-16 19:13:332082 std::move(callback).Run(TranslateGATTErrorAndRecord(
2083 error_code, UMAGATTOperation::kStartNotifications));
Chris Mumforde6fa96a42021-07-20 00:15:142084
2085 // Fail any deferred notification starts blocked on this one.
2086 auto deferred_iter =
2087 characteristic_id_to_deferred_start_.find(characteristic_instance_id);
2088 if (deferred_iter != characteristic_id_to_deferred_start_.end()) {
2089 base::queue<std::unique_ptr<DeferredStartNotificationData>> deferral_queue =
2090 std::move(deferred_iter->second);
2091 characteristic_id_to_deferred_start_.erase(deferred_iter);
2092 while (!deferral_queue.empty()) {
2093 // Run paused start callbacks with the same error code that caused the
2094 // first one to fail.
2095 std::move(deferral_queue.front()->callback)
2096 .Run(TranslateGATTErrorAndRecord(
2097 error_code, UMAGATTOperation::kStartNotifications));
2098 deferral_queue.pop();
2099 }
2100 }
ortunobc3bce12016-04-15 21:22:552101}
2102
2103void WebBluetoothServiceImpl::OnStopNotifySessionComplete(
2104 const std::string& characteristic_instance_id,
tzikcf7bcd652017-06-15 04:19:302105 RemoteCharacteristicStopNotificationsCallback callback) {
ortunobc3bce12016-04-15 21:22:552106 characteristic_id_to_notify_session_.erase(characteristic_instance_id);
tzikcf7bcd652017-06-15 04:19:302107 std::move(callback).Run();
ortunobc3bce12016-04-15 21:22:552108}
2109
Chris Mumford6627ae42021-05-03 18:54:402110void WebBluetoothServiceImpl::OnDescriptorReadValue(
Chris Mumford4d1cf30782021-10-05 23:02:472111 const std::string& descriptor_instance_id,
tzikcf7bcd652017-06-15 04:19:302112 RemoteDescriptorReadValueCallback callback,
Arthur Sonzognic686e8f2024-01-11 08:36:372113 std::optional<GattErrorCode> error_code,
dougta2fe055212017-01-27 05:35:302114 const std::vector<uint8_t>& value) {
2115 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Chris Mumford6627ae42021-05-03 18:54:402116 if (error_code.has_value()) {
Chris Mumford4d1cf30782021-10-05 23:02:472117#if PAIR_BLUETOOTH_ON_DEMAND()
Lei Zhang494bc8b2022-09-20 16:52:412118 if (error_code.value() == GattErrorCode::kNotAuthorized) {
Chris Mumford4d1cf30782021-10-05 23:02:472119 BluetoothDevice* device =
2120 GetCachedDevice(GetDescriptorDeviceId(descriptor_instance_id));
2121 if (device && !device->IsPaired()) {
2122 // Initiate pairing. See (Secure Characteristics) in README.md for more
2123 // information.
2124 pairing_manager_->PairForDescriptorReadValue(descriptor_instance_id,
2125 std::move(callback));
2126 return;
2127 }
2128 }
2129#endif // PAIR_BLUETOOTH_ON_DEMAND()
Chris Mumford40cd29352021-06-16 19:13:332130 std::move(callback).Run(
2131 TranslateGATTErrorAndRecord(error_code.value(),
2132 UMAGATTOperation::kDescriptorReadObsolete),
Reilly Grant8002d42f2024-10-23 22:40:312133 /*value=*/{});
Chris Mumford6627ae42021-05-03 18:54:402134 return;
2135 }
tzikcf7bcd652017-06-15 04:19:302136 std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS, value);
dougta2fe055212017-01-27 05:35:302137}
2138
dougtbe62e9d2017-02-01 16:13:552139void WebBluetoothServiceImpl::OnDescriptorWriteValueSuccess(
tzikcf7bcd652017-06-15 04:19:302140 RemoteDescriptorWriteValueCallback callback) {
dougtbe62e9d2017-02-01 16:13:552141 DCHECK_CURRENTLY_ON(BrowserThread::UI);
tzikcf7bcd652017-06-15 04:19:302142 std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS);
dougtbe62e9d2017-02-01 16:13:552143}
2144
2145void WebBluetoothServiceImpl::OnDescriptorWriteValueFailed(
Chris Mumford4d1cf30782021-10-05 23:02:472146 const std::string& descriptor_instance_id,
2147 const std::vector<uint8_t>& value,
tzikcf7bcd652017-06-15 04:19:302148 RemoteDescriptorWriteValueCallback callback,
Chris Mumforda0e55f372021-09-10 22:47:262149 GattErrorCode error_code) {
dougtbe62e9d2017-02-01 16:13:552150 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Chris Mumford4d1cf30782021-10-05 23:02:472151
2152#if PAIR_BLUETOOTH_ON_DEMAND()
Lei Zhang494bc8b2022-09-20 16:52:412153 if (error_code == GattErrorCode::kNotAuthorized) {
Chris Mumford4d1cf30782021-10-05 23:02:472154 BluetoothDevice* device =
2155 GetCachedDevice(GetDescriptorDeviceId(descriptor_instance_id));
2156 if (device && !device->IsPaired()) {
2157 // Initiate pairing. See (Secure Characteristics) in README.md for more
2158 // information.
2159 pairing_manager_->PairForDescriptorWriteValue(descriptor_instance_id,
2160 value, std::move(callback));
2161 return;
2162 }
2163 }
2164#endif // PAIR_BLUETOOTH_ON_DEMAND()
2165
Chris Mumford40cd29352021-06-16 19:13:332166 std::move(callback).Run(TranslateGATTErrorAndRecord(
2167 error_code, UMAGATTOperation::kDescriptorWriteObsolete));
dougtbe62e9d2017-02-01 16:13:552168}
2169
ortunob6374dd82016-05-27 03:04:072170CacheQueryResult WebBluetoothServiceImpl::QueryCacheForDevice(
Ovidio Henriquez0e8ab7072019-05-31 21:38:072171 const blink::WebBluetoothDeviceId& device_id) {
Lei Zhang9b0be802020-05-05 21:11:482172 std::string device_address;
Ovidio Henriquezc3adec272020-03-03 19:40:592173 if (base::FeatureList::IsEnabled(
2174 features::kWebBluetoothNewPermissionsBackend)) {
2175 BluetoothDelegate* delegate =
2176 GetContentClient()->browser()->GetBluetoothDelegate();
2177 if (delegate) {
2178 device_address =
danakjc70aec1f2022-07-07 15:48:192179 delegate->GetDeviceAddress(&render_frame_host(), device_id);
Ovidio Henriquezc3adec272020-03-03 19:40:592180 }
Ovidio Henriquez3d729f62020-02-07 00:43:292181 } else {
2182 device_address = allowed_devices().GetDeviceAddress(device_id);
2183 }
2184
ortunob6374dd82016-05-27 03:04:072185 if (device_address.empty()) {
Daniel Chengd6978b062023-11-16 00:11:382186 ReceivedBadMessage(bad_message::BDH_DEVICE_NOT_ALLOWED_FOR_ORIGIN);
Daniel Chengafaabacf2024-09-20 01:23:002187 return CacheQueryResult(CacheQueryOutcome::kBadRenderer);
ortunob6374dd82016-05-27 03:04:072188 }
2189
2190 CacheQueryResult result;
2191 result.device = GetAdapter()->GetDevice(device_address);
2192
2193 // When a device can't be found in the BluetoothAdapter, that generally
2194 // indicates that it's gone out of range. We reject with a NetworkError in
2195 // that case.
2196 if (result.device == nullptr) {
Daniel Chengafaabacf2024-09-20 01:23:002197 result.outcome = CacheQueryOutcome::kNoDevice;
ortunob6374dd82016-05-27 03:04:072198 }
2199 return result;
2200}
2201
ortuno67acd832016-04-30 00:13:222202CacheQueryResult WebBluetoothServiceImpl::QueryCacheForService(
2203 const std::string& service_instance_id) {
2204 auto device_iter = service_id_to_device_address_.find(service_instance_id);
2205
2206 // Kill the render, see "ID Not in Map Note" above.
2207 if (device_iter == service_id_to_device_address_.end()) {
Daniel Chengd6978b062023-11-16 00:11:382208 ReceivedBadMessage(bad_message::BDH_INVALID_SERVICE_ID);
Daniel Chengafaabacf2024-09-20 01:23:002209 return CacheQueryResult(CacheQueryOutcome::kBadRenderer);
ortuno67acd832016-04-30 00:13:222210 }
2211
Chris Mumford4d1cf30782021-10-05 23:02:472212 const blink::WebBluetoothDeviceId device_id =
2213 GetWebBluetoothDeviceId(device_iter->second);
2214
ortuno67acd832016-04-30 00:13:222215 // Kill the renderer if origin is not allowed to access the device.
Ovidio Henriquez3d729f62020-02-07 00:43:292216 if (!device_id.IsValid()) {
Daniel Chengd6978b062023-11-16 00:11:382217 ReceivedBadMessage(bad_message::BDH_DEVICE_NOT_ALLOWED_FOR_ORIGIN);
Daniel Chengafaabacf2024-09-20 01:23:002218 return CacheQueryResult(CacheQueryOutcome::kBadRenderer);
ortuno67acd832016-04-30 00:13:222219 }
2220
Ovidio Henriquez3d729f62020-02-07 00:43:292221 CacheQueryResult result = QueryCacheForDevice(device_id);
Daniel Chengafaabacf2024-09-20 01:23:002222 if (result.outcome != CacheQueryOutcome::kSuccess) {
ortuno67acd832016-04-30 00:13:222223 return result;
Daniel Chengafaabacf2024-09-20 01:23:002224 }
ortuno67acd832016-04-30 00:13:222225
2226 result.service = result.device->GetGattService(service_instance_id);
2227 if (result.service == nullptr) {
Daniel Chengafaabacf2024-09-20 01:23:002228 result.outcome = CacheQueryOutcome::kNoService;
Ovidio Henriquez3d729f62020-02-07 00:43:292229 return result;
2230 }
2231
2232 if (!IsAllowedToAccessService(device_id, result.service->GetUUID())) {
Daniel Chengd6978b062023-11-16 00:11:382233 ReceivedBadMessage(bad_message::BDH_SERVICE_NOT_ALLOWED_FOR_ORIGIN);
Daniel Chengafaabacf2024-09-20 01:23:002234 return CacheQueryResult(CacheQueryOutcome::kBadRenderer);
ortuno67acd832016-04-30 00:13:222235 }
2236 return result;
2237}
2238
ortunob0fb6a182016-04-27 01:45:262239CacheQueryResult WebBluetoothServiceImpl::QueryCacheForCharacteristic(
2240 const std::string& characteristic_instance_id) {
2241 auto characteristic_iter =
2242 characteristic_id_to_service_id_.find(characteristic_instance_id);
2243
2244 // Kill the render, see "ID Not in Map Note" above.
2245 if (characteristic_iter == characteristic_id_to_service_id_.end()) {
Daniel Chengd6978b062023-11-16 00:11:382246 ReceivedBadMessage(bad_message::BDH_INVALID_CHARACTERISTIC_ID);
Daniel Chengafaabacf2024-09-20 01:23:002247 return CacheQueryResult(CacheQueryOutcome::kBadRenderer);
ortunob0fb6a182016-04-27 01:45:262248 }
2249
ortuno67acd832016-04-30 00:13:222250 CacheQueryResult result = QueryCacheForService(characteristic_iter->second);
ortunob0fb6a182016-04-27 01:45:262251
Daniel Chengafaabacf2024-09-20 01:23:002252 if (result.outcome != CacheQueryOutcome::kSuccess) {
ortunob0fb6a182016-04-27 01:45:262253 return result;
2254 }
2255
2256 result.characteristic =
2257 result.service->GetCharacteristic(characteristic_instance_id);
2258
2259 if (result.characteristic == nullptr) {
Daniel Chengafaabacf2024-09-20 01:23:002260 result.outcome = CacheQueryOutcome::kNoCharacteristic;
ortunob0fb6a182016-04-27 01:45:262261 }
2262
2263 return result;
2264}
2265
dougta2fe055212017-01-27 05:35:302266CacheQueryResult WebBluetoothServiceImpl::QueryCacheForDescriptor(
2267 const std::string& descriptor_instance_id) {
2268 auto descriptor_iter =
2269 descriptor_id_to_characteristic_id_.find(descriptor_instance_id);
2270
2271 // Kill the render, see "ID Not in Map Note" above.
2272 if (descriptor_iter == descriptor_id_to_characteristic_id_.end()) {
Daniel Chengd6978b062023-11-16 00:11:382273 ReceivedBadMessage(bad_message::BDH_INVALID_DESCRIPTOR_ID);
Daniel Chengafaabacf2024-09-20 01:23:002274 return CacheQueryResult(CacheQueryOutcome::kBadRenderer);
dougta2fe055212017-01-27 05:35:302275 }
2276
2277 CacheQueryResult result =
2278 QueryCacheForCharacteristic(descriptor_iter->second);
2279
Daniel Chengafaabacf2024-09-20 01:23:002280 if (result.outcome != CacheQueryOutcome::kSuccess) {
dougta2fe055212017-01-27 05:35:302281 return result;
2282 }
2283
2284 result.descriptor =
2285 result.characteristic->GetDescriptor(descriptor_instance_id);
2286
2287 if (result.descriptor == nullptr) {
Daniel Chengafaabacf2024-09-20 01:23:002288 result.outcome = CacheQueryOutcome::kNoDescriptor;
dougta2fe055212017-01-27 05:35:302289 }
2290
2291 return result;
2292}
2293
Giovanni Ortuño Urquidi8688a0d2017-05-20 06:12:322294void WebBluetoothServiceImpl::RunPendingPrimaryServicesRequests(
Chris Mumford2f49c612021-04-21 17:54:202295 BluetoothDevice* device) {
Giovanni Ortuño Urquidi8688a0d2017-05-20 06:12:322296 const std::string& device_address = device->GetAddress();
2297
2298 auto iter = pending_primary_services_requests_.find(device_address);
2299 if (iter == pending_primary_services_requests_.end()) {
2300 return;
2301 }
2302 std::vector<PrimaryServicesRequestCallback> requests =
2303 std::move(iter->second);
2304 pending_primary_services_requests_.erase(iter);
2305
tzikcf7bcd652017-06-15 04:19:302306 for (PrimaryServicesRequestCallback& request : requests) {
2307 std::move(request).Run(device);
Giovanni Ortuño Urquidi8688a0d2017-05-20 06:12:322308 }
2309
2310 // Sending get-service responses unexpectedly queued another request.
Jan Wilken Dörrie77c581a2019-06-07 16:25:062311 DCHECK(!base::Contains(pending_primary_services_requests_, device_address));
Giovanni Ortuño Urquidi8688a0d2017-05-20 06:12:322312}
2313
ortunoad6b0fea2016-03-31 18:49:112314RenderProcessHost* WebBluetoothServiceImpl::GetRenderProcessHost() {
danakjc70aec1f2022-07-07 15:48:192315 return render_frame_host().GetProcess();
ortunoad6b0fea2016-03-31 18:49:112316}
2317
Chris Mumford2f49c612021-04-21 17:54:202318BluetoothAdapter* WebBluetoothServiceImpl::GetAdapter() {
ortuno189e9582016-07-08 20:06:452319 return BluetoothAdapterFactoryWrapper::Get().GetAdapter(this);
ortunoad6b0fea2016-03-31 18:49:112320}
2321
Daniel Chengd6978b062023-11-16 00:11:382322void WebBluetoothServiceImpl::ReceivedBadMessage(
ortunoad6b0fea2016-03-31 18:49:112323 bad_message::BadMessageReason reason) {
2324 bad_message::ReceivedBadMessage(GetRenderProcessHost(), reason);
Daniel Chengd6978b062023-11-16 00:11:382325 // Ideally, this would use receiver_.ReportBadMessage(), but for legacy
2326 // reasons, the Bluetooth service code uses the BadMessageReason enum, which
2327 // is incompatible.
2328 receiver_.reset();
ortunoad6b0fea2016-03-31 18:49:112329}
2330
juncaif70c51172017-02-10 23:49:172331BluetoothAllowedDevices& WebBluetoothServiceImpl::allowed_devices() {
Gabriel Britoaa269ba2022-05-17 00:42:092332 // We should use the embedding origin so that permission delegation using
2333 // Permissions Policy works correctly.
2334 const url::Origin& embedding_origin =
danakjc70aec1f2022-07-07 15:48:192335 render_frame_host().GetMainFrame()->GetLastCommittedOrigin();
juncaif70c51172017-02-10 23:49:172336 StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
Lukasz Anforowiczb9a969a2021-04-29 15:26:252337 web_contents()->GetBrowserContext()->GetDefaultStoragePartition());
Joshua Bell3711eb12020-01-30 20:14:192338 return partition->GetBluetoothAllowedDevicesMap()->GetOrCreateAllowedDevices(
Gabriel Britoaa269ba2022-05-17 00:42:092339 embedding_origin);
juncaif70c51172017-02-10 23:49:172340}
2341
Jun Cai732a05e32019-05-29 19:34:192342void WebBluetoothServiceImpl::StoreAllowedScanOptions(
2343 const blink::mojom::WebBluetoothRequestLEScanOptions& options) {
2344 if (options.filters.has_value()) {
2345 for (const auto& filter : options.filters.value())
2346 allowed_scan_filters_.push_back(filter.Clone());
2347 } else {
2348 accept_all_advertisements_ = true;
2349 }
2350}
2351
2352bool WebBluetoothServiceImpl::AreScanFiltersAllowed(
Arthur Sonzognic686e8f2024-01-11 08:36:372353 const std::optional<ScanFilters>& filters) const {
Jun Cai732a05e32019-05-29 19:34:192354 if (accept_all_advertisements_) {
2355 // Previously allowed accepting all advertisements and no filters. In this
2356 // case since filtered advertisements are a subset of all advertisements,
2357 // any filters should be allowed.
2358 return true;
2359 }
2360
2361 if (!filters.has_value()) {
2362 // |acceptAllAdvertisements| is set in the Bluetooth scanning options, but
2363 // accepting all advertisements has not been allowed yet, in this case the
2364 // permission prompt needs to be shown to the user.
2365 return false;
2366 }
2367
Ovidio Henriquez887bb132020-06-04 21:45:552368 // If each |filter| in |filters| can be found in |allowed_scan_filters_|, then
2369 // |filters| are allowed, otherwise |filters| are not allowed.
Jun Cai732a05e32019-05-29 19:34:192370 for (const auto& filter : filters.value()) {
2371 bool allowed = false;
2372 for (const auto& allowed_filter : allowed_scan_filters_) {
2373 if (AreScanFiltersSame(*filter, *allowed_filter)) {
2374 allowed = true;
2375 break;
2376 }
2377 }
2378
2379 if (!allowed)
2380 return false;
2381 }
2382
2383 return true;
2384}
2385
Ovidio Henriquez887bb132020-06-04 21:45:552386void WebBluetoothServiceImpl::ClearAdvertisementClients() {
Ovidio Henriquez405c0142019-11-14 23:04:472387 scanning_clients_.clear();
Ovidio Henriquez887bb132020-06-04 21:45:552388 watch_advertisements_clients_.clear();
Jun Cai732a05e32019-05-29 19:34:192389 allowed_scan_filters_.clear();
2390 accept_all_advertisements_ = false;
ortunoe2d1eb72016-04-20 00:32:582391}
2392
Ovidio Henriquez3d729f62020-02-07 00:43:292393bool WebBluetoothServiceImpl::IsAllowedToAccessAtLeastOneService(
2394 const blink::WebBluetoothDeviceId& device_id) {
Ovidio Henriquezc3adec272020-03-03 19:40:592395 if (base::FeatureList::IsEnabled(
2396 features::kWebBluetoothNewPermissionsBackend)) {
2397 BluetoothDelegate* delegate =
2398 GetContentClient()->browser()->GetBluetoothDelegate();
2399 if (!delegate)
2400 return false;
danakjc70aec1f2022-07-07 15:48:192401 return delegate->IsAllowedToAccessAtLeastOneService(&render_frame_host(),
Ovidio Henriquez3d729f62020-02-07 00:43:292402 device_id);
Ovidio Henriquez3d729f62020-02-07 00:43:292403 }
Ovidio Henriquezbbc7853c2020-09-17 22:36:562404 return allowed_devices().IsAllowedToAccessAtLeastOneService(device_id);
Ovidio Henriquez3d729f62020-02-07 00:43:292405}
2406
2407bool WebBluetoothServiceImpl::IsAllowedToAccessService(
2408 const blink::WebBluetoothDeviceId& device_id,
Chris Mumford2f49c612021-04-21 17:54:202409 const BluetoothUUID& service) {
Ovidio Henriquezc3adec272020-03-03 19:40:592410 if (base::FeatureList::IsEnabled(
2411 features::kWebBluetoothNewPermissionsBackend)) {
2412 BluetoothDelegate* delegate =
2413 GetContentClient()->browser()->GetBluetoothDelegate();
2414 if (!delegate)
2415 return false;
danakjc70aec1f2022-07-07 15:48:192416 return delegate->IsAllowedToAccessService(&render_frame_host(), device_id,
Ovidio Henriquez3d729f62020-02-07 00:43:292417 service);
Ovidio Henriquez3d729f62020-02-07 00:43:292418 }
Ovidio Henriquezbbc7853c2020-09-17 22:36:562419 return allowed_devices().IsAllowedToAccessService(device_id, service);
2420}
2421
2422bool WebBluetoothServiceImpl::IsAllowedToAccessManufacturerData(
2423 const blink::WebBluetoothDeviceId& device_id,
2424 uint16_t manufacturer_code) {
2425 if (base::FeatureList::IsEnabled(
2426 features::kWebBluetoothNewPermissionsBackend)) {
2427 BluetoothDelegate* delegate =
2428 GetContentClient()->browser()->GetBluetoothDelegate();
2429 if (!delegate)
2430 return false;
2431 return delegate->IsAllowedToAccessManufacturerData(
danakjc70aec1f2022-07-07 15:48:192432 &render_frame_host(), device_id, manufacturer_code);
Ovidio Henriquezbbc7853c2020-09-17 22:36:562433 }
2434 return allowed_devices().IsAllowedToAccessManufacturerData(device_id,
2435 manufacturer_code);
Ovidio Henriquez3d729f62020-02-07 00:43:292436}
2437
Ovidio Henriquez887bb132020-06-04 21:45:552438bool WebBluetoothServiceImpl::HasActiveDiscoverySession() {
2439 return (ble_scan_discovery_session_ &&
2440 ble_scan_discovery_session_->IsActive()) ||
2441 (watch_advertisements_discovery_session_ &&
2442 watch_advertisements_discovery_session_->IsActive());
2443}
2444
Matt Reynolds8cca57d52025-02-20 18:20:592445void WebBluetoothServiceImpl::PreventBackForwardCache() {
2446 if (back_forward_cache_feature_handle_.IsValid()) {
2447 return;
2448 }
2449 back_forward_cache_feature_handle_ =
2450 RenderFrameHostImpl::From(&render_frame_host())
2451 ->RegisterBackForwardCacheDisablingNonStickyFeature(
2452 blink::scheduler::WebSchedulerTrackedFeature::kWebBluetooth);
2453}
2454
Chris Mumford02de39b2021-05-06 15:28:592455blink::WebBluetoothDeviceId WebBluetoothServiceImpl::GetCharacteristicDeviceID(
2456 const std::string& characteristic_instance_id) {
2457 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Chris Mumford02de39b2021-05-06 15:28:592458
2459 auto characteristic_iter =
2460 characteristic_id_to_service_id_.find(characteristic_instance_id);
2461 if (characteristic_iter == characteristic_id_to_service_id_.end())
Chris Mumford4d1cf30782021-10-05 23:02:472462 return blink::WebBluetoothDeviceId();
Chris Mumford02de39b2021-05-06 15:28:592463 auto device_iter =
2464 service_id_to_device_address_.find(characteristic_iter->second);
2465 if (device_iter == service_id_to_device_address_.end())
Chris Mumford4d1cf30782021-10-05 23:02:472466 return blink::WebBluetoothDeviceId();
Chris Mumford02de39b2021-05-06 15:28:592467
Chris Mumford4d1cf30782021-10-05 23:02:472468 return GetWebBluetoothDeviceId(device_iter->second);
Chris Mumford02de39b2021-05-06 15:28:592469}
2470
Chris Mumford550e0122021-06-17 00:32:322471BluetoothDevice* WebBluetoothServiceImpl::GetCachedDevice(
2472 const blink::WebBluetoothDeviceId& device_id) {
2473 DCHECK(device_id.IsValid());
2474 CacheQueryResult query_result = QueryCacheForDevice(device_id);
Daniel Chengafaabacf2024-09-20 01:23:002475 if (query_result.outcome != CacheQueryOutcome::kSuccess) {
Chris Mumford550e0122021-06-17 00:32:322476 return nullptr;
Daniel Chengafaabacf2024-09-20 01:23:002477 }
Chris Mumford550e0122021-06-17 00:32:322478
2479 return query_result.device;
2480}
2481
Chris Mumford61ee9ba2021-06-22 16:16:152482blink::WebBluetoothDeviceId WebBluetoothServiceImpl::GetDescriptorDeviceId(
2483 const std::string& descriptor_instance_id) {
2484 DCHECK_CURRENTLY_ON(BrowserThread::UI);
2485 auto iter = descriptor_id_to_characteristic_id_.find(descriptor_instance_id);
2486 if (iter == descriptor_id_to_characteristic_id_.end())
2487 return blink::WebBluetoothDeviceId();
2488
2489 return GetCharacteristicDeviceID(iter->second);
2490}
2491
Chris Mumford4d1cf30782021-10-05 23:02:472492blink::WebBluetoothDeviceId WebBluetoothServiceImpl::GetWebBluetoothDeviceId(
2493 const std::string& device_address) {
2494 if (base::FeatureList::IsEnabled(
2495 features::kWebBluetoothNewPermissionsBackend)) {
2496 BluetoothDelegate* delegate =
2497 GetContentClient()->browser()->GetBluetoothDelegate();
2498 if (!delegate)
2499 return blink::WebBluetoothDeviceId();
danakjc70aec1f2022-07-07 15:48:192500 return delegate->GetWebBluetoothDeviceId(&render_frame_host(),
Chris Mumford4d1cf30782021-10-05 23:02:472501 device_address);
2502 }
2503
2504 const blink::WebBluetoothDeviceId* device_id_ptr =
2505 allowed_devices().GetDeviceId(device_address);
2506 return device_id_ptr ? *device_id_ptr : blink::WebBluetoothDeviceId();
2507}
2508
Chris Mumford02de39b2021-05-06 15:28:592509void WebBluetoothServiceImpl::PairDevice(
2510 const blink::WebBluetoothDeviceId& device_id,
2511 BluetoothDevice::PairingDelegate* pairing_delegate,
Chris Mumfordf59d16b2021-06-23 00:32:112512 BluetoothDevice::ConnectCallback callback) {
Chris Mumford02de39b2021-05-06 15:28:592513 if (!device_id.IsValid()) {
Chris Mumfordf59d16b2021-06-23 00:32:112514 std::move(callback).Run(BluetoothDevice::ConnectErrorCode::ERROR_UNKNOWN);
Chris Mumford02de39b2021-05-06 15:28:592515 return;
2516 }
2517
Chris Mumford550e0122021-06-17 00:32:322518 BluetoothDevice* device = GetCachedDevice(device_id);
Chris Mumford02de39b2021-05-06 15:28:592519 if (!device) {
Chris Mumfordf59d16b2021-06-23 00:32:112520 std::move(callback).Run(BluetoothDevice::ConnectErrorCode::ERROR_UNKNOWN);
Chris Mumford02de39b2021-05-06 15:28:592521 return;
2522 }
2523
2524 DCHECK(!device->IsPaired());
2525
Chris Mumfordf59d16b2021-06-23 00:32:112526 device->Pair(pairing_delegate, std::move(callback));
Chris Mumford02de39b2021-05-06 15:28:592527}
2528
Chris Mumford550e0122021-06-17 00:32:322529void WebBluetoothServiceImpl::CancelPairing(
2530 const blink::WebBluetoothDeviceId& device_id) {
2531 DCHECK(device_id.IsValid());
2532
2533 BluetoothDevice* device = GetCachedDevice(device_id);
2534 if (!device)
2535 return;
2536
2537 device->CancelPairing();
2538}
2539
Chris Mumford4d1cf30782021-10-05 23:02:472540void WebBluetoothServiceImpl::SetPinCode(
2541 const blink::WebBluetoothDeviceId& device_id,
2542 const std::string& pincode) {
2543 DCHECK(device_id.IsValid());
2544
2545 BluetoothDevice* device = GetCachedDevice(device_id);
2546 if (!device)
2547 return;
2548
2549 device->SetPinCode(pincode);
2550}
2551
Alvin Ji3443b9b2022-06-17 19:57:472552void WebBluetoothServiceImpl::PairConfirmed(
2553 const blink::WebBluetoothDeviceId& device_id) {
2554 DCHECK(device_id.IsValid());
2555
2556 BluetoothDevice* device = GetCachedDevice(device_id);
2557 if (!device)
2558 return;
2559
2560 device->ConfirmPairing();
2561}
2562
Alvin Jif6100132022-07-07 21:53:432563void WebBluetoothServiceImpl::PromptForBluetoothPairing(
Chris Mumford4d1cf30782021-10-05 23:02:472564 const std::u16string& device_identifier,
Alvin Jif6100132022-07-07 21:53:432565 BluetoothDelegate::PairPromptCallback callback,
Alvin Jibab887b2022-07-27 00:59:542566 BluetoothDelegate::PairingKind pairing_kind,
Arthur Sonzognic686e8f2024-01-11 08:36:372567 const std::optional<std::u16string>& pin) {
Chris Mumford4d1cf30782021-10-05 23:02:472568 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Chris Mumford4d1cf30782021-10-05 23:02:472569 BluetoothDelegate* delegate =
2570 GetContentClient()->browser()->GetBluetoothDelegate();
Alvin Jif6100132022-07-07 21:53:432571
Chris Mumford4d1cf30782021-10-05 23:02:472572 if (!delegate) {
Alvin Jif6100132022-07-07 21:53:432573 std::move(callback).Run(BluetoothDelegate::PairPromptResult(
2574 BluetoothDelegate::PairPromptStatus::kCancelled));
Chris Mumford4d1cf30782021-10-05 23:02:472575 return;
2576 }
Chris Mumford4d1cf30782021-10-05 23:02:472577
Alvin Jif6100132022-07-07 21:53:432578 switch (pairing_kind) {
2579 case BluetoothDelegate::PairingKind::kConfirmOnly:
2580 case BluetoothDelegate::PairingKind::kProvidePin:
Alvin Jibab887b2022-07-27 00:59:542581 case BluetoothDelegate::PairingKind::kConfirmPinMatch:
Alvin Jif6100132022-07-07 21:53:432582 delegate->ShowDevicePairPrompt(&render_frame_host(), device_identifier,
Alvin Jibab887b2022-07-27 00:59:542583 std::move(callback), pairing_kind, pin);
Alvin Jif6100132022-07-07 21:53:432584 break;
2585 default:
Peter Boströmfc7ddc182024-10-31 19:37:212586 NOTREACHED();
Alvin Ji3443b9b2022-06-17 19:57:472587 }
Alvin Ji3443b9b2022-06-17 19:57:472588}
2589
Chris Mumford4d1cf30782021-10-05 23:02:472590#if PAIR_BLUETOOTH_ON_DEMAND()
2591void WebBluetoothServiceImpl::SetPairingManagerForTesting(
2592 std::unique_ptr<WebBluetoothPairingManager> pairing_manager) {
2593 pairing_manager_ = std::move(pairing_manager);
2594}
2595#endif // PAIR_BLUETOOTH_ON_DEMAND()
2596
ortunoad6b0fea2016-03-31 18:49:112597} // namespace content