| // 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 <windows.devices.enumeration.h> |
| #include <windows.foundation.h> |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback_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/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::ErrorCallback error_callback, |
| BluetoothDevice::ConnectErrorCode error_code) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(error_callback), error_code)); |
| } |
| |
| ComPtr<IDeviceInformationPairing> GetDeviceInformationPairing( |
| ComPtr<IBluetoothLEDevice> ble_device) { |
| if (!ble_device) { |
| BLUETOOTH_LOG(DEBUG) << "No BLE device instance present."; |
| return nullptr; |
| } |
| |
| ComPtr<IBluetoothLEDevice2> 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<IDeviceInformation> 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<IDeviceInformation2> 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<IDeviceInformationPairing> 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<IBluetoothLEDevice> ble_device) { |
| if (!ble_device) |
| return; |
| |
| ComPtr<IClosable> 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<IGattSession> gatt_session) { |
| if (!gatt_session) |
| return; |
| |
| ComPtr<IClosable> 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 { |
| NOTIMPLEMENTED(); |
| 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 { |
| NOTIMPLEMENTED(); |
| return 0; |
| } |
| |
| absl::optional<std::string> 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<IDeviceInformationPairing> 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, |
| base::OnceClosure callback, |
| ConnectErrorCallback error_callback) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceWinrt::Pair(PairingDelegate* pairing_delegate, |
| base::OnceClosure callback, |
| ConnectErrorCallback error_callback) { |
| BLUETOOTH_LOG(DEBUG) << "BluetoothDeviceWinrt::Pair()"; |
| if (pairing_) { |
| BLUETOOTH_LOG(DEBUG) << "Another Pair Operation is already in progress."; |
| PostTask(std::move(error_callback), ERROR_INPROGRESS); |
| return; |
| } |
| |
| ComPtr<IDeviceInformationPairing> pairing = |
| GetDeviceInformationPairing(ble_device_); |
| if (!pairing) { |
| BLUETOOTH_LOG(DEBUG) << "Failed to get DeviceInformationPairing."; |
| PostTask(std::move(error_callback), ERROR_UNKNOWN); |
| return; |
| } |
| |
| ComPtr<IDeviceInformationPairing2> pairing_2; |
| HRESULT hr = pairing.As(&pairing_2); |
| if (FAILED(hr)) { |
| BLUETOOTH_LOG(DEBUG) << "Obtaining IDeviceInformationPairing2 failed: " |
| << logging::SystemErrorCodeToString(hr); |
| PostTask(std::move(error_callback), ERROR_UNKNOWN); |
| return; |
| } |
| |
| ComPtr<IDeviceInformationCustomPairing> custom; |
| hr = pairing_2->get_Custom(&custom); |
| if (FAILED(hr)) { |
| BLUETOOTH_LOG(DEBUG) << "DeviceInformationPairing::get_Custom() failed: " |
| << logging::SystemErrorCodeToString(hr); |
| PostTask(std::move(error_callback), ERROR_UNKNOWN); |
| return; |
| } |
| |
| // Wrap success and error callback, so that they clean up the pairing object |
| // once they are run. |
| base::OnceClosure wrapped_callback = base::BindOnce( |
| [](base::WeakPtr<BluetoothDeviceWinrt> device, |
| base::OnceClosure callback) { |
| if (device) |
| device->pairing_.reset(); |
| std::move(callback).Run(); |
| }, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback)); |
| |
| ConnectErrorCallback wrapped_error_callback = base::BindOnce( |
| [](base::WeakPtr<BluetoothDeviceWinrt> device, |
| ConnectErrorCallback error_callback, ConnectErrorCode error_code) { |
| if (device) |
| device->pairing_.reset(); |
| std::move(error_callback).Run(error_code); |
| }, |
| weak_ptr_factory_.GetWeakPtr(), std::move(error_callback)); |
| |
| pairing_ = std::make_unique<BluetoothPairingWinrt>( |
| 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(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<std::string> local_name) { |
| if (!local_name) |
| return; |
| |
| local_name_ = std::move(local_name); |
| } |
| |
| void BluetoothDeviceWinrt::CreateGattConnectionImpl( |
| absl::optional<BluetoothUUID> service_uuid) { |
| ComPtr<IBluetoothLEDeviceStatics> 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<IAsyncOperation<BluetoothLEDevice*>> 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::DidFailToConnectGatt, |
| 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<IBluetoothLEDevice> 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(); |
| } |
| StartGattDiscovery(); |
| return; |
| } |
| |
| // Next, obtain a GattSession so we can tell the OS to maintain a GATT |
| // connection with |ble_device_|. |
| |
| // BluetoothLEDevice::GetDeviceId() |
| ComPtr<IBluetoothLEDevice4> 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<IBluetoothDeviceId> 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<IAsyncOperation<GattSession*>> 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<IGattSession> 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(); |
| 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(); |
| 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(); |
| } 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<BluetoothGattDiscovererWinrt>(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<BluetoothRemoteGattServiceWinrt*>(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 |