// 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_advertisement_winrt.h" #include #include #include #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/logging.h" #include "base/strings/string_piece.h" #include "base/threading/thread_task_runner_handle.h" #include "base/win/core_winrt_util.h" #include "base/win/scoped_hstring.h" #include "base/win/winrt_storage_util.h" #include "components/device_event_log/device_event_log.h" #include "device/bluetooth/event_utils_winrt.h" namespace device { namespace { using ABI::Windows::Devices::Bluetooth::BluetoothError; using ABI::Windows::Devices::Bluetooth::BluetoothError_NotSupported; using ABI::Windows::Devices::Bluetooth::BluetoothError_RadioNotAvailable; using ABI::Windows::Devices::Bluetooth::Advertisement:: BluetoothLEAdvertisementPublisherStatus; using ABI::Windows::Devices::Bluetooth::Advertisement:: BluetoothLEAdvertisementPublisherStatus_Aborted; using ABI::Windows::Devices::Bluetooth::Advertisement:: BluetoothLEAdvertisementPublisherStatus_Started; using ABI::Windows::Devices::Bluetooth::Advertisement:: BluetoothLEAdvertisementPublisherStatus_Stopped; using ABI::Windows::Devices::Bluetooth::Advertisement:: BluetoothLEManufacturerData; using ABI::Windows::Devices::Bluetooth::Advertisement:: IBluetoothLEAdvertisement; using ABI::Windows::Devices::Bluetooth::Advertisement:: IBluetoothLEAdvertisementPublisher; using ABI::Windows::Devices::Bluetooth::Advertisement:: IBluetoothLEAdvertisementPublisherFactory; using ABI::Windows::Devices::Bluetooth::Advertisement:: IBluetoothLEAdvertisementPublisherStatusChangedEventArgs; using ABI::Windows::Devices::Bluetooth::Advertisement:: IBluetoothLEManufacturerData; using ABI::Windows::Devices::Bluetooth::Advertisement:: IBluetoothLEManufacturerDataFactory; using ABI::Windows::Foundation::Collections::IVector; using ABI::Windows::Storage::Streams::IBuffer; using Microsoft::WRL::ComPtr; void RemoveStatusChangedHandler(IBluetoothLEAdvertisementPublisher* publisher, EventRegistrationToken token) { HRESULT hr = publisher->remove_StatusChanged(token); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "Removing StatusChanged Handler failed: " << logging::SystemErrorCodeToString(hr); } } } // namespace BluetoothAdvertisementWinrt::BluetoothAdvertisementWinrt() {} bool BluetoothAdvertisementWinrt::Initialize( std::unique_ptr advertisement_data) { if (advertisement_data->service_uuids()) { BLUETOOTH_LOG(ERROR) << "Windows does not support advertising Service UUIDs."; return false; } if (advertisement_data->solicit_uuids()) { BLUETOOTH_LOG(ERROR) << "Windows does not support advertising Solicit UUIDs."; return false; } if (advertisement_data->service_data()) { BLUETOOTH_LOG(ERROR) << "Windows does not support advertising Service Data."; return false; } auto manufacturer_data = advertisement_data->manufacturer_data(); if (!manufacturer_data) { BLUETOOTH_LOG(ERROR) << "No Manufacturer Data present."; return false; } ComPtr advertisement; HRESULT hr = ActivateBluetoothLEAdvertisementInstance(&advertisement); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "ActivateBluetoothLEAdvertisementInstance failed: " << logging::SystemErrorCodeToString(hr); return false; } ComPtr> manufacturer_data_list; hr = advertisement->get_ManufacturerData(&manufacturer_data_list); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "Getting ManufacturerData failed: " << logging::SystemErrorCodeToString(hr); return false; } ComPtr manufacturer_data_factory; hr = GetBluetoothLEManufacturerDataFactory(&manufacturer_data_factory); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "GetBluetoothLEManufacturerDataFactory failed: " << logging::SystemErrorCodeToString(hr); return false; } for (const auto& pair : *manufacturer_data) { uint16_t manufacturer = pair.first; const std::vector& data = pair.second; ComPtr buffer; hr = base::win::CreateIBufferFromData(data.data(), data.size(), &buffer); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "CreateIBufferFromData() failed: " << logging::SystemErrorCodeToString(hr); return false; } ComPtr manufacturer_data_entry; hr = manufacturer_data_factory->Create(manufacturer, buffer.Get(), &manufacturer_data_entry); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "Creating BluetoothLEManufacturerData failed: " << logging::SystemErrorCodeToString(hr); return false; } hr = manufacturer_data_list->Append(manufacturer_data_entry.Get()); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "Appending BluetoothLEManufacturerData failed: " << logging::SystemErrorCodeToString(hr); return false; } } ComPtr publisher_factory; hr = GetBluetoothLEAdvertisementPublisherActivationFactory(&publisher_factory); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "GetBluetoothLEAdvertisementPublisherActivationFactory " "failed:" << logging::SystemErrorCodeToString(hr); return false; } hr = publisher_factory->Create(advertisement.Get(), &publisher_); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "Creating IBluetoothLEAdvertisementPublisher failed: " << logging::SystemErrorCodeToString(hr); return false; } return true; } void BluetoothAdvertisementWinrt::Register(SuccessCallback callback, ErrorCallback error_callback) { // Register should only be called once during initialization. DCHECK(!status_changed_token_); DCHECK(!pending_register_callbacks_); DCHECK(!pending_unregister_callbacks_); // Register should only be called after successful initialization. DCHECK(publisher_); status_changed_token_ = AddTypedEventHandler( publisher_.Get(), &IBluetoothLEAdvertisementPublisher::add_StatusChanged, base::BindRepeating(&BluetoothAdvertisementWinrt::OnStatusChanged, weak_ptr_factory_.GetWeakPtr())); if (!status_changed_token_) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(error_callback), ERROR_STARTING_ADVERTISEMENT)); return; } HRESULT hr = publisher_->Start(); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "Starting IBluetoothLEAdvertisementPublisher failed: " << logging::SystemErrorCodeToString(hr); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(error_callback), ERROR_STARTING_ADVERTISEMENT)); RemoveStatusChangedHandler(publisher_.Get(), *status_changed_token_); status_changed_token_.reset(); return; } pending_register_callbacks_ = std::make_unique( std::move(callback), std::move(error_callback)); } void BluetoothAdvertisementWinrt::Unregister( const SuccessCallback& success_callback, const ErrorCallback& error_callback) { // Unregister() should only be called when an advertisement is registered // already, or during destruction. In both of these cases there should be no // pending register callbacks and the publisher should be present. DCHECK(!pending_register_callbacks_); DCHECK(publisher_); if (pending_unregister_callbacks_) { BLUETOOTH_LOG(ERROR) << "An Unregister Operation is already in progress."; base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(error_callback, ERROR_RESET_ADVERTISING)); return; } BluetoothLEAdvertisementPublisherStatus status; HRESULT hr = publisher_->get_Status(&status); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "Getting the Publisher Status failed: " << logging::SystemErrorCodeToString(hr); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(error_callback, ERROR_RESET_ADVERTISING)); return; } if (status == BluetoothLEAdvertisementPublisherStatus_Aborted) { // Report an error if the publisher is in the aborted state. base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(error_callback, ERROR_RESET_ADVERTISING)); return; } if (status == BluetoothLEAdvertisementPublisherStatus_Stopped) { // Report success if the publisher is already stopped. base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, success_callback); return; } hr = publisher_->Stop(); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "IBluetoothLEAdvertisementPublisher::Stop() failed: " << logging::SystemErrorCodeToString(hr); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(error_callback, ERROR_RESET_ADVERTISING)); return; } pending_unregister_callbacks_ = std::make_unique(success_callback, error_callback); } IBluetoothLEAdvertisementPublisher* BluetoothAdvertisementWinrt::GetPublisherForTesting() { return publisher_.Get(); } BluetoothAdvertisementWinrt::~BluetoothAdvertisementWinrt() { if (status_changed_token_) { DCHECK(publisher_); RemoveStatusChangedHandler(publisher_.Get(), *status_changed_token_); } // Stop any pending register operation. if (pending_register_callbacks_) { auto callbacks = std::move(pending_register_callbacks_); std::move(callbacks->error_callback).Run(ERROR_STARTING_ADVERTISEMENT); } // Unregister the advertisement on a best effort basis if it's not already in // process of doing so. if (!pending_unregister_callbacks_ && publisher_) Unregister(base::DoNothing(), base::DoNothing()); } HRESULT BluetoothAdvertisementWinrt:: GetBluetoothLEAdvertisementPublisherActivationFactory( IBluetoothLEAdvertisementPublisherFactory** factory) const { return base::win::GetActivationFactory< IBluetoothLEAdvertisementPublisherFactory, RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementPublisher>( factory); } HRESULT BluetoothAdvertisementWinrt::ActivateBluetoothLEAdvertisementInstance( IBluetoothLEAdvertisement** instance) const { auto advertisement_hstring = base::win::ScopedHString::Create( RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisement); if (!advertisement_hstring.is_valid()) return E_FAIL; ComPtr inspectable; HRESULT hr = base::win::RoActivateInstance(advertisement_hstring.get(), &inspectable); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "RoActivateInstance failed: " << logging::SystemErrorCodeToString(hr); return hr; } ComPtr advertisement; hr = inspectable.As(&advertisement); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "As IBluetoothLEAdvertisementWatcher failed: " << logging::SystemErrorCodeToString(hr); return hr; } return advertisement.CopyTo(instance); } HRESULT BluetoothAdvertisementWinrt::GetBluetoothLEManufacturerDataFactory( IBluetoothLEManufacturerDataFactory** factory) const { return base::win::GetActivationFactory< IBluetoothLEManufacturerDataFactory, RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEManufacturerData>( factory); } BluetoothAdvertisementWinrt::PendingCallbacks::PendingCallbacks( SuccessCallback callback, ErrorCallback error_callback) : callback(std::move(callback)), error_callback(std::move(error_callback)) {} BluetoothAdvertisementWinrt::PendingCallbacks::~PendingCallbacks() = default; void BluetoothAdvertisementWinrt::OnStatusChanged( IBluetoothLEAdvertisementPublisher* publisher, IBluetoothLEAdvertisementPublisherStatusChangedEventArgs* changed) { BluetoothLEAdvertisementPublisherStatus status; HRESULT hr = changed->get_Status(&status); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "Getting the Publisher Status failed: " << logging::SystemErrorCodeToString(hr); return; } BLUETOOTH_LOG(EVENT) << "Publisher Status: " << static_cast(status); if (status == BluetoothLEAdvertisementPublisherStatus_Stopped) { // Notify Observers. for (auto& observer : observers_) observer.AdvertisementReleased(this); } // Return early if there is no pending action. if (!pending_register_callbacks_ && !pending_unregister_callbacks_) return; // Register and Unregister should never be pending at the same time. DCHECK(!pending_register_callbacks_ || !pending_unregister_callbacks_); const bool is_starting = pending_register_callbacks_ != nullptr; ErrorCode error_code = is_starting ? ERROR_STARTING_ADVERTISEMENT : ERROR_RESET_ADVERTISING; // Clears out pending callbacks by moving them into a local variable and runs // the appropriate error callback with |error_code|. auto run_error_cb = [&](ErrorCode error_code) { auto callbacks = std::move(is_starting ? pending_register_callbacks_ : pending_unregister_callbacks_); std::move(callbacks->error_callback).Run(error_code); }; if (status == BluetoothLEAdvertisementPublisherStatus_Aborted) { BluetoothError bluetooth_error; hr = changed->get_Error(&bluetooth_error); if (FAILED(hr)) { BLUETOOTH_LOG(ERROR) << "Getting the Publisher Error failed: " << logging::SystemErrorCodeToString(hr); run_error_cb(error_code); return; } BLUETOOTH_LOG(EVENT) << "Publisher aborted: " << static_cast(bluetooth_error); switch (bluetooth_error) { case BluetoothError_RadioNotAvailable: error_code = ERROR_ADAPTER_POWERED_OFF; break; case BluetoothError_NotSupported: error_code = ERROR_UNSUPPORTED_PLATFORM; break; default: break; } run_error_cb(error_code); return; } if (is_starting && status == BluetoothLEAdvertisementPublisherStatus_Started) { BLUETOOTH_LOG(EVENT) << "Starting the Publisher was successful."; auto callbacks = std::move(pending_register_callbacks_); std::move(callbacks->callback).Run(); return; } if (!is_starting && status == BluetoothLEAdvertisementPublisherStatus_Stopped) { BLUETOOTH_LOG(EVENT) << "Stopping the Publisher was successful."; auto callbacks = std::move(pending_unregister_callbacks_); std::move(callbacks->callback).Run(); return; } // The other states are temporary and we expect a future StatusChanged // event. } } // namespace device