// 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_device_winrt.h" #include #include #include #include "base/bind_helpers.h" #include "base/logging.h" #include "base/stl_util.h" #include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" #include "base/win/core_winrt_util.h" #include "base/win/scoped_hstring.h" #include "device/bluetooth/bluetooth_adapter_winrt.h" #include "device/bluetooth/bluetooth_gatt_discoverer_winrt.h" #include "device/bluetooth/bluetooth_pairing_winrt.h" #include "device/bluetooth/bluetooth_remote_gatt_service_winrt.h" #include "device/bluetooth/event_utils_winrt.h" namespace device { namespace { using ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus; using ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus_Connected; using ABI::Windows::Devices::Bluetooth::BluetoothLEDevice; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: GattCommunicationStatus; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: GattCommunicationStatus_Success; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: GattDeviceServicesResult; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: IGattDeviceServicesResult; using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice2; using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice3; using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice; using ABI::Windows::Devices::Bluetooth::IBluetoothLEDeviceStatics; using ABI::Windows::Devices::Enumeration::DevicePairingResultStatus; using ABI::Windows::Devices::Enumeration::IDeviceInformation2; using ABI::Windows::Devices::Enumeration::IDeviceInformation; using ABI::Windows::Devices::Enumeration::IDeviceInformationCustomPairing; using ABI::Windows::Devices::Enumeration::IDeviceInformationPairing2; using ABI::Windows::Devices::Enumeration::IDeviceInformationPairing; using ABI::Windows::Devices::Enumeration::IDevicePairingRequestedEventArgs; using ABI::Windows::Foundation::IAsyncOperation; using ABI::Windows::Foundation::IClosable; using Microsoft::WRL::ComPtr; void PostTask(BluetoothPairingWinrt::ErrorCallback error_callback, BluetoothDevice::ConnectErrorCode error_code) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(error_callback), error_code)); } ComPtr GetDeviceInformationPairing( ComPtr ble_device) { if (!ble_device) { VLOG(2) << "No BLE device instance present."; return nullptr; } ComPtr ble_device_2; HRESULT hr = ble_device.As(&ble_device_2); if (FAILED(hr)) { VLOG(2) << "Obtaining IBluetoothLEDevice2 failed: " << logging::SystemErrorCodeToString(hr); return nullptr; } ComPtr device_information; hr = ble_device_2->get_DeviceInformation(&device_information); if (FAILED(hr)) { VLOG(2) << "Getting Device Information failed: " << logging::SystemErrorCodeToString(hr); return nullptr; } ComPtr device_information_2; hr = device_information.As(&device_information_2); if (FAILED(hr)) { VLOG(2) << "Obtaining IDeviceInformation2 failed: " << logging::SystemErrorCodeToString(hr); return nullptr; } ComPtr pairing; hr = device_information_2->get_Pairing(&pairing); if (FAILED(hr)) { VLOG(2) << "DeviceInformation::get_Pairing() failed: " << logging::SystemErrorCodeToString(hr); return nullptr; } return pairing; } void CloseDevice(ComPtr ble_device) { if (!ble_device) return; ComPtr closable; HRESULT hr = ble_device.As(&closable); if (FAILED(hr)) { VLOG(2) << "As IClosable failed: " << logging::SystemErrorCodeToString(hr); return; } hr = closable->Close(); if (FAILED(hr)) { VLOG(2) << "IClosable::close() failed: " << logging::SystemErrorCodeToString(hr); } } void RemoveConnectionStatusHandler(IBluetoothLEDevice* ble_device, EventRegistrationToken token) { HRESULT hr = ble_device->remove_ConnectionStatusChanged(token); if (FAILED(hr)) { VLOG(2) << "Removing ConnectionStatus Handler failed: " << logging::SystemErrorCodeToString(hr); } } void RemoveGattServicesChangedHandler(IBluetoothLEDevice* ble_device, EventRegistrationToken token) { HRESULT hr = ble_device->remove_GattServicesChanged(token); if (FAILED(hr)) { VLOG(2) << "Removing Gatt Services Changed Handler failed: " << logging::SystemErrorCodeToString(hr); } } } // namespace BluetoothDeviceWinrt::BluetoothDeviceWinrt( BluetoothAdapterWinrt* adapter, uint64_t raw_address, base::Optional local_name) : BluetoothDevice(adapter), raw_address_(raw_address), address_(CanonicalizeAddress(raw_address)), local_name_(std::move(local_name)), weak_ptr_factory_(this) {} BluetoothDeviceWinrt::~BluetoothDeviceWinrt() { CloseDevice(ble_device_); if (!connection_changed_token_) return; RemoveConnectionStatusHandler(ble_device_.Get(), *connection_changed_token_); } uint32_t BluetoothDeviceWinrt::GetBluetoothClass() const { NOTIMPLEMENTED(); return 0; } std::string BluetoothDeviceWinrt::GetAddress() const { return address_; } BluetoothDevice::VendorIDSource BluetoothDeviceWinrt::GetVendorIDSource() const { NOTIMPLEMENTED(); return VendorIDSource(); } uint16_t BluetoothDeviceWinrt::GetVendorID() const { NOTIMPLEMENTED(); return 0; } uint16_t BluetoothDeviceWinrt::GetProductID() const { NOTIMPLEMENTED(); return 0; } uint16_t BluetoothDeviceWinrt::GetDeviceID() const { NOTIMPLEMENTED(); return 0; } uint16_t BluetoothDeviceWinrt::GetAppearance() const { NOTIMPLEMENTED(); return 0; } base::Optional BluetoothDeviceWinrt::GetName() const { if (!ble_device_) return local_name_; HSTRING name; HRESULT hr = ble_device_->get_Name(&name); if (FAILED(hr)) { VLOG(2) << "Getting Name failed: " << logging::SystemErrorCodeToString(hr); return local_name_; } return base::win::ScopedHString(name).GetAsUTF8(); } bool BluetoothDeviceWinrt::IsPaired() const { ComPtr pairing = GetDeviceInformationPairing(ble_device_); if (!pairing) { VLOG(2) << "Failed to get DeviceInformationPairing."; return false; } boolean is_paired; HRESULT hr = pairing->get_IsPaired(&is_paired); if (FAILED(hr)) { VLOG(2) << "DeviceInformationPairing::get_IsPaired() failed: " << logging::SystemErrorCodeToString(hr); return false; } VLOG(2) << "BluetoothDeviceWinrt::IsPaired(): " << (is_paired ? "True" : "False"); return is_paired; } bool BluetoothDeviceWinrt::IsConnected() const { return IsGattConnected(); } bool BluetoothDeviceWinrt::IsGattConnected() const { if (!ble_device_) return false; BluetoothConnectionStatus status; HRESULT hr = ble_device_->get_ConnectionStatus(&status); if (FAILED(hr)) { VLOG(2) << "Getting ConnectionStatus failed: " << logging::SystemErrorCodeToString(hr); return false; } return status == BluetoothConnectionStatus_Connected; } bool BluetoothDeviceWinrt::IsConnectable() const { NOTIMPLEMENTED(); return false; } bool BluetoothDeviceWinrt::IsConnecting() const { NOTIMPLEMENTED(); return false; } bool BluetoothDeviceWinrt::ExpectingPinCode() const { return pairing_ && pairing_->ExpectingPinCode(); } bool BluetoothDeviceWinrt::ExpectingPasskey() const { NOTIMPLEMENTED(); return false; } bool BluetoothDeviceWinrt::ExpectingConfirmation() const { NOTIMPLEMENTED(); return false; } void BluetoothDeviceWinrt::GetConnectionInfo( const ConnectionInfoCallback& callback) { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::SetConnectionLatency( ConnectionLatency connection_latency, const base::Closure& callback, const ErrorCallback& error_callback) { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::Connect(PairingDelegate* pairing_delegate, const base::Closure& callback, const ConnectErrorCallback& error_callback) { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::Pair(PairingDelegate* pairing_delegate, const base::Closure& callback, const ConnectErrorCallback& error_callback) { VLOG(2) << "BluetoothDeviceWinrt::Pair()"; if (pairing_) { VLOG(2) << "Another Pair Operation is already in progress."; PostTask(error_callback, ERROR_INPROGRESS); return; } ComPtr pairing = GetDeviceInformationPairing(ble_device_); if (!pairing) { VLOG(2) << "Failed to get DeviceInformationPairing."; PostTask(error_callback, ERROR_UNKNOWN); return; } ComPtr pairing_2; HRESULT hr = pairing.As(&pairing_2); if (FAILED(hr)) { VLOG(2) << "Obtaining IDeviceInformationPairing2 failed: " << logging::SystemErrorCodeToString(hr); PostTask(error_callback, ERROR_UNKNOWN); return; } ComPtr custom; hr = pairing_2->get_Custom(&custom); if (FAILED(hr)) { VLOG(2) << "DeviceInformationPairing::get_Custom() failed: " << logging::SystemErrorCodeToString(hr); PostTask(error_callback, ERROR_UNKNOWN); return; } // Wrap success and error callback, so that they clean up the pairing object // once they are run. auto wrapped_callback = base::BindOnce( [](base::WeakPtr device, base::OnceClosure callback) { if (device) device->pairing_.reset(); std::move(callback).Run(); }, weak_ptr_factory_.GetWeakPtr(), callback); auto wrapped_error_callback = base::BindOnce( [](base::WeakPtr device, ConnectErrorCallback error_callback, ConnectErrorCode error_code) { if (device) device->pairing_.reset(); std::move(error_callback).Run(error_code); }, weak_ptr_factory_.GetWeakPtr(), error_callback); pairing_ = std::make_unique( this, pairing_delegate, std::move(custom), std::move(wrapped_callback), std::move(wrapped_error_callback)); pairing_->StartPairing(); } void BluetoothDeviceWinrt::SetPinCode(const std::string& pincode) { if (pairing_) pairing_->SetPinCode(pincode); } void BluetoothDeviceWinrt::SetPasskey(uint32_t passkey) { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::ConfirmPairing() { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::RejectPairing() { if (pairing_) pairing_->RejectPairing(); } void BluetoothDeviceWinrt::CancelPairing() { if (pairing_) pairing_->CancelPairing(); } void BluetoothDeviceWinrt::Disconnect(const base::Closure& callback, const ErrorCallback& error_callback) { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::Forget(const base::Closure& callback, const ErrorCallback& error_callback) { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::ConnectToService( const BluetoothUUID& uuid, const ConnectToServiceCallback& callback, const ConnectToServiceErrorCallback& error_callback) { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::ConnectToServiceInsecurely( const device::BluetoothUUID& uuid, const ConnectToServiceCallback& callback, const ConnectToServiceErrorCallback& error_callback) { NOTIMPLEMENTED(); } // static std::string BluetoothDeviceWinrt::CanonicalizeAddress(uint64_t address) { std::string bluetooth_address = BluetoothDevice::CanonicalizeAddress( base::StringPrintf("%012llX", address)); DCHECK(!bluetooth_address.empty()); return bluetooth_address; } void BluetoothDeviceWinrt::CreateGattConnectionImpl() { ComPtr device_statics; HRESULT hr = GetBluetoothLEDeviceStaticsActivationFactory(&device_statics); if (FAILED(hr)) { VLOG(2) << "GetBluetoothLEDeviceStaticsActivationFactory failed: " << logging::SystemErrorCodeToString(hr); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&BluetoothDeviceWinrt::DidFailToConnectGatt, weak_ptr_factory_.GetWeakPtr(), ConnectErrorCode::ERROR_FAILED)); return; } // Note: Even though we might have obtained a BluetoothLEDevice instance in // the past, we need to request a new instance as the old device might have // been closed. See also // https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/gatt-client#connecting-to-the-device ComPtr> from_bluetooth_address_op; hr = device_statics->FromBluetoothAddressAsync(raw_address_, &from_bluetooth_address_op); if (FAILED(hr)) { VLOG(2) << "BluetoothLEDevice::FromBluetoothAddressAsync failed: " << logging::SystemErrorCodeToString(hr); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&BluetoothDeviceWinrt::DidFailToConnectGatt, weak_ptr_factory_.GetWeakPtr(), ConnectErrorCode::ERROR_FAILED)); return; } hr = PostAsyncResults( std::move(from_bluetooth_address_op), base::BindOnce(&BluetoothDeviceWinrt::OnFromBluetoothAddress, weak_ptr_factory_.GetWeakPtr())); if (FAILED(hr)) { VLOG(2) << "PostAsyncResults failed: " << logging::SystemErrorCodeToString(hr); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&BluetoothDeviceWinrt::DidFailToConnectGatt, weak_ptr_factory_.GetWeakPtr(), ConnectErrorCode::ERROR_FAILED)); } } void BluetoothDeviceWinrt::DisconnectGatt() { // Closing the device and disposing of all references will trigger a Gatt // Disconnection after a short timeout. Since the Gatt Services store a // reference to |ble_device_| as well, we need to clear them to drop all // remaining references, so that the OS disconnects. // Reference: // - https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/gatt-client CloseDevice(ble_device_); ClearGattServices(); // Stop any pending Gatt Discovery sessions and report an error. This will // destroy |gatt_discoverer_| and release remaining references the discoverer // might have hold. if (gatt_discoverer_) OnGattDiscoveryComplete(false); } HRESULT BluetoothDeviceWinrt::GetBluetoothLEDeviceStaticsActivationFactory( IBluetoothLEDeviceStatics** statics) const { return base::win::GetActivationFactory< IBluetoothLEDeviceStatics, RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice>(statics); } void BluetoothDeviceWinrt::OnFromBluetoothAddress( ComPtr ble_device) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (!ble_device) { VLOG(2) << "Getting Device From Bluetooth Address failed."; DidFailToConnectGatt(ConnectErrorCode::ERROR_FAILED); return; } if (connection_changed_token_) { // As we are about to replace |ble_device_| with |ble_device| we will also // unregister the existing event handler and add a new one to the new // device. RemoveConnectionStatusHandler(ble_device_.Get(), *connection_changed_token_); } ble_device_ = std::move(ble_device); connection_changed_token_ = AddTypedEventHandler( ble_device_.Get(), &IBluetoothLEDevice::add_ConnectionStatusChanged, base::BindRepeating(&BluetoothDeviceWinrt::OnConnectionStatusChanged, weak_ptr_factory_.GetWeakPtr())); // For paired devices the OS immediately establishes a GATT connection after // the first advertisement. In this case our handler is registered too late to // catch the initial connection changed event, and we need to perform an // explicit check ourselves. if (IsGattConnected()) DidConnectGatt(); if (gatt_services_changed_token_) { RemoveGattServicesChangedHandler(ble_device_.Get(), *gatt_services_changed_token_); } gatt_services_changed_token_ = AddTypedEventHandler( ble_device_.Get(), &IBluetoothLEDevice::add_GattServicesChanged, base::BindRepeating(&BluetoothDeviceWinrt::OnGattServicesChanged, weak_ptr_factory_.GetWeakPtr())); gatt_discoverer_ = std::make_unique(ble_device_); // Initiating the GATT discovery will result in a GATT connection attempt as // well and triggers OnConnectionStatusChanged on success. gatt_discoverer_->StartGattDiscovery( base::BindOnce(&BluetoothDeviceWinrt::OnGattDiscoveryComplete, weak_ptr_factory_.GetWeakPtr())); } void BluetoothDeviceWinrt::OnConnectionStatusChanged( IBluetoothLEDevice* ble_device, IInspectable* object) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (IsGattConnected()) { DidConnectGatt(); } else { gatt_discoverer_.reset(); ClearGattServices(); DidDisconnectGatt(); } } void BluetoothDeviceWinrt::OnGattServicesChanged(IBluetoothLEDevice* ble_device, IInspectable* object) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); // Note: We don't clear out |gatt_services_| here, as we don't want to break // existing references to Gatt Services that did not change. device_uuids_.ClearServiceUUIDs(); SetGattServicesDiscoveryComplete(false); adapter_->NotifyDeviceChanged(this); if (IsGattConnected()) { // In order to stop a potential ongoing GATT discovery, the GattDiscoverer // is reset and a new discovery is initiated. gatt_discoverer_ = std::make_unique(ble_device); gatt_discoverer_->StartGattDiscovery( base::BindOnce(&BluetoothDeviceWinrt::OnGattDiscoveryComplete, weak_ptr_factory_.GetWeakPtr())); } } void BluetoothDeviceWinrt::OnGattDiscoveryComplete(bool success) { if (!success) { if (!IsGattConnected()) DidFailToConnectGatt(ConnectErrorCode::ERROR_FAILED); gatt_discoverer_.reset(); return; } // Instead of clearing out |gatt_services_| and creating each service from // scratch, we create a new map and move already existing services into it in // order to preserve pointer stability. GattServiceMap gatt_services; for (const auto& gatt_service : gatt_discoverer_->GetGattServices()) { auto gatt_service_winrt = BluetoothRemoteGattServiceWinrt::Create(this, gatt_service); if (!gatt_service_winrt) continue; std::string identifier = gatt_service_winrt->GetIdentifier(); auto iter = gatt_services_.find(identifier); if (iter != gatt_services_.end()) { iter = gatt_services.emplace(std::move(*iter)).first; } else { iter = gatt_services .emplace(std::move(identifier), std::move(gatt_service_winrt)) .first; } static_cast(iter->second.get()) ->UpdateCharacteristics(gatt_discoverer_.get()); } std::swap(gatt_services, gatt_services_); device_uuids_.ReplaceServiceUUIDs(gatt_services_); SetGattServicesDiscoveryComplete(true); adapter_->NotifyGattServicesDiscovered(this); adapter_->NotifyDeviceChanged(this); gatt_discoverer_.reset(); } void BluetoothDeviceWinrt::ClearGattServices() { gatt_services_.clear(); device_uuids_.ClearServiceUUIDs(); SetGattServicesDiscoveryComplete(false); } } // namespace device