diff options
author | jlebel <jlebel@chromium.org> | 2016-03-14 03:33:28 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-14 10:35:25 +0000 |
commit | a58fd7578542a34fe30bf56ae52b884ad71c9061 (patch) | |
tree | 5709a920ca4a36a25c2c6ccffa58aafc80e8f5c8 /device | |
parent | a70190e18d697d66bfd0ef4e9a2e4e578668e335 (diff) | |
download | chromium_src-a58fd7578542a34fe30bf56ae52b884ad71c9061.zip chromium_src-a58fd7578542a34fe30bf56ae52b884ad71c9061.tar.gz chromium_src-a58fd7578542a34fe30bf56ae52b884ad71c9061.tar.bz2 |
Implementing GATT connection/disconnect on OS X.
BUG=520774
Review URL: https://codereview.chromium.org/1538173003
Cr-Commit-Position: refs/heads/master@{#380951}
Diffstat (limited to 'device')
-rw-r--r-- | device/BUILD.gn | 4 | ||||
-rw-r--r-- | device/bluetooth/BUILD.gn | 2 | ||||
-rw-r--r-- | device/bluetooth/bluetooth.gyp | 2 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_adapter_mac.h | 24 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_adapter_mac.mm | 93 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_adapter_mac_unittest.mm | 8 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_device_unittest.cc | 92 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_low_energy_central_manager_delegate.mm | 31 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_low_energy_device_mac.h | 13 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_low_energy_device_mac.mm | 37 | ||||
-rw-r--r-- | device/bluetooth/test/bluetooth_test_mac.h | 13 | ||||
-rw-r--r-- | device/bluetooth/test/bluetooth_test_mac.mm | 111 | ||||
-rw-r--r-- | device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h | 25 | ||||
-rw-r--r-- | device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm | 42 | ||||
-rw-r--r-- | device/bluetooth/test/mock_bluetooth_central_manager_mac.h | 9 | ||||
-rw-r--r-- | device/bluetooth/test/mock_bluetooth_central_manager_mac.mm | 35 | ||||
-rw-r--r-- | device/device_tests.gyp | 4 |
17 files changed, 465 insertions, 80 deletions
diff --git a/device/BUILD.gn b/device/BUILD.gn index a65caaf..a8870d3 100644 --- a/device/BUILD.gn +++ b/device/BUILD.gn @@ -42,6 +42,10 @@ test("device_unittests") { "bluetooth/test/bluetooth_test_mac.mm", "bluetooth/test/bluetooth_test_win.cc", "bluetooth/test/bluetooth_test_win.h", + "bluetooth/test/mock_bluetooth_cbperipheral_mac.h", + "bluetooth/test/mock_bluetooth_cbperipheral_mac.mm", + "bluetooth/test/mock_bluetooth_central_manager_mac.h", + "bluetooth/test/mock_bluetooth_central_manager_mac.mm", "bluetooth/test/test_bluetooth_adapter_observer.cc", "bluetooth/test/test_bluetooth_adapter_observer.h", "nfc/nfc_chromeos_unittest.cc", diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn index b3394b9..299d3ac 100644 --- a/device/bluetooth/BUILD.gn +++ b/device/bluetooth/BUILD.gn @@ -286,8 +286,6 @@ static_library("mocks") { "test/mock_bluetooth_adapter.h", "test/mock_bluetooth_advertisement.cc", "test/mock_bluetooth_advertisement.h", - "test/mock_bluetooth_central_manager_mac.h", - "test/mock_bluetooth_central_manager_mac.mm", "test/mock_bluetooth_device.cc", "test/mock_bluetooth_device.h", "test/mock_bluetooth_discovery_session.cc", diff --git a/device/bluetooth/bluetooth.gyp b/device/bluetooth/bluetooth.gyp index d46a243..26c90d3 100644 --- a/device/bluetooth/bluetooth.gyp +++ b/device/bluetooth/bluetooth.gyp @@ -354,8 +354,6 @@ 'test/mock_bluetooth_adapter.h', 'test/mock_bluetooth_advertisement.cc', 'test/mock_bluetooth_advertisement.h', - 'test/mock_bluetooth_central_manager_mac.mm', - 'test/mock_bluetooth_central_manager_mac.h', 'test/mock_bluetooth_device.cc', 'test/mock_bluetooth_device.h', 'test/mock_bluetooth_discovery_session.cc', diff --git a/device/bluetooth/bluetooth_adapter_mac.h b/device/bluetooth/bluetooth_adapter_mac.h index 472c2f9..7bd221d 100644 --- a/device/bluetooth/bluetooth_adapter_mac.h +++ b/device/bluetooth/bluetooth_adapter_mac.h @@ -97,10 +97,16 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterMac // (crbug.com/506287). static bool IsLowEnergyAvailable(); - // Resets |low_energy_central_manager_| to |central_manager| and sets - // |low_energy_central_manager_delegate_| as the manager's delegate. Should - // be called only when |IsLowEnergyAvailable()|. - void SetCentralManagerForTesting(CBCentralManager* central_manager); + // Creates a GATT connection by calling CoreBluetooth APIs. + void CreateGattConnection(BluetoothLowEnergyDeviceMac* device_mac); + + // Closes the GATT connection by calling CoreBluetooth APIs. + void DisconnectGatt(BluetoothLowEnergyDeviceMac* device_mac); + + // Methods called from CBCentralManager delegate. + void DidConnectPeripheral(CBPeripheral* peripheral); + void DidFailToConnectPeripheral(CBPeripheral* peripheral, NSError* error); + void DidDisconnectPeripheral(CBPeripheral* peripheral, NSError* error); protected: // BluetoothAdapter override: @@ -108,6 +114,12 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterMac device::BluetoothDevice::PairingDelegate* pairing_delegate) override; private: + // Resets |low_energy_central_manager_| to |central_manager| and sets + // |low_energy_central_manager_delegate_| as the manager's delegate. Should + // be called only when |IsLowEnergyAvailable()|. + void SetCentralManagerForTesting(CBCentralManager* central_manager); + CBCentralManager* GetCentralManagerForTesting(); + // The length of time that must elapse since the last Inquiry response (on // Classic devices) or call to BluetoothLowEnergyDevice::Update() (on Low // Energy) before a discovered device is considered to be no longer available. @@ -162,6 +174,10 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterMac // observers. void AddPairedDevices(); + // Returns the BLE device associated with the CoreBluetooth peripheral. + BluetoothLowEnergyDeviceMac* GetBluetoothLowEnergyDeviceMac( + CBPeripheral* peripheral); + std::string address_; std::string name_; bool classic_powered_; diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm index 76dad60..2c431a4 100644 --- a/device/bluetooth/bluetooth_adapter_mac.mm +++ b/device/bluetooth/bluetooth_adapter_mac.mm @@ -92,6 +92,14 @@ BluetoothAdapterMac::BluetoothAdapterMac() } BluetoothAdapterMac::~BluetoothAdapterMac() { + // When devices will be destroyed, they will need this current instance to + // disconnect the gatt connection. To make sure they don't use the mac + // adapter, they should be explicitly destroyed here. + devices_.clear(); + // Set low_energy_central_manager_ to nil so no devices will try to use it + // while being destroyed after this method. |devices_| is owned by + // BluetoothAdapter. + low_energy_central_manager_.reset(); } std::string BluetoothAdapterMac::GetAddress() const { @@ -221,17 +229,21 @@ bool BluetoothAdapterMac::IsLowEnergyAvailable() { return base::mac::IsOSYosemiteOrLater(); } +void BluetoothAdapterMac::RemovePairingDelegateInternal( + BluetoothDevice::PairingDelegate* pairing_delegate) {} + void BluetoothAdapterMac::SetCentralManagerForTesting( CBCentralManager* central_manager) { CHECK(BluetoothAdapterMac::IsLowEnergyAvailable()); central_manager.delegate = low_energy_central_manager_delegate_; - low_energy_central_manager_.reset(central_manager); + low_energy_central_manager_.reset(central_manager, + base::scoped_policy::RETAIN); low_energy_discovery_manager_->SetCentralManager( low_energy_central_manager_.get()); } -void BluetoothAdapterMac::RemovePairingDelegateInternal( - BluetoothDevice::PairingDelegate* pairing_delegate) { +CBCentralManager* BluetoothAdapterMac::GetCentralManagerForTesting() { + return low_energy_central_manager_; } void BluetoothAdapterMac::AddDiscoverySession( @@ -454,25 +466,23 @@ void BluetoothAdapterMac::LowEnergyDeviceUpdated( CBPeripheral* peripheral, NSDictionary* advertisement_data, int rssi) { - std::string device_address = - BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral); - // Try to find device from |devices_| with key |device_address|, + BluetoothLowEnergyDeviceMac* device_mac = + GetBluetoothLowEnergyDeviceMac(peripheral); // if has no entry in the map, create new device and insert into |devices_|, // otherwise update the existing device. - DevicesMap::const_iterator iter = devices_.find(device_address); - if (iter == devices_.end()) { + if (!device_mac) { VLOG(1) << "LowEnergyDeviceUpdated new device"; // A new device has been found. - BluetoothLowEnergyDeviceMac* device_mac = new BluetoothLowEnergyDeviceMac( - this, peripheral, advertisement_data, rssi); + device_mac = new BluetoothLowEnergyDeviceMac(this, peripheral, + advertisement_data, rssi); + std::string device_address = + BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral); devices_.add(device_address, scoped_ptr<BluetoothDevice>(device_mac)); FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, DeviceAdded(this, device_mac)); return; } - BluetoothLowEnergyDeviceMac* device_mac = - static_cast<BluetoothLowEnergyDeviceMac*>(iter->second); std::string stored_device_id = device_mac->GetIdentifier(); std::string updated_device_id = BluetoothLowEnergyDeviceMac::GetPeripheralIdentifier(peripheral); @@ -535,4 +545,63 @@ void BluetoothAdapterMac::AddPairedDevices() { } } +void BluetoothAdapterMac::CreateGattConnection( + BluetoothLowEnergyDeviceMac* device_mac) { + [low_energy_central_manager_ connectPeripheral:device_mac->peripheral_ + options:nil]; +} + +void BluetoothAdapterMac::DisconnectGatt( + BluetoothLowEnergyDeviceMac* device_mac) { + [low_energy_central_manager_ + cancelPeripheralConnection:device_mac->peripheral_]; +} + +void BluetoothAdapterMac::DidConnectPeripheral(CBPeripheral* peripheral) { + BluetoothLowEnergyDeviceMac* device_mac = + GetBluetoothLowEnergyDeviceMac(peripheral); + if (!device_mac) { + [low_energy_central_manager_ cancelPeripheralConnection:peripheral]; + return; + } + device_mac->DidConnectGatt(); +} + +void BluetoothAdapterMac::DidFailToConnectPeripheral(CBPeripheral* peripheral, + NSError* error) { + BluetoothLowEnergyDeviceMac* device_mac = + GetBluetoothLowEnergyDeviceMac(peripheral); + if (!device_mac) { + [low_energy_central_manager_ cancelPeripheralConnection:peripheral]; + return; + } + VLOG(1) << "Bluetooth error, domain: " << error.domain.UTF8String + << ", error code: " << error.code; + // TODO(http://crbug.com/585894): Need to convert the error. + device_mac->DidFailToConnectGatt(BluetoothClassicDeviceMac::ERROR_UNKNOWN); +} + +void BluetoothAdapterMac::DidDisconnectPeripheral(CBPeripheral* peripheral, + NSError* error) { + BluetoothLowEnergyDeviceMac* device_mac = + GetBluetoothLowEnergyDeviceMac(peripheral); + if (!device_mac) { + [low_energy_central_manager_ cancelPeripheralConnection:peripheral]; + return; + } + // TODO(http://crbug.com/585897): Need to pass the error. + device_mac->DidDisconnectPeripheral(); +} + +BluetoothLowEnergyDeviceMac* +BluetoothAdapterMac::GetBluetoothLowEnergyDeviceMac(CBPeripheral* peripheral) { + std::string device_address = + BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral); + DevicesMap::const_iterator iter = devices_.find(device_address); + if (iter == devices_.end()) { + return nil; + } + return static_cast<BluetoothLowEnergyDeviceMac*>(iter->second); +} + } // namespace device diff --git a/device/bluetooth/bluetooth_adapter_mac_unittest.mm b/device/bluetooth/bluetooth_adapter_mac_unittest.mm index bc63499..f855e72 100644 --- a/device/bluetooth/bluetooth_adapter_mac_unittest.mm +++ b/device/bluetooth/bluetooth_adapter_mac_unittest.mm @@ -116,9 +116,11 @@ class BluetoothAdapterMacTest : public testing::Test { LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; return false; } - mock_central_manager_ = [[MockCentralManager alloc] init]; + mock_central_manager_.reset([[MockCentralManager alloc] init]); [mock_central_manager_ setState:desired_state]; - adapter_mac_->SetCentralManagerForTesting(mock_central_manager_); + CBCentralManager* centralManager = + (CBCentralManager*)mock_central_manager_.get(); + adapter_mac_->SetCentralManagerForTesting(centralManager); return true; } @@ -153,7 +155,7 @@ class BluetoothAdapterMacTest : public testing::Test { BluetoothAdapterMac* adapter_mac_; // Owned by |adapter_mac_|. - id mock_central_manager_ = NULL; + base::scoped_nsobject<MockCentralManager> mock_central_manager_; int callback_count_; int error_callback_count_; diff --git a/device/bluetooth/bluetooth_device_unittest.cc b/device/bluetooth/bluetooth_device_unittest.cc index 6d956c0..13607f492 100644 --- a/device/bluetooth/bluetooth_device_unittest.cc +++ b/device/bluetooth/bluetooth_device_unittest.cc @@ -122,9 +122,13 @@ TEST_F(BluetoothTest, LowEnergyDeviceNoUUIDs) { // also require build configuration to generate string resources into a .pak // file. -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_MACOSX) // Basic CreateGattConnection test. TEST_F(BluetoothTest, CreateGattConnection) { + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } InitWithFakeAdapter(); StartLowEnergyDiscoverySession(); BluetoothDevice* device = DiscoverLowEnergyDevice(3); @@ -137,12 +141,16 @@ TEST_F(BluetoothTest, CreateGattConnection) { EXPECT_TRUE(device->IsGattConnected()); EXPECT_TRUE(gatt_connections_[0]->IsConnected()); } -#endif // defined(OS_ANDROID) +#endif // defined(OS_ANDROID) || defined(OS_MACOSX) -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_MACOSX) // Creates BluetoothGattConnection instances and tests that the interface // functions even when some Disconnect and the BluetoothDevice is destroyed. TEST_F(BluetoothTest, BluetoothGattConnection) { + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } InitWithFakeAdapter(); StartLowEnergyDiscoverySession(); BluetoothDevice* device = DiscoverLowEnergyDevice(3); @@ -198,12 +206,16 @@ TEST_F(BluetoothTest, BluetoothGattConnection) { EXPECT_EQ(device_address, gatt_connections_[0]->GetDeviceAddress()); EXPECT_EQ(device_address, gatt_connections_[1]->GetDeviceAddress()); } -#endif // defined(OS_ANDROID) +#endif // defined(OS_ANDROID) || defined(OS_MACOSX) -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_MACOSX) // Calls CreateGattConnection then simulates multiple connections from platform. TEST_F(BluetoothTest, BluetoothGattConnection_ConnectWithMultipleOSConnections) { + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } InitWithFakeAdapter(); StartLowEnergyDiscoverySession(); BluetoothDevice* device = DiscoverLowEnergyDevice(3); @@ -224,11 +236,15 @@ TEST_F(BluetoothTest, SimulateGattDisconnection(device); EXPECT_FALSE(gatt_connections_[0]->IsConnected()); } -#endif // defined(OS_ANDROID) +#endif // defined(OS_ANDROID) || defined(OS_MACOSX) -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_MACOSX) // Calls CreateGattConnection after already connected. TEST_F(BluetoothTest, BluetoothGattConnection_AlreadyConnected) { + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } InitWithFakeAdapter(); StartLowEnergyDiscoverySession(); BluetoothDevice* device = DiscoverLowEnergyDevice(3); @@ -246,12 +262,16 @@ TEST_F(BluetoothTest, BluetoothGattConnection_AlreadyConnected) { EXPECT_EQ(0, gatt_connection_attempts_); EXPECT_TRUE(gatt_connections_[1]->IsConnected()); } -#endif // defined(OS_ANDROID) +#endif // defined(OS_ANDROID) || defined(OS_MACOSX) -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_MACOSX) // Creates BluetoothGattConnection after one exists that has disconnected. TEST_F(BluetoothTest, BluetoothGattConnection_NewConnectionLeavesPreviousDisconnected) { + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } InitWithFakeAdapter(); StartLowEnergyDiscoverySession(); BluetoothDevice* device = DiscoverLowEnergyDevice(3); @@ -275,11 +295,15 @@ TEST_F(BluetoothTest, "connection is created."; EXPECT_TRUE(gatt_connections_[1]->IsConnected()); } -#endif // defined(OS_ANDROID) +#endif // defined(OS_ANDROID) || defined(OS_MACOSX) -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_MACOSX) // Deletes BluetoothGattConnection causing disconnection. TEST_F(BluetoothTest, BluetoothGattConnection_DisconnectWhenObjectsDestroyed) { + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } InitWithFakeAdapter(); StartLowEnergyDiscoverySession(); BluetoothDevice* device = DiscoverLowEnergyDevice(3); @@ -296,11 +320,15 @@ TEST_F(BluetoothTest, BluetoothGattConnection_DisconnectWhenObjectsDestroyed) { gatt_connections_.clear(); EXPECT_EQ(1, gatt_disconnection_attempts_); } -#endif // defined(OS_ANDROID) +#endif // defined(OS_ANDROID) || defined(OS_MACOSX) -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_MACOSX) // Starts process of disconnecting and then calls BluetoothGattConnection. TEST_F(BluetoothTest, BluetoothGattConnection_DisconnectInProgress) { + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } InitWithFakeAdapter(); StartLowEnergyDiscoverySession(); BluetoothDevice* device = DiscoverLowEnergyDevice(3); @@ -333,12 +361,16 @@ TEST_F(BluetoothTest, BluetoothGattConnection_DisconnectInProgress) { for (BluetoothGattConnection* connection : gatt_connections_) EXPECT_FALSE(connection->IsConnected()); } -#endif // defined(OS_ANDROID) +#endif // defined(OS_ANDROID) || defined(OS_MACOSX) -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_MACOSX) // Calls CreateGattConnection but receives notice that the device disconnected // before it ever connects. TEST_F(BluetoothTest, BluetoothGattConnection_SimulateDisconnect) { + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } InitWithFakeAdapter(); StartLowEnergyDiscoverySession(); BluetoothDevice* device = DiscoverLowEnergyDevice(3); @@ -352,11 +384,15 @@ TEST_F(BluetoothTest, BluetoothGattConnection_SimulateDisconnect) { for (BluetoothGattConnection* connection : gatt_connections_) EXPECT_FALSE(connection->IsConnected()); } -#endif // defined(OS_ANDROID) +#endif // defined(OS_ANDROID) || defined(OS_MACOSX) -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_MACOSX) // Calls CreateGattConnection & DisconnectGatt, then simulates connection. TEST_F(BluetoothTest, BluetoothGattConnection_DisconnectGatt_SimulateConnect) { + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } InitWithFakeAdapter(); StartLowEnergyDiscoverySession(); BluetoothDevice* device = DiscoverLowEnergyDevice(3); @@ -376,12 +412,16 @@ TEST_F(BluetoothTest, BluetoothGattConnection_DisconnectGatt_SimulateConnect) { EXPECT_EQ(0, callback_count_); EXPECT_EQ(0, error_callback_count_); } -#endif // defined(OS_ANDROID) +#endif // defined(OS_ANDROID) || defined(OS_MACOSX) -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_MACOSX) // Calls CreateGattConnection & DisconnectGatt, then simulates disconnection. TEST_F(BluetoothTest, BluetoothGattConnection_DisconnectGatt_SimulateDisconnect) { + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } InitWithFakeAdapter(); StartLowEnergyDiscoverySession(); BluetoothDevice* device = DiscoverLowEnergyDevice(3); @@ -397,12 +437,16 @@ TEST_F(BluetoothTest, for (BluetoothGattConnection* connection : gatt_connections_) EXPECT_FALSE(connection->IsConnected()); } -#endif // defined(OS_ANDROID) +#endif // defined(OS_ANDROID) || defined(OS_MACOSX) #if defined(OS_ANDROID) // Calls CreateGattConnection & DisconnectGatt, then checks that gatt services // have been cleaned up. TEST_F(BluetoothTest, BluetoothGattConnection_DisconnectGatt_Cleanup) { + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } InitWithFakeAdapter(); StartLowEnergyDiscoverySession(); BluetoothDevice* device = DiscoverLowEnergyDevice(3); @@ -452,6 +496,10 @@ TEST_F(BluetoothTest, BluetoothGattConnection_DisconnectGatt_Cleanup) { // Calls CreateGattConnection, but simulate errors connecting. Also, verifies // multiple errors should only invoke callbacks once. TEST_F(BluetoothTest, BluetoothGattConnection_ErrorAfterConnection) { + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } InitWithFakeAdapter(); StartLowEnergyDiscoverySession(); BluetoothDevice* device = DiscoverLowEnergyDevice(3); @@ -533,6 +581,10 @@ TEST_F(BluetoothTest, GetGattServices_and_GetGattService) { #if defined(OS_ANDROID) TEST_F(BluetoothTest, GetGattServices_DiscoveryError) { + if (!PlatformSupportsLowEnergy()) { + LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; + return; + } InitWithFakeAdapter(); StartLowEnergyDiscoverySession(); BluetoothDevice* device = DiscoverLowEnergyDevice(3); diff --git a/device/bluetooth/bluetooth_low_energy_central_manager_delegate.mm b/device/bluetooth/bluetooth_low_energy_central_manager_delegate.mm index 598e4b9..95e452c 100644 --- a/device/bluetooth/bluetooth_low_energy_central_manager_delegate.mm +++ b/device/bluetooth/bluetooth_low_energy_central_manager_delegate.mm @@ -33,6 +33,20 @@ class BluetoothLowEnergyCentralManagerBridge { adapter_->LowEnergyCentralManagerUpdatedState(); } + virtual void DidConnectPeripheral(CBPeripheral* peripheral) { + adapter_->DidConnectPeripheral(peripheral); + } + + virtual void DidFailToConnectPeripheral(CBPeripheral* peripheral, + NSError* error) { + adapter_->DidFailToConnectPeripheral(peripheral, error); + } + + virtual void DidDisconnectPeripheral(CBPeripheral* peripheral, + NSError* error) { + adapter_->DidDisconnectPeripheral(peripheral, error); + } + private: BluetoothLowEnergyDiscoveryManagerMac* discovery_manager_; BluetoothAdapterMac* adapter_; @@ -65,4 +79,21 @@ class BluetoothLowEnergyCentralManagerBridge { bridge_->UpdatedState(); } +- (void)centralManager:(CBCentralManager*)central + didConnectPeripheral:(CBPeripheral*)peripheral { + bridge_->DidConnectPeripheral(peripheral); +} + +- (void)centralManager:(CBCentralManager*)central + didFailToConnectPeripheral:(CBPeripheral*)peripheral + error:(nullable NSError*)error { + bridge_->DidFailToConnectPeripheral(peripheral, error); +} + +- (void)centralManager:(CBCentralManager*)central + didDisconnectPeripheral:(CBPeripheral*)peripheral + error:(nullable NSError*)error { + bridge_->DidDisconnectPeripheral(peripheral, error); +} + @end diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.h b/device/bluetooth/bluetooth_low_energy_device_mac.h index ad713a5..6f33c89 100644 --- a/device/bluetooth/bluetooth_low_energy_device_mac.h +++ b/device/bluetooth/bluetooth_low_energy_device_mac.h @@ -79,9 +79,6 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyDeviceMac const device::BluetoothUUID& uuid, const ConnectToServiceCallback& callback, const ConnectToServiceErrorCallback& error_callback) override; - void CreateGattConnection( - const GattConnectionCallback& callback, - const ConnectErrorCallback& error_callback) override; // BluetoothDeviceMac override. NSDate* GetLastUpdateTime() const override; @@ -108,6 +105,16 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyDeviceMac private: friend class BluetoothAdapterMac; friend class BluetoothAdapterMacTest; + friend class BluetoothTestMac; + + // Returns the Bluetooth adapter. + BluetoothAdapterMac* GetMacAdapter(); + + // Returns the CoreBluetooth Peripheral. + CBPeripheral* GetPeripheral(); + + // Callback used when the CoreBluetooth Peripheral is disconnected. + void DidDisconnectPeripheral(); // CoreBluetooth data structure. base::scoped_nsobject<CBPeripheral> peripheral_; diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.mm b/device/bluetooth/bluetooth_low_energy_device_mac.mm index 00712ae..d0f3f3d 100644 --- a/device/bluetooth/bluetooth_low_energy_device_mac.mm +++ b/device/bluetooth/bluetooth_low_energy_device_mac.mm @@ -43,6 +43,9 @@ BluetoothLowEnergyDeviceMac::BluetoothLowEnergyDeviceMac( } BluetoothLowEnergyDeviceMac::~BluetoothLowEnergyDeviceMac() { + if (IsGattConnected()) { + GetMacAdapter()->DisconnectGatt(this); + } } void BluetoothLowEnergyDeviceMac::Update(CBPeripheral* peripheral, @@ -134,7 +137,7 @@ bool BluetoothLowEnergyDeviceMac::IsConnectable() const { } bool BluetoothLowEnergyDeviceMac::IsConnecting() const { - return false; + return ([peripheral_ state] == CBPeripheralStateConnecting); } BluetoothDevice::UUIDList BluetoothLowEnergyDeviceMac::GetUUIDs() const { @@ -220,12 +223,6 @@ void BluetoothLowEnergyDeviceMac::ConnectToServiceInsecurely( NOTIMPLEMENTED(); } -void BluetoothLowEnergyDeviceMac::CreateGattConnection( - const GattConnectionCallback& callback, - const ConnectErrorCallback& error_callback) { - NOTIMPLEMENTED(); -} - NSDate* BluetoothLowEnergyDeviceMac::GetLastUpdateTime() const { return last_update_time_.get(); } @@ -235,15 +232,13 @@ std::string BluetoothLowEnergyDeviceMac::GetDeviceName() const { } void BluetoothLowEnergyDeviceMac::CreateGattConnectionImpl() { - // Mac implementation does not yet use the default CreateGattConnection - // implementation. http://crbug.com/520774 - NOTIMPLEMENTED(); + if (!IsGattConnected()) { + GetMacAdapter()->CreateGattConnection(this); + } } void BluetoothLowEnergyDeviceMac::DisconnectGatt() { - // Mac implementation does not yet use the default CreateGattConnection - // implementation. http://crbug.com/520774 - NOTIMPLEMENTED(); + GetMacAdapter()->DisconnectGatt(this); } // static @@ -265,3 +260,19 @@ std::string BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress( std::string hash = base::HexEncode(raw, sizeof(raw)); return BluetoothDevice::CanonicalizeAddress(hash); } + +device::BluetoothAdapterMac* BluetoothLowEnergyDeviceMac::GetMacAdapter() { + return static_cast<BluetoothAdapterMac*>(this->adapter_); +} + +CBPeripheral* BluetoothLowEnergyDeviceMac::GetPeripheral() { + return peripheral_; +} + +void BluetoothLowEnergyDeviceMac::DidDisconnectPeripheral() { + if (create_gatt_connection_error_callbacks_.empty()) { + DidDisconnectGatt(); + } else { + DidFailToConnectGatt(ERROR_FAILED); + } +} diff --git a/device/bluetooth/test/bluetooth_test_mac.h b/device/bluetooth/test/bluetooth_test_mac.h index fd354da..1b40b2b 100644 --- a/device/bluetooth/test/bluetooth_test_mac.h +++ b/device/bluetooth/test/bluetooth_test_mac.h @@ -5,6 +5,7 @@ #ifndef DEVICE_BLUETOOTH_TEST_BLUETOOTH_TEST_MAC_H_ #define DEVICE_BLUETOOTH_TEST_BLUETOOTH_TEST_MAC_H_ +#include "base/memory/scoped_ptr.h" #include "base/test/test_simple_task_runner.h" #include "device/bluetooth/test/bluetooth_test.h" @@ -30,12 +31,24 @@ class BluetoothTestMac : public BluetoothTestBase { void InitWithoutDefaultAdapter() override; void InitWithFakeAdapter() override; BluetoothDevice* DiscoverLowEnergyDevice(int device_ordinal) override; + void SimulateGattConnection(BluetoothDevice* device) override; + void SimulateGattDisconnection(BluetoothDevice* device) override; + void SimulateGattConnectionError( + BluetoothDevice* device, + BluetoothDevice::ConnectErrorCode errorCode) override; + + // Callback for the bluetooth central manager mock. + void OnFakeBluetoothDeviceConnectGattCalled(); + void OnFakeBluetoothGattDisconnect(); protected: + class ScopedMockCentralManager; + // Utility function for finding CBUUIDs with relatively nice SHA256 hashes. std::string FindCBUUIDForHashTarget(); BluetoothAdapterMac* adapter_mac_ = NULL; + scoped_ptr<ScopedMockCentralManager> mock_central_manager_; }; // Defines common test fixture name. Use TEST_F(BluetoothTest, YourTestName). diff --git a/device/bluetooth/test/bluetooth_test_mac.mm b/device/bluetooth/test/bluetooth_test_mac.mm index d64b5f7..9ad4583 100644 --- a/device/bluetooth/test/bluetooth_test_mac.mm +++ b/device/bluetooth/test/bluetooth_test_mac.mm @@ -6,21 +6,38 @@ #include <stdint.h> +#include "base/mac/foundation_util.h" #include "base/strings/string_number_conversions.h" #include "build/build_config.h" #include "device/bluetooth/bluetooth_adapter_mac.h" +#include "device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h" #include "device/bluetooth/test/mock_bluetooth_central_manager_mac.h" #include "device/bluetooth/test/test_bluetooth_adapter_observer.h" #include "third_party/ocmock/OCMock/OCMock.h" -#if defined(OS_IOS) #import <CoreBluetooth/CoreBluetooth.h> -#else // !defined(OS_IOS) -#import <IOBluetooth/IOBluetooth.h> -#endif // defined(OS_IOS) + +using base::mac::ObjCCast; +using base::scoped_nsobject; namespace device { +// This class hides Objective-C from bluetooth_test_mac.h. +class BluetoothTestMac::ScopedMockCentralManager { + public: + explicit ScopedMockCentralManager(MockCentralManager* mock_central_manager) { + mock_central_manager_.reset(mock_central_manager); + } + + // Returns MockCentralManager instance. + MockCentralManager* get() { return mock_central_manager_.get(); }; + + private: + scoped_nsobject<MockCentralManager> mock_central_manager_; + + DISALLOW_COPY_AND_ASSIGN(ScopedMockCentralManager); +}; + namespace { CBPeripheral* CreateMockPeripheral(NSString* peripheral_identifier) { @@ -83,9 +100,11 @@ void BluetoothTestMac::InitWithoutDefaultAdapter() { adapter_ = adapter_mac_; if (BluetoothAdapterMac::IsLowEnergyAvailable()) { - id low_energy_central_manager = [[MockCentralManager alloc] init]; - [low_energy_central_manager setState:CBCentralManagerStateUnsupported]; - adapter_mac_->SetCentralManagerForTesting(low_energy_central_manager); + mock_central_manager_.reset( + new ScopedMockCentralManager([[MockCentralManager alloc] init])); + [mock_central_manager_->get() setBluetoothTestMac:this]; + [mock_central_manager_->get() setState:CBCentralManagerStateUnsupported]; + adapter_mac_->SetCentralManagerForTesting((id)mock_central_manager_->get()); } } @@ -97,9 +116,11 @@ void BluetoothTestMac::InitWithFakeAdapter() { adapter_ = adapter_mac_; if (BluetoothAdapterMac::IsLowEnergyAvailable()) { - id low_energy_central_manager = [[MockCentralManager alloc] init]; - [low_energy_central_manager setState:CBCentralManagerStatePoweredOn]; - adapter_mac_->SetCentralManagerForTesting(low_energy_central_manager); + mock_central_manager_.reset( + new ScopedMockCentralManager([[MockCentralManager alloc] init])); + mock_central_manager_->get().bluetoothTestMac = this; + [mock_central_manager_->get() setState:CBCentralManagerStatePoweredOn]; + adapter_mac_->SetCentralManagerForTesting((id)mock_central_manager_->get()); } } @@ -151,12 +172,17 @@ BluetoothDevice* BluetoothTestMac::DiscoverLowEnergyDevice(int device_ordinal) { break; } case 3: { - CBPeripheral* peripheral = CreateMockPeripheral( - [NSString stringWithUTF8String:kTestPeripheralUUID1.c_str()]); - NSString* name = - [NSString stringWithUTF8String:kTestDeviceNameEmpty.c_str()]; - NSArray* uuids = nil; - NSDictionary* advertisement_data = CreateAdvertisementData(name, uuids); + scoped_nsobject<NSString> uuid_string( + [[NSString alloc] initWithUTF8String:kTestPeripheralUUID1.c_str()]); + scoped_nsobject<NSUUID> identifier( + [[NSUUID alloc] initWithUUIDString:uuid_string]); + scoped_nsobject<CBPeripheral> peripheral; + peripheral.reset(static_cast<CBPeripheral*>( + [[MockCBPeripheral alloc] initWithIdentifier:identifier])); + scoped_nsobject<NSString> name( + [[NSString alloc] initWithUTF8String:kTestDeviceNameEmpty.c_str()]); + scoped_nsobject<NSDictionary> advertisement_data( + [CreateAdvertisementData(name, nil) retain]); [central_manager_delegate centralManager:central_manager didDiscoverPeripheral:peripheral advertisementData:advertisement_data @@ -180,6 +206,59 @@ BluetoothDevice* BluetoothTestMac::DiscoverLowEnergyDevice(int device_ordinal) { return observer.last_device(); } +void BluetoothTestMac::SimulateGattConnection(BluetoothDevice* device) { + BluetoothLowEnergyDeviceMac* lowEnergyDeviceMac = + static_cast<BluetoothLowEnergyDeviceMac*>(device); + CBPeripheral* peripheral = lowEnergyDeviceMac->GetPeripheral(); + MockCBPeripheral* mockPeripheral = (MockCBPeripheral*)peripheral; + [mockPeripheral setState:CBPeripheralStateConnected]; + CBCentralManager* centralManager = + ObjCCast<CBCentralManager>(mock_central_manager_->get()); + [centralManager.delegate centralManager:centralManager + didConnectPeripheral:peripheral]; +} + +void BluetoothTestMac::SimulateGattDisconnection(BluetoothDevice* device) { + BluetoothLowEnergyDeviceMac* lowEnergyDeviceMac = + static_cast<BluetoothLowEnergyDeviceMac*>(device); + CBPeripheral* peripheral = lowEnergyDeviceMac->GetPeripheral(); + MockCBPeripheral* mockPeripheral = (MockCBPeripheral*)peripheral; + [mockPeripheral setState:CBPeripheralStateDisconnected]; + CBCentralManager* centralManager = + ObjCCast<CBCentralManager>(mock_central_manager_->get()); + [centralManager.delegate centralManager:centralManager + didDisconnectPeripheral:peripheral + error:nil]; +} + +void BluetoothTestMac::SimulateGattConnectionError( + BluetoothDevice* device, + BluetoothDevice::ConnectErrorCode errorCode) { + BluetoothLowEnergyDeviceMac* lowEnergyDeviceMac = + static_cast<BluetoothLowEnergyDeviceMac*>(device); + CBPeripheral* peripheral = lowEnergyDeviceMac->GetPeripheral(); + MockCBPeripheral* mockPeripheral = (MockCBPeripheral*)peripheral; + [mockPeripheral setState:CBPeripheralStateDisconnected]; + CBCentralManager* centralManager = + ObjCCast<CBCentralManager>(mock_central_manager_->get()); + // TODO(http://crbug.com/585894): Need to convert the connect error code into + // NSError + NSError* error = [NSError errorWithDomain:@"BluetoothDevice::ConnectErrorCode" + code:-1 + userInfo:nil]; + [centralManager.delegate centralManager:centralManager + didFailToConnectPeripheral:peripheral + error:error]; +} + +void BluetoothTestMac::OnFakeBluetoothDeviceConnectGattCalled() { + gatt_connection_attempts_++; +} + +void BluetoothTestMac::OnFakeBluetoothGattDisconnect() { + gatt_disconnection_attempts_++; +} + // Utility function for generating new (CBUUID, address) pairs where CBUUID // hashes to address. For use when adding a new device address to the testing // suite because CoreBluetooth peripherals have CBUUIDs in place of addresses, diff --git a/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h b/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h new file mode 100644 index 0000000..f2e66da --- /dev/null +++ b/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h @@ -0,0 +1,25 @@ +// 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. + +#ifndef DEVICE_BLUETOOTH_MOCK_BLUETOOTH_CBPERIPHERAL_MAC_H_ +#define DEVICE_BLUETOOTH_MOCK_BLUETOOTH_CBPERIPHERAL_MAC_H_ + +#include "base/mac/sdk_forward_declarations.h" +#include "build/build_config.h" + +#import <CoreBluetooth/CoreBluetooth.h> + +// This class mocks the behavior of a CBPeripheral. +@interface MockCBPeripheral : NSObject + +@property(nonatomic, readonly) CBPeripheralState state; +@property(nonatomic, readonly) NSUUID* identifier; +@property(nonatomic, readonly) NSString* name; + +- (instancetype)initWithIdentifier:(NSUUID*)identifier; +- (void)setState:(CBPeripheralState)state; + +@end + +#endif // DEVICE_BLUETOOTH_MOCK_BLUETOOTH_CBPERIPHERAL_MAC_H_ diff --git a/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm b/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm new file mode 100644 index 0000000..0ca4a46 --- /dev/null +++ b/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm @@ -0,0 +1,42 @@ +// 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. + +#import "mock_bluetooth_cbperipheral_mac.h" + +#include "base/mac/scoped_nsobject.h" + +@interface MockCBPeripheral () { + base::scoped_nsobject<NSUUID> _identifier; + base::scoped_nsobject<NSString> _name; +} + +@end + +@implementation MockCBPeripheral + +@synthesize state = _state; + +- (instancetype)initWithIdentifier:(NSUUID*)identifier { + self = [super init]; + if (self) { + _identifier.reset([identifier retain]); + _name.reset([@"FakeBluetoothDevice" retain]); + _state = CBPeripheralStateDisconnected; + } + return self; +} + +- (void)setState:(CBPeripheralState)state { + _state = state; +} + +- (NSUUID*)identifier { + return _identifier.get(); +} + +- (NSString*)name { + return _name.get(); +} + +@end diff --git a/device/bluetooth/test/mock_bluetooth_central_manager_mac.h b/device/bluetooth/test/mock_bluetooth_central_manager_mac.h index 535d72d2..5bc0245 100644 --- a/device/bluetooth/test/mock_bluetooth_central_manager_mac.h +++ b/device/bluetooth/test/mock_bluetooth_central_manager_mac.h @@ -7,12 +7,9 @@ #include "base/mac/sdk_forward_declarations.h" #include "build/build_config.h" +#include "device/bluetooth/test/bluetooth_test_mac.h" -#if defined(OS_IOS) #import <CoreBluetooth/CoreBluetooth.h> -#else -#import <IOBluetooth/IOBluetooth.h> -#endif // Class to mock a CBCentralManager. Cannot use a OCMockObject because mocking // the 'state' property gives a compiler warning when mock_central_manager is of @@ -25,12 +22,16 @@ @property(nonatomic, assign) NSInteger stopScanCallCount; @property(nonatomic, assign) id<CBCentralManagerDelegate> delegate; @property(nonatomic, assign) CBCentralManagerState state; +@property(nonatomic, assign) device::BluetoothTestMac* bluetoothTestMac; - (void)scanForPeripheralsWithServices:(NSArray*)serviceUUIDs options:(NSDictionary*)options; - (void)stopScan; +- (void)connectPeripheral:(CBPeripheral*)peripheral + options:(NSDictionary*)options; + @end #endif // DEVICE_BLUETOOTH_MOCK_BLUETOOTH_CENTRAL_MANAGER_MAC_H_ diff --git a/device/bluetooth/test/mock_bluetooth_central_manager_mac.mm b/device/bluetooth/test/mock_bluetooth_central_manager_mac.mm index 96e7ce9..fbb0dff 100644 --- a/device/bluetooth/test/mock_bluetooth_central_manager_mac.mm +++ b/device/bluetooth/test/mock_bluetooth_central_manager_mac.mm @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "mock_bluetooth_central_manager_mac.h" +#import "device/bluetooth/test/mock_bluetooth_central_manager_mac.h" + +#import "device/bluetooth/test/bluetooth_test_mac.h" +#import "device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h" @implementation MockCentralManager @@ -10,6 +13,23 @@ @synthesize stopScanCallCount = _stopScanCallCount; @synthesize delegate = _delegate; @synthesize state = _state; +@synthesize bluetoothTestMac = _bluetoothTestMac; + +- (BOOL)isKindOfClass:(Class)aClass { + if (aClass == [CBCentralManager class] || + [aClass isSubclassOfClass:[CBCentralManager class]]) { + return YES; + } + return [super isKindOfClass:aClass]; +} + +- (BOOL)isMemberOfClass:(Class)aClass { + if (aClass == [CBCentralManager class] || + [aClass isSubclassOfClass:[CBCentralManager class]]) { + return YES; + } + return [super isKindOfClass:aClass]; +} - (void)scanForPeripheralsWithServices:(NSArray*)serviceUUIDs options:(NSDictionary*)options { @@ -20,4 +40,17 @@ _stopScanCallCount++; } +- (void)connectPeripheral:(CBPeripheral*)peripheral + options:(NSDictionary*)options { + if (_bluetoothTestMac) { + _bluetoothTestMac->OnFakeBluetoothDeviceConnectGattCalled(); + } +} + +- (void)cancelPeripheralConnection:(CBPeripheral*)peripheral { + if (_bluetoothTestMac) { + _bluetoothTestMac->OnFakeBluetoothGattDisconnect(); + } +} + @end diff --git a/device/device_tests.gyp b/device/device_tests.gyp index 7a22fb3..e477b11 100644 --- a/device/device_tests.gyp +++ b/device/device_tests.gyp @@ -59,6 +59,10 @@ 'bluetooth/test/bluetooth_test_mac.mm', 'bluetooth/test/bluetooth_test_win.h', 'bluetooth/test/bluetooth_test_win.cc', + 'bluetooth/test/mock_bluetooth_cbperipheral_mac.mm', + 'bluetooth/test/mock_bluetooth_cbperipheral_mac.h', + 'bluetooth/test/mock_bluetooth_central_manager_mac.mm', + 'bluetooth/test/mock_bluetooth_central_manager_mac.h', 'bluetooth/test/test_bluetooth_adapter_observer.cc', 'bluetooth/test/test_bluetooth_adapter_observer.h', 'hid/hid_connection_unittest.cc', |