// Copyright 2015 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_low_energy_discovery_manager_mac.h" #include "base/mac/mac_util.h" #include "base/memory/scoped_ptr.h" #include "base/strings/sys_string_conversions.h" #include "device/bluetooth/bluetooth_low_energy_device_mac.h" using device::BluetoothLowEnergyDeviceMac; using device::BluetoothLowEnergyDiscoveryManagerMac; using device::BluetoothLowEnergyDiscoveryManagerMacDelegate; namespace device { // This class is a helper to call some protected methods in // BluetoothLowEnergyDiscoveryManagerMac. class BluetoothLowEnergyDiscoveryManagerMacDelegate { public: BluetoothLowEnergyDiscoveryManagerMacDelegate( BluetoothLowEnergyDiscoveryManagerMac* manager) : manager_(manager) {} virtual ~BluetoothLowEnergyDiscoveryManagerMacDelegate() {} virtual void DiscoveredPeripheral(CBPeripheral* peripheral, NSDictionary* advertisementData, int rssi) { manager_->DiscoveredPeripheral(peripheral, advertisementData, rssi); } virtual void TryStartDiscovery() { manager_->TryStartDiscovery(); } private: BluetoothLowEnergyDiscoveryManagerMac* manager_; }; } // namespace device // This class will serve as the Objective-C delegate of CBCentralManager. @interface BluetoothLowEnergyDiscoveryManagerMacBridge : NSObject { BluetoothLowEnergyDiscoveryManagerMac* manager_; scoped_ptr delegate_; } - (id)initWithManager:(BluetoothLowEnergyDiscoveryManagerMac*)manager; @end @implementation BluetoothLowEnergyDiscoveryManagerMacBridge - (id)initWithManager:(BluetoothLowEnergyDiscoveryManagerMac*)manager { if ((self = [super init])) { manager_ = manager; delegate_.reset( new BluetoothLowEnergyDiscoveryManagerMacDelegate(manager_)); } return self; } - (void)centralManager:(CBCentralManager*)central didDiscoverPeripheral:(CBPeripheral*)peripheral advertisementData:(NSDictionary*)advertisementData RSSI:(NSNumber*)RSSI { // Notifies the discovery of a device. delegate_->DiscoveredPeripheral(peripheral, advertisementData, [RSSI intValue]); } - (void)centralManagerDidUpdateState:(CBCentralManager*)central { // Notifies when the powered state of the central manager changed. delegate_->TryStartDiscovery(); } @end BluetoothLowEnergyDiscoveryManagerMac:: ~BluetoothLowEnergyDiscoveryManagerMac() { ClearDevices(); } bool BluetoothLowEnergyDiscoveryManagerMac::IsDiscovering() const { return discovering_; } void BluetoothLowEnergyDiscoveryManagerMac::StartDiscovery( BluetoothDevice::UUIDList services_uuids) { ClearDevices(); discovering_ = true; pending_ = true; services_uuids_ = services_uuids; TryStartDiscovery(); } void BluetoothLowEnergyDiscoveryManagerMac::TryStartDiscovery() { if (!discovering_) { return; } if (!pending_) { return; } // Can only start if the bluetooth power is turned on. if ([manager_ state] != CBCentralManagerStatePoweredOn) { return; } // Converts the services UUIDs to a CoreBluetooth data structure. NSMutableArray* services = nil; if (!services_uuids_.empty()) { services = [NSMutableArray array]; for (auto& service_uuid : services_uuids_) { NSString* uuidString = base::SysUTF8ToNSString(service_uuid.canonical_value().c_str()); Class aClass = NSClassFromString(@"CBUUID"); CBUUID* uuid = [aClass UUIDWithString:uuidString]; [services addObject:uuid]; } }; [manager_ scanForPeripheralsWithServices:services options:nil]; pending_ = false; } void BluetoothLowEnergyDiscoveryManagerMac::StopDiscovery() { if (discovering_ && !pending_) { [manager_ stopScan]; } discovering_ = false; } void BluetoothLowEnergyDiscoveryManagerMac::DiscoveredPeripheral( CBPeripheral* peripheral, NSDictionary* advertisementData, int rssi) { // Look for existing device. auto iter = devices_.find( BluetoothLowEnergyDeviceMac::GetPeripheralIdentifier(peripheral)); if (iter == devices_.end()) { // A device has been added. BluetoothLowEnergyDeviceMac* device = new BluetoothLowEnergyDeviceMac(peripheral, advertisementData, rssi); devices_.insert(devices_.begin(), std::make_pair(device->GetIdentifier(), device)); observer_->DeviceFound(device); return; } // A device has an update. BluetoothLowEnergyDeviceMac* old_device = iter->second; old_device->Update(peripheral, advertisementData, rssi); observer_->DeviceUpdated(old_device); } BluetoothLowEnergyDiscoveryManagerMac* BluetoothLowEnergyDiscoveryManagerMac::Create(Observer* observer) { return new BluetoothLowEnergyDiscoveryManagerMac(observer); } BluetoothLowEnergyDiscoveryManagerMac::BluetoothLowEnergyDiscoveryManagerMac( Observer* observer) : observer_(observer) { bridge_.reset([[BluetoothLowEnergyDiscoveryManagerMacBridge alloc] initWithManager:this]); // Since CoreBluetooth is only available on OS X 10.7 or later, we // instantiate CBCentralManager only for OS X >= 10.7. if (base::mac::IsOSLionOrLater()) { Class aClass = NSClassFromString(@"CBCentralManager"); manager_.reset( [[aClass alloc] initWithDelegate:bridge_ queue:dispatch_get_main_queue()]); } discovering_ = false; } void BluetoothLowEnergyDiscoveryManagerMac::ClearDevices() { STLDeleteValues(&devices_); }