// 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.h" #include "base/callback_helpers.h" #include "base/logging.h" #include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" #include "base/win/core_winrt_util.h" #include "base/win/post_async_results.h" #include "base/win/scoped_hstring.h" #include "components/device_event_log/device_event_log.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" #include "device/bluetooth/public/cpp/bluetooth_address.h" #include "device/bluetooth/public/cpp/bluetooth_uuid.h" namespace device { namespace { using ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus; using ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus_Connected; using ABI::Windows::Devices::Bluetooth::BluetoothError; using ABI::Windows::Devices::Bluetooth::BluetoothError_Success; using ABI::Windows::Devices::Bluetooth::BluetoothLEDevice; using ABI::Windows::Devices::Bluetooth::IBluetoothDeviceId; using ABI::Windows::Devices::Bluetooth::IBluetoothDeviceIdStatics; using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice; using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice2; using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice4; using ABI::Windows::Devices::Bluetooth::IBluetoothLEDeviceStatics; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::GattSession; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: GattSessionStatus; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: GattSessionStatus_Active; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: GattSessionStatus_Closed; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattSession; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: IGattSessionStatics; using ABI::Windows::Devices::Enumeration::IDeviceInformation; using ABI::Windows::Devices::Enumeration::IDeviceInformation2; using ABI::Windows::Devices::Enumeration::IDeviceInformationCustomPairing; using ABI::Windows::Devices::Enumeration::IDeviceInformationPairing; using ABI::Windows::Devices::Enumeration::IDeviceInformationPairing2; using ABI::Windows::Foundation::IAsyncOperation; using ABI::Windows::Foundation::IClosable; using Microsoft::WRL::ComPtr; void PostTask(BluetoothPairingWinrt::ConnectCallback callback, absl::optional error_code) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), error_code)); } ComPtr GetDeviceInformationPairing( ComPtr ble_device) { if (!ble_device) { BLUETOOTH_LOG(DEBUG) << "No BLE device instance present."; return nullptr; } ComPtr ble_device_2; HRESULT hr = ble_device.As(&ble_device_2); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "Obtaining IBluetoothLEDevice2 failed: " << logging::SystemErrorCodeToString(hr); return nullptr; } ComPtr device_information; hr = ble_device_2->get_DeviceInformation(&device_information); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "Getting Device Information failed: " << logging::SystemErrorCodeToString(hr); return nullptr; } ComPtr device_information_2; hr = device_information.As(&device_information_2); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "Obtaining IDeviceInformation2 failed: " << logging::SystemErrorCodeToString(hr); return nullptr; } ComPtr pairing; hr = device_information_2->get_Pairing(&pairing); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "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)) { BLUETOOTH_LOG(DEBUG) << "As IClosable failed: " << logging::SystemErrorCodeToString(hr); return; } hr = closable->Close(); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "IClosable::Close() failed: " << logging::SystemErrorCodeToString(hr); } } void CloseGattSession(ComPtr gatt_session) { if (!gatt_session) return; ComPtr closable; HRESULT hr = gatt_session.As(&closable); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "As IClosable failed: " << logging::SystemErrorCodeToString(hr); return; } hr = closable->Close(); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "IClosable::Close() failed: " << logging::SystemErrorCodeToString(hr); } } void RemoveConnectionStatusHandler(IBluetoothLEDevice* ble_device, EventRegistrationToken token) { HRESULT hr = ble_device->remove_ConnectionStatusChanged(token); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "Removing ConnectionStatus Handler failed: " << logging::SystemErrorCodeToString(hr); } } void RemoveGattSessionStatusHandler(IGattSession* gatt_session, EventRegistrationToken token) { HRESULT hr = gatt_session->remove_SessionStatusChanged(token); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "Removing ConnectionStatus Handler failed: " << logging::SystemErrorCodeToString(hr); } } void RemoveGattServicesChangedHandler(IBluetoothLEDevice* ble_device, EventRegistrationToken token) { HRESULT hr = ble_device->remove_GattServicesChanged(token); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "Removing Gatt Services Changed Handler failed: " << logging::SystemErrorCodeToString(hr); } } void RemoveNameChangedHandler(IBluetoothLEDevice* ble_device, EventRegistrationToken token) { HRESULT hr = ble_device->remove_NameChanged(token); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "Removing NameChanged Handler failed: " << logging::SystemErrorCodeToString(hr); } } } // namespace BluetoothDeviceWinrt::BluetoothDeviceWinrt(BluetoothAdapterWinrt* adapter, uint64_t raw_address) : BluetoothDevice(adapter), raw_address_(raw_address), address_(CanonicalizeAddress(raw_address)) { supports_service_specific_discovery_ = true; } BluetoothDeviceWinrt::~BluetoothDeviceWinrt() { CloseGattSession(gatt_session_); CloseDevice(ble_device_); ClearEventRegistrations(); } uint32_t BluetoothDeviceWinrt::GetBluetoothClass() const { // No logging - called too frequenty. return 0; } std::string BluetoothDeviceWinrt::GetAddress() const { return address_; } BluetoothDevice::AddressType BluetoothDeviceWinrt::GetAddressType() const { NOTIMPLEMENTED(); return ADDR_TYPE_UNKNOWN; } 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 { // No logging - called too frequenty. return 0; } absl::optional BluetoothDeviceWinrt::GetName() const { if (!ble_device_) return local_name_; HSTRING name; HRESULT hr = ble_device_->get_Name(&name); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "Getting Name failed: " << logging::SystemErrorCodeToString(hr); return local_name_; } // Prefer returning |local_name_| over an empty string. if (!name) return local_name_; return base::win::ScopedHString(name).GetAsUTF8(); } bool BluetoothDeviceWinrt::IsPaired() const { ComPtr pairing = GetDeviceInformationPairing(ble_device_); if (!pairing) { BLUETOOTH_LOG(DEBUG) << "Failed to get DeviceInformationPairing."; return false; } boolean is_paired; HRESULT hr = pairing->get_IsPaired(&is_paired); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "DeviceInformationPairing::get_IsPaired() failed: " << logging::SystemErrorCodeToString(hr); return false; } BLUETOOTH_LOG(DEBUG) << "BluetoothDeviceWinrt::IsPaired(): " << (is_paired ? "True" : "False"); return is_paired; } bool BluetoothDeviceWinrt::IsConnected() const { return ble_device_ && connection_status_ == BluetoothConnectionStatus_Connected; } bool BluetoothDeviceWinrt::IsGattConnected() const { if (!observe_gatt_session_status_change_events_) return IsConnected(); return gatt_session_ && gatt_session_status_ == GattSessionStatus_Active; } 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(ConnectionInfoCallback callback) { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::SetConnectionLatency( ConnectionLatency connection_latency, base::OnceClosure callback, ErrorCallback error_callback) { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::Connect(PairingDelegate* pairing_delegate, ConnectCallback callback) { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::Pair(PairingDelegate* pairing_delegate, ConnectCallback callback) { BLUETOOTH_LOG(DEBUG) << "BluetoothDeviceWinrt::Pair()"; if (pairing_) { BLUETOOTH_LOG(DEBUG) << "Another Pair Operation is already in progress."; PostTask(std::move(callback), ERROR_INPROGRESS); return; } ComPtr pairing = GetDeviceInformationPairing(ble_device_); if (!pairing) { BLUETOOTH_LOG(DEBUG) << "Failed to get DeviceInformationPairing."; PostTask(std::move(callback), ERROR_UNKNOWN); return; } ComPtr pairing_2; HRESULT hr = pairing.As(&pairing_2); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "Obtaining IDeviceInformationPairing2 failed: " << logging::SystemErrorCodeToString(hr); PostTask(std::move(callback), ERROR_UNKNOWN); return; } ComPtr custom; hr = pairing_2->get_Custom(&custom); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "DeviceInformationPairing::get_Custom() failed: " << logging::SystemErrorCodeToString(hr); PostTask(std::move(callback), ERROR_UNKNOWN); return; } // Wrap callback, so that it cleans up the pairing object when run. auto wrapped_callback = base::BindOnce( [](base::WeakPtr device, ConnectCallback callback, absl::optional error_code) { if (device) device->pairing_.reset(); std::move(callback).Run(error_code); }, weak_ptr_factory_.GetWeakPtr(), std::move(callback)); pairing_ = std::make_unique( this, pairing_delegate, std::move(custom), std::move(wrapped_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() { if (pairing_) pairing_->ConfirmPairing(); } void BluetoothDeviceWinrt::RejectPairing() { if (pairing_) pairing_->RejectPairing(); } void BluetoothDeviceWinrt::CancelPairing() { if (pairing_) pairing_->CancelPairing(); } void BluetoothDeviceWinrt::Disconnect(base::OnceClosure callback, ErrorCallback error_callback) { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::Forget(base::OnceClosure callback, ErrorCallback error_callback) { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::ConnectToService( const BluetoothUUID& uuid, ConnectToServiceCallback callback, ConnectToServiceErrorCallback error_callback) { NOTIMPLEMENTED(); } void BluetoothDeviceWinrt::ConnectToServiceInsecurely( const device::BluetoothUUID& uuid, ConnectToServiceCallback callback, ConnectToServiceErrorCallback error_callback) { NOTIMPLEMENTED(); } // static std::string BluetoothDeviceWinrt::CanonicalizeAddress(uint64_t address) { std::string bluetooth_address = CanonicalizeBluetoothAddress(base::StringPrintf("%012llX", address)); DCHECK(!bluetooth_address.empty()); return bluetooth_address; } void BluetoothDeviceWinrt::UpdateLocalName( absl::optional local_name) { if (!local_name) return; local_name_ = std::move(local_name); } void BluetoothDeviceWinrt::CreateGattConnectionImpl( absl::optional service_uuid) { ComPtr device_statics; HRESULT hr = GetBluetoothLEDeviceStaticsActivationFactory(&device_statics); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "GetBluetoothLEDeviceStaticsActivationFactory failed: " << logging::SystemErrorCodeToString(hr); NotifyGattConnectFailure(); 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)) { BLUETOOTH_LOG(DEBUG) << "BluetoothLEDevice::FromBluetoothAddressAsync failed: " << logging::SystemErrorCodeToString(hr); NotifyGattConnectFailure(); return; } hr = base::win::PostAsyncResults( std::move(from_bluetooth_address_op), base::BindOnce( &BluetoothDeviceWinrt::OnBluetoothLEDeviceFromBluetoothAddress, weak_ptr_factory_.GetWeakPtr())); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "PostAsyncResults failed: " << logging::SystemErrorCodeToString(hr); NotifyGattConnectFailure(); return; } target_uuid_ = std::move(service_uuid); pending_gatt_service_discovery_start_ = true; } void BluetoothDeviceWinrt::NotifyGattConnectFailure() { // Reset |pending_gatt_service_discovery_start_| so that // UpgradeToFullDiscovery() doesn't mistakenly believe GATT discovery is // imminent and therefore avoids starting one itself. pending_gatt_service_discovery_start_ = false; base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&BluetoothDeviceWinrt::DidConnectGatt, weak_ptr_factory_.GetWeakPtr(), ConnectErrorCode::ERROR_FAILED)); } void BluetoothDeviceWinrt::UpgradeToFullDiscovery() { // |CreateGattConnectionImpl| has been called previously but having a specific // |target_uuid_| was too optimistic and now a complete enumeration of // services is needed. target_uuid_.reset(); if (pending_gatt_service_discovery_start_) { // There is an imminent call to StartDiscovery(). Resetting |target_uuid_| // now will be sufficient to change the discovery that will be started. return; } DCHECK(ble_device_); DCHECK(!observe_gatt_session_status_change_events_ || IsGattConnected()); // Restart discovery. StartGattDiscovery(); } 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 CloseGattSession(gatt_session_); 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); } HRESULT BluetoothDeviceWinrt::GetGattSessionStaticsActivationFactory( IGattSessionStatics** statics) const { return base::win::GetActivationFactory< IGattSessionStatics, RuntimeClass_Windows_Devices_Bluetooth_GenericAttributeProfile_GattSession>( statics); } void BluetoothDeviceWinrt::OnBluetoothLEDeviceFromBluetoothAddress( ComPtr ble_device) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (!ble_device) { BLUETOOTH_LOG(DEBUG) << "Getting Device From Bluetooth Address failed."; NotifyGattConnectFailure(); return; } // As we are about to replace |ble_device_| with |ble_device| existing event // handlers need to be unregistered. New ones will be added below. ClearEventRegistrations(); ble_device_ = std::move(ble_device); ble_device_->get_ConnectionStatus(&connection_status_); connection_changed_token_ = AddTypedEventHandler( ble_device_.Get(), &IBluetoothLEDevice::add_ConnectionStatusChanged, base::BindRepeating(&BluetoothDeviceWinrt::OnConnectionStatusChanged, weak_ptr_factory_.GetWeakPtr())); name_changed_token_ = AddTypedEventHandler( ble_device_.Get(), &IBluetoothLEDevice::add_NameChanged, base::BindRepeating(&BluetoothDeviceWinrt::OnNameChanged, weak_ptr_factory_.GetWeakPtr())); if (!observe_gatt_session_status_change_events_) { // GattSession SessionStatusChanged events can not be observed on // 1703 (RS2) because BluetoothLEDevice::GetDeviceId() is not // available. Instead, initiate GATT discovery which should result // in a GATT connection attempt as well and trigger // OnConnectionStatusChanged on success. if (IsGattConnected()) { DidConnectGatt(/*error_code=*/absl::nullopt); } StartGattDiscovery(); return; } // Next, obtain a GattSession so we can tell the OS to maintain a GATT // connection with |ble_device_|. // BluetoothLEDevice::GetDeviceId() ComPtr ble_device_4; HRESULT hr = ble_device_.As(&ble_device_4); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "Obtaining IBluetoothLEDevice4 failed: " << logging::SystemErrorCodeToString(hr); NotifyGattConnectFailure(); return; } ComPtr bluetooth_device_id; hr = ble_device_4->get_BluetoothDeviceId(&bluetooth_device_id); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "BluetoothDeviceId::FromId failed: " << logging::SystemErrorCodeToString(hr); NotifyGattConnectFailure(); return; } // GattSession::FromDeviceIdAsync() IGattSessionStatics* gatt_session_statics = nullptr; hr = GetGattSessionStaticsActivationFactory(&gatt_session_statics); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "GetGattSessionStaticsActivationFactory() failed: " << logging::SystemErrorCodeToString(hr); NotifyGattConnectFailure(); return; } ComPtr> gatt_session_from_device_id_async_op; hr = gatt_session_statics->FromDeviceIdAsync( bluetooth_device_id.Get(), &gatt_session_from_device_id_async_op); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "GattSession::FromDeviceId failed: " << logging::SystemErrorCodeToString(hr); NotifyGattConnectFailure(); return; } hr = base::win::PostAsyncResults( std::move(gatt_session_from_device_id_async_op), base::BindOnce(&BluetoothDeviceWinrt::OnGattSessionFromDeviceId, weak_ptr_factory_.GetWeakPtr())); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "PostAsyncResults failed: " << logging::SystemErrorCodeToString(hr); NotifyGattConnectFailure(); return; } } void BluetoothDeviceWinrt::OnGattSessionFromDeviceId( ComPtr gatt_session) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(observe_gatt_session_status_change_events_); if (!gatt_session) { BLUETOOTH_LOG(DEBUG) << "Getting GattSession failed"; NotifyGattConnectFailure(); return; } gatt_session_ = std::move(gatt_session); // Tell the OS to automatically establish and maintain a GATT connection. HRESULT hr = gatt_session_->put_MaintainConnection(true); if (FAILED(hr)) { BLUETOOTH_LOG(DEBUG) << "Setting GattSession.MaintainConnection failed: " << logging::SystemErrorCodeToString(hr); NotifyGattConnectFailure(); return; } // Observe GattSessionStatus changes. gatt_session_->get_SessionStatus(&gatt_session_status_); gatt_session_status_changed_token_ = AddTypedEventHandler( gatt_session_.Get(), &IGattSession::add_SessionStatusChanged, base::BindRepeating(&BluetoothDeviceWinrt::OnGattSessionStatusChanged, weak_ptr_factory_.GetWeakPtr())); // Check whether we missed the initial GattSessionStatus change notification // because the OS had already established a connection. if (IsGattConnected()) { DidConnectGatt(/*error_code=*/absl::nullopt); StartGattDiscovery(); } } void BluetoothDeviceWinrt::OnGattSessionStatusChanged( IGattSession* gatt_session, ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: IGattSessionStatusChangedEventArgs* event_args) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(observe_gatt_session_status_change_events_); DCHECK_EQ(gatt_session_.Get(), gatt_session); GattSessionStatus old_status = gatt_session_status_; event_args->get_Status(&gatt_session_status_); BluetoothError error; event_args->get_Error(&error); BLUETOOTH_LOG(DEBUG) << "OnGattSessionStatusChanged() status=" << gatt_session_status_ << ", error=" << error; if (pending_gatt_service_discovery_start_ && error != BluetoothError_Success) { NotifyGattConnectFailure(); return; } // Spurious status change notifications may occur. if (old_status == gatt_session_status_) { return; } if (IsGattConnected()) { DidConnectGatt(/*error_code=*/absl::nullopt); StartGattDiscovery(); } else { gatt_discoverer_.reset(); ClearGattServices(); DidDisconnectGatt(); } } void BluetoothDeviceWinrt::OnConnectionStatusChanged( IBluetoothLEDevice* ble_device, IInspectable* object) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); BluetoothConnectionStatus old_status = connection_status_; ble_device->get_ConnectionStatus(&connection_status_); BLUETOOTH_LOG(DEBUG) << "OnConnectionStatusChanged() status=" << connection_status_; // Spurious status change notifications may occur. if (old_status == connection_status_) { return; } if (observe_gatt_session_status_change_events_) { return; } if (IsGattConnected()) { DidConnectGatt(/*error_code=*/absl::nullopt); } else { gatt_discoverer_.reset(); ClearGattServices(); DidDisconnectGatt(); } } void BluetoothDeviceWinrt::OnGattServicesChanged(IBluetoothLEDevice* ble_device, IInspectable* object) { BLUETOOTH_LOG(DEBUG) << "OnGattServicesChanged()"; DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); // TODO(crbug/1085596): This event fires once for every newly discovered GATT // service. Hence, the initial GATT service discovery aborts and restarts // itself here once for every service discovered, which is unnecessary and // slow. // 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. BLUETOOTH_LOG(DEBUG) << "Discovering GATT services anew"; StartGattDiscovery(); } } void BluetoothDeviceWinrt::OnNameChanged(IBluetoothLEDevice* ble_device, IInspectable* object) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); adapter_->NotifyDeviceChanged(this); } void BluetoothDeviceWinrt::StartGattDiscovery() { BLUETOOTH_LOG(DEBUG) << "StartGattDiscovery()"; pending_gatt_service_discovery_start_ = false; if (!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_, target_uuid_); gatt_discoverer_->StartGattDiscovery( base::BindOnce(&BluetoothDeviceWinrt::OnGattDiscoveryComplete, weak_ptr_factory_.GetWeakPtr())); } void BluetoothDeviceWinrt::OnGattDiscoveryComplete(bool success) { BLUETOOTH_LOG(DEBUG) << "OnGattDiscoveryComplete() success=" << success; if (!success) { if (!IsGattConnected()) { NotifyGattConnectFailure(); } 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() { // Clearing |gatt_services_| can trigger callbacks. Move the existing // objects into a local variable to avoid re-entrancy into clear(). GattServiceMap temp_gatt_services; temp_gatt_services.swap(gatt_services_); temp_gatt_services.clear(); device_uuids_.ClearServiceUUIDs(); SetGattServicesDiscoveryComplete(false); } void BluetoothDeviceWinrt::ClearEventRegistrations() { if (connection_changed_token_) { RemoveConnectionStatusHandler(ble_device_.Get(), *connection_changed_token_); } if (gatt_session_status_changed_token_) { RemoveGattSessionStatusHandler(gatt_session_.Get(), *gatt_session_status_changed_token_); } if (gatt_services_changed_token_) { RemoveGattServicesChangedHandler(ble_device_.Get(), *gatt_services_changed_token_); } if (name_changed_token_) RemoveNameChangedHandler(ble_device_.Get(), *name_changed_token_); } } // namespace device