blob: 1052a6a8aaf8a9ee11ef92bb1109731e027e5e11 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/bluetooth/bluetooth_adapter_winrt.h"
#include <windows.foundation.collections.h>
#include <windows.foundation.h>
#include <windows.storage.streams.h>
#include <wrl/event.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/win/core_winrt_util.h"
#include "device/bluetooth/bluetooth_advertisement_winrt.h"
#include "device/bluetooth/bluetooth_device_winrt.h"
#include "device/bluetooth/bluetooth_discovery_filter.h"
#include "device/bluetooth/bluetooth_discovery_session_outcome.h"
#include "device/bluetooth/event_utils_winrt.h"
namespace device {
namespace {
// In order to avoid a name clash with device::BluetoothAdapter we need this
// auxiliary namespace.
namespace uwp {
using ABI::Windows::Devices::Bluetooth::BluetoothAdapter;
} // namespace uwp
using ABI::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementDataSection;
using ABI::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementFlags;
using ABI::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcherStatus;
using ABI::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcherStatus_Aborted;
using ABI::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEManufacturerData;
using ABI::Windows::Devices::Bluetooth::Advertisement::BluetoothLEScanningMode;
using ABI::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEScanningMode_Active;
using ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEAdvertisement;
using ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEAdvertisementDataSection;
using ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEAdvertisementPublisherFactory;
using ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEAdvertisementReceivedEventArgs;
using ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEAdvertisementWatcher;
using ABI::Windows::Devices::Bluetooth::Advertisement::
IBluetoothLEManufacturerData;
using ABI::Windows::Devices::Bluetooth::IBluetoothAdapter;
using ABI::Windows::Devices::Bluetooth::IBluetoothAdapterStatics;
using ABI::Windows::Devices::Enumeration::DeviceInformation;
using ABI::Windows::Devices::Enumeration::IDeviceInformation;
using ABI::Windows::Devices::Enumeration::IDeviceInformationStatics;
using ABI::Windows::Devices::Enumeration::IDeviceInformationUpdate;
using ABI::Windows::Devices::Enumeration::IDeviceWatcher;
using ABI::Windows::Devices::Radios::IRadio;
using ABI::Windows::Devices::Radios::IRadioStatics;
using ABI::Windows::Devices::Radios::Radio;
using ABI::Windows::Devices::Radios::RadioAccessStatus;
using ABI::Windows::Devices::Radios::RadioAccessStatus_Allowed;
using ABI::Windows::Devices::Radios::RadioAccessStatus_DeniedBySystem;
using ABI::Windows::Devices::Radios::RadioAccessStatus_DeniedByUser;
using ABI::Windows::Devices::Radios::RadioAccessStatus_Unspecified;
using ABI::Windows::Devices::Radios::RadioState;
using ABI::Windows::Devices::Radios::RadioState_Off;
using ABI::Windows::Devices::Radios::RadioState_On;
using ABI::Windows::Foundation::Collections::IVector;
using ABI::Windows::Foundation::Collections::IVectorView;
using ABI::Windows::Foundation::IAsyncOperation;
using ABI::Windows::Foundation::IReference;
using ABI::Windows::Storage::Streams::IBuffer;
using ABI::Windows::Storage::Streams::IDataReader;
using ABI::Windows::Storage::Streams::IDataReaderStatics;
using Microsoft::WRL::Callback;
using Microsoft::WRL::ComPtr;
bool ResolveCoreWinRT() {
return base::win::ResolveCoreWinRTDelayload() &&
base::win::ScopedHString::ResolveCoreWinRTStringDelayload();
}
// Query string for powered Bluetooth radios. GUID Reference:
// https://docs.microsoft.com/en-us/windows-hardware/drivers/install/guid-bthport-device-interface
// TODO(https://crbug.com/821766): Consider adding WindowsCreateStringReference
// to base::win::ScopedHString to avoid allocating memory for this string.
constexpr wchar_t kPoweredRadiosAqsFilter[] =
L"System.Devices.InterfaceClassGuid:=\"{0850302A-B344-4fda-9BE9-"
L"90576B8D46F0}\" AND "
L"System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True";
// Utility functions to pretty print enum values.
constexpr const char* ToCString(RadioAccessStatus access_status) {
switch (access_status) {
case RadioAccessStatus_Unspecified:
return "RadioAccessStatus::Unspecified";
case RadioAccessStatus_Allowed:
return "RadioAccessStatus::Allowed";
case RadioAccessStatus_DeniedByUser:
return "RadioAccessStatus::DeniedByUser";
case RadioAccessStatus_DeniedBySystem:
return "RadioAccessStatus::DeniedBySystem";
}
NOTREACHED();
return "";
}
template <typename VectorView, typename T>
bool ToStdVector(VectorView* view, std::vector<T>* vector) {
unsigned size;
HRESULT hr = view->get_Size(&size);
if (FAILED(hr)) {
VLOG(2) << "get_Size() failed: " << logging::SystemErrorCodeToString(hr);
return false;
}
vector->resize(size);
for (size_t i = 0; i < size; ++i) {
hr = view->GetAt(i, &(*vector)[i]);
DCHECK(SUCCEEDED(hr)) << "GetAt(" << i << ") failed: "
<< logging::SystemErrorCodeToString(hr);
}
return true;
}
base::Optional<std::vector<uint8_t>> ExtractVector(IBuffer* buffer) {
ComPtr<IDataReaderStatics> data_reader_statics;
HRESULT hr = base::win::GetActivationFactory<
IDataReaderStatics, RuntimeClass_Windows_Storage_Streams_DataReader>(
&data_reader_statics);
if (FAILED(hr)) {
VLOG(2) << "Getting DataReaderStatics Activation Factory failed: "
<< logging::SystemErrorCodeToString(hr);
return base::nullopt;
}
ComPtr<IDataReader> data_reader;
hr = data_reader_statics->FromBuffer(buffer, &data_reader);
if (FAILED(hr)) {
VLOG(2) << "FromBuffer() failed: " << logging::SystemErrorCodeToString(hr);
return base::nullopt;
}
uint32_t buffer_length;
hr = buffer->get_Length(&buffer_length);
if (FAILED(hr)) {
VLOG(2) << "get_Length() failed: " << logging::SystemErrorCodeToString(hr);
return base::nullopt;
}
std::vector<uint8_t> bytes(buffer_length);
hr = data_reader->ReadBytes(buffer_length, bytes.data());
if (FAILED(hr)) {
VLOG(2) << "ReadBytes() failed: " << logging::SystemErrorCodeToString(hr);
return base::nullopt;
}
return bytes;
}
base::Optional<uint8_t> ExtractFlags(IBluetoothLEAdvertisement* advertisement) {
if (!advertisement)
return base::nullopt;
ComPtr<IReference<BluetoothLEAdvertisementFlags>> flags_ref;
HRESULT hr = advertisement->get_Flags(&flags_ref);
if (FAILED(hr)) {
VLOG(2) << "get_Flags() failed: " << logging::SystemErrorCodeToString(hr);
return base::nullopt;
}
if (!flags_ref) {
VLOG(2) << "No advertisement flags found.";
return base::nullopt;
}
BluetoothLEAdvertisementFlags flags;
hr = flags_ref->get_Value(&flags);
if (FAILED(hr)) {
VLOG(2) << "get_Value() failed: " << logging::SystemErrorCodeToString(hr);
return base::nullopt;
}
return flags;
}
BluetoothDevice::UUIDList ExtractAdvertisedUUIDs(
IBluetoothLEAdvertisement* advertisement) {
if (!advertisement)
return {};
ComPtr<IVector<GUID>> service_uuids;
HRESULT hr = advertisement->get_ServiceUuids(&service_uuids);
if (FAILED(hr)) {
VLOG(2) << "get_ServiceUuids() failed: "
<< logging::SystemErrorCodeToString(hr);
return {};
}
std::vector<GUID> guids;
if (!ToStdVector(service_uuids.Get(), &guids))
return {};
BluetoothDevice::UUIDList advertised_uuids;
advertised_uuids.reserve(guids.size());
for (const auto& guid : guids)
advertised_uuids.emplace_back(guid);
return advertised_uuids;
}
// This method populates service data for a particular sized UUID. Given the
// lack of tailored platform APIs, we need to parse the raw advertisement data
// sections ourselves. These data sections are effectively a list of blobs,
// where each blob starts with the corresponding UUID in little endian order,
// followed by the corresponding service data.
void PopulateServiceData(
BluetoothDevice::ServiceDataMap* service_data,
const std::vector<ComPtr<IBluetoothLEAdvertisementDataSection>>&
data_sections,
size_t num_bytes_uuid) {
for (const auto& data_section : data_sections) {
ComPtr<IBuffer> buffer;
HRESULT hr = data_section->get_Data(&buffer);
if (FAILED(hr)) {
VLOG(2) << "get_Data() failed: " << logging::SystemErrorCodeToString(hr);
continue;
}
auto bytes = ExtractVector(buffer.Get());
if (!bytes)
continue;
auto bytes_span = base::make_span(*bytes);
if (bytes_span.size() < num_bytes_uuid) {
VLOG(2) << "Buffer Length is too small: " << bytes_span.size() << " vs. "
<< num_bytes_uuid;
continue;
}
auto uuid_span = bytes_span.first(num_bytes_uuid);
// The UUID is specified in little endian format, thus we reverse the bytes
// here.
std::vector<uint8_t> uuid_bytes(uuid_span.rbegin(), uuid_span.rend());
// HexEncode the bytes and add dashes as required.
std::string uuid_str;
for (char c : base::HexEncode(uuid_bytes.data(), uuid_bytes.size())) {
const size_t size = uuid_str.size();
if (size == 8 || size == 13 || size == 18 || size == 23)
uuid_str.push_back('-');
uuid_str.push_back(c);
}
auto service_data_span = bytes_span.subspan(num_bytes_uuid);
auto result = service_data->emplace(
BluetoothUUID(uuid_str), std::vector<uint8_t>(service_data_span.begin(),
service_data_span.end()));
// Check that an insertion happened.
DCHECK(result.second);
// Check that the inserted UUID is valid.
DCHECK(result.first->first.IsValid());
}
}
BluetoothDevice::ServiceDataMap ExtractServiceData(
IBluetoothLEAdvertisement* advertisement) {
BluetoothDevice::ServiceDataMap service_data;
if (!advertisement)
return service_data;
static constexpr std::pair<uint8_t, size_t> kServiceDataTypesAndNumBits[] = {
{BluetoothDeviceWinrt::k16BitServiceDataSection, 16},
{BluetoothDeviceWinrt::k32BitServiceDataSection, 32},
{BluetoothDeviceWinrt::k128BitServiceDataSection, 128},
};
for (const auto& data_type_and_num_bits : kServiceDataTypesAndNumBits) {
ComPtr<IVectorView<BluetoothLEAdvertisementDataSection*>> data_sections;
HRESULT hr = advertisement->GetSectionsByType(data_type_and_num_bits.first,
&data_sections);
if (FAILED(hr)) {
VLOG(2) << "GetSectionsByType() failed: "
<< logging::SystemErrorCodeToString(hr);
continue;
}
std::vector<ComPtr<IBluetoothLEAdvertisementDataSection>> vector;
if (!ToStdVector(data_sections.Get(), &vector))
continue;
PopulateServiceData(&service_data, vector,
data_type_and_num_bits.second / 8);
}
return service_data;
}
BluetoothDevice::ManufacturerDataMap ExtractManufacturerData(
IBluetoothLEAdvertisement* advertisement) {
if (!advertisement)
return {};
ComPtr<IVector<BluetoothLEManufacturerData*>> manufacturer_data_ptr;
HRESULT hr = advertisement->get_ManufacturerData(&manufacturer_data_ptr);
if (FAILED(hr)) {
VLOG(2) << "GetManufacturerData() failed: "
<< logging::SystemErrorCodeToString(hr);
return {};
}
std::vector<ComPtr<IBluetoothLEManufacturerData>> manufacturer_data;
if (!ToStdVector(manufacturer_data_ptr.Get(), &manufacturer_data))
return {};
BluetoothDevice::ManufacturerDataMap manufacturer_data_map;
for (const auto& manufacturer_datum : manufacturer_data) {
uint16_t company_id;
hr = manufacturer_datum->get_CompanyId(&company_id);
if (FAILED(hr)) {
VLOG(2) << "get_CompanyId() failed: "
<< logging::SystemErrorCodeToString(hr);
continue;
}
ComPtr<IBuffer> buffer;
hr = manufacturer_datum->get_Data(&buffer);
if (FAILED(hr)) {
VLOG(2) << "get_Data() failed: " << logging::SystemErrorCodeToString(hr);
continue;
}
auto bytes = ExtractVector(buffer.Get());
if (!bytes)
continue;
manufacturer_data_map.emplace(company_id, std::move(*bytes));
}
return manufacturer_data_map;
}
// Similarly to extracting the service data Windows does not provide a specific
// API to extract the tx power. Thus we also parse the raw data sections here.
// If present, we expect a single entry for tx power with a blob of size 1 byte.
base::Optional<int8_t> ExtractTxPower(
IBluetoothLEAdvertisement* advertisement) {
if (!advertisement)
return base::nullopt;
ComPtr<IVectorView<BluetoothLEAdvertisementDataSection*>> data_sections;
HRESULT hr = advertisement->GetSectionsByType(
BluetoothDeviceWinrt::kTxPowerLevelDataSection, &data_sections);
if (FAILED(hr)) {
VLOG(2) << "GetSectionsByType() failed: "
<< logging::SystemErrorCodeToString(hr);
return base::nullopt;
}
std::vector<ComPtr<IBluetoothLEAdvertisementDataSection>> vector;
if (!ToStdVector(data_sections.Get(), &vector) || vector.empty())
return base::nullopt;
if (vector.size() != 1u) {
VLOG(2) << "Unexpected number of data sections: " << vector.size();
return base::nullopt;
}
ComPtr<IBuffer> buffer;
hr = vector.front()->get_Data(&buffer);
if (FAILED(hr)) {
VLOG(2) << "get_Data() failed: " << logging::SystemErrorCodeToString(hr);
return base::nullopt;
}
auto bytes = ExtractVector(buffer.Get());
if (!bytes)
return base::nullopt;
if (bytes->size() != 1) {
VLOG(2) << "Unexpected number of bytes: " << bytes->size();
return base::nullopt;
}
return bytes->front();
}
ComPtr<IBluetoothLEAdvertisement> GetAdvertisement(
IBluetoothLEAdvertisementReceivedEventArgs* received) {
ComPtr<IBluetoothLEAdvertisement> advertisement;
HRESULT hr = received->get_Advertisement(&advertisement);
if (FAILED(hr)) {
VLOG(2) << "get_Advertisement() failed: "
<< logging::SystemErrorCodeToString(hr);
}
return advertisement;
}
base::Optional<std::string> ExtractDeviceName(
IBluetoothLEAdvertisement* advertisement) {
if (!advertisement)
return base::nullopt;
HSTRING local_name;
HRESULT hr = advertisement->get_LocalName(&local_name);
if (FAILED(hr)) {
VLOG(2) << "Getting Local Name failed: "
<< logging::SystemErrorCodeToString(hr);
return base::nullopt;
}
// Return early otherwise ScopedHString will create an empty string.
if (!local_name)
return base::nullopt;
return base::win::ScopedHString(local_name).GetAsUTF8();
}
void ExtractAndUpdateAdvertisementData(
IBluetoothLEAdvertisementReceivedEventArgs* received,
BluetoothDevice* device) {
int16_t rssi = 0;
HRESULT hr = received->get_RawSignalStrengthInDBm(&rssi);
if (FAILED(hr)) {
VLOG(2) << "get_RawSignalStrengthInDBm() failed: "
<< logging::SystemErrorCodeToString(hr);
}
ComPtr<IBluetoothLEAdvertisement> advertisement = GetAdvertisement(received);
static_cast<BluetoothDeviceWinrt*>(device)->UpdateLocalName(
ExtractDeviceName(advertisement.Get()));
device->UpdateAdvertisementData(rssi, ExtractFlags(advertisement.Get()),
ExtractAdvertisedUUIDs(advertisement.Get()),
ExtractTxPower(advertisement.Get()),
ExtractServiceData(advertisement.Get()),
ExtractManufacturerData(advertisement.Get()));
}
} // namespace
std::string BluetoothAdapterWinrt::GetAddress() const {
return address_;
}
std::string BluetoothAdapterWinrt::GetName() const {
return name_;
}
void BluetoothAdapterWinrt::SetName(const std::string& name,
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
bool BluetoothAdapterWinrt::IsInitialized() const {
return is_initialized_;
}
bool BluetoothAdapterWinrt::IsPresent() const {
// Obtaining the default adapter will fail if no physical adapter is present.
// Thus a non-zero |adapter| implies that a physical adapter is present.
return adapter_ != nullptr;
}
bool BluetoothAdapterWinrt::CanPower() const {
return radio_ != nullptr;
}
bool BluetoothAdapterWinrt::IsPowered() const {
// Due to an issue on WoW64 we might fail to obtain the radio in OnGetRadio().
// This is why it can be null here.
if (!radio_)
return num_powered_radios_ != 0;
RadioState state;
HRESULT hr = radio_->get_State(&state);
if (FAILED(hr)) {
VLOG(2) << "Getting Radio State failed: "
<< logging::SystemErrorCodeToString(hr);
return false;
}
return state == RadioState_On;
}
bool BluetoothAdapterWinrt::IsDiscoverable() const {
NOTIMPLEMENTED();
return false;
}
void BluetoothAdapterWinrt::SetDiscoverable(
bool discoverable,
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
bool BluetoothAdapterWinrt::IsDiscovering() const {
return num_discovery_sessions_ != 0;
}
BluetoothAdapter::UUIDList BluetoothAdapterWinrt::GetUUIDs() const {
NOTIMPLEMENTED();
return UUIDList();
}
void BluetoothAdapterWinrt::CreateRfcommService(
const BluetoothUUID& uuid,
const ServiceOptions& options,
const CreateServiceCallback& callback,
const CreateServiceErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
void BluetoothAdapterWinrt::CreateL2capService(
const BluetoothUUID& uuid,
const ServiceOptions& options,
const CreateServiceCallback& callback,
const CreateServiceErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
void BluetoothAdapterWinrt::RegisterAdvertisement(
std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
const CreateAdvertisementCallback& callback,
const AdvertisementErrorCallback& error_callback) {
auto advertisement = CreateAdvertisement();
if (!advertisement->Initialize(std::move(advertisement_data))) {
VLOG(2) << "Failed to Initialize Advertisement.";
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(error_callback,
BluetoothAdvertisement::ERROR_STARTING_ADVERTISEMENT));
return;
}
// In order to avoid |advertisement| holding a strong reference to itself, we
// pass only a weak reference to the callbacks, and store a strong reference
// in |pending_advertisements_|. When the callbacks are run, they will remove
// the corresponding advertisement from the list of pending advertisements.
advertisement->Register(
base::Bind(&BluetoothAdapterWinrt::OnRegisterAdvertisement,
weak_ptr_factory_.GetWeakPtr(),
base::Unretained(advertisement.get()), callback),
base::Bind(&BluetoothAdapterWinrt::OnRegisterAdvertisementError,
weak_ptr_factory_.GetWeakPtr(),
base::Unretained(advertisement.get()), error_callback));
pending_advertisements_.push_back(std::move(advertisement));
}
std::vector<BluetoothAdvertisement*>
BluetoothAdapterWinrt::GetPendingAdvertisementsForTesting() const {
std::vector<BluetoothAdvertisement*> pending_advertisements;
for (const auto& pending_advertisement : pending_advertisements_)
pending_advertisements.push_back(pending_advertisement.get());
return pending_advertisements;
}
BluetoothLocalGattService* BluetoothAdapterWinrt::GetGattService(
const std::string& identifier) const {
NOTIMPLEMENTED();
return nullptr;
}
IRadio* BluetoothAdapterWinrt::GetRadioForTesting() {
return radio_.Get();
}
IDeviceWatcher* BluetoothAdapterWinrt::GetPoweredRadioWatcherForTesting() {
return powered_radio_watcher_.Get();
}
BluetoothAdapterWinrt::BluetoothAdapterWinrt() : weak_ptr_factory_(this) {
ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
}
BluetoothAdapterWinrt::~BluetoothAdapterWinrt() {
// Explicitly move |pending_advertisements_| into a local variable and clear
// them out. Any remaining pending advertisement will attempt to remove itself
// from |pending_advertisements_|, which would result in a double-free
// otherwise.
auto pending_advertisements = std::move(pending_advertisements_);
pending_advertisements_.clear();
if (radio_)
TryRemoveRadioStateChangedHandler();
if (powered_radio_watcher_) {
TryRemovePoweredRadioEventHandlers();
HRESULT hr = powered_radio_watcher_->Stop();
if (FAILED(hr)) {
VLOG(2) << "Stopping powered radio watcher failed: "
<< logging::SystemErrorCodeToString(hr);
}
}
}
void BluetoothAdapterWinrt::Init(InitCallback init_cb) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// We are wrapping |init_cb| in a ScopedClosureRunner to ensure it gets run no
// matter how the function exits. Furthermore, we set |is_initialized_| to
// true if adapter is still active when the callback gets run.
base::ScopedClosureRunner on_init(base::BindOnce(
[](base::WeakPtr<BluetoothAdapterWinrt> adapter, InitCallback init_cb) {
if (adapter)
adapter->is_initialized_ = true;
std::move(init_cb).Run();
},
weak_ptr_factory_.GetWeakPtr(), std::move(init_cb)));
if (!ResolveCoreWinRT())
return;
ComPtr<IBluetoothAdapterStatics> adapter_statics;
HRESULT hr = GetBluetoothAdapterStaticsActivationFactory(&adapter_statics);
if (FAILED(hr)) {
VLOG(2) << "GetBluetoothAdapterStaticsActivationFactory failed: "
<< logging::SystemErrorCodeToString(hr);
return;
}
ComPtr<IAsyncOperation<uwp::BluetoothAdapter*>> get_default_adapter_op;
hr = adapter_statics->GetDefaultAsync(&get_default_adapter_op);
if (FAILED(hr)) {
VLOG(2) << "BluetoothAdapter::GetDefaultAsync failed: "
<< logging::SystemErrorCodeToString(hr);
return;
}
hr = PostAsyncResults(
std::move(get_default_adapter_op),
base::BindOnce(&BluetoothAdapterWinrt::OnGetDefaultAdapter,
weak_ptr_factory_.GetWeakPtr(), std::move(on_init)));
if (FAILED(hr)) {
VLOG(2) << "PostAsyncResults failed: "
<< logging::SystemErrorCodeToString(hr);
}
}
bool BluetoothAdapterWinrt::SetPoweredImpl(bool powered) {
// Due to an issue on WoW64 we might fail to obtain the radio in OnGetRadio().
// This is why it can be null here.
if (!radio_)
return false;
const RadioState state = powered ? RadioState_On : RadioState_Off;
ComPtr<IAsyncOperation<RadioAccessStatus>> set_state_op;
HRESULT hr = radio_->SetStateAsync(state, &set_state_op);
if (FAILED(hr)) {
VLOG(2) << "Radio::SetStateAsync failed: "
<< logging::SystemErrorCodeToString(hr);
return false;
}
hr = PostAsyncResults(std::move(set_state_op),
base::BindOnce(&BluetoothAdapterWinrt::OnSetRadioState,
weak_ptr_factory_.GetWeakPtr()));
if (FAILED(hr)) {
VLOG(2) << "PostAsyncResults failed: "
<< logging::SystemErrorCodeToString(hr);
return false;
}
return true;
}
void BluetoothAdapterWinrt::AddDiscoverySession(
BluetoothDiscoveryFilter* discovery_filter,
const base::Closure& callback,
DiscoverySessionErrorCallback error_callback) {
if (num_discovery_sessions_ > 0) {
ui_task_runner_->PostTask(FROM_HERE, std::move(callback));
return;
}
HRESULT hr = ActivateBluetoothAdvertisementLEWatcherInstance(
&ble_advertisement_watcher_);
if (FAILED(hr)) {
VLOG(2) << "ActivateBluetoothAdvertisementLEWatcherInstance failed: "
<< logging::SystemErrorCodeToString(hr);
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(error_callback),
UMABluetoothDiscoverySessionOutcome::UNKNOWN));
return;
}
hr = ble_advertisement_watcher_->put_ScanningMode(
BluetoothLEScanningMode_Active);
if (FAILED(hr)) {
VLOG(2) << "Setting ScanningMode to Active failed: "
<< logging::SystemErrorCodeToString(hr);
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(error_callback),
UMABluetoothDiscoverySessionOutcome::UNKNOWN));
return;
}
auto advertisement_received_token = AddTypedEventHandler(
ble_advertisement_watcher_.Get(),
&IBluetoothLEAdvertisementWatcher::add_Received,
base::BindRepeating(&BluetoothAdapterWinrt::OnAdvertisementReceived,
weak_ptr_factory_.GetWeakPtr()));
if (!advertisement_received_token) {
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(error_callback),
UMABluetoothDiscoverySessionOutcome::UNKNOWN));
return;
}
advertisement_received_token_ = *advertisement_received_token;
hr = ble_advertisement_watcher_->Start();
if (FAILED(hr)) {
VLOG(2) << "Starting the Advertisement Watcher failed: "
<< logging::SystemErrorCodeToString(hr);
RemoveAdvertisementReceivedHandler();
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(error_callback),
UMABluetoothDiscoverySessionOutcome::UNKNOWN));
return;
}
BluetoothLEAdvertisementWatcherStatus watcher_status;
hr = ble_advertisement_watcher_->get_Status(&watcher_status);
if (FAILED(hr)) {
VLOG(2) << "Getting the Watcher Status failed: "
<< logging::SystemErrorCodeToString(hr);
} else if (watcher_status == BluetoothLEAdvertisementWatcherStatus_Aborted) {
VLOG(2)
<< "Starting Advertisement Watcher failed, it is in the Aborted state.";
RemoveAdvertisementReceivedHandler();
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(error_callback),
UMABluetoothDiscoverySessionOutcome::UNKNOWN));
return;
}
++num_discovery_sessions_;
ui_task_runner_->PostTask(FROM_HERE, std::move(callback));
}
void BluetoothAdapterWinrt::RemoveDiscoverySession(
BluetoothDiscoveryFilter* discovery_filter,
const base::Closure& callback,
DiscoverySessionErrorCallback error_callback) {
if (num_discovery_sessions_ == 0) {
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(error_callback),
UMABluetoothDiscoverySessionOutcome::UNKNOWN));
return;
}
if (num_discovery_sessions_ > 1) {
--num_discovery_sessions_;
ui_task_runner_->PostTask(FROM_HERE, std::move(callback));
return;
}
RemoveAdvertisementReceivedHandler();
HRESULT hr = ble_advertisement_watcher_->Stop();
if (FAILED(hr)) {
VLOG(2) << "Stopped the Advertisement Watcher failed: "
<< logging::SystemErrorCodeToString(hr);
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(error_callback),
UMABluetoothDiscoverySessionOutcome::UNKNOWN));
return;
}
for (auto& device : devices_)
device.second->ClearAdvertisementData();
ble_advertisement_watcher_.Reset();
--num_discovery_sessions_;
ui_task_runner_->PostTask(FROM_HERE, std::move(callback));
}
void BluetoothAdapterWinrt::SetDiscoveryFilter(
std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter,
const base::Closure& callback,
DiscoverySessionErrorCallback error_callback) {
NOTIMPLEMENTED();
}
void BluetoothAdapterWinrt::RemovePairingDelegateInternal(
BluetoothDevice::PairingDelegate* pairing_delegate) {
NOTIMPLEMENTED();
}
HRESULT BluetoothAdapterWinrt::GetBluetoothAdapterStaticsActivationFactory(
IBluetoothAdapterStatics** statics) const {
return base::win::GetActivationFactory<
IBluetoothAdapterStatics,
RuntimeClass_Windows_Devices_Bluetooth_BluetoothAdapter>(statics);
}
HRESULT BluetoothAdapterWinrt::GetDeviceInformationStaticsActivationFactory(
IDeviceInformationStatics** statics) const {
return base::win::GetActivationFactory<
IDeviceInformationStatics,
RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>(statics);
}
HRESULT BluetoothAdapterWinrt::GetRadioStaticsActivationFactory(
IRadioStatics** statics) const {
return base::win::GetActivationFactory<
IRadioStatics, RuntimeClass_Windows_Devices_Radios_Radio>(statics);
}
HRESULT
BluetoothAdapterWinrt::ActivateBluetoothAdvertisementLEWatcherInstance(
IBluetoothLEAdvertisementWatcher** instance) const {
auto watcher_hstring = base::win::ScopedHString::Create(
RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementWatcher);
if (!watcher_hstring.is_valid())
return E_FAIL;
ComPtr<IInspectable> inspectable;
HRESULT hr =
base::win::RoActivateInstance(watcher_hstring.get(), &inspectable);
if (FAILED(hr)) {
VLOG(2) << "RoActivateInstance failed: "
<< logging::SystemErrorCodeToString(hr);
return hr;
}
ComPtr<IBluetoothLEAdvertisementWatcher> watcher;
hr = inspectable.As(&watcher);
if (FAILED(hr)) {
VLOG(2) << "As IBluetoothLEAdvertisementWatcher failed: "
<< logging::SystemErrorCodeToString(hr);
return hr;
}
return watcher.CopyTo(instance);
}
scoped_refptr<BluetoothAdvertisementWinrt>
BluetoothAdapterWinrt::CreateAdvertisement() const {
return base::MakeRefCounted<BluetoothAdvertisementWinrt>();
}
std::unique_ptr<BluetoothDeviceWinrt> BluetoothAdapterWinrt::CreateDevice(
uint64_t raw_address) {
return std::make_unique<BluetoothDeviceWinrt>(this, raw_address);
}
void BluetoothAdapterWinrt::OnGetDefaultAdapter(
base::ScopedClosureRunner on_init,
ComPtr<IBluetoothAdapter> adapter) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!adapter) {
VLOG(2) << "Getting Default Adapter failed.";
return;
}
adapter_ = std::move(adapter);
uint64_t raw_address;
HRESULT hr = adapter_->get_BluetoothAddress(&raw_address);
if (FAILED(hr)) {
VLOG(2) << "Getting BluetoothAddress failed: "
<< logging::SystemErrorCodeToString(hr);
return;
}
address_ = BluetoothDevice::CanonicalizeAddress(
base::StringPrintf("%012llX", raw_address));
DCHECK(!address_.empty());
HSTRING device_id;
hr = adapter_->get_DeviceId(&device_id);
if (FAILED(hr)) {
VLOG(2) << "Getting DeviceId failed: "
<< logging::SystemErrorCodeToString(hr);
return;
}
ComPtr<IDeviceInformationStatics> device_information_statics;
hr =
GetDeviceInformationStaticsActivationFactory(&device_information_statics);
if (FAILED(hr)) {
VLOG(2) << "GetDeviceInformationStaticsActivationFactory failed: "
<< logging::SystemErrorCodeToString(hr);
return;
}
ComPtr<IAsyncOperation<DeviceInformation*>> create_from_id_op;
hr = device_information_statics->CreateFromIdAsync(device_id,
&create_from_id_op);
if (FAILED(hr)) {
VLOG(2) << "CreateFromIdAsync failed: "
<< logging::SystemErrorCodeToString(hr);
return;
}
hr = PostAsyncResults(
std::move(create_from_id_op),
base::BindOnce(&BluetoothAdapterWinrt::OnCreateFromIdAsync,
weak_ptr_factory_.GetWeakPtr(), std::move(on_init)));
if (FAILED(hr)) {
VLOG(2) << "PostAsyncResults failed: "
<< logging::SystemErrorCodeToString(hr);
}
}
void BluetoothAdapterWinrt::OnCreateFromIdAsync(
base::ScopedClosureRunner on_init,
ComPtr<IDeviceInformation> device_information) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!device_information) {
VLOG(2) << "Getting Device Information failed.";
return;
}
HSTRING name;
HRESULT hr = device_information->get_Name(&name);
if (FAILED(hr)) {
VLOG(2) << "Getting Name failed: " << logging::SystemErrorCodeToString(hr);
return;
}
name_ = base::win::ScopedHString(name).GetAsUTF8();
ComPtr<IRadioStatics> radio_statics;
hr = GetRadioStaticsActivationFactory(&radio_statics);
if (FAILED(hr)) {
VLOG(2) << "GetRadioStaticsActivationFactory failed: "
<< logging::SystemErrorCodeToString(hr);
return;
}
ComPtr<IAsyncOperation<RadioAccessStatus>> request_access_op;
hr = radio_statics->RequestAccessAsync(&request_access_op);
if (FAILED(hr)) {
VLOG(2) << "RequestAccessAsync failed: "
<< logging::SystemErrorCodeToString(hr);
return;
}
hr = PostAsyncResults(
std::move(request_access_op),
base::BindOnce(&BluetoothAdapterWinrt::OnRequestRadioAccess,
weak_ptr_factory_.GetWeakPtr(), std::move(on_init)));
if (FAILED(hr)) {
VLOG(2) << "PostAsyncResults failed: "
<< logging::SystemErrorCodeToString(hr);
}
}
void BluetoothAdapterWinrt::OnRequestRadioAccess(
base::ScopedClosureRunner on_init,
RadioAccessStatus access_status) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (access_status != RadioAccessStatus_Allowed) {
VLOG(2) << "Got unexpected Radio Access Status: "
<< ToCString(access_status);
return;
}
ComPtr<IAsyncOperation<Radio*>> get_radio_op;
HRESULT hr = adapter_->GetRadioAsync(&get_radio_op);
if (FAILED(hr)) {
VLOG(2) << "GetRadioAsync failed: " << logging::SystemErrorCodeToString(hr);
return;
}
hr = PostAsyncResults(
std::move(get_radio_op),
base::BindOnce(&BluetoothAdapterWinrt::OnGetRadio,
weak_ptr_factory_.GetWeakPtr(), std::move(on_init)));
if (FAILED(hr)) {
VLOG(2) << "PostAsyncResults failed: "
<< logging::SystemErrorCodeToString(hr);
}
}
void BluetoothAdapterWinrt::OnGetRadio(base::ScopedClosureRunner on_init,
ComPtr<IRadio> radio) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (radio) {
radio_ = std::move(radio);
radio_state_changed_token_ = AddTypedEventHandler(
radio_.Get(), &IRadio::add_StateChanged,
base::BindRepeating(&BluetoothAdapterWinrt::OnRadioStateChanged,
weak_ptr_factory_.GetWeakPtr()));
if (!radio_state_changed_token_)
VLOG(2) << "Adding Radio State Changed Handler failed.";
return;
}
// This happens within WoW64, due to an issue with non-native APIs.
VLOG(2) << "Getting Radio failed. Chrome will be unable to change the power "
"state by itself.";
// Attempt to create a DeviceWatcher for powered radios, so that querying
// the power state is still possible.
ComPtr<IDeviceInformationStatics> device_information_statics;
HRESULT hr =
GetDeviceInformationStaticsActivationFactory(&device_information_statics);
if (FAILED(hr)) {
VLOG(2) << "GetDeviceInformationStaticsActivationFactory failed: "
<< logging::SystemErrorCodeToString(hr);
return;
}
auto aqs_filter = base::win::ScopedHString::Create(kPoweredRadiosAqsFilter);
hr = device_information_statics->CreateWatcherAqsFilter(
aqs_filter.get(), &powered_radio_watcher_);
if (FAILED(hr)) {
VLOG(2) << "Creating Powered Radios Watcher failed: "
<< logging::SystemErrorCodeToString(hr);
return;
}
powered_radio_added_token_ = AddTypedEventHandler(
powered_radio_watcher_.Get(), &IDeviceWatcher::add_Added,
base::BindRepeating(&BluetoothAdapterWinrt::OnPoweredRadioAdded,
weak_ptr_factory_.GetWeakPtr()));
powered_radio_removed_token_ = AddTypedEventHandler(
powered_radio_watcher_.Get(), &IDeviceWatcher::add_Removed,
base::BindRepeating(&BluetoothAdapterWinrt::OnPoweredRadioRemoved,
weak_ptr_factory_.GetWeakPtr()));
powered_radios_enumerated_token_ = AddTypedEventHandler(
powered_radio_watcher_.Get(), &IDeviceWatcher::add_EnumerationCompleted,
base::BindRepeating(&BluetoothAdapterWinrt::OnPoweredRadiosEnumerated,
weak_ptr_factory_.GetWeakPtr()));
if (!powered_radio_added_token_ || !powered_radio_removed_token_ ||
!powered_radios_enumerated_token_) {
VLOG(2) << "Failed to Register Powered Radio Event Handlers.";
TryRemovePoweredRadioEventHandlers();
return;
}
hr = powered_radio_watcher_->Start();
if (FAILED(hr)) {
VLOG(2) << "Starting the Powered Radio Watcher failed: "
<< logging::SystemErrorCodeToString(hr);
TryRemovePoweredRadioEventHandlers();
return;
}
// Store the Closure Runner. It is expected that OnPoweredRadiosEnumerated()
// is invoked soon after.
on_init_ = std::make_unique<base::ScopedClosureRunner>(std::move(on_init));
}
void BluetoothAdapterWinrt::OnSetRadioState(RadioAccessStatus access_status) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (access_status != RadioAccessStatus_Allowed) {
VLOG(2) << "Got unexpected Radio Access Status: "
<< ToCString(access_status);
RunPendingPowerCallbacks();
}
}
void BluetoothAdapterWinrt::OnRadioStateChanged(IRadio* radio,
IInspectable* object) {
RunPendingPowerCallbacks();
NotifyAdapterPoweredChanged(IsPowered());
}
void BluetoothAdapterWinrt::OnPoweredRadioAdded(IDeviceWatcher* watcher,
IDeviceInformation* info) {
if (++num_powered_radios_ == 1)
NotifyAdapterPoweredChanged(true);
VLOG(2) << "OnPoweredRadioAdded(), Number of Powered Radios: "
<< num_powered_radios_;
}
void BluetoothAdapterWinrt::OnPoweredRadioRemoved(
IDeviceWatcher* watcher,
IDeviceInformationUpdate* update) {
if (--num_powered_radios_ == 0)
NotifyAdapterPoweredChanged(false);
VLOG(2) << "OnPoweredRadioRemoved(), Number of Powered Radios: "
<< num_powered_radios_;
}
void BluetoothAdapterWinrt::OnPoweredRadiosEnumerated(IDeviceWatcher* watcher,
IInspectable* object) {
// Destroy the ScopedClosureRunner, triggering the contained Closure to be
// run.
DCHECK(on_init_);
on_init_.reset();
VLOG(2) << "OnPoweredRadiosEnumerated(), Number of Powered Radios: "
<< num_powered_radios_;
}
void BluetoothAdapterWinrt::OnAdvertisementReceived(
IBluetoothLEAdvertisementWatcher* watcher,
IBluetoothLEAdvertisementReceivedEventArgs* received) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
uint64_t raw_bluetooth_address;
HRESULT hr = received->get_BluetoothAddress(&raw_bluetooth_address);
if (FAILED(hr)) {
VLOG(2) << "get_BluetoothAddress() failed: "
<< logging::SystemErrorCodeToString(hr);
return;
}
std::string bluetooth_address =
BluetoothDeviceWinrt::CanonicalizeAddress(raw_bluetooth_address);
auto it = devices_.find(bluetooth_address);
const bool is_new_device = (it == devices_.end());
if (is_new_device) {
bool was_inserted = false;
std::tie(it, was_inserted) = devices_.emplace(
std::move(bluetooth_address), CreateDevice(raw_bluetooth_address));
DCHECK(was_inserted);
}
BluetoothDevice* const device = it->second.get();
ExtractAndUpdateAdvertisementData(received, device);
for (auto& observer : observers_) {
is_new_device ? observer.DeviceAdded(this, device)
: observer.DeviceChanged(this, device);
}
}
void BluetoothAdapterWinrt::OnRegisterAdvertisement(
BluetoothAdvertisement* advertisement,
const CreateAdvertisementCallback& callback) {
DCHECK(base::ContainsValue(pending_advertisements_, advertisement));
auto wrapped_advertisement = base::WrapRefCounted(advertisement);
base::Erase(pending_advertisements_, advertisement);
callback.Run(std::move(wrapped_advertisement));
}
void BluetoothAdapterWinrt::OnRegisterAdvertisementError(
BluetoothAdvertisement* advertisement,
const AdvertisementErrorCallback& error_callback,
BluetoothAdvertisement::ErrorCode error_code) {
// Note: We are not DCHECKing that |pending_advertisements_| contains
// |advertisement|, as this method might be invoked during destruction.
base::Erase(pending_advertisements_, advertisement);
error_callback.Run(error_code);
}
void BluetoothAdapterWinrt::TryRemoveRadioStateChangedHandler() {
DCHECK(radio_);
if (!radio_state_changed_token_)
return;
HRESULT hr = radio_->remove_StateChanged(*radio_state_changed_token_);
if (FAILED(hr)) {
VLOG(2) << "Removing Radio State Changed Handler failed: "
<< logging::SystemErrorCodeToString(hr);
}
radio_state_changed_token_.reset();
}
void BluetoothAdapterWinrt::TryRemovePoweredRadioEventHandlers() {
DCHECK(powered_radio_watcher_);
if (powered_radio_added_token_) {
HRESULT hr =
powered_radio_watcher_->remove_Added(*powered_radio_removed_token_);
if (FAILED(hr)) {
VLOG(2) << "Removing the Powered Radio Added Handler failed: "
<< logging::SystemErrorCodeToString(hr);
}
powered_radio_added_token_.reset();
}
if (powered_radio_removed_token_) {
HRESULT hr =
powered_radio_watcher_->remove_Removed(*powered_radio_removed_token_);
if (FAILED(hr)) {
VLOG(2) << "Removing the Powered Radio Removed Handler failed: "
<< logging::SystemErrorCodeToString(hr);
}
powered_radio_removed_token_.reset();
}
if (powered_radios_enumerated_token_) {
HRESULT hr = powered_radio_watcher_->remove_EnumerationCompleted(
*powered_radios_enumerated_token_);
if (FAILED(hr)) {
VLOG(2) << "Removing the Powered Radios Enumerated Handler failed: "
<< logging::SystemErrorCodeToString(hr);
}
powered_radios_enumerated_token_.reset();
}
}
void BluetoothAdapterWinrt::RemoveAdvertisementReceivedHandler() {
DCHECK(ble_advertisement_watcher_);
HRESULT hr = ble_advertisement_watcher_->remove_Received(
advertisement_received_token_);
if (FAILED(hr)) {
VLOG(2) << "Removing the Received Handler failed: "
<< logging::SystemErrorCodeToString(hr);
}
}
} // namespace device