| // Copyright 2016 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_remote_gatt_service_mac.h" |
| |
| #import <CoreBluetooth/CoreBluetooth.h> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "device/bluetooth/bluetooth_adapter_mac.h" |
| #include "device/bluetooth/bluetooth_low_energy_device_mac.h" |
| #include "device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h" |
| #include "device/bluetooth/bluetooth_uuid.h" |
| |
| namespace device { |
| |
| BluetoothRemoteGattServiceMac::BluetoothRemoteGattServiceMac( |
| BluetoothLowEnergyDeviceMac* bluetooth_device_mac, |
| CBService* service, |
| bool is_primary) |
| : bluetooth_device_mac_(bluetooth_device_mac), |
| service_(service, base::scoped_policy::RETAIN), |
| is_primary_(is_primary), |
| discovery_pending_count_(0) { |
| uuid_ = BluetoothAdapterMac::BluetoothUUIDWithCBUUID([service_ UUID]); |
| identifier_ = base::SysNSStringToUTF8( |
| [NSString stringWithFormat:@"%s-%p", uuid_.canonical_value().c_str(), |
| service_.get()]); |
| } |
| |
| BluetoothRemoteGattServiceMac::~BluetoothRemoteGattServiceMac() {} |
| |
| std::string BluetoothRemoteGattServiceMac::GetIdentifier() const { |
| return identifier_; |
| } |
| |
| BluetoothUUID BluetoothRemoteGattServiceMac::GetUUID() const { |
| return uuid_; |
| } |
| |
| bool BluetoothRemoteGattServiceMac::IsPrimary() const { |
| return is_primary_; |
| } |
| |
| BluetoothDevice* BluetoothRemoteGattServiceMac::GetDevice() const { |
| return bluetooth_device_mac_; |
| } |
| |
| std::vector<BluetoothRemoteGattService*> |
| BluetoothRemoteGattServiceMac::GetIncludedServices() const { |
| NOTIMPLEMENTED(); |
| return std::vector<BluetoothRemoteGattService*>(); |
| } |
| |
| void BluetoothRemoteGattServiceMac::DiscoverCharacteristics() { |
| VLOG(1) << *this << ": DiscoverCharacteristics."; |
| SetDiscoveryComplete(false); |
| ++discovery_pending_count_; |
| [GetCBPeripheral() discoverCharacteristics:nil forService:GetService()]; |
| } |
| |
| void BluetoothRemoteGattServiceMac::DidDiscoverCharacteristics() { |
| if (IsDiscoveryComplete() || discovery_pending_count_ == 0) { |
| // This should never happen, just in case it happens with a device, this |
| // notification should be ignored. |
| VLOG(1) |
| << *this |
| << ": Unmatch DiscoverCharacteristics and DidDiscoverCharacteristics."; |
| return; |
| } |
| VLOG(1) << *this << ": DidDiscoverCharacteristics."; |
| --discovery_pending_count_; |
| std::unordered_set<std::string> characteristic_identifier_to_remove; |
| for (const auto& iter : characteristics_) { |
| characteristic_identifier_to_remove.insert(iter.first); |
| } |
| |
| for (CBCharacteristic* cb_characteristic in GetService().characteristics) { |
| BluetoothRemoteGattCharacteristicMac* gatt_characteristic_mac = |
| GetBluetoothRemoteGattCharacteristicMac(cb_characteristic); |
| if (gatt_characteristic_mac) { |
| VLOG(1) << *gatt_characteristic_mac |
| << ": Known characteristic, properties " |
| << gatt_characteristic_mac->GetProperties(); |
| const std::string& identifier = gatt_characteristic_mac->GetIdentifier(); |
| characteristic_identifier_to_remove.erase(identifier); |
| gatt_characteristic_mac->DiscoverDescriptors(); |
| continue; |
| } |
| gatt_characteristic_mac = |
| new BluetoothRemoteGattCharacteristicMac(this, cb_characteristic); |
| bool result = AddCharacteristic(base::WrapUnique(gatt_characteristic_mac)); |
| DCHECK(result); |
| VLOG(1) << *gatt_characteristic_mac << ": New characteristic, properties " |
| << gatt_characteristic_mac->GetProperties(); |
| if (discovery_pending_count_ == 0) { |
| gatt_characteristic_mac->DiscoverDescriptors(); |
| } |
| GetMacAdapter()->NotifyGattCharacteristicAdded(gatt_characteristic_mac); |
| } |
| |
| for (const std::string& identifier : characteristic_identifier_to_remove) { |
| auto pair_to_remove = characteristics_.find(identifier); |
| auto characteristic_to_remove = std::move(pair_to_remove->second); |
| VLOG(1) << static_cast<BluetoothRemoteGattCharacteristicMac&>( |
| *characteristic_to_remove) |
| << ": Removed characteristic."; |
| characteristics_.erase(pair_to_remove); |
| GetMacAdapter()->NotifyGattCharacteristicRemoved( |
| characteristic_to_remove.get()); |
| } |
| SendNotificationIfComplete(); |
| } |
| |
| void BluetoothRemoteGattServiceMac::DidDiscoverDescriptors( |
| CBCharacteristic* characteristic) { |
| if (IsDiscoveryComplete()) { |
| // This should never happen, just in case it happens with a device, this |
| // notification should be ignored. |
| VLOG(1) << *this |
| << ": Discovery complete, ignoring DidDiscoverDescriptors."; |
| return; |
| } |
| BluetoothRemoteGattCharacteristicMac* gatt_characteristic = |
| GetBluetoothRemoteGattCharacteristicMac(characteristic); |
| DCHECK(gatt_characteristic); |
| gatt_characteristic->DidDiscoverDescriptors(); |
| SendNotificationIfComplete(); |
| } |
| |
| void BluetoothRemoteGattServiceMac::SendNotificationIfComplete() { |
| DCHECK(!IsDiscoveryComplete()); |
| // Notify when all characteristics have been fully discovered. |
| SetDiscoveryComplete( |
| discovery_pending_count_ == 0 && |
| std::all_of(characteristics_.begin(), characteristics_.end(), |
| [](const auto& pair) { |
| return static_cast<BluetoothRemoteGattCharacteristicMac*>( |
| pair.second.get()) |
| ->IsDiscoveryComplete(); |
| })); |
| if (IsDiscoveryComplete()) { |
| VLOG(1) << *this << ": Discovery complete."; |
| GetMacAdapter()->NotifyGattServiceChanged(this); |
| } |
| } |
| |
| BluetoothAdapterMac* BluetoothRemoteGattServiceMac::GetMacAdapter() const { |
| return bluetooth_device_mac_->GetMacAdapter(); |
| } |
| |
| CBPeripheral* BluetoothRemoteGattServiceMac::GetCBPeripheral() const { |
| return bluetooth_device_mac_->GetPeripheral(); |
| } |
| |
| CBService* BluetoothRemoteGattServiceMac::GetService() const { |
| return service_; |
| } |
| |
| BluetoothRemoteGattCharacteristicMac* |
| BluetoothRemoteGattServiceMac::GetBluetoothRemoteGattCharacteristicMac( |
| CBCharacteristic* cb_characteristic) const { |
| for (const auto& pair : characteristics_) { |
| auto* characteristic_mac = |
| static_cast<BluetoothRemoteGattCharacteristicMac*>(pair.second.get()); |
| if (characteristic_mac->GetCBCharacteristic() == cb_characteristic) |
| return characteristic_mac; |
| } |
| |
| return nullptr; |
| } |
| |
| BluetoothRemoteGattDescriptorMac* |
| BluetoothRemoteGattServiceMac::GetBluetoothRemoteGattDescriptorMac( |
| CBDescriptor* cb_descriptor) const { |
| CBCharacteristic* cb_characteristic = [cb_descriptor characteristic]; |
| BluetoothRemoteGattCharacteristicMac* gatt_characteristic_mac = |
| GetBluetoothRemoteGattCharacteristicMac(cb_characteristic); |
| if (!gatt_characteristic_mac) { |
| return nullptr; |
| } |
| return gatt_characteristic_mac->GetBluetoothRemoteGattDescriptorMac( |
| cb_descriptor); |
| } |
| |
| DEVICE_BLUETOOTH_EXPORT std::ostream& operator<<( |
| std::ostream& out, |
| const BluetoothRemoteGattServiceMac& service) { |
| const BluetoothLowEnergyDeviceMac* bluetooth_device_mac_ = |
| static_cast<const BluetoothLowEnergyDeviceMac*>(service.GetDevice()); |
| return out << "<BluetoothRemoteGattServiceMac " |
| << service.GetUUID().canonical_value() << "/" << &service |
| << ", device: " << bluetooth_device_mac_->GetAddress() << "/" |
| << bluetooth_device_mac_ << ">"; |
| } |
| |
| } // namespace device |