| // Copyright (c) 2012 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_win.h" |
| |
| #include <string> |
| #include <unordered_map> |
| |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/strings/stringprintf.h" |
| #include "device/bluetooth/bluetooth_adapter_win.h" |
| #include "device/bluetooth/bluetooth_remote_gatt_service_win.h" |
| #include "device/bluetooth/bluetooth_service_record_win.h" |
| #include "device/bluetooth/bluetooth_socket_thread.h" |
| #include "device/bluetooth/bluetooth_socket_win.h" |
| #include "device/bluetooth/bluetooth_task_manager_win.h" |
| #include "device/bluetooth/bluetooth_uuid.h" |
| |
| namespace { |
| |
| const char kApiUnavailable[] = "This API is not implemented on this platform."; |
| |
| } // namespace |
| |
| namespace device { |
| |
| BluetoothDeviceWin::BluetoothDeviceWin( |
| BluetoothAdapterWin* adapter, |
| const BluetoothTaskManagerWin::DeviceState& device_state, |
| scoped_refptr<base::SequencedTaskRunner> ui_task_runner, |
| scoped_refptr<BluetoothSocketThread> socket_thread) |
| : BluetoothDevice(adapter), |
| ui_task_runner_(std::move(ui_task_runner)), |
| socket_thread_(std::move(socket_thread)) { |
| Update(device_state); |
| } |
| |
| BluetoothDeviceWin::~BluetoothDeviceWin() { |
| // Explicitly take and erase GATT services one by one to ensure that calling |
| // GetGattService on removed service in GattServiceRemoved returns null. |
| std::vector<std::string> service_keys; |
| for (const auto& gatt_service : gatt_services_) { |
| service_keys.push_back(gatt_service.first); |
| } |
| for (const auto& key : service_keys) { |
| std::unique_ptr<BluetoothRemoteGattService> service = |
| std::move(gatt_services_[key]); |
| gatt_services_.erase(key); |
| } |
| } |
| |
| uint32_t BluetoothDeviceWin::GetBluetoothClass() const { |
| return bluetooth_class_; |
| } |
| |
| std::string BluetoothDeviceWin::GetAddress() const { |
| return address_; |
| } |
| |
| BluetoothDevice::VendorIDSource |
| BluetoothDeviceWin::GetVendorIDSource() const { |
| return VENDOR_ID_UNKNOWN; |
| } |
| |
| uint16_t BluetoothDeviceWin::GetVendorID() const { |
| return 0; |
| } |
| |
| uint16_t BluetoothDeviceWin::GetProductID() const { |
| return 0; |
| } |
| |
| uint16_t BluetoothDeviceWin::GetDeviceID() const { |
| return 0; |
| } |
| |
| uint16_t BluetoothDeviceWin::GetAppearance() const { |
| // TODO(crbug.com/588083): Implementing GetAppearance() |
| // on mac, win, and android platforms for chrome |
| NOTIMPLEMENTED(); |
| return 0; |
| } |
| |
| base::Optional<std::string> BluetoothDeviceWin::GetName() const { |
| return name_; |
| } |
| |
| bool BluetoothDeviceWin::IsPaired() const { |
| return paired_; |
| } |
| |
| bool BluetoothDeviceWin::IsConnected() const { |
| return connected_; |
| } |
| |
| bool BluetoothDeviceWin::IsGattConnected() const { |
| return gatt_connected_; |
| } |
| |
| bool BluetoothDeviceWin::IsConnectable() const { |
| return false; |
| } |
| |
| bool BluetoothDeviceWin::IsConnecting() const { |
| return false; |
| } |
| |
| BluetoothDevice::UUIDSet BluetoothDeviceWin::GetUUIDs() const { |
| return uuids_; |
| } |
| |
| base::Optional<int8_t> BluetoothDeviceWin::GetInquiryRSSI() const { |
| // In windows, we can only get connected devices and connected |
| // devices don't have an Inquiry RSSI. |
| return base::nullopt; |
| } |
| |
| base::Optional<int8_t> BluetoothDeviceWin::GetInquiryTxPower() const { |
| // In windows, we can only get connected devices and connected |
| // devices don't have an Inquiry Tx Power. |
| return base::nullopt; |
| } |
| |
| bool BluetoothDeviceWin::ExpectingPinCode() const { |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| bool BluetoothDeviceWin::ExpectingPasskey() const { |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| bool BluetoothDeviceWin::ExpectingConfirmation() const { |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| void BluetoothDeviceWin::GetConnectionInfo( |
| const ConnectionInfoCallback& callback) { |
| NOTIMPLEMENTED(); |
| callback.Run(ConnectionInfo()); |
| } |
| |
| void BluetoothDeviceWin::SetConnectionLatency( |
| ConnectionLatency connection_latency, |
| const base::Closure& callback, |
| const ErrorCallback& error_callback) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceWin::Connect( |
| PairingDelegate* pairing_delegate, |
| const base::Closure& callback, |
| const ConnectErrorCallback& error_callback) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceWin::SetPinCode(const std::string& pincode) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceWin::SetPasskey(uint32_t passkey) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceWin::ConfirmPairing() { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceWin::RejectPairing() { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceWin::CancelPairing() { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceWin::Disconnect( |
| const base::Closure& callback, |
| const ErrorCallback& error_callback) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceWin::Forget(const base::Closure& callback, |
| const ErrorCallback& error_callback) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceWin::ConnectToService( |
| const BluetoothUUID& uuid, |
| const ConnectToServiceCallback& callback, |
| const ConnectToServiceErrorCallback& error_callback) { |
| scoped_refptr<BluetoothSocketWin> socket( |
| BluetoothSocketWin::CreateBluetoothSocket( |
| ui_task_runner_, socket_thread_)); |
| socket->Connect(this, uuid, base::Bind(callback, socket), error_callback); |
| } |
| |
| void BluetoothDeviceWin::ConnectToServiceInsecurely( |
| const BluetoothUUID& uuid, |
| const ConnectToServiceCallback& callback, |
| const ConnectToServiceErrorCallback& error_callback) { |
| error_callback.Run(kApiUnavailable); |
| } |
| |
| const BluetoothServiceRecordWin* BluetoothDeviceWin::GetServiceRecord( |
| const device::BluetoothUUID& uuid) const { |
| for (const auto& record : service_record_list_) |
| if (record->uuid() == uuid) |
| return record.get(); |
| |
| return nullptr; |
| } |
| |
| bool BluetoothDeviceWin::IsEqual( |
| const BluetoothTaskManagerWin::DeviceState& device_state) { |
| if (address_ != device_state.address || name_ != device_state.name || |
| bluetooth_class_ != device_state.bluetooth_class || |
| visible_ != device_state.visible || |
| connected_ != device_state.connected || |
| gatt_connected_ == device_state.is_bluetooth_classic() || |
| paired_ != device_state.authenticated) { |
| return false; |
| } |
| |
| // Checks service collection |
| UUIDSet new_services; |
| std::unordered_map<std::string, std::unique_ptr<BluetoothServiceRecordWin>> |
| new_service_records; |
| for (auto iter = device_state.service_record_states.begin(); |
| iter != device_state.service_record_states.end(); ++iter) { |
| auto service_record = std::make_unique<BluetoothServiceRecordWin>( |
| address_, (*iter)->name, (*iter)->sdp_bytes, (*iter)->gatt_uuid); |
| new_services.insert(service_record->uuid()); |
| new_service_records[service_record->uuid().canonical_value()] = |
| std::move(service_record); |
| } |
| |
| // Check that no new services have been added or removed. |
| if (uuids_ != new_services) { |
| return false; |
| } |
| |
| for (const auto& service_record : service_record_list_) { |
| BluetoothServiceRecordWin* new_service_record = |
| new_service_records[service_record->uuid().canonical_value()].get(); |
| if (!service_record->IsEqual(*new_service_record)) |
| return false; |
| } |
| return true; |
| } |
| |
| void BluetoothDeviceWin::Update( |
| const BluetoothTaskManagerWin::DeviceState& device_state) { |
| address_ = device_state.address; |
| // Note: Callers are responsible for providing a canonicalized address. |
| DCHECK_EQ(address_, BluetoothDevice::CanonicalizeAddress(address_)); |
| name_ = device_state.name; |
| bluetooth_class_ = device_state.bluetooth_class; |
| visible_ = device_state.visible; |
| connected_ = device_state.connected; |
| // If a BLE device is not GATT connected, Windows will automatically |
| // reconnect. |
| gatt_connected_ = !device_state.is_bluetooth_classic(); |
| paired_ = device_state.authenticated; |
| UpdateServices(device_state); |
| } |
| |
| void BluetoothDeviceWin::GattServiceDiscoveryComplete( |
| BluetoothRemoteGattServiceWin* service) { |
| DCHECK(ui_task_runner_->RunsTasksInCurrentSequence()); |
| DCHECK(BluetoothDeviceWin::IsGattServiceDiscovered( |
| service->GetUUID(), service->GetAttributeHandle())); |
| |
| discovery_completed_included_services_.insert( |
| {service->GetUUID(), service->GetAttributeHandle()}); |
| if (discovery_completed_included_services_.size() != gatt_services_.size()) |
| return; |
| |
| SetGattServicesDiscoveryComplete(true); |
| adapter_->NotifyGattServicesDiscovered(this); |
| } |
| |
| void BluetoothDeviceWin::CreateGattConnectionImpl() { |
| // Windows will create the Gatt connection as needed. See: |
| // https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/gatt-client#connecting-to-the-device |
| } |
| |
| void BluetoothDeviceWin::DisconnectGatt() { |
| // On Windows, the adapter cannot force a disconnection. |
| } |
| |
| void BluetoothDeviceWin::SetVisible(bool visible) { |
| visible_ = visible; |
| } |
| |
| void BluetoothDeviceWin::UpdateServices( |
| const BluetoothTaskManagerWin::DeviceState& device_state) { |
| uuids_.clear(); |
| service_record_list_.clear(); |
| |
| for (const auto& record_state : device_state.service_record_states) { |
| auto service_record = std::make_unique<BluetoothServiceRecordWin>( |
| device_state.address, record_state->name, record_state->sdp_bytes, |
| record_state->gatt_uuid); |
| uuids_.insert(service_record->uuid()); |
| service_record_list_.push_back(std::move(service_record)); |
| } |
| |
| if (!device_state.is_bluetooth_classic()) |
| UpdateGattServices(device_state.service_record_states); |
| } |
| |
| bool BluetoothDeviceWin::IsGattServiceDiscovered(const BluetoothUUID& uuid, |
| uint16_t attribute_handle) { |
| for (const auto& gatt_service : gatt_services_) { |
| uint16_t it_att_handle = |
| static_cast<BluetoothRemoteGattServiceWin*>(gatt_service.second.get()) |
| ->GetAttributeHandle(); |
| BluetoothUUID it_uuid = gatt_service.second->GetUUID(); |
| if (attribute_handle == it_att_handle && uuid == it_uuid) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool BluetoothDeviceWin::DoesGattServiceExist( |
| const std::vector<std::unique_ptr< |
| BluetoothTaskManagerWin::ServiceRecordState>>& service_state, |
| BluetoothRemoteGattService* service) { |
| uint16_t attribute_handle = |
| static_cast<BluetoothRemoteGattServiceWin*>(service) |
| ->GetAttributeHandle(); |
| BluetoothUUID uuid = service->GetUUID(); |
| for (const auto& record_state : service_state) { |
| if (attribute_handle == record_state->attribute_handle && |
| uuid == record_state->gatt_uuid) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void BluetoothDeviceWin::UpdateGattServices( |
| const std::vector< |
| std::unique_ptr<BluetoothTaskManagerWin::ServiceRecordState>>& |
| service_state) { |
| // First, remove no longer existent GATT service. |
| { |
| std::vector<std::string> to_be_removed_services; |
| for (const auto& gatt_service : gatt_services_) { |
| if (!DoesGattServiceExist(service_state, gatt_service.second.get())) { |
| to_be_removed_services.push_back(gatt_service.first); |
| } |
| } |
| for (const auto& service : to_be_removed_services) { |
| std::unique_ptr<BluetoothRemoteGattService> service_ptr = |
| std::move(gatt_services_[service]); |
| gatt_services_.erase(service); |
| } |
| // Update previously discovered services. |
| for (const auto& gatt_service : gatt_services_) { |
| static_cast<BluetoothRemoteGattServiceWin*>(gatt_service.second.get()) |
| ->Update(); |
| } |
| } |
| |
| // Return if no new services have been added. |
| if (gatt_services_.size() == service_state.size()) |
| return; |
| |
| // Add new services. |
| for (const auto& record_state : service_state) { |
| if (!IsGattServiceDiscovered(record_state->gatt_uuid, |
| record_state->attribute_handle)) { |
| BluetoothRemoteGattServiceWin* primary_service = |
| new BluetoothRemoteGattServiceWin( |
| this, record_state->path, record_state->gatt_uuid, |
| record_state->attribute_handle, true, nullptr, ui_task_runner_); |
| gatt_services_[primary_service->GetIdentifier()] = |
| base::WrapUnique(primary_service); |
| adapter_->NotifyGattServiceAdded(primary_service); |
| } |
| } |
| } |
| |
| } // namespace device |