Commit 74fe98e2 authored by jlebel's avatar jlebel Committed by Commit bot

Bluetooth: macOS: DidModifyServices can happens while scanning

This patch is to fix this scenario:
* Device: DidModifyServices notification
    - Chrome: Scan for primary services
* Device: DidDiscoverServices
    - Chrome: Scan for characteristics
* Device: DidModifyServices notification
    - Chrome: Scan for primary services
* Device: DidDiscoverCharacteristics
* Device: DidDiscoverServices
    - Chrome: Scan for characteristics
* Device: DidDiscoverCharacteristics

We need to wait until all the pending characteristic discoveries are done until we can start scanning
for descriptors. We need to make sure descriptors are not ready if DidModifyServices notification
is received while scanning for descriptors.

Adding discovery_pending_count_ in BluetoothRemoteGattServiceMac and BluetoothRemoteGattCharacteristicMac.
|is_discovery_complete_| can be set to true when discovery_pending_count_ is equal to 0.

BUG=690204

Review-Url: https://codereview.chromium.org/2638653002
Cr-Commit-Position: refs/heads/master@{#469457}
parent 63120f6b
...@@ -483,11 +483,11 @@ TEST_F(BluetoothTest, GetUUIDs_Connection) { ...@@ -483,11 +483,11 @@ TEST_F(BluetoothTest, GetUUIDs_Connection) {
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
// Tests that receiving 2 notifications in a row from macOS that services has // Tests that receiving 2 notifications in a row from macOS that services has
// changed is handled correctly. Each notification should generate a notfication // changed is handled correctly. Each notification should generate a
// that the gatt device has changed, and each notification should ask to macOS // notification that the gatt device has changed, and each notification should
// to scan for services. Only after the second service scan is received, the // ask to macOS to scan for services. Only after the second service scan is
// device changed notification should be sent and the characteristic discovery // received, the device changed notification should be sent and the
// procedure should be started. // characteristic discovery procedure should be started.
// Android: This test doesn't apply to Android because there is no services // Android: This test doesn't apply to Android because there is no services
// changed event that could arrive during a discovery procedure. // changed event that could arrive during a discovery procedure.
TEST_F(BluetoothTest, TwoPendingServiceDiscoveryRequests) { TEST_F(BluetoothTest, TwoPendingServiceDiscoveryRequests) {
...@@ -511,16 +511,21 @@ TEST_F(BluetoothTest, TwoPendingServiceDiscoveryRequests) { ...@@ -511,16 +511,21 @@ TEST_F(BluetoothTest, TwoPendingServiceDiscoveryRequests) {
EXPECT_EQ(1, observer.device_changed_count()); EXPECT_EQ(1, observer.device_changed_count());
EXPECT_FALSE(device->IsGattServicesDiscoveryComplete()); EXPECT_FALSE(device->IsGattServicesDiscoveryComplete());
// Fist system call to // First system call to
// -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:] // -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:] using
// SimulateDidDiscoverServicesMac().
observer.Reset(); observer.Reset();
SimulateDidDiscoverServices(device, {kTestUUIDHeartRate}); AddServicesToDeviceMac(device, {kTestUUIDHeartRate});
SimulateDidDiscoverServicesMac(device);
EXPECT_EQ(0, observer.device_changed_count()); EXPECT_EQ(0, observer.device_changed_count());
EXPECT_FALSE(device->IsGattServicesDiscoveryComplete()); EXPECT_FALSE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(gatt_characteristic_discovery_attempts_, 0); EXPECT_EQ(gatt_characteristic_discovery_attempts_, 0);
// Second system call to // Second system call to
// -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:] // -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:] using the
// generic call to SimulateGattServicesDiscovered(). This method triggers
// the full discovery cycles (services, characteristics and descriptors),
// which includes -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:].
SimulateGattServicesDiscovered( SimulateGattServicesDiscovered(
device, std::vector<std::string>({kTestUUIDImmediateAlert})); device, std::vector<std::string>({kTestUUIDImmediateAlert}));
EXPECT_EQ(1, observer.device_changed_count()); EXPECT_EQ(1, observer.device_changed_count());
...@@ -551,7 +556,7 @@ TEST_F(BluetoothTest, ExtraDidDiscoverServicesCall) { ...@@ -551,7 +556,7 @@ TEST_F(BluetoothTest, ExtraDidDiscoverServicesCall) {
EXPECT_FALSE(device->IsGattServicesDiscoveryComplete()); EXPECT_FALSE(device->IsGattServicesDiscoveryComplete());
// Legitimate system call to // Legitimate system call to
// -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:] // -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:].
observer.Reset(); observer.Reset();
SimulateGattServicesDiscovered( SimulateGattServicesDiscovered(
device, std::vector<std::string>({kTestUUIDHeartRate})); device, std::vector<std::string>({kTestUUIDHeartRate}));
...@@ -561,8 +566,15 @@ TEST_F(BluetoothTest, ExtraDidDiscoverServicesCall) { ...@@ -561,8 +566,15 @@ TEST_F(BluetoothTest, ExtraDidDiscoverServicesCall) {
EXPECT_EQ(1u, device->GetGattServices().size()); EXPECT_EQ(1u, device->GetGattServices().size());
// Unexpected system call to // Unexpected system call to
// -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:] // -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:]:
SimulateDidDiscoverServices(device, {kTestUUIDImmediateAlert}); // This system call is expected only once after -[CBCentralManager
// discoverServices:]. The call to -[CBCentralManager discoverServices:] and
// its answer with -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:]
// is done with SimulateGattServicesDiscovered(). So a second system call to
// -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:] is not expected
// and should be ignored.
AddServicesToDeviceMac(device, {kTestUUIDImmediateAlert});
SimulateDidDiscoverServicesMac(device);
EXPECT_EQ(1, observer.device_changed_count()); EXPECT_EQ(1, observer.device_changed_count());
EXPECT_TRUE(device->IsGattServicesDiscoveryComplete()); EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
......
...@@ -189,7 +189,7 @@ void BluetoothLowEnergyDeviceMac::DisconnectGatt() { ...@@ -189,7 +189,7 @@ void BluetoothLowEnergyDeviceMac::DisconnectGatt() {
void BluetoothLowEnergyDeviceMac::DidDiscoverPrimaryServices(NSError* error) { void BluetoothLowEnergyDeviceMac::DidDiscoverPrimaryServices(NSError* error) {
--discovery_pending_count_; --discovery_pending_count_;
if (discovery_pending_count_ < 0) { if (discovery_pending_count_ < 0) {
// This should never happens, just in case it happens with a device, // This should never happen, just in case it happens with a device,
// discovery_pending_count_ is set back to 0. // discovery_pending_count_ is set back to 0.
VLOG(1) << *this VLOG(1) << *this
<< ": BluetoothLowEnergyDeviceMac::discovery_pending_count_ " << ": BluetoothLowEnergyDeviceMac::discovery_pending_count_ "
...@@ -256,6 +256,13 @@ void BluetoothLowEnergyDeviceMac::DidDiscoverCharacteristics( ...@@ -256,6 +256,13 @@ void BluetoothLowEnergyDeviceMac::DidDiscoverCharacteristics(
// Don't create characteristics if the device disconnected. // Don't create characteristics if the device disconnected.
return; return;
} }
if (IsGattServicesDiscoveryComplete()) {
// This should never happen, just in case it happens with a device, this
// notification should be ignored.
VLOG(1) << *this
<< ": Discovery complete, ignoring DidDiscoverCharacteristics.";
return;
}
BluetoothRemoteGattServiceMac* gatt_service = BluetoothRemoteGattServiceMac* gatt_service =
GetBluetoothRemoteGattService(cb_service); GetBluetoothRemoteGattService(cb_service);
...@@ -327,6 +334,13 @@ void BluetoothLowEnergyDeviceMac::DidDiscoverDescriptors( ...@@ -327,6 +334,13 @@ void BluetoothLowEnergyDeviceMac::DidDiscoverDescriptors(
// Don't discover descriptors if the device disconnected. // Don't discover descriptors if the device disconnected.
return; return;
} }
if (IsGattServicesDiscoveryComplete()) {
// 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;
}
BluetoothRemoteGattServiceMac* gatt_service = BluetoothRemoteGattServiceMac* gatt_service =
GetBluetoothRemoteGattService(cb_characteristic.service); GetBluetoothRemoteGattService(cb_characteristic.service);
DCHECK(gatt_service); DCHECK(gatt_service);
...@@ -380,6 +394,7 @@ void BluetoothLowEnergyDeviceMac::DiscoverPrimaryServices() { ...@@ -380,6 +394,7 @@ void BluetoothLowEnergyDeviceMac::DiscoverPrimaryServices() {
} }
void BluetoothLowEnergyDeviceMac::SendNotificationIfDiscoveryComplete() { void BluetoothLowEnergyDeviceMac::SendNotificationIfDiscoveryComplete() {
DCHECK(!IsGattServicesDiscoveryComplete());
// Notify when all services have been discovered. // Notify when all services have been discovered.
bool discovery_complete = bool discovery_complete =
discovery_pending_count_ == 0 && discovery_pending_count_ == 0 &&
......
...@@ -105,8 +105,12 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicMac ...@@ -105,8 +105,12 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicMac
bool HasPendingWrite() const { bool HasPendingWrite() const {
return !write_characteristic_value_callbacks_.first.is_null(); return !write_characteristic_value_callbacks_.first.is_null();
}; };
// Is true if the characteristic has been discovered with all its descriptors. // Is true if the characteristic has been discovered with all its descriptors
// and discovery_pending_count_ is 0.
bool is_discovery_complete_; bool is_discovery_complete_;
// Increased each time DiscoverDescriptors() is called. And decreased when
// DidDiscoverDescriptors() is called.
int discovery_pending_count_;
// gatt_service_ owns instances of this class. // gatt_service_ owns instances of this class.
BluetoothRemoteGattServiceMac* gatt_service_; BluetoothRemoteGattServiceMac* gatt_service_;
// A characteristic from CBPeripheral.services.characteristics. // A characteristic from CBPeripheral.services.characteristics.
......
...@@ -69,7 +69,9 @@ static BluetoothGattCharacteristic::Properties ConvertProperties( ...@@ -69,7 +69,9 @@ static BluetoothGattCharacteristic::Properties ConvertProperties(
BluetoothRemoteGattCharacteristicMac::BluetoothRemoteGattCharacteristicMac( BluetoothRemoteGattCharacteristicMac::BluetoothRemoteGattCharacteristicMac(
BluetoothRemoteGattServiceMac* gatt_service, BluetoothRemoteGattServiceMac* gatt_service,
CBCharacteristic* cb_characteristic) CBCharacteristic* cb_characteristic)
: gatt_service_(gatt_service), : is_discovery_complete_(false),
discovery_pending_count_(0),
gatt_service_(gatt_service),
cb_characteristic_(cb_characteristic, base::scoped_policy::RETAIN) { cb_characteristic_(cb_characteristic, base::scoped_policy::RETAIN) {
uuid_ = BluetoothAdapterMac::BluetoothUUIDWithCBUUID( uuid_ = BluetoothAdapterMac::BluetoothUUIDWithCBUUID(
[cb_characteristic_.get() UUID]); [cb_characteristic_.get() UUID]);
...@@ -242,6 +244,7 @@ void BluetoothRemoteGattCharacteristicMac::UnsubscribeFromNotifications( ...@@ -242,6 +244,7 @@ void BluetoothRemoteGattCharacteristicMac::UnsubscribeFromNotifications(
void BluetoothRemoteGattCharacteristicMac::DiscoverDescriptors() { void BluetoothRemoteGattCharacteristicMac::DiscoverDescriptors() {
VLOG(1) << *this << ": Discover descriptors."; VLOG(1) << *this << ": Discover descriptors.";
is_discovery_complete_ = false; is_discovery_complete_ = false;
++discovery_pending_count_;
[GetCBPeripheral() [GetCBPeripheral()
discoverDescriptorsForCharacteristic:cb_characteristic_.get()]; discoverDescriptorsForCharacteristic:cb_characteristic_.get()];
} }
...@@ -340,8 +343,15 @@ void BluetoothRemoteGattCharacteristicMac::DidUpdateNotificationState( ...@@ -340,8 +343,15 @@ void BluetoothRemoteGattCharacteristicMac::DidUpdateNotificationState(
} }
void BluetoothRemoteGattCharacteristicMac::DidDiscoverDescriptors() { void BluetoothRemoteGattCharacteristicMac::DidDiscoverDescriptors() {
DCHECK(!is_discovery_complete_); if (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 DiscoverDescriptors and DidDiscoverDescriptors.";
return;
}
VLOG(1) << *this << ": Did discover descriptors."; VLOG(1) << *this << ": Did discover descriptors.";
--discovery_pending_count_;
std::unordered_set<std::string> descriptor_identifier_to_remove; std::unordered_set<std::string> descriptor_identifier_to_remove;
for (const auto& iter : gatt_descriptor_macs_) { for (const auto& iter : gatt_descriptor_macs_) {
descriptor_identifier_to_remove.insert(iter.first); descriptor_identifier_to_remove.insert(iter.first);
...@@ -374,7 +384,7 @@ void BluetoothRemoteGattCharacteristicMac::DidDiscoverDescriptors() { ...@@ -374,7 +384,7 @@ void BluetoothRemoteGattCharacteristicMac::DidDiscoverDescriptors() {
gatt_descriptor_macs_.erase(pair_to_remove); gatt_descriptor_macs_.erase(pair_to_remove);
GetMacAdapter()->NotifyGattDescriptorRemoved(descriptor_to_remove.get()); GetMacAdapter()->NotifyGattDescriptorRemoved(descriptor_to_remove.get());
} }
is_discovery_complete_ = true; is_discovery_complete_ = discovery_pending_count_ == 0;
} }
bool BluetoothRemoteGattCharacteristicMac::IsReadable() const { bool BluetoothRemoteGattCharacteristicMac::IsReadable() const {
......
...@@ -2386,7 +2386,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, ReadDuringDisconnect) { ...@@ -2386,7 +2386,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, ReadDuringDisconnect) {
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED, EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED,
last_gatt_error_code_); last_gatt_error_code_);
} }
#endif #endif // defined(OS_ANDROID)
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
// Tests that write requests after a device disconnects but before the // Tests that write requests after a device disconnects but before the
...@@ -2411,7 +2411,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, WriteDuringDisconnect) { ...@@ -2411,7 +2411,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, WriteDuringDisconnect) {
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED, EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED,
last_gatt_error_code_); last_gatt_error_code_);
} }
#endif #endif // defined(OS_ANDROID)
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
// Tests that start notifications requests after a device disconnects but before // Tests that start notifications requests after a device disconnects but before
...@@ -2440,7 +2440,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, ...@@ -2440,7 +2440,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED, EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED,
last_gatt_error_code_); last_gatt_error_code_);
} }
#endif #endif // defined(OS_ANDROID)
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
// Tests that stop notifications requests after a device disconnects but before // Tests that stop notifications requests after a device disconnects but before
...@@ -2461,7 +2461,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, ...@@ -2461,7 +2461,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
notify_sessions_[0]->Stop(GetStopNotifyCallback(Call::EXPECTED)); notify_sessions_[0]->Stop(GetStopNotifyCallback(Call::EXPECTED));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
#endif #endif // defined(OS_ANDROID)
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
// Tests that deleting notify sessions after a device disconnects but before the // Tests that deleting notify sessions after a device disconnects but before the
...@@ -2482,5 +2482,121 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, ...@@ -2482,5 +2482,121 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
notify_sessions_.clear(); notify_sessions_.clear();
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
#endif #endif // defined(OS_ANDROID)
#if defined(OS_MACOSX)
// Tests to receive a services changed notification from macOS, while
// discovering descriptors. This test simulate having 2 descriptor scan at the
// same time. Only once both descriptor scanning is done, the gatt device is
// ready.
// Android: This test doesn't apply to Android because there is no services
// changed event that could arrive during a discovery procedure.
TEST_F(BluetoothRemoteGattCharacteristicTest,
SimulateDeviceModificationWhileDiscoveringDescriptors) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
TestBluetoothAdapterObserver observer(adapter_);
// Starts first discovery process.
SimulateGattConnection(device);
EXPECT_EQ(1, observer.device_changed_count());
AddServicesToDeviceMac(device, {kTestUUIDHeartRate});
SimulateDidDiscoverServicesMac(device);
EXPECT_EQ(1u, device->GetGattServices().size());
BluetoothRemoteGattService* service = device->GetGattServices()[0];
std::string characteristic_uuid = "11111111-0000-1000-8000-00805f9b34fb";
AddCharacteristicToServiceMac(service, characteristic_uuid,
/* properties */ 0);
SimulateDidDiscoverCharacteristicsMac(service);
EXPECT_EQ(1u, service->GetCharacteristics().size());
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristics()[0];
std::string descriptor_uuid = "22222222-0000-1000-8000-00805f9b34fb";
AddDescriptorToCharacteristicMac(characteristic, descriptor_uuid);
// Now waiting for descriptor discovery.
// Starts second discovery process.
SimulateGattServicesChanged(device);
EXPECT_EQ(2, observer.device_changed_count());
SimulateDidDiscoverServicesMac(device);
SimulateDidDiscoverCharacteristicsMac(service);
// Now waiting for a second descriptor discovery.
// Finish discovery process.
// First system call to -[id<CBPeripheralDelegate>
// peripheral:didDiscoverDescriptorsForCharacteristic:error:]
SimulateDidDiscoverDescriptorsMac(characteristic);
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(1u, service->GetCharacteristics().size());
EXPECT_EQ(1u, characteristic->GetDescriptors().size());
EXPECT_EQ(2, observer.device_changed_count());
// Second system call to -[id<CBPeripheralDelegate>
// peripheral:didDiscoverDescriptorsForCharacteristic:error:]
// Finish second discovery process.
observer.Reset();
SimulateDidDiscoverDescriptorsMac(characteristic);
EXPECT_EQ(1, observer.gatt_service_changed_count());
EXPECT_EQ(1, observer.device_changed_count());
}
#endif // defined(OS_MACOSX)
#if defined(OS_MACOSX)
// Simulates to receive an extra discovery descriptor notifications from macOS.
// Those notifications should be ignored.
// Android: This test doesn't apply to Android because there is no services
// changed event that could arrive during a discovery procedure.
TEST_F(BluetoothRemoteGattCharacteristicTest, ExtraDidDiscoverDescriptorsCall) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
TestBluetoothAdapterObserver observer(adapter_);
// Starts first discovery process.
SimulateGattConnection(device);
AddServicesToDeviceMac(device, {kTestUUIDHeartRate});
SimulateDidDiscoverServicesMac(device);
EXPECT_EQ(1u, device->GetGattServices().size());
BluetoothRemoteGattService* service = device->GetGattServices()[0];
std::string characteristic_uuid = "11111111-0000-1000-8000-00805f9b34fb";
AddCharacteristicToServiceMac(service, characteristic_uuid,
/* properties */ 0);
SimulateDidDiscoverCharacteristicsMac(service);
EXPECT_EQ(1u, service->GetCharacteristics().size());
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristics()[0];
std::string descriptor_uuid = "22222222-0000-1000-8000-00805f9b34fb";
AddDescriptorToCharacteristicMac(characteristic, descriptor_uuid);
SimulateDidDiscoverDescriptorsMac(characteristic);
EXPECT_EQ(1, observer.gatt_service_changed_count());
EXPECT_EQ(1u, service->GetCharacteristics().size());
EXPECT_EQ(1u, characteristic->GetDescriptors().size());
observer.Reset();
SimulateDidDiscoverDescriptorsMac(characteristic); // Extra system call.
SimulateGattServicesChanged(device);
SimulateDidDiscoverDescriptorsMac(characteristic); // Extra system call.
SimulateDidDiscoverServicesMac(device);
SimulateDidDiscoverDescriptorsMac(characteristic); // Extra system call.
SimulateDidDiscoverCharacteristicsMac(service);
SimulateDidDiscoverDescriptorsMac(characteristic);
SimulateDidDiscoverDescriptorsMac(characteristic); // Extra system call.
EXPECT_EQ(2, observer.device_changed_count());
}
#endif // defined(OS_MACOSX)
} // namespace device } // namespace device
...@@ -780,7 +780,7 @@ TEST_F(BluetoothRemoteGattDescriptorTest, ReadRemoteDescriptor_NSString) { ...@@ -780,7 +780,7 @@ TEST_F(BluetoothRemoteGattDescriptorTest, ReadRemoteDescriptor_NSString) {
EXPECT_EQ(1, gatt_read_descriptor_attempts_); EXPECT_EQ(1, gatt_read_descriptor_attempts_);
std::string test_string = "Hello"; std::string test_string = "Hello";
SimulateGattDescriptorReadNSString(descriptor1_, test_string); SimulateGattDescriptorReadNSStringMac(descriptor1_, test_string);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
std::vector<uint8_t> test_vector(test_string.begin(), test_string.end()); std::vector<uint8_t> test_vector(test_string.begin(), test_string.end());
...@@ -802,7 +802,7 @@ TEST_F(BluetoothRemoteGattDescriptorTest, ReadRemoteDescriptor_NSNumber) { ...@@ -802,7 +802,7 @@ TEST_F(BluetoothRemoteGattDescriptorTest, ReadRemoteDescriptor_NSNumber) {
EXPECT_EQ(1, gatt_read_descriptor_attempts_); EXPECT_EQ(1, gatt_read_descriptor_attempts_);
const short test_number = 0x1234; const short test_number = 0x1234;
SimulateGattDescriptorReadNSNumber(descriptor1_, test_number); SimulateGattDescriptorReadNSNumberMac(descriptor1_, test_number);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
uint8_t values[] = {0x34, 0x12}; uint8_t values[] = {0x34, 0x12};
......
...@@ -96,8 +96,12 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattServiceMac ...@@ -96,8 +96,12 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattServiceMac
std::string identifier_; std::string identifier_;
// Service UUID. // Service UUID.
BluetoothUUID uuid_; BluetoothUUID uuid_;
// Is true if the characteristics has been discovered. // Is true if the characteristics has been discovered and
// discovery_pending_count_ is 0.
bool is_discovery_complete_; bool is_discovery_complete_;
// Increased each time DiscoverCharacteristics() is called. And decreased when
// DidDiscoverCharacteristics() is called.
int discovery_pending_count_;
DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattServiceMac); DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattServiceMac);
}; };
......
...@@ -23,7 +23,8 @@ BluetoothRemoteGattServiceMac::BluetoothRemoteGattServiceMac( ...@@ -23,7 +23,8 @@ BluetoothRemoteGattServiceMac::BluetoothRemoteGattServiceMac(
: bluetooth_device_mac_(bluetooth_device_mac), : bluetooth_device_mac_(bluetooth_device_mac),
service_(service, base::scoped_policy::RETAIN), service_(service, base::scoped_policy::RETAIN),
is_primary_(is_primary), is_primary_(is_primary),
is_discovery_complete_(false) { is_discovery_complete_(false),
discovery_pending_count_(0) {
uuid_ = BluetoothAdapterMac::BluetoothUUIDWithCBUUID([service_.get() UUID]); uuid_ = BluetoothAdapterMac::BluetoothUUIDWithCBUUID([service_.get() UUID]);
identifier_ = identifier_ =
[NSString stringWithFormat:@"%s-%p", uuid_.canonical_value().c_str(), [NSString stringWithFormat:@"%s-%p", uuid_.canonical_value().c_str(),
...@@ -80,12 +81,21 @@ BluetoothRemoteGattServiceMac::GetCharacteristic( ...@@ -80,12 +81,21 @@ BluetoothRemoteGattServiceMac::GetCharacteristic(
void BluetoothRemoteGattServiceMac::DiscoverCharacteristics() { void BluetoothRemoteGattServiceMac::DiscoverCharacteristics() {
VLOG(1) << *this << ": DiscoverCharacteristics."; VLOG(1) << *this << ": DiscoverCharacteristics.";
is_discovery_complete_ = false; is_discovery_complete_ = false;
++discovery_pending_count_;
[GetCBPeripheral() discoverCharacteristics:nil forService:GetService()]; [GetCBPeripheral() discoverCharacteristics:nil forService:GetService()];
} }
void BluetoothRemoteGattServiceMac::DidDiscoverCharacteristics() { void BluetoothRemoteGattServiceMac::DidDiscoverCharacteristics() {
DCHECK(!is_discovery_complete_); if (is_discovery_complete_ || 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."; VLOG(1) << *this << ": DidDiscoverCharacteristics.";
--discovery_pending_count_;
std::unordered_set<std::string> characteristic_identifier_to_remove; std::unordered_set<std::string> characteristic_identifier_to_remove;
for (const auto& iter : gatt_characteristic_macs_) { for (const auto& iter : gatt_characteristic_macs_) {
characteristic_identifier_to_remove.insert(iter.first); characteristic_identifier_to_remove.insert(iter.first);
...@@ -111,7 +121,9 @@ void BluetoothRemoteGattServiceMac::DidDiscoverCharacteristics() { ...@@ -111,7 +121,9 @@ void BluetoothRemoteGattServiceMac::DidDiscoverCharacteristics() {
DCHECK(result_iter.second); DCHECK(result_iter.second);
VLOG(1) << *gatt_characteristic_mac << ": New characteristic, properties " VLOG(1) << *gatt_characteristic_mac << ": New characteristic, properties "
<< gatt_characteristic_mac->GetProperties(); << gatt_characteristic_mac->GetProperties();
gatt_characteristic_mac->DiscoverDescriptors(); if (discovery_pending_count_ == 0) {
gatt_characteristic_mac->DiscoverDescriptors();
}
GetMacAdapter()->NotifyGattCharacteristicAdded(gatt_characteristic_mac); GetMacAdapter()->NotifyGattCharacteristicAdded(gatt_characteristic_mac);
} }
...@@ -130,7 +142,13 @@ void BluetoothRemoteGattServiceMac::DidDiscoverCharacteristics() { ...@@ -130,7 +142,13 @@ void BluetoothRemoteGattServiceMac::DidDiscoverCharacteristics() {
void BluetoothRemoteGattServiceMac::DidDiscoverDescriptors( void BluetoothRemoteGattServiceMac::DidDiscoverDescriptors(
CBCharacteristic* characteristic) { CBCharacteristic* characteristic) {
DCHECK(!is_discovery_complete_); if (is_discovery_complete_) {
// 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 = BluetoothRemoteGattCharacteristicMac* gatt_characteristic =
GetBluetoothRemoteGattCharacteristicMac(characteristic); GetBluetoothRemoteGattCharacteristicMac(characteristic);
DCHECK(gatt_characteristic); DCHECK(gatt_characteristic);
...@@ -142,6 +160,7 @@ void BluetoothRemoteGattServiceMac::SendNotificationIfComplete() { ...@@ -142,6 +160,7 @@ void BluetoothRemoteGattServiceMac::SendNotificationIfComplete() {
DCHECK(!is_discovery_complete_); DCHECK(!is_discovery_complete_);
// Notify when all characteristics have been fully discovered. // Notify when all characteristics have been fully discovered.
is_discovery_complete_ = is_discovery_complete_ =
discovery_pending_count_ == 0 &&
std::find_if_not( std::find_if_not(
gatt_characteristic_macs_.begin(), gatt_characteristic_macs_.end(), gatt_characteristic_macs_.begin(), gatt_characteristic_macs_.end(),
[](const std::pair< [](const std::pair<
......
...@@ -336,6 +336,177 @@ TEST_F(BluetoothRemoteGattServiceTest, SimulateGattServiceRemove) { ...@@ -336,6 +336,177 @@ TEST_F(BluetoothRemoteGattServiceTest, SimulateGattServiceRemove) {
EXPECT_FALSE(device->GetGattService(removed_service)); EXPECT_FALSE(device->GetGattService(removed_service));
EXPECT_EQ(device->GetGattServices()[0], service2); EXPECT_EQ(device->GetGattServices()[0], service2);
} }
#endif // defined(OS_WIN) || defined(OS_MACOSX) #endif // defined(OS_MACOSX) || defined(OS_WIN)
#if defined(OS_MACOSX)
// Tests to receive a services changed notification from macOS, while
// discovering characteristics. The gatt device should scan again for services
// and characteristics, before scanning for descriptors. Only after the gatt
// service changed notification should be sent.
// Android: This test doesn't apply to Android because there is no services
// changed event that could arrive during a discovery procedure.
TEST_F(BluetoothRemoteGattServiceTest,
SimulateDeviceModificationWhileDiscoveringCharacteristics) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
TestBluetoothAdapterObserver observer(adapter_);
// Starts first discovery process.
SimulateGattConnection(device);
AddServicesToDeviceMac(device, {kTestUUIDHeartRate});
SimulateDidDiscoverServicesMac(device);
EXPECT_EQ(1u, device->GetGattServices().size());
BluetoothRemoteGattService* service = device->GetGattServices()[0];
std::string characteristic_uuid1 = "11111111-0000-1000-8000-00805f9b34fb";
AddCharacteristicToServiceMac(service, characteristic_uuid1,
/* properties */ 0);
// Now waiting for characteristic discovery.
// Starts second discovery process.
SimulateGattServicesChanged(device);
SimulateDidDiscoverServicesMac(device);
// Now waiting for the second characteristic discovery.
// First system call to -[id<CBPeripheralDelegate>
// peripheral:didDiscoverCharacteristicsForService:error:]
SimulateDidDiscoverCharacteristicsMac(service);
EXPECT_EQ(0, observer.gatt_service_changed_count());
EXPECT_EQ(1u, service->GetCharacteristics().size());
// Finish discovery process.
std::string characteristic_uuid2 = "22222222-0000-1000-8000-00805f9b34fb";
AddCharacteristicToServiceMac(service, characteristic_uuid2,
/* properties */ 0);
// Second system call to -[id<CBPeripheralDelegate>
// peripheral:didDiscoverCharacteristicsForService:error:]
SimulateDidDiscoverCharacteristicsMac(service);
EXPECT_EQ(2u, service->GetCharacteristics().size());
EXPECT_EQ(0, observer.gatt_service_changed_count());
BluetoothRemoteGattCharacteristic* characteristic1 =
service->GetCharacteristics()[0];
BluetoothRemoteGattCharacteristic* characteristic2 =
service->GetCharacteristics()[1];
if (characteristic1->GetUUID().canonical_value() == characteristic_uuid2) {
BluetoothRemoteGattCharacteristic* tmp = characteristic1;
characteristic1 = characteristic2;
characteristic2 = tmp;
}
EXPECT_EQ(characteristic_uuid1, characteristic1->GetUUID().canonical_value());
EXPECT_EQ(characteristic_uuid2, characteristic2->GetUUID().canonical_value());
SimulateDidDiscoverDescriptorsMac(characteristic1);
SimulateDidDiscoverDescriptorsMac(characteristic2);
EXPECT_EQ(1, observer.gatt_service_changed_count());
}
#endif // defined(OS_MACOSX)
#if defined(OS_MACOSX)
// Simulates to receive an extra discovery characteristic notifications from
// macOS. Those notifications should be ignored.
// Android: This test doesn't apply to Android because there is no services
// changed event that could arrive during a discovery procedure.
TEST_F(BluetoothRemoteGattServiceTest, ExtraDidDiscoverServicesCall) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
TestBluetoothAdapterObserver observer(adapter_);
// Starts first discovery process.
SimulateGattConnection(device);
AddServicesToDeviceMac(device, {kTestUUIDHeartRate});
SimulateDidDiscoverServicesMac(device);
EXPECT_EQ(1u, device->GetGattServices().size());
BluetoothRemoteGattService* service = device->GetGattServices()[0];
std::string characteristic_uuid = "11111111-0000-1000-8000-00805f9b34fb";
AddCharacteristicToServiceMac(service, characteristic_uuid,
/* properties */ 0);
SimulateDidDiscoverCharacteristicsMac(service);
EXPECT_EQ(1u, service->GetCharacteristics().size());
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristics()[0];
std::string descriptor_uuid = "22222222-0000-1000-8000-00805f9b34fb";
AddDescriptorToCharacteristicMac(characteristic, descriptor_uuid);
SimulateDidDiscoverDescriptorsMac(characteristic);
EXPECT_EQ(1, observer.gatt_service_changed_count());
EXPECT_EQ(1u, service->GetCharacteristics().size());
EXPECT_EQ(1u, characteristic->GetDescriptors().size());
observer.Reset();
SimulateDidDiscoverServicesMac(device); // Extra system call.
SimulateGattServicesChanged(device);
SimulateDidDiscoverServicesMac(device);
SimulateDidDiscoverServicesMac(device); // Extra system call.
SimulateDidDiscoverCharacteristicsMac(service);
SimulateDidDiscoverServicesMac(device); // Extra system call.
SimulateDidDiscoverDescriptorsMac(characteristic);
SimulateDidDiscoverServicesMac(device); // Extra system call.
EXPECT_EQ(2, observer.device_changed_count());
}
#endif // defined(OS_MACOSX)
#if defined(OS_MACOSX)
// Simulates to receive an extra discovery characteristic notifications from
// macOS. Those notifications should be ignored.
// Android: This test doesn't apply to Android because there is no services
// changed event that could arrive during a discovery procedure.
TEST_F(BluetoothRemoteGattServiceTest, ExtraDidDiscoverCharacteristicsCall) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
TestBluetoothAdapterObserver observer(adapter_);
// Starts first discovery process.
SimulateGattConnection(device);
AddServicesToDeviceMac(device, {kTestUUIDHeartRate});
SimulateDidDiscoverServicesMac(device);
EXPECT_EQ(1u, device->GetGattServices().size());
BluetoothRemoteGattService* service = device->GetGattServices()[0];
std::string characteristic_uuid = "11111111-0000-1000-8000-00805f9b34fb";
AddCharacteristicToServiceMac(service, characteristic_uuid,
/* properties */ 0);
SimulateDidDiscoverCharacteristicsMac(service);
EXPECT_EQ(1u, service->GetCharacteristics().size());
BluetoothRemoteGattCharacteristic* characteristic =
service->GetCharacteristics()[0];
std::string descriptor_uuid = "22222222-0000-1000-8000-00805f9b34fb";
AddDescriptorToCharacteristicMac(characteristic, descriptor_uuid);
SimulateDidDiscoverDescriptorsMac(characteristic);
EXPECT_EQ(1, observer.gatt_service_changed_count());
EXPECT_EQ(1u, service->GetCharacteristics().size());
EXPECT_EQ(1u, characteristic->GetDescriptors().size());
observer.Reset();
SimulateDidDiscoverCharacteristicsMac(service); // Extra system call.
SimulateGattServicesChanged(device);
SimulateDidDiscoverCharacteristicsMac(service); // Extra system call.
SimulateDidDiscoverServicesMac(device);
SimulateDidDiscoverCharacteristicsMac(service);
SimulateDidDiscoverCharacteristicsMac(service); // Extra system call.
SimulateDidDiscoverDescriptorsMac(characteristic);
SimulateDidDiscoverCharacteristicsMac(service); // Extra system call.
EXPECT_EQ(2, observer.device_changed_count());
}
#endif // defined(OS_MACOSX)
} // namespace device } // namespace device
...@@ -103,24 +103,42 @@ class BluetoothTestMac : public BluetoothTestBase { ...@@ -103,24 +103,42 @@ class BluetoothTestMac : public BluetoothTestBase {
// macOS is the only platform for which we need to discover each set of // macOS is the only platform for which we need to discover each set of
// attributes individually so we need a method to simulate discovering each // attributes individually so we need a method to simulate discovering each
// set of attributes. // set of attributes.
void SimulateDidDiscoverServices(BluetoothDevice* device, // Simulates service discovery for a device.
const std::vector<std::string>& uuids); void SimulateDidDiscoverServicesMac(BluetoothDevice* device);
// Simulates characteristic discovery for a service.
void SimulateDidDiscoverCharacteristicsMac(
BluetoothRemoteGattService* service);
// Simulates descriptor discovery for a characteristic.
void SimulateDidDiscoverDescriptorsMac(
BluetoothRemoteGattCharacteristic* characteristic);
// CoreBluetooth can return NSData when reading remote gatt descriptors. // CoreBluetooth can return NSData when reading remote gatt descriptors.
// This methods simulate receiving NSData from CoreBluetooth. // This methods simulate receiving NSData from CoreBluetooth.
void SimulateGattDescriptorReadNSData( void SimulateGattDescriptorReadNSDataMac(
BluetoothRemoteGattDescriptor* descriptor, BluetoothRemoteGattDescriptor* descriptor,
const std::vector<uint8_t>& value); const std::vector<uint8_t>& value);
// CoreBluetooth can return NSString when reading remote gatt descriptors. // CoreBluetooth can return NSString when reading remote gatt descriptors.
// This methods simulate receiving NSString from CoreBluetooth. // This methods simulate receiving NSString from CoreBluetooth.
void SimulateGattDescriptorReadNSString( void SimulateGattDescriptorReadNSStringMac(
BluetoothRemoteGattDescriptor* descriptor, BluetoothRemoteGattDescriptor* descriptor,
const std::string& value); const std::string& value);
// CoreBluetooth can return NSString when reading remote gatt descriptors. // CoreBluetooth can return NSString when reading remote gatt descriptors.
// This methods simulate receiving NSString from CoreBluetooth. // This methods simulate receiving NSString from CoreBluetooth.
void SimulateGattDescriptorReadNSNumber( void SimulateGattDescriptorReadNSNumberMac(
BluetoothRemoteGattDescriptor* descriptor, BluetoothRemoteGattDescriptor* descriptor,
short value); short value);
// Adds services in MockCBPeripheral.
void AddServicesToDeviceMac(BluetoothDevice* device,
const std::vector<std::string>& uuids);
// Adds a characteristic in MockCBService.
void AddCharacteristicToServiceMac(BluetoothRemoteGattService* service,
const std::string& characteristic_uuid,
int properties);
// Adds a descriptor in MockCBCharacteristic.
void AddDescriptorToCharacteristicMac(
BluetoothRemoteGattCharacteristic* characteristic,
const std::string& uuid);
// Callback for the bluetooth central manager mock. // Callback for the bluetooth central manager mock.
void OnFakeBluetoothDeviceConnectGattCalled(); void OnFakeBluetoothDeviceConnectGattCalled();
void OnFakeBluetoothGattDisconnect(); void OnFakeBluetoothGattDisconnect();
...@@ -159,9 +177,6 @@ class BluetoothTestMac : public BluetoothTestBase { ...@@ -159,9 +177,6 @@ class BluetoothTestMac : public BluetoothTestBase {
// Returns MockCBDescriptor from BluetoothRemoteGattDescriptor. // Returns MockCBDescriptor from BluetoothRemoteGattDescriptor.
MockCBDescriptor* GetCBMockDescriptor( MockCBDescriptor* GetCBMockDescriptor(
BluetoothRemoteGattDescriptor* descriptor) const; BluetoothRemoteGattDescriptor* descriptor) const;
// Adds services in MockCBPeripheral.
void AddServicesToDevice(BluetoothDevice* device,
const std::vector<std::string>& uuids);
// Utility function for finding CBUUIDs with relatively nice SHA256 hashes. // Utility function for finding CBUUIDs with relatively nice SHA256 hashes.
std::string FindCBUUIDForHashTarget(); std::string FindCBUUIDForHashTarget();
......
...@@ -299,7 +299,7 @@ void BluetoothTestMac::SimulateGattDisconnection(BluetoothDevice* device) { ...@@ -299,7 +299,7 @@ void BluetoothTestMac::SimulateGattDisconnection(BluetoothDevice* device) {
void BluetoothTestMac::SimulateGattServicesDiscovered( void BluetoothTestMac::SimulateGattServicesDiscovered(
BluetoothDevice* device, BluetoothDevice* device,
const std::vector<std::string>& uuids) { const std::vector<std::string>& uuids) {
AddServicesToDevice(device, uuids); AddServicesToDeviceMac(device, uuids);
[GetMockCBPeripheral(device) mockDidDiscoverEvents]; [GetMockCBPeripheral(device) mockDidDiscoverEvents];
} }
...@@ -379,7 +379,7 @@ void BluetoothTestMac::SimulateGattDescriptor( ...@@ -379,7 +379,7 @@ void BluetoothTestMac::SimulateGattDescriptor(
MockCBCharacteristic* characteristic_mock = MockCBCharacteristic* characteristic_mock =
GetCBMockCharacteristic(characteristic); GetCBMockCharacteristic(characteristic);
CBUUID* cb_uuid = [CBUUID UUIDWithString:@(uuid.c_str())]; CBUUID* cb_uuid = [CBUUID UUIDWithString:@(uuid.c_str())];
[characteristic_mock simulateDescriptorWithUUID:cb_uuid]; [characteristic_mock addDescriptorWithUUID:cb_uuid];
MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(characteristic); MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(characteristic);
[peripheral_mock didModifyServices:@[]]; [peripheral_mock didModifyServices:@[]];
// After -[MockCBPeripheral didModifyServices:], BluetoothLowEnergyDeviceMac // After -[MockCBPeripheral didModifyServices:], BluetoothLowEnergyDeviceMac
...@@ -451,7 +451,7 @@ void BluetoothTestMac::SimulateGattCharacteristicRemoved( ...@@ -451,7 +451,7 @@ void BluetoothTestMac::SimulateGattCharacteristicRemoved(
void BluetoothTestMac::SimulateGattDescriptorRead( void BluetoothTestMac::SimulateGattDescriptorRead(
BluetoothRemoteGattDescriptor* descriptor, BluetoothRemoteGattDescriptor* descriptor,
const std::vector<uint8_t>& value) { const std::vector<uint8_t>& value) {
SimulateGattDescriptorReadNSData(descriptor, value); SimulateGattDescriptorReadNSDataMac(descriptor, value);
} }
void BluetoothTestMac::SimulateGattDescriptorReadError( void BluetoothTestMac::SimulateGattDescriptorReadError(
...@@ -490,14 +490,30 @@ void BluetoothTestMac::ExpectedNotifyValue( ...@@ -490,14 +490,30 @@ void BluetoothTestMac::ExpectedNotifyValue(
} }
} }
void BluetoothTestMac::SimulateDidDiscoverServices( void BluetoothTestMac::SimulateDidDiscoverServicesMac(BluetoothDevice* device) {
BluetoothDevice* device,
const std::vector<std::string>& uuids) {
AddServicesToDevice(device, uuids);
[GetMockCBPeripheral(device) mockDidDiscoverServices]; [GetMockCBPeripheral(device) mockDidDiscoverServices];
} }
void BluetoothTestMac::SimulateGattDescriptorReadNSData( void BluetoothTestMac::SimulateDidDiscoverCharacteristicsMac(
BluetoothRemoteGattService* service) {
BluetoothRemoteGattServiceMac* mac_gatt_service =
static_cast<BluetoothRemoteGattServiceMac*>(service);
CBService* cb_service = mac_gatt_service->GetService();
[GetMockCBPeripheral(service)
mockDidDiscoverCharacteristicsForService:cb_service];
}
void BluetoothTestMac::SimulateDidDiscoverDescriptorsMac(
BluetoothRemoteGattCharacteristic* characteristic) {
BluetoothRemoteGattCharacteristicMac* mac_gatt_characteristic =
static_cast<BluetoothRemoteGattCharacteristicMac*>(characteristic);
CBCharacteristic* cb_characteristic =
mac_gatt_characteristic->GetCBCharacteristic();
[GetMockCBPeripheral(characteristic)
mockDidDiscoverDescriptorsForCharacteristic:cb_characteristic];
}
void BluetoothTestMac::SimulateGattDescriptorReadNSDataMac(
BluetoothRemoteGattDescriptor* descriptor, BluetoothRemoteGattDescriptor* descriptor,
const std::vector<uint8_t>& value) { const std::vector<uint8_t>& value) {
scoped_nsobject<NSData> data( scoped_nsobject<NSData> data(
...@@ -505,20 +521,56 @@ void BluetoothTestMac::SimulateGattDescriptorReadNSData( ...@@ -505,20 +521,56 @@ void BluetoothTestMac::SimulateGattDescriptorReadNSData(
[GetCBMockDescriptor(descriptor) simulateReadWithValue:data error:nil]; [GetCBMockDescriptor(descriptor) simulateReadWithValue:data error:nil];
} }
void BluetoothTestMac::SimulateGattDescriptorReadNSString( void BluetoothTestMac::SimulateGattDescriptorReadNSStringMac(
BluetoothRemoteGattDescriptor* descriptor, BluetoothRemoteGattDescriptor* descriptor,
const std::string& value) { const std::string& value) {
NSString* string = base::SysUTF8ToNSString(value); NSString* string = base::SysUTF8ToNSString(value);
[GetCBMockDescriptor(descriptor) simulateReadWithValue:string error:nil]; [GetCBMockDescriptor(descriptor) simulateReadWithValue:string error:nil];
} }
void BluetoothTestMac::SimulateGattDescriptorReadNSNumber( void BluetoothTestMac::SimulateGattDescriptorReadNSNumberMac(
BluetoothRemoteGattDescriptor* descriptor, BluetoothRemoteGattDescriptor* descriptor,
short value) { short value) {
NSNumber* number = [NSNumber numberWithShort:value]; NSNumber* number = [NSNumber numberWithShort:value];
[GetCBMockDescriptor(descriptor) simulateReadWithValue:number error:nil]; [GetCBMockDescriptor(descriptor) simulateReadWithValue:number error:nil];
} }
void BluetoothTest::AddServicesToDeviceMac(
BluetoothDevice* device,
const std::vector<std::string>& uuids) {
scoped_nsobject<NSMutableArray> services([[NSMutableArray alloc] init]);
for (auto uuid : uuids) {
CBUUID* cb_service_uuid = [CBUUID UUIDWithString:@(uuid.c_str())];
[services addObject:cb_service_uuid];
}
[GetMockCBPeripheral(device) addServices:services];
}
void BluetoothTestMac::AddCharacteristicToServiceMac(
BluetoothRemoteGattService* service,
const std::string& characteristic_uuid,
int properties) {
BluetoothRemoteGattServiceMac* mac_gatt_service =
static_cast<BluetoothRemoteGattServiceMac*>(service);
CBService* cb_service = mac_gatt_service->GetService();
MockCBService* service_mock = ObjCCast<MockCBService>(cb_service);
CBUUID* cb_uuid = [CBUUID UUIDWithString:@(characteristic_uuid.c_str())];
[service_mock addCharacteristicWithUUID:cb_uuid properties:properties];
}
void BluetoothTestMac::AddDescriptorToCharacteristicMac(
BluetoothRemoteGattCharacteristic* characteristic,
const std::string& uuid) {
BluetoothRemoteGattCharacteristicMac* mac_gatt_characteristic =
static_cast<BluetoothRemoteGattCharacteristicMac*>(characteristic);
CBCharacteristic* cb_characteristic =
mac_gatt_characteristic->GetCBCharacteristic();
MockCBCharacteristic* characteristic_mock =
ObjCCast<MockCBCharacteristic>(cb_characteristic);
CBUUID* cb_uuid = [CBUUID UUIDWithString:@(uuid.c_str())];
[characteristic_mock addDescriptorWithUUID:cb_uuid];
}
void BluetoothTestMac::OnFakeBluetoothDeviceConnectGattCalled() { void BluetoothTestMac::OnFakeBluetoothDeviceConnectGattCalled() {
gatt_connection_attempts_++; gatt_connection_attempts_++;
} }
...@@ -621,17 +673,6 @@ MockCBDescriptor* BluetoothTest::GetCBMockDescriptor( ...@@ -621,17 +673,6 @@ MockCBDescriptor* BluetoothTest::GetCBMockDescriptor(
CBDescriptor* cb_descriptor = mac_gatt_descriptor->GetCBDescriptor(); CBDescriptor* cb_descriptor = mac_gatt_descriptor->GetCBDescriptor();
return ObjCCast<MockCBDescriptor>(cb_descriptor); return ObjCCast<MockCBDescriptor>(cb_descriptor);
} }
void BluetoothTest::AddServicesToDevice(BluetoothDevice* device,
const std::vector<std::string>& uuids) {
scoped_nsobject<NSMutableArray> services([[NSMutableArray alloc] init]);
for (auto uuid : uuids) {
CBUUID* cb_service_uuid = [CBUUID UUIDWithString:@(uuid.c_str())];
[services addObject:cb_service_uuid];
}
[GetMockCBPeripheral(device) addServices:services];
}
// Utility function for generating new (CBUUID, address) pairs where CBUUID // Utility function for generating new (CBUUID, address) pairs where CBUUID
// hashes to address. For use when adding a new device address to the testing // hashes to address. For use when adding a new device address to the testing
// suite because CoreBluetooth peripherals have CBUUIDs in place of addresses, // suite because CoreBluetooth peripherals have CBUUIDs in place of addresses,
......
...@@ -29,8 +29,7 @@ ...@@ -29,8 +29,7 @@
- (void)simulateGattNotifySessionStopped; - (void)simulateGattNotifySessionStopped;
- (void)simulateGattNotifySessionStoppedWithError:(NSError*)error; - (void)simulateGattNotifySessionStoppedWithError:(NSError*)error;
- (void)simulateGattCharacteristicChangedWithValue:(NSData*)value; - (void)simulateGattCharacteristicChangedWithValue:(NSData*)value;
- (void)simulateDescriptorWithUUID:(CBUUID*)uuid; - (void)addDescriptorWithUUID:(CBUUID*)uuid;
- (void)discoverDescriptors;
@end @end
......
...@@ -94,8 +94,7 @@ CBCharacteristicProperties GattCharacteristicPropertyToCBCharacteristicProperty( ...@@ -94,8 +94,7 @@ CBCharacteristicProperties GattCharacteristicPropertyToCBCharacteristicProperty(
CBService* _service; CBService* _service;
scoped_nsobject<CBUUID> _UUID; scoped_nsobject<CBUUID> _UUID;
CBCharacteristicProperties _cb_properties; CBCharacteristicProperties _cb_properties;
scoped_nsobject<NSMutableArray> _simulatedDescriptors; scoped_nsobject<NSMutableArray> _descriptors;
scoped_nsobject<NSArray> _descriptors;
scoped_nsobject<NSObject> _value; scoped_nsobject<NSObject> _value;
BOOL _notifying; BOOL _notifying;
} }
...@@ -113,7 +112,7 @@ CBCharacteristicProperties GattCharacteristicPropertyToCBCharacteristicProperty( ...@@ -113,7 +112,7 @@ CBCharacteristicProperties GattCharacteristicPropertyToCBCharacteristicProperty(
_cb_properties = _cb_properties =
device::GattCharacteristicPropertyToCBCharacteristicProperty( device::GattCharacteristicPropertyToCBCharacteristicProperty(
properties); properties);
_simulatedDescriptors.reset([[NSMutableArray alloc] init]); _descriptors.reset([[NSMutableArray alloc] init]);
} }
return self; return self;
} }
...@@ -189,15 +188,11 @@ CBCharacteristicProperties GattCharacteristicPropertyToCBCharacteristicProperty( ...@@ -189,15 +188,11 @@ CBCharacteristicProperties GattCharacteristicPropertyToCBCharacteristicProperty(
error:nil]; error:nil];
} }
- (void)simulateDescriptorWithUUID:(CBUUID*)uuid { - (void)addDescriptorWithUUID:(CBUUID*)uuid {
scoped_nsobject<MockCBDescriptor> descriptor_mock([[MockCBDescriptor alloc] scoped_nsobject<MockCBDescriptor> descriptor_mock([[MockCBDescriptor alloc]
initWithCharacteristic:self.characteristic initWithCharacteristic:self.characteristic
CBUUID:uuid]); CBUUID:uuid]);
[_simulatedDescriptors.get() addObject:descriptor_mock]; [_descriptors.get() addObject:descriptor_mock];
}
- (void)discoverDescriptors {
_descriptors.reset([_simulatedDescriptors copy]);
} }
- (CBUUID*)UUID { - (CBUUID*)UUID {
......
...@@ -42,6 +42,9 @@ class BluetoothTestMac; ...@@ -42,6 +42,9 @@ class BluetoothTestMac;
- (void)didDiscoverServicesWithError:(NSError*)error; - (void)didDiscoverServicesWithError:(NSError*)error;
- (void)removeService:(CBService*)uuid; - (void)removeService:(CBService*)uuid;
- (void)mockDidDiscoverServices; - (void)mockDidDiscoverServices;
- (void)mockDidDiscoverCharacteristicsForService:(CBService*)service;
- (void)mockDidDiscoverDescriptorsForCharacteristic:
(CBCharacteristic*)characteristic;
- (void)mockDidDiscoverEvents; - (void)mockDidDiscoverEvents;
- (void)didModifyServices:(NSArray*)invalidatedServices; - (void)didModifyServices:(NSArray*)invalidatedServices;
- (void)didDiscoverDescriptorsWithCharacteristic: - (void)didDiscoverDescriptorsWithCharacteristic:
......
...@@ -94,9 +94,6 @@ using base::scoped_nsobject; ...@@ -94,9 +94,6 @@ using base::scoped_nsobject;
} }
- (void)discoverDescriptorsForCharacteristic:(CBCharacteristic*)characteristic { - (void)discoverDescriptorsForCharacteristic:(CBCharacteristic*)characteristic {
MockCBCharacteristic* mock_characteristic =
ObjCCast<MockCBCharacteristic>(characteristic);
[mock_characteristic discoverDescriptors];
} }
- (void)readValueForCharacteristic:(CBCharacteristic*)characteristic { - (void)readValueForCharacteristic:(CBCharacteristic*)characteristic {
...@@ -158,6 +155,19 @@ using base::scoped_nsobject; ...@@ -158,6 +155,19 @@ using base::scoped_nsobject;
[_delegate peripheral:self.peripheral didDiscoverServices:nil]; [_delegate peripheral:self.peripheral didDiscoverServices:nil];
} }
- (void)mockDidDiscoverCharacteristicsForService:(CBService*)service {
[_delegate peripheral:self.peripheral
didDiscoverCharacteristicsForService:service
error:nil];
}
- (void)mockDidDiscoverDescriptorsForCharacteristic:
(CBCharacteristic*)characteristic {
[_delegate peripheral:self.peripheral
didDiscoverDescriptorsForCharacteristic:characteristic
error:nil];
}
- (void)mockDidDiscoverEvents { - (void)mockDidDiscoverEvents {
[self mockDidDiscoverServices]; [self mockDidDiscoverServices];
// BluetoothLowEnergyDeviceMac is expected to call // BluetoothLowEnergyDeviceMac is expected to call
...@@ -165,9 +175,7 @@ using base::scoped_nsobject; ...@@ -165,9 +175,7 @@ using base::scoped_nsobject;
// so -[<CBPeripheralDelegate peripheral:didDiscoverCharacteristicsForService: // so -[<CBPeripheralDelegate peripheral:didDiscoverCharacteristicsForService:
// error:] needs to be called for all services. // error:] needs to be called for all services.
for (CBService* service in _services.get()) { for (CBService* service in _services.get()) {
[_delegate peripheral:self.peripheral [self mockDidDiscoverCharacteristicsForService:service];
didDiscoverCharacteristicsForService:service
error:nil];
for (CBCharacteristic* characteristic in service.characteristics) { for (CBCharacteristic* characteristic in service.characteristics) {
// After discovering services, BluetoothLowEnergyDeviceMac is expected to // After discovering services, BluetoothLowEnergyDeviceMac is expected to
// discover characteristics for all services. // discover characteristics for all services.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment