summaryrefslogtreecommitdiff
path: root/chromium/content/browser/bluetooth
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/content/browser/bluetooth
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-85-based.tar.gz
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/content/browser/bluetooth')
-rw-r--r--chromium/content/browser/bluetooth/web_bluetooth_service_impl.cc704
-rw-r--r--chromium/content/browser/bluetooth/web_bluetooth_service_impl.h120
2 files changed, 530 insertions, 294 deletions
diff --git a/chromium/content/browser/bluetooth/web_bluetooth_service_impl.cc b/chromium/content/browser/bluetooth/web_bluetooth_service_impl.cc
index 847c0750756..a8271551e12 100644
--- a/chromium/content/browser/bluetooth/web_bluetooth_service_impl.cc
+++ b/chromium/content/browser/bluetooth/web_bluetooth_service_impl.cc
@@ -48,6 +48,7 @@
#include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom.h"
using device::BluetoothAdapterFactoryWrapper;
+using device::BluetoothGattCharacteristic;
using device::BluetoothUUID;
namespace content {
@@ -171,6 +172,181 @@ bool IsValidRequestScanOptions(
} // namespace
+class WebBluetoothServiceImpl::AdvertisementClient {
+ public:
+ virtual void SendEvent(blink::mojom::WebBluetoothAdvertisingEvent& event) = 0;
+
+ bool is_connected() { return client_.is_connected(); }
+
+ protected:
+ explicit AdvertisementClient(
+ WebBluetoothServiceImpl* service,
+ mojo::PendingAssociatedRemote<
+ blink::mojom::WebBluetoothAdvertisementClient> client_info)
+ : client_(std::move(client_info)) {
+ // Using base::Unretained() is safe here because all instances of this class
+ // will be owned by |service|.
+ client_.set_disconnect_handler(
+ base::BindOnce(&WebBluetoothServiceImpl::RemoveDisconnectedClients,
+ base::Unretained(service)));
+ }
+ virtual ~AdvertisementClient() = default;
+
+ mojo::AssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient> client_;
+};
+
+class WebBluetoothServiceImpl::WatchAdvertisementsClient
+ : public WebBluetoothServiceImpl::AdvertisementClient {
+ public:
+ WatchAdvertisementsClient(
+ WebBluetoothServiceImpl* service,
+ mojo::PendingAssociatedRemote<
+ blink::mojom::WebBluetoothAdvertisementClient> client_info,
+ blink::WebBluetoothDeviceId device_id)
+ : AdvertisementClient(service, std::move(client_info)),
+ device_id_(device_id) {
+ DCHECK(device_id_.IsValid());
+ }
+ ~WatchAdvertisementsClient() override = default;
+
+ // AdvertisementClient implementation:
+ void SendEvent(blink::mojom::WebBluetoothAdvertisingEvent& event) override {
+ if (event.device->id == device_id_)
+ client_->AdvertisingEvent(event.Clone());
+ }
+
+ blink::WebBluetoothDeviceId device_id() const { return device_id_; }
+
+ private:
+ blink::WebBluetoothDeviceId device_id_;
+};
+
+class WebBluetoothServiceImpl::ScanningClient
+ : public WebBluetoothServiceImpl::AdvertisementClient {
+ public:
+ ScanningClient(WebBluetoothServiceImpl* service,
+ mojo::PendingAssociatedRemote<
+ blink::mojom::WebBluetoothAdvertisementClient> client_info,
+ blink::mojom::WebBluetoothRequestLEScanOptionsPtr options,
+ RequestScanningStartCallback callback)
+ : AdvertisementClient(service, std::move(client_info)),
+ options_(std::move(options)),
+ callback_(std::move(callback)) {
+ DCHECK(options_->filters.has_value() ||
+ options_->accept_all_advertisements);
+ }
+
+ ~ScanningClient() override = default;
+
+ void SetPromptController(
+ BluetoothDeviceScanningPromptController* prompt_controller) {
+ prompt_controller_ = prompt_controller;
+ }
+
+ // AdvertisingClient implementation:
+ void SendEvent(blink::mojom::WebBluetoothAdvertisingEvent& event) override {
+ if (options_->accept_all_advertisements) {
+ if (prompt_controller_)
+ AddFilteredDeviceToPrompt(event.device->id.str(), event.name);
+
+ if (allow_send_event_)
+ client_->AdvertisingEvent(event.Clone());
+
+ return;
+ }
+
+ DCHECK(options_->filters.has_value());
+
+ // For every filter, we're going to check to see if a |name|, |name_prefix|,
+ // or |services| have been set. If one of these is set, we will check the
+ // scan result to see if it matches the filter's value. If it doesn't,
+ // we'll just continue with the next filter. If all of the properties in a
+ // filter have a match, we can post the AdvertisingEvent. Otherwise, we are
+ // going to drop it. This logic can be reduced a bit, but I think clarity
+ // will decrease.
+ for (auto& filter : options_->filters.value()) {
+ // Check to see if there is a direct match against the advertisement name
+ if (filter->name.has_value()) {
+ if (!event.name.has_value() ||
+ filter->name.value() != event.name.value()) {
+ continue;
+ }
+ }
+
+ // Check if there is a name prefix match
+ if (filter->name_prefix.has_value()) {
+ if (!event.name.has_value() ||
+ !base::StartsWith(event.name.value(), filter->name_prefix.value(),
+ base::CompareCase::SENSITIVE)) {
+ continue;
+ }
+ }
+
+ // Check to see if there is a service uuid match
+ if (filter->services.has_value()) {
+ auto it = std::find_if(
+ filter->services.value().begin(), filter->services.value().end(),
+ [&event](const BluetoothUUID& filter_uuid) {
+ return base::Contains(event.uuids, filter_uuid);
+ });
+ if (it == filter->services.value().end())
+ continue;
+ }
+
+ // TODO(crbug.com/707635): Support manufacturerData and serviceData
+ // filters.
+
+ if (prompt_controller_)
+ AddFilteredDeviceToPrompt(event.device->id.str(), event.name);
+
+ if (allow_send_event_)
+ client_->AdvertisingEvent(event.Clone());
+ return;
+ }
+ }
+
+ void RunRequestScanningStartCallback(
+ blink::mojom::WebBluetoothResult result) {
+ DCHECK(result == blink::mojom::WebBluetoothResult::SUCCESS ||
+ result == blink::mojom::WebBluetoothResult::SCANNING_BLOCKED ||
+ result == blink::mojom::WebBluetoothResult::PROMPT_CANCELED);
+ std::move(callback_).Run(result);
+ }
+
+ void set_prompt_controller(
+ BluetoothDeviceScanningPromptController* prompt_controller) {
+ prompt_controller_ = prompt_controller;
+ }
+
+ BluetoothDeviceScanningPromptController* prompt_controller() {
+ return prompt_controller_;
+ }
+
+ void set_allow_send_event(bool allow_send_event) {
+ allow_send_event_ = allow_send_event;
+ }
+
+ const blink::mojom::WebBluetoothRequestLEScanOptions& scan_options() {
+ return *options_;
+ }
+
+ private:
+ void AddFilteredDeviceToPrompt(
+ const std::string& device_id,
+ const base::Optional<std::string>& device_name) {
+ bool should_update_name = device_name.has_value();
+ base::string16 device_name_for_display =
+ base::UTF8ToUTF16(device_name.value_or(""));
+ prompt_controller_->AddFilteredDevice(device_id, should_update_name,
+ device_name_for_display);
+ }
+
+ bool allow_send_event_ = false;
+ blink::mojom::WebBluetoothRequestLEScanOptionsPtr options_;
+ RequestScanningStartCallback callback_;
+ BluetoothDeviceScanningPromptController* prompt_controller_ = nullptr;
+};
+
bool HasValidFilter(
const base::Optional<
std::vector<blink::mojom::WebBluetoothLeScanFilterPtr>>& filters) {
@@ -351,115 +527,6 @@ void WebBluetoothServiceImpl::OnBluetoothScanningPromptEvent(
}
}
-WebBluetoothServiceImpl::ScanningClient::ScanningClient(
- mojo::AssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
- client,
- blink::mojom::WebBluetoothRequestLEScanOptionsPtr options,
- RequestScanningStartCallback callback,
- BluetoothDeviceScanningPromptController* prompt_controller)
- : client_(std::move(client)),
- options_(std::move(options)),
- callback_(std::move(callback)),
- prompt_controller_(prompt_controller) {
- DCHECK(options_->filters.has_value() || options_->accept_all_advertisements);
- client_.set_disconnect_handler(base::BindOnce(
- &ScanningClient::DisconnectionHandler, base::Unretained(this)));
-}
-
-WebBluetoothServiceImpl::ScanningClient::~ScanningClient() {}
-
-bool WebBluetoothServiceImpl::ScanningClient::SendEvent(
- blink::mojom::WebBluetoothAdvertisingEventPtr result) {
- if (disconnected_)
- return false;
-
- if (options_->accept_all_advertisements) {
- if (prompt_controller_)
- AddFilteredDeviceToPrompt(result->device->id.str(), result->name);
-
- if (allow_send_event_)
- client_->AdvertisingEvent(std::move(result));
-
- return true;
- }
-
- DCHECK(options_->filters.has_value());
-
- // For every filter, we're going to check to see if a |name|, |name_prefix|,
- // or |services| have been set. If one of these is set, we will check the
- // scan result to see if it matches the filter's value. If it doesn't, we'll
- // just continue with the next filter. If all of the properties in a filter
- // have a match, we can post the AdvertisingEvent. Otherwise, we are going to
- // drop it. This logic can be reduced a bit, but I think clarity will
- // decrease.
-
- for (auto& filter : options_->filters.value()) {
- // Check to see if there is a direct match against the advertisement name
- if (filter->name.has_value()) {
- if (!result->name.has_value())
- continue;
-
- if (filter->name.value() != result->name.value())
- continue;
- }
-
- // Check if there is a name prefix match
- if (filter->name_prefix.has_value()) {
- if (!result->name.has_value())
- continue;
-
- if (!base::StartsWith(result->name.value(), filter->name_prefix.value(),
- base::CompareCase::SENSITIVE)) {
- continue;
- }
- }
- // Check to see if there is a service uuid match
- if (filter->services.has_value()) {
- bool found_uuid_match = false;
- for (auto& filter_uuid : filter->services.value()) {
- found_uuid_match = base::Contains(result->uuids, filter_uuid);
- if (found_uuid_match)
- break;
- }
- if (!found_uuid_match)
- continue;
- }
- // TODO(crbug.com/707635): Support manufacturerData and serviceData filters.
-
- if (prompt_controller_)
- AddFilteredDeviceToPrompt(result->device->id.str(), result->name);
-
- if (allow_send_event_)
- client_->AdvertisingEvent(std::move(result));
- return true;
- }
-
- // Event was filtered out.
- return true;
-}
-
-void WebBluetoothServiceImpl::ScanningClient::RunRequestScanningStartCallback(
- blink::mojom::WebBluetoothResult result) {
- DCHECK(result == blink::mojom::WebBluetoothResult::SUCCESS ||
- result == blink::mojom::WebBluetoothResult::SCANNING_BLOCKED ||
- result == blink::mojom::WebBluetoothResult::PROMPT_CANCELED);
- std::move(callback_).Run(result);
-}
-
-void WebBluetoothServiceImpl::ScanningClient::DisconnectionHandler() {
- disconnected_ = true;
-}
-
-void WebBluetoothServiceImpl::ScanningClient::AddFilteredDeviceToPrompt(
- const std::string& device_id,
- const base::Optional<std::string>& device_name) {
- bool should_update_name = device_name.has_value();
- base::string16 device_name_for_display =
- base::UTF8ToUTF16(device_name.value_or(""));
- prompt_controller_->AddFilteredDevice(device_id, should_update_name,
- device_name_for_display);
-}
-
void WebBluetoothServiceImpl::DidFinishNavigation(
NavigationHandle* navigation_handle) {
if (navigation_handle->HasCommitted() &&
@@ -472,13 +539,13 @@ void WebBluetoothServiceImpl::DidFinishNavigation(
void WebBluetoothServiceImpl::OnVisibilityChanged(Visibility visibility) {
if (visibility == content::Visibility::HIDDEN ||
visibility == content::Visibility::OCCLUDED) {
- ClearDeviceAdvertisementClients();
+ ClearAdvertisementClients();
}
}
void WebBluetoothServiceImpl::OnWebContentsLostFocus(
RenderWidgetHost* render_widget_host) {
- ClearDeviceAdvertisementClients();
+ ClearAdvertisementClients();
}
void WebBluetoothServiceImpl::AdapterPoweredChanged(
@@ -530,73 +597,66 @@ void WebBluetoothServiceImpl::DeviceAdvertisementReceived(
const device::BluetoothDevice::ManufacturerDataMap& manufacturer_data_map) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- if (!discovery_session_ || !discovery_session_->IsActive())
- return;
-
- auto client = scanning_clients_.begin();
- while (client != scanning_clients_.end()) {
- auto device = blink::mojom::WebBluetoothDevice::New();
- if (base::FeatureList::IsEnabled(
- features::kWebBluetoothNewPermissionsBackend)) {
- BluetoothDelegate* delegate =
- GetContentClient()->browser()->GetBluetoothDelegate();
- if (!delegate)
- return;
- device->id =
- delegate->AddScannedDevice(render_frame_host_, device_address);
- } else {
- device->id = allowed_devices().AddDevice(device_address);
- }
- device->name = device_name;
+ if (!HasActiveDiscoverySession())
+ return;
- auto result = blink::mojom::WebBluetoothAdvertisingEvent::New();
- result->device = std::move(device);
+ // Construct the WebBluetoothAdvertisingEvent.
+ auto device = blink::mojom::WebBluetoothDevice::New();
+ if (base::FeatureList::IsEnabled(
+ features::kWebBluetoothNewPermissionsBackend)) {
+ BluetoothDelegate* delegate =
+ GetContentClient()->browser()->GetBluetoothDelegate();
+ if (!delegate)
+ return;
+ device->id = delegate->AddScannedDevice(render_frame_host_, device_address);
+ } else {
+ device->id = allowed_devices().AddDevice(device_address);
+ }
+ device->name = device_name;
- result->name = advertisement_name;
+ auto result = blink::mojom::WebBluetoothAdvertisingEvent::New();
+ result->device = std::move(device);
- // Note about the default value for these optional types. On the other side
- // of this IPC, the receiver will be checking to see if |*_is_set| is true
- // before using the value. Here we chose reasonable defaults in case the
- // other side does something incorrect. We have to do this manual
- // serialization because mojo does not support optional primitive types.
- result->appearance_is_set = appearance.has_value();
- result->appearance = appearance.value_or(/*not present=*/0xffc0);
+ result->name = advertisement_name;
- result->rssi_is_set = rssi.has_value();
- result->rssi = rssi.value_or(/*invalid value=*/128);
+ // Note about the default value for these optional types. On the other side of
+ // this IPC, the receiver will be checking to see if |*_is_set| is true before
+ // using the value. Here we chose reasonable defaults in case the other side
+ // does something incorrect. We have to do this manual serialization because
+ // mojo does not support optional primitive types.
+ result->appearance_is_set = appearance.has_value();
+ result->appearance = appearance.value_or(/*not present=*/0xffc0);
- result->tx_power_is_set = tx_power.has_value();
- result->tx_power = tx_power.value_or(/*invalid value=*/128);
+ result->rssi_is_set = rssi.has_value();
+ result->rssi = rssi.value_or(/*invalid value=*/128);
- std::vector<device::BluetoothUUID> uuids;
- for (auto& uuid : advertised_uuids)
- uuids.push_back(device::BluetoothUUID(uuid.canonical_value()));
- result->uuids = std::move(uuids);
+ result->tx_power_is_set = tx_power.has_value();
+ result->tx_power = tx_power.value_or(/*invalid value=*/128);
- auto& manufacturer_data = result->manufacturer_data;
- for (auto& it : manufacturer_data_map)
- manufacturer_data.emplace(it.first, it.second);
+ std::vector<device::BluetoothUUID> uuids;
+ for (auto& uuid : advertised_uuids)
+ uuids.push_back(device::BluetoothUUID(uuid.canonical_value()));
+ result->uuids = std::move(uuids);
- base::flat_map<std::string, std::vector<uint8_t>> services;
- for (auto& it : service_data_map)
- services[it.first.canonical_value()] = it.second;
- result->service_data = std::move(services);
+ auto& manufacturer_data = result->manufacturer_data;
+ manufacturer_data.insert(manufacturer_data_map.begin(),
+ manufacturer_data_map.end());
- bool okay = (*client)->SendEvent(std::move(result));
- if (!okay) {
- client = scanning_clients_.erase(client);
- continue;
- }
+ std::vector<std::pair<std::string, std::vector<uint8_t>>> services;
+ for (auto& it : service_data_map)
+ services.emplace_back(it.first.canonical_value(), it.second);
+ result->service_data = base::flat_map<std::string, std::vector<uint8_t>>(
+ services.begin(), services.end());
- ++client;
- }
+ // TODO(https://crbug.com/1087007): These two classes can potentially be
+ // combined into the same container.
+ for (const auto& scanning_client : scanning_clients_)
+ scanning_client->SendEvent(*result);
- // If we don't have any bound clients, clean things up.
- if (scanning_clients_.empty()) {
- discovery_session_->Stop();
- discovery_session_ = nullptr;
- return;
- }
+ for (const auto& watch_advertisements_client : watch_advertisements_clients_)
+ watch_advertisements_client->SendEvent(*result);
+
+ MaybeStopDiscovery();
}
void WebBluetoothServiceImpl::GattServicesDiscovered(
@@ -1049,6 +1109,7 @@ void WebBluetoothServiceImpl::RemoteCharacteristicReadValue(
void WebBluetoothServiceImpl::RemoteCharacteristicWriteValue(
const std::string& characteristic_instance_id,
const std::vector<uint8_t>& value,
+ blink::mojom::WebBluetoothWriteType write_type,
RemoteCharacteristicWriteValueCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -1084,13 +1145,30 @@ void WebBluetoothServiceImpl::RemoteCharacteristicWriteValue(
// TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating
// the callee interface.
auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
- query_result.characteristic->WriteRemoteCharacteristic(
- value,
- base::BindOnce(
- &WebBluetoothServiceImpl::OnCharacteristicWriteValueSuccess,
- weak_ptr_factory_.GetWeakPtr(), copyable_callback),
+ base::OnceClosure write_callback = base::BindOnce(
+ &WebBluetoothServiceImpl::OnCharacteristicWriteValueSuccess,
+ weak_ptr_factory_.GetWeakPtr(), copyable_callback);
+ device::BluetoothGattCharacteristic::ErrorCallback write_error_callback =
base::BindOnce(&WebBluetoothServiceImpl::OnCharacteristicWriteValueFailed,
- weak_ptr_factory_.GetWeakPtr(), copyable_callback));
+ weak_ptr_factory_.GetWeakPtr(), copyable_callback);
+ using WebBluetoothWriteType = blink::mojom::WebBluetoothWriteType;
+ using WriteType = device::BluetoothRemoteGattCharacteristic::WriteType;
+ switch (write_type) {
+ case WebBluetoothWriteType::kWriteDefaultDeprecated:
+ query_result.characteristic->DeprecatedWriteRemoteCharacteristic(
+ value, std::move(write_callback), std::move(write_error_callback));
+ break;
+ case WebBluetoothWriteType::kWriteWithResponse:
+ query_result.characteristic->WriteRemoteCharacteristic(
+ value, WriteType::kWithResponse, std::move(write_callback),
+ std::move(write_error_callback));
+ break;
+ case WebBluetoothWriteType::kWriteWithoutResponse:
+ query_result.characteristic->WriteRemoteCharacteristic(
+ value, WriteType::kWithoutResponse, std::move(write_callback),
+ std::move(write_error_callback));
+ break;
+ }
}
void WebBluetoothServiceImpl::RemoteCharacteristicStartNotifications(
@@ -1263,15 +1341,30 @@ void WebBluetoothServiceImpl::RequestScanningStart(
RequestScanningStartCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- mojo::AssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient> client(
- std::move(client_info));
+ const url::Origin requesting_origin =
+ render_frame_host_->GetLastCommittedOrigin();
+ const url::Origin embedding_origin =
+ web_contents()->GetMainFrame()->GetLastCommittedOrigin();
+
+ bool blocked = GetContentClient()->browser()->IsBluetoothScanningBlocked(
+ web_contents()->GetBrowserContext(), requesting_origin, embedding_origin);
+ if (blocked) {
+ std::move(callback).Run(blink::mojom::WebBluetoothResult::SCANNING_BLOCKED);
+ return;
+ }
+
+ // The renderer should never send invalid options.
+ if (!IsValidRequestScanOptions(options)) {
+ CrashRendererAndClosePipe(bad_message::BDH_INVALID_OPTIONS);
+ return;
+ }
if (!GetAdapter()) {
if (BluetoothAdapterFactoryWrapper::Get().IsLowEnergySupported()) {
BluetoothAdapterFactoryWrapper::Get().AcquireAdapter(
this,
base::BindOnce(&WebBluetoothServiceImpl::RequestScanningStartImpl,
- weak_ptr_factory_.GetWeakPtr(), std::move(client),
+ weak_ptr_factory_.GetWeakPtr(), std::move(client_info),
std::move(options), std::move(callback)));
return;
}
@@ -1280,52 +1373,96 @@ void WebBluetoothServiceImpl::RequestScanningStart(
return;
}
- RequestScanningStartImpl(std::move(client), std::move(options),
+ RequestScanningStartImpl(std::move(client_info), std::move(options),
std::move(callback), GetAdapter());
}
-void WebBluetoothServiceImpl::RequestScanningStartImpl(
- mojo::AssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
- client,
- blink::mojom::WebBluetoothRequestLEScanOptionsPtr options,
- RequestScanningStartCallback callback,
- scoped_refptr<device::BluetoothAdapter> adapter) {
+void WebBluetoothServiceImpl::WatchAdvertisementsForDevice(
+ const blink::WebBluetoothDeviceId& device_id,
+ mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
+ client_info,
+ WatchAdvertisementsForDeviceCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- // The renderer should never send invalid options.
- if (!IsValidRequestScanOptions(options)) {
+ blink::mojom::WebBluetoothResult allowed_result = GetBluetoothAllowed();
+ if (allowed_result != blink::mojom::WebBluetoothResult::SUCCESS) {
+ std::move(callback).Run(allowed_result);
+ return;
+ }
+
+ // The renderer should never send an invalid |device_id|.
+ if (!device_id.IsValid()) {
CrashRendererAndClosePipe(bad_message::BDH_INVALID_OPTIONS);
return;
}
- if (!adapter) {
+ if (!GetAdapter()) {
+ if (BluetoothAdapterFactoryWrapper::Get().IsLowEnergySupported()) {
+ BluetoothAdapterFactoryWrapper::Get().AcquireAdapter(
+ this, base::BindOnce(
+ &WebBluetoothServiceImpl::WatchAdvertisementsForDeviceImpl,
+ weak_ptr_factory_.GetWeakPtr(), device_id,
+ std::move(client_info), std::move(callback)));
+ return;
+ }
std::move(callback).Run(
blink::mojom::WebBluetoothResult::BLUETOOTH_LOW_ENERGY_NOT_AVAILABLE);
return;
}
- const url::Origin requesting_origin =
- render_frame_host_->GetLastCommittedOrigin();
- const url::Origin embedding_origin =
- web_contents()->GetMainFrame()->GetLastCommittedOrigin();
+ WatchAdvertisementsForDeviceImpl(std::move(device_id), std::move(client_info),
+ std::move(callback), GetAdapter());
+}
- bool blocked = GetContentClient()->browser()->IsBluetoothScanningBlocked(
- web_contents()->GetBrowserContext(), requesting_origin, embedding_origin);
+void WebBluetoothServiceImpl::RemoveDisconnectedClients() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
- if (blocked) {
- std::move(callback).Run(blink::mojom::WebBluetoothResult::SCANNING_BLOCKED);
+ // TODO(https://crbug.com/1087007): These two classes can potentially be
+ // combined into the same container.
+ base::EraseIf(scanning_clients_,
+ [](const std::unique_ptr<ScanningClient>& client) {
+ return !client->is_connected();
+ });
+ base::EraseIf(watch_advertisements_clients_,
+ [](const std::unique_ptr<WatchAdvertisementsClient>& client) {
+ return !client->is_connected();
+ });
+ MaybeStopDiscovery();
+}
+
+void WebBluetoothServiceImpl::MaybeStopDiscovery() {
+ if (scanning_clients_.empty())
+ ble_scan_discovery_session_.reset();
+
+ if (watch_advertisements_clients_.empty())
+ watch_advertisements_discovery_session_.reset();
+}
+
+void WebBluetoothServiceImpl::RequestScanningStartImpl(
+ mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
+ client_info,
+ blink::mojom::WebBluetoothRequestLEScanOptionsPtr options,
+ RequestScanningStartCallback callback,
+ scoped_refptr<device::BluetoothAdapter> adapter) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (!adapter) {
+ std::move(callback).Run(
+ blink::mojom::WebBluetoothResult::BLUETOOTH_LOW_ENERGY_NOT_AVAILABLE);
return;
}
- if (discovery_callback_) {
+ if (request_scanning_start_callback_) {
std::move(callback).Run(blink::mojom::WebBluetoothResult::PROMPT_CANCELED);
return;
}
- if (discovery_session_) {
- if (AreScanFiltersAllowed(options->filters)) {
- auto scanning_client = std::make_unique<ScanningClient>(
- std::move(client), std::move(options), std::move(callback), nullptr);
+ if (ble_scan_discovery_session_) {
+ auto scanning_client = std::make_unique<ScanningClient>(
+ /*service=*/this, std::move(client_info), std::move(options),
+ std::move(callback));
+
+ if (AreScanFiltersAllowed(scanning_client->scan_options().filters)) {
scanning_client->RunRequestScanningStartCallback(
blink::mojom::WebBluetoothResult::SUCCESS);
scanning_client->set_allow_send_event(true);
@@ -1338,41 +1475,43 @@ void WebBluetoothServiceImpl::RequestScanningStartImpl(
device_scanning_prompt_controller_ =
std::make_unique<BluetoothDeviceScanningPromptController>(
this, render_frame_host_);
-
- scanning_clients_.push_back(std::make_unique<ScanningClient>(
- std::move(client), std::move(options), std::move(callback),
- device_scanning_prompt_controller_.get()));
+ scanning_client->SetPromptController(
+ device_scanning_prompt_controller_.get());
+ scanning_clients_.push_back(std::move(scanning_client));
device_scanning_prompt_controller_->ShowPermissionPrompt();
return;
}
- discovery_callback_ = std::move(callback);
+ request_scanning_start_callback_ = std::move(callback);
// TODO(https://crbug.com/969109): Since scanning without a filter wastes
// resources, we need use StartDiscoverySessionWithFilter() instead of
// StartDiscoverySession() here.
adapter->StartDiscoverySession(
- base::BindOnce(&WebBluetoothServiceImpl::OnStartDiscoverySession,
- weak_ptr_factory_.GetWeakPtr(), std::move(client),
- std::move(options)),
- base::BindOnce(&WebBluetoothServiceImpl::OnDiscoverySessionError,
- weak_ptr_factory_.GetWeakPtr()));
+ base::BindOnce(
+ &WebBluetoothServiceImpl::OnStartDiscoverySessionForScanning,
+ weak_ptr_factory_.GetWeakPtr(), std::move(client_info),
+ std::move(options)),
+ base::BindOnce(
+ &WebBluetoothServiceImpl::OnDiscoverySessionErrorForScanning,
+ weak_ptr_factory_.GetWeakPtr()));
}
-void WebBluetoothServiceImpl::OnStartDiscoverySession(
- mojo::AssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
- client,
+void WebBluetoothServiceImpl::OnStartDiscoverySessionForScanning(
+ mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
+ client_info,
blink::mojom::WebBluetoothRequestLEScanOptionsPtr options,
std::unique_ptr<device::BluetoothDiscoverySession> session) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- DCHECK(!discovery_session_);
+ DCHECK(!ble_scan_discovery_session_);
- discovery_session_ = std::move(session);
+ ble_scan_discovery_session_ = std::move(session);
- if (AreScanFiltersAllowed(options->filters)) {
- auto scanning_client = std::make_unique<ScanningClient>(
- std::move(client), std::move(options), std::move(discovery_callback_),
- nullptr);
+ auto scanning_client = std::make_unique<ScanningClient>(
+ /*service=*/this, std::move(client_info), std::move(options),
+ std::move(request_scanning_start_callback_));
+
+ if (AreScanFiltersAllowed(scanning_client->scan_options().filters)) {
scanning_client->RunRequestScanningStartCallback(
blink::mojom::WebBluetoothResult::SUCCESS);
scanning_client->set_allow_send_event(true);
@@ -1383,20 +1522,20 @@ void WebBluetoothServiceImpl::OnStartDiscoverySession(
device_scanning_prompt_controller_ =
std::make_unique<BluetoothDeviceScanningPromptController>(
this, render_frame_host_);
-
- scanning_clients_.push_back(std::make_unique<ScanningClient>(
- std::move(client), std::move(options), std::move(discovery_callback_),
- device_scanning_prompt_controller_.get()));
+ scanning_client->SetPromptController(
+ device_scanning_prompt_controller_.get());
+ scanning_clients_.push_back(std::move(scanning_client));
device_scanning_prompt_controller_->ShowPermissionPrompt();
}
-void WebBluetoothServiceImpl::OnDiscoverySessionError() {
+void WebBluetoothServiceImpl::OnDiscoverySessionErrorForScanning() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
device_scanning_prompt_controller_.reset();
- std::move(discovery_callback_)
+ std::move(request_scanning_start_callback_)
.Run(blink::mojom::WebBluetoothResult::NO_BLUETOOTH_ADAPTER);
+ ClearAdvertisementClients();
}
void WebBluetoothServiceImpl::RequestDeviceImpl(
@@ -1410,11 +1549,11 @@ void WebBluetoothServiceImpl::RequestDeviceImpl(
}
// Calls to requestDevice() require user activation (user gestures). We
- // should close any opened chooser when a duplicate requestDevice call is made
- // with the same user activation or when any gesture occurs outside of the
- // opened chooser. This does not happen on all platforms so we don't DCHECK
- // that the old one is closed. We destroy the old chooser before constructing
- // the new one to make sure they can't conflict.
+ // should close any opened chooser when a duplicate requestDevice call is
+ // made with the same user activation or when any gesture occurs outside
+ // of the opened chooser. This does not happen on all platforms so we
+ // don't DCHECK that the old one is closed. We destroy the old chooser
+ // before constructing the new one to make sure they can't conflict.
device_chooser_controller_.reset();
device_chooser_controller_.reset(new BluetoothDeviceChooserController(
@@ -1447,9 +1586,9 @@ void WebBluetoothServiceImpl::GetDevicesImpl(
return;
}
- // BluetoothAllowedDevices does not provide a way to get all of the permitted
- // devices, so instead return all of the allowed devices that are currently
- // known to the system.
+ // BluetoothAllowedDevices does not provide a way to get all of the
+ // permitted devices, so instead return all of the allowed devices that
+ // are currently known to the system.
std::vector<blink::mojom::WebBluetoothDevicePtr> web_bluetooth_devices;
for (const auto* device : adapter->GetDevices()) {
const blink::WebBluetoothDeviceId* device_id =
@@ -1463,6 +1602,91 @@ void WebBluetoothServiceImpl::GetDevicesImpl(
std::move(callback).Run(std::move(web_bluetooth_devices));
}
+void WebBluetoothServiceImpl::WatchAdvertisementsForDeviceImpl(
+ const blink::WebBluetoothDeviceId& device_id,
+ mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
+ client_info,
+ WatchAdvertisementsForDeviceCallback callback,
+ scoped_refptr<device::BluetoothAdapter> adapter) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (!adapter) {
+ std::move(callback).Run(
+ blink::mojom::WebBluetoothResult::BLUETOOTH_LOW_ENERGY_NOT_AVAILABLE);
+ return;
+ }
+
+ auto watch_advertisements_client =
+ std::make_unique<WatchAdvertisementsClient>(
+ /*service=*/this, std::move(client_info), std::move(device_id));
+ if (watch_advertisements_discovery_session_) {
+ watch_advertisements_clients_.push_back(
+ std::move(watch_advertisements_client));
+ std::move(callback).Run(blink::mojom::WebBluetoothResult::SUCCESS);
+ return;
+ }
+
+ // If |watch_advertismeents_callbacks_and_clients_| has more than one entry,
+ // then it means that a previous watch advertisements operation has already
+ // started a discovery session, so the |callback| and |client| for this
+ // operation needs to be stored until the start discovery operation is
+ // complete.
+ watch_advertisements_callbacks_and_clients_.emplace_back(
+ std::move(callback), std::move(watch_advertisements_client));
+ if (watch_advertisements_callbacks_and_clients_.size() > 1)
+ return;
+
+ // Not all platforms support filtering by address.
+ // TODO(https://crbug.com/969109): Use StartDiscoverySessionWithFilter() to
+ // filter out by MAC address when platforms provide this capability.
+ adapter->StartDiscoverySession(
+ base::BindOnce(&WebBluetoothServiceImpl::
+ OnStartDiscoverySessionForWatchAdvertisements,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::BindOnce(&WebBluetoothServiceImpl::
+ OnDiscoverySessionErrorForWatchAdvertisements,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void WebBluetoothServiceImpl::OnStartDiscoverySessionForWatchAdvertisements(
+ std::unique_ptr<device::BluetoothDiscoverySession> session) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(!watch_advertisements_discovery_session_);
+ watch_advertisements_discovery_session_ = std::move(session);
+
+ for (auto& callback_and_client :
+ watch_advertisements_callbacks_and_clients_) {
+ if (callback_and_client.second->is_connected()) {
+ watch_advertisements_clients_.push_back(
+ std::move(callback_and_client.second));
+ std::move(callback_and_client.first)
+ .Run(blink::mojom::WebBluetoothResult::SUCCESS);
+ continue;
+ }
+
+ std::move(callback_and_client.first)
+ .Run(blink::mojom::WebBluetoothResult::WATCH_ADVERTISEMENTS_ABORTED);
+ }
+
+ watch_advertisements_callbacks_and_clients_.clear();
+
+ // If a client was disconncted while a discovery session was being started,
+ // then there may not be any valid clients, so discovery should be stopped.
+ MaybeStopDiscovery();
+}
+
+void WebBluetoothServiceImpl::OnDiscoverySessionErrorForWatchAdvertisements() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ for (auto& callback_and_client :
+ watch_advertisements_callbacks_and_clients_) {
+ std::move(callback_and_client.first)
+ .Run(blink::mojom::WebBluetoothResult::NO_BLUETOOTH_ADAPTER);
+ }
+ watch_advertisements_callbacks_and_clients_.clear();
+ ClearAdvertisementClients();
+}
+
void WebBluetoothServiceImpl::RemoteServerGetPrimaryServicesImpl(
const blink::WebBluetoothDeviceId& device_id,
blink::mojom::WebBluetoothGATTQueryQuantity quantity,
@@ -1920,8 +2144,8 @@ bool WebBluetoothServiceImpl::AreScanFiltersAllowed(
return false;
}
- // If each |filter| in |filters| can be found in |allowed_scan_filters_|,
- // then |filters| are allowed, otherwise |filters| are not allowed.
+ // If each |filter| in |filters| can be found in |allowed_scan_filters_|, then
+ // |filters| are allowed, otherwise |filters| are not allowed.
for (const auto& filter : filters.value()) {
bool allowed = false;
for (const auto& allowed_filter : allowed_scan_filters_) {
@@ -1952,12 +2176,13 @@ void WebBluetoothServiceImpl::ClearState() {
new FrameConnectedBluetoothDevices(render_frame_host_));
device_chooser_controller_.reset();
device_scanning_prompt_controller_.reset();
- ClearDeviceAdvertisementClients();
+ ClearAdvertisementClients();
BluetoothAdapterFactoryWrapper::Get().ReleaseAdapter(this);
}
-void WebBluetoothServiceImpl::ClearDeviceAdvertisementClients() {
+void WebBluetoothServiceImpl::ClearAdvertisementClients() {
scanning_clients_.clear();
+ watch_advertisements_clients_.clear();
allowed_scan_filters_.clear();
accept_all_advertisements_ = false;
}
@@ -1993,4 +2218,11 @@ bool WebBluetoothServiceImpl::IsAllowedToAccessService(
}
}
+bool WebBluetoothServiceImpl::HasActiveDiscoverySession() {
+ return (ble_scan_discovery_session_ &&
+ ble_scan_discovery_session_->IsActive()) ||
+ (watch_advertisements_discovery_session_ &&
+ watch_advertisements_discovery_session_->IsActive());
+}
+
} // namespace content
diff --git a/chromium/content/browser/bluetooth/web_bluetooth_service_impl.h b/chromium/content/browser/bluetooth/web_bluetooth_service_impl.h
index ee48a997ce9..219bf42a5a3 100644
--- a/chromium/content/browser/bluetooth/web_bluetooth_service_impl.h
+++ b/chromium/content/browser/bluetooth/web_bluetooth_service_impl.h
@@ -10,6 +10,7 @@
#include <unordered_map>
#include <vector>
+#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/optional.h"
@@ -113,50 +114,9 @@ class CONTENT_EXPORT WebBluetoothServiceImpl
base::OnceCallback<void(device::BluetoothDevice*)>;
using ScanFilters = std::vector<blink::mojom::WebBluetoothLeScanFilterPtr>;
- class ScanningClient {
- public:
- ScanningClient(mojo::AssociatedRemote<
- blink::mojom::WebBluetoothAdvertisementClient> client,
- blink::mojom::WebBluetoothRequestLEScanOptionsPtr options,
- RequestScanningStartCallback callback,
- BluetoothDeviceScanningPromptController* prompt_controller);
- ~ScanningClient();
- bool SendEvent(blink::mojom::WebBluetoothAdvertisingEventPtr result);
-
- void set_prompt_controller(
- BluetoothDeviceScanningPromptController* prompt_controller) {
- prompt_controller_ = prompt_controller;
- }
-
- BluetoothDeviceScanningPromptController* prompt_controller() {
- return prompt_controller_;
- }
-
- void set_allow_send_event(bool allow_send_event) {
- allow_send_event_ = allow_send_event;
- }
-
- void RunRequestScanningStartCallback(
- blink::mojom::WebBluetoothResult result);
-
- const blink::mojom::WebBluetoothRequestLEScanOptions& scan_options() {
- return *options_;
- }
-
- private:
- void DisconnectionHandler();
- void AddFilteredDeviceToPrompt(
- const std::string& device_id,
- const base::Optional<std::string>& device_name);
-
- bool disconnected_ = false;
- bool allow_send_event_ = false;
- mojo::AssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
- client_;
- blink::mojom::WebBluetoothRequestLEScanOptionsPtr options_;
- RequestScanningStartCallback callback_;
- BluetoothDeviceScanningPromptController* prompt_controller_;
- };
+ class AdvertisementClient;
+ class WatchAdvertisementsClient;
+ class ScanningClient;
// WebContentsObserver:
// These functions should always check that the affected RenderFrameHost
@@ -225,6 +185,7 @@ class CONTENT_EXPORT WebBluetoothServiceImpl
void RemoteCharacteristicWriteValue(
const std::string& characteristic_instance_id,
const std::vector<uint8_t>& value,
+ blink::mojom::WebBluetoothWriteType write_type,
RemoteCharacteristicWriteValueCallback callback) override;
void RemoteCharacteristicStartNotifications(
const std::string& characteristic_instance_id,
@@ -248,9 +209,14 @@ class CONTENT_EXPORT WebBluetoothServiceImpl
RemoteDescriptorWriteValueCallback callback) override;
void RequestScanningStart(
mojo::PendingAssociatedRemote<
- blink::mojom::WebBluetoothAdvertisementClient> client,
+ blink::mojom::WebBluetoothAdvertisementClient> client_info,
blink::mojom::WebBluetoothRequestLEScanOptionsPtr options,
RequestScanningStartCallback callback) override;
+ void WatchAdvertisementsForDevice(
+ const blink::WebBluetoothDeviceId& device_id,
+ mojo::PendingAssociatedRemote<
+ blink::mojom::WebBluetoothAdvertisementClient> client_info,
+ WatchAdvertisementsForDeviceCallback callback) override;
void RequestDeviceImpl(
blink::mojom::WebBluetoothRequestDeviceOptionsPtr options,
@@ -260,20 +226,38 @@ class CONTENT_EXPORT WebBluetoothServiceImpl
void GetDevicesImpl(GetDevicesCallback callback,
scoped_refptr<device::BluetoothAdapter> adapter);
+ // Callbacks for BLE scanning.
void RequestScanningStartImpl(
- mojo::AssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
- client,
+ mojo::PendingAssociatedRemote<
+ blink::mojom::WebBluetoothAdvertisementClient> client_info,
blink::mojom::WebBluetoothRequestLEScanOptionsPtr options,
RequestScanningStartCallback callback,
scoped_refptr<device::BluetoothAdapter> adapter);
-
- void OnStartDiscoverySession(
- mojo::AssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
- client,
+ void OnStartDiscoverySessionForScanning(
+ mojo::PendingAssociatedRemote<
+ blink::mojom::WebBluetoothAdvertisementClient> client_info,
blink::mojom::WebBluetoothRequestLEScanOptionsPtr options,
std::unique_ptr<device::BluetoothDiscoverySession> session);
+ void OnDiscoverySessionErrorForScanning();
- void OnDiscoverySessionError();
+ // Callbacks for watch advertisements for device.
+ void WatchAdvertisementsForDeviceImpl(
+ const blink::WebBluetoothDeviceId& device_id,
+ mojo::PendingAssociatedRemote<
+ blink::mojom::WebBluetoothAdvertisementClient> client_info,
+ WatchAdvertisementsForDeviceCallback callback,
+ scoped_refptr<device::BluetoothAdapter> adapter);
+ void OnStartDiscoverySessionForWatchAdvertisements(
+ std::unique_ptr<device::BluetoothDiscoverySession> session);
+ void OnDiscoverySessionErrorForWatchAdvertisements();
+
+ // Remove WatchAdvertisementsClients and ScanningClients with disconnected
+ // WebBluetoothAdvertisementClients from their respective containers.
+ void RemoveDisconnectedClients();
+
+ // Stop active discovery sessions and destroy them if there aren't any active
+ // AdvertisementClients.
+ void MaybeStopDiscovery();
// Should only be run after the services have been discovered for
// |device_address|.
@@ -392,13 +376,17 @@ class CONTENT_EXPORT WebBluetoothServiceImpl
void ClearState();
// Clears state associated with Bluetooth LE Scanning.
- void ClearDeviceAdvertisementClients();
+ void ClearAdvertisementClients();
bool IsAllowedToAccessAtLeastOneService(
const blink::WebBluetoothDeviceId& device_id);
bool IsAllowedToAccessService(const blink::WebBluetoothDeviceId& device_id,
const device::BluetoothUUID& service);
+ // Returns true if at least |ble_scan_discovery_session_| or
+ // |watch_advertisements_discovery_session_| is active.
+ bool HasActiveDiscoverySession();
+
// Used to open a BluetoothChooser and start a device discovery session.
std::unique_ptr<BluetoothDeviceChooserController> device_chooser_controller_;
@@ -429,14 +417,30 @@ class CONTENT_EXPORT WebBluetoothServiceImpl
RenderFrameHost* render_frame_host_;
// Keeps track of our BLE scanning session.
- std::unique_ptr<device::BluetoothDiscoverySession> discovery_session_;
-
- // This queues up start callback so that we only have one
- // BluetoothDiscoverySession start request at a time.
- RequestScanningStartCallback discovery_callback_;
+ std::unique_ptr<device::BluetoothDiscoverySession>
+ ble_scan_discovery_session_;
+
+ // Keeps track of our watch advertisements discovery session.
+ std::unique_ptr<device::BluetoothDiscoverySession>
+ watch_advertisements_discovery_session_;
+
+ // This queues up a scanning start callback so that we only have one
+ // BluetoothDiscoverySession start request at a time for a BLE scan.
+ RequestScanningStartCallback request_scanning_start_callback_;
+
+ // This queues up pending watch advertisements callbacks and clients so that
+ // we only have one BluetoothDiscoverySession start request at a time for
+ // watching device advertisements.
+ using WatchAdvertisementsCallbackAndClient =
+ std::pair<WatchAdvertisementsForDeviceCallback,
+ std::unique_ptr<WatchAdvertisementsClient>>;
+ std::vector<WatchAdvertisementsCallbackAndClient>
+ watch_advertisements_callbacks_and_clients_;
// List of clients that we must broadcast scan changes to.
std::vector<std::unique_ptr<ScanningClient>> scanning_clients_;
+ std::vector<std::unique_ptr<WatchAdvertisementsClient>>
+ watch_advertisements_clients_;
// Allowed Bluetooth scanning filters.
ScanFilters allowed_scan_filters_;