#include "content/browser/bluetooth/advertisement_client.h"
#include <utility>
#include <vector>
#include "base/strings/string_util.h"
#include "content/browser/bluetooth/bluetooth_blocklist.h"
#include "content/browser/bluetooth/bluetooth_metrics.h"
#include "content/browser/web_contents/web_contents_impl.h"
namespace content {
namespace {
using ::device::BluetoothUUID;
}
WebBluetoothServiceImpl::AdvertisementClient::AdvertisementClient(
WebBluetoothServiceImpl* service,
mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
client_remote,
RequestCallback callback)
: client_remote_(std::move(client_remote)),
web_contents_(static_cast<WebContentsImpl*>(
WebContents::FromRenderFrameHost(&service->render_frame_host()))),
service_(service),
callback_(std::move(callback)) {
client_remote_.set_disconnect_handler(
base::BindOnce(&WebBluetoothServiceImpl::RemoveDisconnectedClients,
base::Unretained(service)));
web_contents_->IncrementBluetoothScanningSessionsCount();
}
WebBluetoothServiceImpl::AdvertisementClient::~AdvertisementClient() {
web_contents_->DecrementBluetoothScanningSessionsCount();
}
WebBluetoothServiceImpl::WatchAdvertisementsClient::WatchAdvertisementsClient(
WebBluetoothServiceImpl* service,
mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
client_remote,
blink::WebBluetoothDeviceId device_id,
RequestCallback callback)
: AdvertisementClient(service,
std::move(client_remote),
std::move(callback)),
device_id_(device_id) {
DCHECK(device_id_.IsValid());
}
WebBluetoothServiceImpl::WatchAdvertisementsClient::
~WatchAdvertisementsClient() = default;
void WebBluetoothServiceImpl::WatchAdvertisementsClient::SendEvent(
const blink::mojom::WebBluetoothAdvertisingEvent& event) {
if (event.device->id != device_id_) {
return;
}
auto filtered_event = event.Clone();
std::erase_if(filtered_event->uuids, [this](const BluetoothUUID& uuid) {
return !service_->IsAllowedToAccessService(device_id_, uuid);
});
base::EraseIf(
filtered_event->service_data,
[this](const std::pair<BluetoothUUID, std::vector<uint8_t>>& entry) {
return !service_->IsAllowedToAccessService(device_id_, entry.first);
});
base::EraseIf(filtered_event->manufacturer_data,
[this](const std::pair<blink::mojom::WebBluetoothCompanyPtr,
std::vector<uint8_t>>& entry) {
return !service_->IsAllowedToAccessManufacturerData(
device_id_, entry.first->id) ||
BluetoothBlocklist::Get().IsExcluded(entry.first->id,
entry.second);
});
client_remote_->AdvertisingEvent(std::move(filtered_event));
}
WebBluetoothServiceImpl::ScanningClient::ScanningClient(
WebBluetoothServiceImpl* service,
mojo::PendingAssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient>
client_remote,
blink::mojom::WebBluetoothRequestLEScanOptionsPtr options,
RequestCallback callback)
: AdvertisementClient(service,
std::move(client_remote),
std::move(callback)),
options_(std::move(options)) {
DCHECK(options_->filters.has_value() || options_->accept_all_advertisements);
}
WebBluetoothServiceImpl::ScanningClient::~ScanningClient() = default;
void WebBluetoothServiceImpl::ScanningClient::SendEvent(
const blink::mojom::WebBluetoothAdvertisingEvent& event) {
auto filtered_event = event.Clone();
if (options_->accept_all_advertisements) {
if (prompt_controller_) {
AddFilteredDeviceToPrompt(filtered_event->device->id.str(),
filtered_event->name);
}
if (allow_send_event_) {
client_remote_->AdvertisingEvent(std::move(filtered_event));
}
return;
}
DCHECK(options_->filters.has_value());
for (auto& filter : options_->filters.value()) {
if (filter->name.has_value()) {
if (!filtered_event->name.has_value() ||
filter->name.value() != filtered_event->name.value()) {
continue;
}
}
if (filter->name_prefix.has_value()) {
if (!filtered_event->name.has_value() ||
!base::StartsWith(filtered_event->name.value(),
filter->name_prefix.value(),
base::CompareCase::SENSITIVE)) {
continue;
}
}
if (filter->services.has_value()) {
if (std::ranges::none_of(
filter->services.value(),
[&filtered_event](const BluetoothUUID& filter_uuid) {
return base::Contains(filtered_event->uuids, filter_uuid);
})) {
continue;
}
}
if (prompt_controller_) {
AddFilteredDeviceToPrompt(filtered_event->device->id.str(),
filtered_event->name);
}
if (allow_send_event_) {
client_remote_->AdvertisingEvent(std::move(filtered_event));
}
return;
}
}
}