Commit 620327db authored by jlebel's avatar jlebel Committed by Commit bot

bluetooth: mac: Initial BluetoothRemoteGattCharacteristicMac implementation

Fix for: https://codereview.chromium.org/1950033002/

Adding initial remote characteristic implementation on OS X, with only the basic methods (e.g. GetUUID) implemented.
Characteristic discovery also implemented in BluetoothRemoteGattServiceMac.

BUG=609064

Review-Url: https://codereview.chromium.org/2071323002
Cr-Commit-Position: refs/heads/master@{#400663}
parent 69d855b2
...@@ -47,6 +47,8 @@ test("device_unittests") { ...@@ -47,6 +47,8 @@ test("device_unittests") {
"bluetooth/test/bluetooth_test_mac.mm", "bluetooth/test/bluetooth_test_mac.mm",
"bluetooth/test/bluetooth_test_win.cc", "bluetooth/test/bluetooth_test_win.cc",
"bluetooth/test/bluetooth_test_win.h", "bluetooth/test/bluetooth_test_win.h",
"bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h",
"bluetooth/test/mock_bluetooth_cbcharacteristic_mac.mm",
"bluetooth/test/mock_bluetooth_cbperipheral_mac.h", "bluetooth/test/mock_bluetooth_cbperipheral_mac.h",
"bluetooth/test/mock_bluetooth_cbperipheral_mac.mm", "bluetooth/test/mock_bluetooth_cbperipheral_mac.mm",
"bluetooth/test/mock_bluetooth_cbservice_mac.h", "bluetooth/test/mock_bluetooth_cbservice_mac.h",
......
...@@ -101,6 +101,8 @@ component("bluetooth") { ...@@ -101,6 +101,8 @@ component("bluetooth") {
"bluetooth_remote_gatt_characteristic.h", "bluetooth_remote_gatt_characteristic.h",
"bluetooth_remote_gatt_characteristic_android.cc", "bluetooth_remote_gatt_characteristic_android.cc",
"bluetooth_remote_gatt_characteristic_android.h", "bluetooth_remote_gatt_characteristic_android.h",
"bluetooth_remote_gatt_characteristic_mac.h",
"bluetooth_remote_gatt_characteristic_mac.mm",
"bluetooth_remote_gatt_characteristic_win.cc", "bluetooth_remote_gatt_characteristic_win.cc",
"bluetooth_remote_gatt_characteristic_win.h", "bluetooth_remote_gatt_characteristic_win.h",
"bluetooth_remote_gatt_descriptor.cc", "bluetooth_remote_gatt_descriptor.cc",
......
...@@ -103,6 +103,8 @@ ...@@ -103,6 +103,8 @@
'bluetooth_remote_gatt_characteristic.h', 'bluetooth_remote_gatt_characteristic.h',
'bluetooth_remote_gatt_characteristic_android.cc', 'bluetooth_remote_gatt_characteristic_android.cc',
'bluetooth_remote_gatt_characteristic_android.h', 'bluetooth_remote_gatt_characteristic_android.h',
'bluetooth_remote_gatt_characteristic_mac.h',
'bluetooth_remote_gatt_characteristic_mac.mm',
'bluetooth_remote_gatt_characteristic_win.cc', 'bluetooth_remote_gatt_characteristic_win.cc',
'bluetooth_remote_gatt_characteristic_win.h', 'bluetooth_remote_gatt_characteristic_win.h',
'bluetooth_remote_gatt_descriptor.cc', 'bluetooth_remote_gatt_descriptor.cc',
......
...@@ -95,6 +95,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyDeviceMac ...@@ -95,6 +95,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyDeviceMac
// Methods used by BluetoothLowEnergyPeripheralBridge. // Methods used by BluetoothLowEnergyPeripheralBridge.
void DidDiscoverPrimaryServices(NSError* error); void DidDiscoverPrimaryServices(NSError* error);
void DidModifyServices(NSArray* invalidatedServices); void DidModifyServices(NSArray* invalidatedServices);
void DidDiscoverCharacteristics(CBService* cb_service, NSError* error);
// Updates information about the device. // Updates information about the device.
virtual void Update(NSDictionary* advertisement_data, int rssi); virtual void Update(NSDictionary* advertisement_data, int rssi);
...@@ -111,6 +112,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyDeviceMac ...@@ -111,6 +112,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyDeviceMac
friend class BluetoothAdapterMac; friend class BluetoothAdapterMac;
friend class BluetoothAdapterMacTest; friend class BluetoothAdapterMacTest;
friend class BluetoothLowEnergyPeripheralBridge; friend class BluetoothLowEnergyPeripheralBridge;
friend class BluetoothRemoteGattServiceMac;
friend class BluetoothTestMac; friend class BluetoothTestMac;
// Returns the Bluetooth adapter. // Returns the Bluetooth adapter.
......
...@@ -239,7 +239,7 @@ void BluetoothLowEnergyDeviceMac::DisconnectGatt() { ...@@ -239,7 +239,7 @@ void BluetoothLowEnergyDeviceMac::DisconnectGatt() {
void BluetoothLowEnergyDeviceMac::DidDiscoverPrimaryServices(NSError* error) { void BluetoothLowEnergyDeviceMac::DidDiscoverPrimaryServices(NSError* error) {
if (error) { if (error) {
// TODO(http://crbug.com/609320): Need to pass the error. // TODO(http://crbug.com/609320): Need to pass the error.
// TODO(http://crbug.com/609844): Decide what to do if we fail to discover // TODO(http://crbug.com/609844): Decide what to do if discover failed
// a device services. // a device services.
VLOG(1) << "Can't discover primary services: " VLOG(1) << "Can't discover primary services: "
<< error.localizedDescription.UTF8String << " (" << error.domain << error.localizedDescription.UTF8String << " (" << error.domain
...@@ -258,10 +258,44 @@ void BluetoothLowEnergyDeviceMac::DidDiscoverPrimaryServices(NSError* error) { ...@@ -258,10 +258,44 @@ void BluetoothLowEnergyDeviceMac::DidDiscoverPrimaryServices(NSError* error) {
adapter_->NotifyGattServiceAdded(gatt_service); adapter_->NotifyGattServiceAdded(gatt_service);
} }
} }
// TODO(http://crbug.com/609064): Services are fully discovered once all for (GattServiceMap::const_iterator it = gatt_services_.begin();
// characteristics have been found. it != gatt_services_.end(); ++it) {
SetGattServicesDiscoveryComplete(true); device::BluetoothRemoteGattService* gatt_service = it->second;
adapter_->NotifyGattServicesDiscovered(this); device::BluetoothRemoteGattServiceMac* gatt_service_mac =
static_cast<BluetoothRemoteGattServiceMac*>(gatt_service);
gatt_service_mac->DiscoverCharacteristics();
}
}
void BluetoothLowEnergyDeviceMac::DidDiscoverCharacteristics(
CBService* cb_service,
NSError* error) {
if (error) {
// TODO(http://crbug.com/609320): Need to pass the error.
// TODO(http://crbug.com/609844): Decide what to do if discover failed
VLOG(1) << "Can't discover characteristics: "
<< error.localizedDescription.UTF8String << " (" << error.domain
<< ": " << error.code << ")";
return;
}
BluetoothRemoteGattServiceMac* gatt_service =
GetBluetoothRemoteGattService(cb_service);
DCHECK(gatt_service);
gatt_service->DidDiscoverCharacteristics();
// Notify when all services have been discovered.
bool discovery_complete =
std::find_if_not(
gatt_services_.begin(), gatt_services_.end(),
[](std::pair<std::string, BluetoothRemoteGattService*> pair) {
BluetoothRemoteGattService* gatt_service = pair.second;
return static_cast<BluetoothRemoteGattServiceMac*>(gatt_service)
->IsDiscoveryComplete();
}) == gatt_services_.end();
if (discovery_complete) {
SetGattServicesDiscoveryComplete(true);
adapter_->NotifyGattServicesDiscovered(this);
}
} }
void BluetoothLowEnergyDeviceMac::DidModifyServices( void BluetoothLowEnergyDeviceMac::DidModifyServices(
......
...@@ -27,6 +27,10 @@ class BluetoothLowEnergyPeripheralBridge { ...@@ -27,6 +27,10 @@ class BluetoothLowEnergyPeripheralBridge {
device_mac_->DidDiscoverPrimaryServices(error); device_mac_->DidDiscoverPrimaryServices(error);
}; };
void DidDiscoverCharacteristics(CBService* service, NSError* error) {
device_mac_->DidDiscoverCharacteristics(service, error);
};
CBPeripheral* GetPeripheral() { return device_mac_->GetPeripheral(); } CBPeripheral* GetPeripheral() { return device_mac_->GetPeripheral(); }
private: private:
...@@ -60,4 +64,10 @@ class BluetoothLowEnergyPeripheralBridge { ...@@ -60,4 +64,10 @@ class BluetoothLowEnergyPeripheralBridge {
bridge_->DidDiscoverPrimaryServices(error); bridge_->DidDiscoverPrimaryServices(error);
} }
- (void)peripheral:(CBPeripheral*)peripheral
didDiscoverCharacteristicsForService:(CBService*)service
error:(NSError*)error {
bridge_->DidDiscoverCharacteristics(service, error);
}
@end @end
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_MAC_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_MAC_H_
#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
#include "base/mac/scoped_nsobject.h"
@class CBCharacteristic;
namespace device {
class BluetoothRemoteGattServiceMac;
// The BluetoothRemoteGattCharacteristicMac class implements
// BluetoothRemoteGattCharacteristic for remote GATT services on OS X.
class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicMac
: public BluetoothRemoteGattCharacteristic {
public:
BluetoothRemoteGattCharacteristicMac(
BluetoothRemoteGattServiceMac* gatt_service,
CBCharacteristic* cb_characteristic);
~BluetoothRemoteGattCharacteristicMac() override;
// Override BluetoothGattCharacteristic methods.
std::string GetIdentifier() const override;
BluetoothUUID GetUUID() const override;
Properties GetProperties() const override;
Permissions GetPermissions() const override;
// Override BluetoothRemoteGattCharacteristic methods.
const std::vector<uint8_t>& GetValue() const override;
BluetoothRemoteGattService* GetService() const override;
bool IsNotifying() const override;
std::vector<BluetoothRemoteGattDescriptor*> GetDescriptors() const override;
BluetoothRemoteGattDescriptor* GetDescriptor(
const std::string& identifier) const override;
void StartNotifySession(const NotifySessionCallback& callback,
const ErrorCallback& error_callback) override;
void ReadRemoteCharacteristic(const ValueCallback& callback,
const ErrorCallback& error_callback) override;
void WriteRemoteCharacteristic(const std::vector<uint8_t>& new_value,
const base::Closure& callback,
const ErrorCallback& error_callback) override;
DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattCharacteristicMac);
private:
friend class BluetoothRemoteGattServiceMac;
friend class BluetoothTestMac;
// Returns CoreBluetooth characteristic.
CBCharacteristic* GetCBCharacteristic() const;
// gatt_service_ owns instances of this class.
BluetoothRemoteGattServiceMac* gatt_service_;
// A characteristic from CBPeripheral.services.characteristics.
base::scoped_nsobject<CBCharacteristic> cb_characteristic_;
// Characteristic identifier.
std::string identifier_;
// Service UUID.
BluetoothUUID uuid_;
// Characteristic value.
std::vector<uint8_t> value_;
};
} // namespace device
#endif // DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_MAC_H_
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h"
#import <CoreBluetooth/CoreBluetooth.h>
#include "device/bluetooth/bluetooth_adapter_mac.h"
#include "device/bluetooth/bluetooth_remote_gatt_service_mac.h"
namespace device {
namespace {
static BluetoothGattCharacteristic::Properties ConvertProperties(
CBCharacteristicProperties cb_property) {
BluetoothGattCharacteristic::Properties result =
BluetoothGattCharacteristic::PROPERTY_NONE;
if (cb_property & CBCharacteristicPropertyBroadcast) {
result |= BluetoothGattCharacteristic::PROPERTY_BROADCAST;
}
if (cb_property & CBCharacteristicPropertyRead) {
result |= BluetoothGattCharacteristic::PROPERTY_READ;
}
if (cb_property & CBCharacteristicPropertyWriteWithoutResponse) {
result |= BluetoothGattCharacteristic::PROPERTY_WRITE_WITHOUT_RESPONSE;
}
if (cb_property & CBCharacteristicPropertyWrite) {
result |= BluetoothGattCharacteristic::PROPERTY_WRITE;
}
if (cb_property & CBCharacteristicPropertyNotify) {
result |= BluetoothGattCharacteristic::PROPERTY_NOTIFY;
}
if (cb_property & CBCharacteristicPropertyIndicate) {
result |= BluetoothGattCharacteristic::PROPERTY_INDICATE;
}
if (cb_property & CBCharacteristicPropertyAuthenticatedSignedWrites) {
result |= BluetoothGattCharacteristic::PROPERTY_AUTHENTICATED_SIGNED_WRITES;
}
if (cb_property & CBCharacteristicPropertyExtendedProperties) {
result |= BluetoothGattCharacteristic::PROPERTY_EXTENDED_PROPERTIES;
}
if (cb_property & CBCharacteristicPropertyNotifyEncryptionRequired) {
// This property is used only in CBMutableCharacteristic
// (local characteristic). So this value should never appear for
// CBCharacteristic (remote characteristic). Apple is not able to send
// this value over BLE since it is not part of the spec.
DCHECK(false);
result |= BluetoothGattCharacteristic::PROPERTY_NOTIFY;
}
if (cb_property & CBCharacteristicPropertyIndicateEncryptionRequired) {
// This property is used only in CBMutableCharacteristic
// (local characteristic). So this value should never appear for
// CBCharacteristic (remote characteristic). Apple is not able to send
// this value over BLE since it is not part of the spec.
DCHECK(false);
result |= BluetoothGattCharacteristic::PROPERTY_INDICATE;
}
return result;
}
} // namespace
BluetoothRemoteGattCharacteristicMac::BluetoothRemoteGattCharacteristicMac(
BluetoothRemoteGattServiceMac* gatt_service,
CBCharacteristic* cb_characteristic)
: gatt_service_(gatt_service),
cb_characteristic_(cb_characteristic, base::scoped_policy::RETAIN) {
uuid_ = BluetoothAdapterMac::BluetoothUUIDWithCBUUID(
[cb_characteristic_.get() UUID]);
identifier_ =
[NSString stringWithFormat:@"%s-%p", uuid_.canonical_value().c_str(),
(void*)cb_characteristic_]
.UTF8String;
}
BluetoothRemoteGattCharacteristicMac::~BluetoothRemoteGattCharacteristicMac() {}
std::string BluetoothRemoteGattCharacteristicMac::GetIdentifier() const {
return identifier_;
}
BluetoothUUID BluetoothRemoteGattCharacteristicMac::GetUUID() const {
return uuid_;
}
BluetoothGattCharacteristic::Properties
BluetoothRemoteGattCharacteristicMac::GetProperties() const {
return ConvertProperties(cb_characteristic_.get().properties);
}
BluetoothGattCharacteristic::Permissions
BluetoothRemoteGattCharacteristicMac::GetPermissions() const {
// Not supported for remote characteristics for CoreBluetooth.
NOTIMPLEMENTED();
return PERMISSION_NONE;
}
const std::vector<uint8_t>& BluetoothRemoteGattCharacteristicMac::GetValue()
const {
return value_;
}
BluetoothRemoteGattService* BluetoothRemoteGattCharacteristicMac::GetService()
const {
return static_cast<BluetoothRemoteGattService*>(gatt_service_);
}
bool BluetoothRemoteGattCharacteristicMac::IsNotifying() const {
NOTIMPLEMENTED();
return false;
}
std::vector<BluetoothRemoteGattDescriptor*>
BluetoothRemoteGattCharacteristicMac::GetDescriptors() const {
NOTIMPLEMENTED();
return std::vector<BluetoothRemoteGattDescriptor*>();
}
BluetoothRemoteGattDescriptor*
BluetoothRemoteGattCharacteristicMac::GetDescriptor(
const std::string& identifier) const {
NOTIMPLEMENTED();
return nullptr;
}
void BluetoothRemoteGattCharacteristicMac::StartNotifySession(
const NotifySessionCallback& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
void BluetoothRemoteGattCharacteristicMac::ReadRemoteCharacteristic(
const ValueCallback& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
void BluetoothRemoteGattCharacteristicMac::WriteRemoteCharacteristic(
const std::vector<uint8_t>& new_value,
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
CBCharacteristic* BluetoothRemoteGattCharacteristicMac::GetCBCharacteristic()
const {
return cb_characteristic_.get();
}
} // namespace device.
...@@ -136,8 +136,12 @@ class BluetoothRemoteGattCharacteristicTest : public BluetoothTest { ...@@ -136,8 +136,12 @@ class BluetoothRemoteGattCharacteristicTest : public BluetoothTest {
}; };
#endif #endif
#if defined(OS_ANDROID) || defined(OS_WIN) #if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
TEST_F(BluetoothRemoteGattCharacteristicTest, GetIdentifier) { TEST_F(BluetoothRemoteGattCharacteristicTest, GetIdentifier) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter(); InitWithFakeAdapter();
StartLowEnergyDiscoverySession(); StartLowEnergyDiscoverySession();
// 2 devices to verify unique IDs across them. // 2 devices to verify unique IDs across them.
...@@ -197,10 +201,14 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, GetIdentifier) { ...@@ -197,10 +201,14 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, GetIdentifier) {
EXPECT_NE(char5->GetIdentifier(), char6->GetIdentifier()); EXPECT_NE(char5->GetIdentifier(), char6->GetIdentifier());
} }
#endif // defined(OS_ANDROID) || defined(OS_WIN) #endif // defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
#if defined(OS_ANDROID) || defined(OS_WIN) #if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
TEST_F(BluetoothRemoteGattCharacteristicTest, GetUUID) { TEST_F(BluetoothRemoteGattCharacteristicTest, GetUUID) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter(); InitWithFakeAdapter();
StartLowEnergyDiscoverySession(); StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3); BluetoothDevice* device = SimulateLowEnergyDevice(3);
...@@ -235,10 +243,14 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, GetUUID) { ...@@ -235,10 +243,14 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, GetUUID) {
EXPECT_EQ(uuid2, char2->GetUUID()); EXPECT_EQ(uuid2, char2->GetUUID());
EXPECT_EQ(uuid2, char3->GetUUID()); EXPECT_EQ(uuid2, char3->GetUUID());
} }
#endif // defined(OS_ANDROID) || defined(OS_WIN) #endif // defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
#if defined(OS_ANDROID) || defined(OS_WIN) #if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
TEST_F(BluetoothRemoteGattCharacteristicTest, GetProperties) { TEST_F(BluetoothRemoteGattCharacteristicTest, GetProperties) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter(); InitWithFakeAdapter();
StartLowEnergyDiscoverySession(); StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3); BluetoothDevice* device = SimulateLowEnergyDevice(3);
...@@ -263,17 +275,21 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, GetProperties) { ...@@ -263,17 +275,21 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, GetProperties) {
EXPECT_EQ(0, properties1); EXPECT_EQ(0, properties1);
EXPECT_EQ(7, properties2); EXPECT_EQ(7, properties2);
} }
#endif // defined(OS_ANDROID) || defined(OS_WIN) #endif // defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
#if defined(OS_ANDROID) || defined(OS_WIN) #if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
// Tests GetService. // Tests GetService.
TEST_F(BluetoothRemoteGattCharacteristicTest, GetService) { TEST_F(BluetoothRemoteGattCharacteristicTest, GetService) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate()); ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate());
EXPECT_EQ(service_, characteristic1_->GetService()); EXPECT_EQ(service_, characteristic1_->GetService());
EXPECT_EQ(service_, characteristic2_->GetService()); EXPECT_EQ(service_, characteristic2_->GetService());
} }
#endif // defined(OS_ANDROID) || defined(OS_WIN) #endif // defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
#if defined(OS_ANDROID) || defined(OS_WIN) #if defined(OS_ANDROID) || defined(OS_WIN)
// Tests ReadRemoteCharacteristic and GetValue with empty value buffer. // Tests ReadRemoteCharacteristic and GetValue with empty value buffer.
......
...@@ -9,15 +9,19 @@ ...@@ -9,15 +9,19 @@
#include <vector> #include <vector>
#include "base/containers/scoped_ptr_hash_map.h"
#include "base/mac/scoped_nsobject.h" #include "base/mac/scoped_nsobject.h"
#include "device/bluetooth/bluetooth_remote_gatt_service.h" #include "device/bluetooth/bluetooth_remote_gatt_service.h"
@class CBCharacteristic;
@class CBPeripheral;
@class CBService; @class CBService;
namespace device { namespace device {
class BluetoothAdapterMac;
class BluetoothDevice; class BluetoothDevice;
class BluetoothGattCharacteristic; class BluetoothRemoteGattCharacteristicMac;
class BluetoothLowEnergyDeviceMac; class BluetoothLowEnergyDeviceMac;
class BluetoothTestMac; class BluetoothTestMac;
...@@ -45,18 +49,39 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattServiceMac ...@@ -45,18 +49,39 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattServiceMac
friend BluetoothLowEnergyDeviceMac; friend BluetoothLowEnergyDeviceMac;
friend BluetoothTestMac; friend BluetoothTestMac;
// Starts discovering characteristics by calling CoreBluetooth.
void DiscoverCharacteristics();
// Called by the BluetoothLowEnergyDeviceMac instance when the characteristics
// has been discovered.
void DidDiscoverCharacteristics();
// Returns true if the characteristics has been discovered.
bool IsDiscoveryComplete();
// Returns the mac adapter.
BluetoothAdapterMac* GetMacAdapter() const;
// Returns CBPeripheral.
CBPeripheral* GetCBPeripheral() const;
// Returns CBService. // Returns CBService.
CBService* GetService() const; CBService* GetService() const;
// Returns a remote characteristic based on the CBCharacteristic.
BluetoothRemoteGattCharacteristicMac* GetBluetoothRemoteGattCharacteristicMac(
CBCharacteristic* characteristic) const;
// bluetooth_device_mac_ owns instances of this class. // bluetooth_device_mac_ owns instances of this class.
BluetoothLowEnergyDeviceMac* bluetooth_device_mac_; BluetoothLowEnergyDeviceMac* bluetooth_device_mac_;
// A service from CBPeripheral.services. // A service from CBPeripheral.services.
base::scoped_nsobject<CBService> service_; base::scoped_nsobject<CBService> service_;
// Map of characteristics, keyed by characteristic identifier.
std::unordered_map<std::string,
std::unique_ptr<BluetoothRemoteGattCharacteristicMac>>
gatt_characteristic_macs_;
bool is_primary_; bool is_primary_;
// Service identifier. // Service identifier.
std::string identifier_; std::string identifier_;
// Service UUID. // Service UUID.
BluetoothUUID uuid_; BluetoothUUID uuid_;
// Is true if the characteristics has been discovered.
bool is_discovery_complete_;
DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattServiceMac); DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattServiceMac);
}; };
......
...@@ -8,8 +8,10 @@ ...@@ -8,8 +8,10 @@
#include <vector> #include <vector>
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "device/bluetooth/bluetooth_adapter_mac.h" #include "device/bluetooth/bluetooth_adapter_mac.h"
#include "device/bluetooth/bluetooth_low_energy_device_mac.h" #include "device/bluetooth/bluetooth_low_energy_device_mac.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h"
#include "device/bluetooth/bluetooth_uuid.h" #include "device/bluetooth/bluetooth_uuid.h"
namespace device { namespace device {
...@@ -20,7 +22,8 @@ BluetoothRemoteGattServiceMac::BluetoothRemoteGattServiceMac( ...@@ -20,7 +22,8 @@ BluetoothRemoteGattServiceMac::BluetoothRemoteGattServiceMac(
bool is_primary) bool is_primary)
: 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) {
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(),
...@@ -48,8 +51,13 @@ BluetoothDevice* BluetoothRemoteGattServiceMac::GetDevice() const { ...@@ -48,8 +51,13 @@ BluetoothDevice* BluetoothRemoteGattServiceMac::GetDevice() const {
std::vector<BluetoothRemoteGattCharacteristic*> std::vector<BluetoothRemoteGattCharacteristic*>
BluetoothRemoteGattServiceMac::GetCharacteristics() const { BluetoothRemoteGattServiceMac::GetCharacteristics() const {
NOTIMPLEMENTED(); std::vector<BluetoothRemoteGattCharacteristic*> gatt_characteristics;
return std::vector<BluetoothRemoteGattCharacteristic*>(); for (const auto& iter : gatt_characteristic_macs_) {
BluetoothRemoteGattCharacteristic* gatt_characteristic =
static_cast<BluetoothRemoteGattCharacteristic*>(iter.second.get());
gatt_characteristics.push_back(gatt_characteristic);
}
return gatt_characteristics;
} }
std::vector<BluetoothRemoteGattService*> std::vector<BluetoothRemoteGattService*>
...@@ -61,12 +69,88 @@ BluetoothRemoteGattServiceMac::GetIncludedServices() const { ...@@ -61,12 +69,88 @@ BluetoothRemoteGattServiceMac::GetIncludedServices() const {
BluetoothRemoteGattCharacteristic* BluetoothRemoteGattCharacteristic*
BluetoothRemoteGattServiceMac::GetCharacteristic( BluetoothRemoteGattServiceMac::GetCharacteristic(
const std::string& identifier) const { const std::string& identifier) const {
NOTIMPLEMENTED(); auto searched_pair = gatt_characteristic_macs_.find(identifier);
return nullptr; if (searched_pair == gatt_characteristic_macs_.end()) {
return nullptr;
}
return static_cast<BluetoothRemoteGattCharacteristic*>(
searched_pair->second.get());
}
void BluetoothRemoteGattServiceMac::DiscoverCharacteristics() {
is_discovery_complete_ = false;
[GetCBPeripheral() discoverCharacteristics:nil forService:GetService()];
}
void BluetoothRemoteGattServiceMac::DidDiscoverCharacteristics() {
DCHECK(!is_discovery_complete_);
std::unordered_set<std::string> characteristic_identifier_to_remove;
for (const auto& iter : gatt_characteristic_macs_) {
characteristic_identifier_to_remove.insert(iter.first);
}
for (CBCharacteristic* cb_characteristic in GetService().characteristics) {
BluetoothRemoteGattCharacteristicMac* gatt_characteristic_mac =
GetBluetoothRemoteGattCharacteristicMac(cb_characteristic);
if (gatt_characteristic_mac) {
const std::string& identifier = gatt_characteristic_mac->GetIdentifier();
characteristic_identifier_to_remove.erase(identifier);
continue;
}
gatt_characteristic_mac =
new BluetoothRemoteGattCharacteristicMac(this, cb_characteristic);
const std::string& identifier = gatt_characteristic_mac->GetIdentifier();
auto result_iter = gatt_characteristic_macs_.insert(
{identifier, base::WrapUnique(gatt_characteristic_mac)});
DCHECK(result_iter.second);
GetMacAdapter()->NotifyGattCharacteristicAdded(gatt_characteristic_mac);
}
for (const std::string& identifier : characteristic_identifier_to_remove) {
auto pair_to_remove = gatt_characteristic_macs_.find(identifier);
std::unique_ptr<BluetoothRemoteGattCharacteristicMac>
characteristic_to_remove;
pair_to_remove->second.swap(characteristic_to_remove);
gatt_characteristic_macs_.erase(pair_to_remove);
GetMacAdapter()->NotifyGattCharacteristicRemoved(
characteristic_to_remove.get());
}
is_discovery_complete_ = true;
GetMacAdapter()->NotifyGattServiceChanged(this);
}
bool BluetoothRemoteGattServiceMac::IsDiscoveryComplete() {
return is_discovery_complete_;
}
BluetoothAdapterMac* BluetoothRemoteGattServiceMac::GetMacAdapter() const {
return bluetooth_device_mac_->GetMacAdapter();
}
CBPeripheral* BluetoothRemoteGattServiceMac::GetCBPeripheral() const {
return bluetooth_device_mac_->GetPeripheral();
} }
CBService* BluetoothRemoteGattServiceMac::GetService() const { CBService* BluetoothRemoteGattServiceMac::GetService() const {
return service_.get(); return service_.get();
} }
BluetoothRemoteGattCharacteristicMac*
BluetoothRemoteGattServiceMac::GetBluetoothRemoteGattCharacteristicMac(
CBCharacteristic* characteristic) const {
auto found = std::find_if(
gatt_characteristic_macs_.begin(), gatt_characteristic_macs_.end(),
[characteristic](
const std::pair<
const std::string,
std::unique_ptr<BluetoothRemoteGattCharacteristicMac>>& pair) {
return pair.second->GetCBCharacteristic() == characteristic;
});
if (found == gatt_characteristic_macs_.end()) {
return nullptr;
} else {
return found->second.get();
}
}
} // namespace device } // namespace device
...@@ -23,7 +23,7 @@ namespace device { ...@@ -23,7 +23,7 @@ namespace device {
class BluetoothRemoteGattServiceTest : public BluetoothTest {}; class BluetoothRemoteGattServiceTest : public BluetoothTest {};
#endif #endif
#if defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_MACOSX) #if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
TEST_F(BluetoothRemoteGattServiceTest, GetIdentifier) { TEST_F(BluetoothRemoteGattServiceTest, GetIdentifier) {
if (!PlatformSupportsLowEnergy()) { if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
...@@ -63,9 +63,9 @@ TEST_F(BluetoothRemoteGattServiceTest, GetIdentifier) { ...@@ -63,9 +63,9 @@ TEST_F(BluetoothRemoteGattServiceTest, GetIdentifier) {
EXPECT_NE(service3->GetIdentifier(), service4->GetIdentifier()); EXPECT_NE(service3->GetIdentifier(), service4->GetIdentifier());
} }
#endif // defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_MACOSX) #endif // defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
#if defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_MACOSX) #if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
TEST_F(BluetoothRemoteGattServiceTest, GetUUID) { TEST_F(BluetoothRemoteGattServiceTest, GetUUID) {
if (!PlatformSupportsLowEnergy()) { if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
...@@ -89,9 +89,9 @@ TEST_F(BluetoothRemoteGattServiceTest, GetUUID) { ...@@ -89,9 +89,9 @@ TEST_F(BluetoothRemoteGattServiceTest, GetUUID) {
EXPECT_EQ(uuid, device->GetGattServices()[0]->GetUUID()); EXPECT_EQ(uuid, device->GetGattServices()[0]->GetUUID());
EXPECT_EQ(uuid, device->GetGattServices()[1]->GetUUID()); EXPECT_EQ(uuid, device->GetGattServices()[1]->GetUUID());
} }
#endif // defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_MACOSX) #endif // defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
#if defined(OS_ANDROID) || defined(OS_WIN) #if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
TEST_F(BluetoothRemoteGattServiceTest, GetCharacteristics_FindNone) { TEST_F(BluetoothRemoteGattServiceTest, GetCharacteristics_FindNone) {
if (!PlatformSupportsLowEnergy()) { if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test."; LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
...@@ -112,11 +112,15 @@ TEST_F(BluetoothRemoteGattServiceTest, GetCharacteristics_FindNone) { ...@@ -112,11 +112,15 @@ TEST_F(BluetoothRemoteGattServiceTest, GetCharacteristics_FindNone) {
EXPECT_EQ(0u, service->GetCharacteristics().size()); EXPECT_EQ(0u, service->GetCharacteristics().size());
} }
#endif // defined(OS_ANDROID) || defined(OS_WIN) #endif // defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
#if defined(OS_ANDROID) || defined(OS_WIN) #if defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
TEST_F(BluetoothRemoteGattServiceTest, TEST_F(BluetoothRemoteGattServiceTest,
GetCharacteristics_and_GetCharacteristic) { GetCharacteristics_and_GetCharacteristic) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter(); InitWithFakeAdapter();
StartLowEnergyDiscoverySession(); StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3); BluetoothDevice* device = SimulateLowEnergyDevice(3);
...@@ -161,11 +165,15 @@ TEST_F(BluetoothRemoteGattServiceTest, ...@@ -161,11 +165,15 @@ TEST_F(BluetoothRemoteGattServiceTest,
EXPECT_EQ(service->GetCharacteristic(char_id1), EXPECT_EQ(service->GetCharacteristic(char_id1),
service->GetCharacteristic(char_id1)); service->GetCharacteristic(char_id1));
} }
#endif // defined(OS_ANDROID) || defined(OS_WIN) #endif // defined(OS_ANDROID) || defined(OS_MACOSX) || defined(OS_WIN)
#if defined(OS_WIN) #if defined(OS_MACOSX) || defined(OS_WIN)
TEST_F(BluetoothRemoteGattServiceTest, TEST_F(BluetoothRemoteGattServiceTest,
GetCharacteristic_CharacteristicRemoved) { GetCharacteristic_CharacteristicRemoved) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter(); InitWithFakeAdapter();
StartLowEnergyDiscoverySession(); StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3); BluetoothDevice* device = SimulateLowEnergyDevice(3);
...@@ -216,9 +224,16 @@ TEST_F(BluetoothRemoteGattServiceTest, ...@@ -216,9 +224,16 @@ TEST_F(BluetoothRemoteGattServiceTest,
EXPECT_FALSE(service->GetCharacteristic(removed_char)); EXPECT_FALSE(service->GetCharacteristic(removed_char));
EXPECT_EQ(0u, service->GetCharacteristics().size()); EXPECT_EQ(0u, service->GetCharacteristics().size());
#if defined(OS_MACOSX)
// SimulateGattServicesDiscovered
// 4 * SimulateGattCharacteristic
// 4 * SimulateGattCharacteristicRemoved
EXPECT_EQ(9, observer.gatt_service_changed_count());
#else // defined(OS_MACOSX)
EXPECT_EQ(4, observer.gatt_service_changed_count()); EXPECT_EQ(4, observer.gatt_service_changed_count());
#endif // defined(OS_MACOSX)
} }
#endif // defined(OS_WIN) #endif // defined(OS_MACOSX) || defined(OS_WIN)
#if defined(OS_WIN) || defined(OS_MACOSX) #if defined(OS_WIN) || defined(OS_MACOSX)
TEST_F(BluetoothRemoteGattServiceTest, SimulateGattServiceRemove) { TEST_F(BluetoothRemoteGattServiceTest, SimulateGattServiceRemove) {
......
...@@ -10,6 +10,12 @@ ...@@ -10,6 +10,12 @@
#include "base/test/test_simple_task_runner.h" #include "base/test/test_simple_task_runner.h"
#include "device/bluetooth/test/bluetooth_test.h" #include "device/bluetooth/test/bluetooth_test.h"
#if __OBJC__
@class MockCBPeripheral;
#else // __OBJC__
class MockCBPeripheral;
#endif // __OBJC__
namespace device { namespace device {
class BluetoothAdapterMac; class BluetoothAdapterMac;
...@@ -41,6 +47,12 @@ class BluetoothTestMac : public BluetoothTestBase { ...@@ -41,6 +47,12 @@ class BluetoothTestMac : public BluetoothTestBase {
BluetoothDevice* device, BluetoothDevice* device,
const std::vector<std::string>& uuids) override; const std::vector<std::string>& uuids) override;
void SimulateGattServiceRemoved(BluetoothRemoteGattService* service) override; void SimulateGattServiceRemoved(BluetoothRemoteGattService* service) override;
void SimulateGattCharacteristic(BluetoothRemoteGattService* service,
const std::string& uuid,
int properties) override;
void SimulateGattCharacteristicRemoved(
BluetoothRemoteGattService* service,
BluetoothRemoteGattCharacteristic* characteristic) override;
// Callback for the bluetooth central manager mock. // Callback for the bluetooth central manager mock.
void OnFakeBluetoothDeviceConnectGattCalled(); void OnFakeBluetoothDeviceConnectGattCalled();
...@@ -52,6 +64,10 @@ class BluetoothTestMac : public BluetoothTestBase { ...@@ -52,6 +64,10 @@ class BluetoothTestMac : public BluetoothTestBase {
protected: protected:
class ScopedMockCentralManager; class ScopedMockCentralManager;
// Returns MockCBPeripheral from BluetoothRemoteGattService.
MockCBPeripheral* GetMockCBPeripheral(
BluetoothRemoteGattService* service) const;
// 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();
......
...@@ -10,8 +10,11 @@ ...@@ -10,8 +10,11 @@
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "device/bluetooth/bluetooth_adapter_mac.h" #include "device/bluetooth/bluetooth_adapter_mac.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h"
#include "device/bluetooth/bluetooth_remote_gatt_service_mac.h" #include "device/bluetooth/bluetooth_remote_gatt_service_mac.h"
#include "device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h"
#include "device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h" #include "device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h"
#include "device/bluetooth/test/mock_bluetooth_cbservice_mac.h"
#include "device/bluetooth/test/mock_bluetooth_central_manager_mac.h" #include "device/bluetooth/test/mock_bluetooth_central_manager_mac.h"
#include "device/bluetooth/test/test_bluetooth_adapter_observer.h" #include "device/bluetooth/test/test_bluetooth_adapter_observer.h"
#include "third_party/ocmock/OCMock/OCMock.h" #include "third_party/ocmock/OCMock/OCMock.h"
...@@ -21,6 +24,18 @@ ...@@ -21,6 +24,18 @@
using base::mac::ObjCCast; using base::mac::ObjCCast;
using base::scoped_nsobject; using base::scoped_nsobject;
namespace {
void DidDiscoverServices(MockCBPeripheral* peripheral_mock) {
[peripheral_mock didDiscoverServicesWithError:nil];
// BluetoothLowEnergyDeviceMac is expected to call
// -[CBPeripheral discoverCharacteristics:forService:] for each services,
// so -[<CBPeripheralDelegate peripheral:didDiscoverCharacteristicsForService:
// error:] needs to be called
[peripheral_mock didDiscoverCharactericsForAllServices];
}
} // namespace
namespace device { namespace device {
// This class hides Objective-C from bluetooth_test_mac.h. // This class hides Objective-C from bluetooth_test_mac.h.
...@@ -237,7 +252,7 @@ void BluetoothTestMac::SimulateGattServicesDiscovered( ...@@ -237,7 +252,7 @@ void BluetoothTestMac::SimulateGattServicesDiscovered(
[services addObject:cb_service_uuid]; [services addObject:cb_service_uuid];
} }
[peripheral_mock addServices:services]; [peripheral_mock addServices:services];
[peripheral_mock didDiscoverServicesWithError:nil]; DidDiscoverServices(peripheral_mock);
} }
void BluetoothTestMac::SimulateGattServiceRemoved( void BluetoothTestMac::SimulateGattServiceRemoved(
...@@ -252,7 +267,44 @@ void BluetoothTestMac::SimulateGattServiceRemoved( ...@@ -252,7 +267,44 @@ void BluetoothTestMac::SimulateGattServiceRemoved(
CBPeripheral* peripheral = device_mac->GetPeripheral(); CBPeripheral* peripheral = device_mac->GetPeripheral();
MockCBPeripheral* peripheral_mock = ObjCCast<MockCBPeripheral>(peripheral); MockCBPeripheral* peripheral_mock = ObjCCast<MockCBPeripheral>(peripheral);
[peripheral_mock removeService:mac_gatt_service->GetService()]; [peripheral_mock removeService:mac_gatt_service->GetService()];
[peripheral_mock didDiscoverServicesWithError:nil]; // After -[MockCBPeripheral removeService:], BluetoothLowEnergyDeviceMac is
// expected to call -[CBPeripheral discoverServices:]
DidDiscoverServices(peripheral_mock);
}
void BluetoothTestMac::SimulateGattCharacteristic(
BluetoothRemoteGattService* service,
const std::string& 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:@(uuid.c_str())];
[service_mock addCharacteristicWithUUID:cb_uuid properties:properties];
MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(service);
[peripheral_mock didModifyServices:@[]];
// After -[MockCBPeripheral didModifyServices:], BluetoothLowEnergyDeviceMac
// is expected to call -[CBPeripheral discoverServices:]
DidDiscoverServices(peripheral_mock);
}
void BluetoothTestMac::SimulateGattCharacteristicRemoved(
BluetoothRemoteGattService* service,
BluetoothRemoteGattCharacteristic* characteristic) {
MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(service);
BluetoothRemoteGattServiceMac* mac_gatt_service =
static_cast<BluetoothRemoteGattServiceMac*>(service);
CBService* cb_service = mac_gatt_service->GetService();
MockCBService* service_mock = ObjCCast<MockCBService>(cb_service);
BluetoothRemoteGattCharacteristicMac* characteristic_mac =
static_cast<BluetoothRemoteGattCharacteristicMac*>(characteristic);
CBCharacteristic* cb_characteristic =
characteristic_mac->GetCBCharacteristic();
MockCBCharacteristic* characteristic_mock =
ObjCCast<MockCBCharacteristic>(cb_characteristic);
[service_mock removeCharacteristicMock:characteristic_mock];
DidDiscoverServices(peripheral_mock);
} }
void BluetoothTestMac::OnFakeBluetoothDeviceConnectGattCalled() { void BluetoothTestMac::OnFakeBluetoothDeviceConnectGattCalled() {
...@@ -267,6 +319,15 @@ void BluetoothTestMac::OnFakeBluetoothServiceDiscovery() { ...@@ -267,6 +319,15 @@ void BluetoothTestMac::OnFakeBluetoothServiceDiscovery() {
gatt_discovery_attempts_++; gatt_discovery_attempts_++;
} }
MockCBPeripheral* BluetoothTestMac::GetMockCBPeripheral(
BluetoothRemoteGattService* service) const {
BluetoothDevice* device = service->GetDevice();
BluetoothLowEnergyDeviceMac* device_mac =
static_cast<BluetoothLowEnergyDeviceMac*>(device);
CBPeripheral* cb_peripheral = device_mac->GetPeripheral();
return ObjCCast<MockCBPeripheral>(cb_peripheral);
}
// 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,
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_CBCHARACTERISTIC_MAC_H_
#define DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_CBCHARACTERISTIC_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 CBCharacteristic.
@interface MockCBCharacteristic : NSObject
@property(readonly, nonatomic) CBUUID* UUID;
@property(readonly, nonatomic) CBCharacteristic* characteristic;
- (instancetype)initWithCBUUID:(CBUUID*)uuid properties:(int)properties;
@end
#endif // DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_CBCHARACTERISTIC_MAC_H_
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_nsobject.h"
#include "device/bluetooth/bluetooth_gatt_characteristic.h"
using base::mac::ObjCCast;
using base::scoped_nsobject;
namespace device {
namespace {
CBCharacteristicProperties AddCBCharacteristicProperties(
CBCharacteristicProperties value1,
CBCharacteristicProperties value2) {
return static_cast<CBCharacteristicProperties>(value1 | value2);
}
CBCharacteristicProperties GattCharacteristicPropertyToCBCharacteristicProperty(
BluetoothGattCharacteristic::Properties gatt_property) {
CBCharacteristicProperties result =
static_cast<CBCharacteristicProperties>(0);
if (gatt_property & BluetoothGattCharacteristic::PROPERTY_BROADCAST) {
result = AddCBCharacteristicProperties(result,
CBCharacteristicPropertyBroadcast);
}
if (gatt_property & BluetoothGattCharacteristic::PROPERTY_READ) {
result =
AddCBCharacteristicProperties(result, CBCharacteristicPropertyRead);
}
if (gatt_property &
BluetoothGattCharacteristic::PROPERTY_WRITE_WITHOUT_RESPONSE) {
result = AddCBCharacteristicProperties(
result, CBCharacteristicPropertyWriteWithoutResponse);
}
if (gatt_property & BluetoothGattCharacteristic::PROPERTY_WRITE) {
result =
AddCBCharacteristicProperties(result, CBCharacteristicPropertyWrite);
}
if (gatt_property & BluetoothGattCharacteristic::PROPERTY_NOTIFY) {
result =
AddCBCharacteristicProperties(result, CBCharacteristicPropertyNotify);
}
if (gatt_property & BluetoothGattCharacteristic::PROPERTY_INDICATE) {
result =
AddCBCharacteristicProperties(result, CBCharacteristicPropertyIndicate);
}
if (gatt_property &
BluetoothGattCharacteristic::PROPERTY_AUTHENTICATED_SIGNED_WRITES) {
result = AddCBCharacteristicProperties(
result, CBCharacteristicPropertyAuthenticatedSignedWrites);
}
if (gatt_property &
BluetoothGattCharacteristic::PROPERTY_EXTENDED_PROPERTIES) {
result = AddCBCharacteristicProperties(
result, CBCharacteristicPropertyExtendedProperties);
}
if (gatt_property & BluetoothGattCharacteristic::PROPERTY_RELIABLE_WRITE) {
}
if (gatt_property &
BluetoothGattCharacteristic::PROPERTY_WRITABLE_AUXILIARIES) {
}
if (gatt_property & BluetoothGattCharacteristic::PROPERTY_READ_ENCRYPTED) {
result =
AddCBCharacteristicProperties(result, CBCharacteristicPropertyRead);
}
if (gatt_property & BluetoothGattCharacteristic::PROPERTY_WRITE_ENCRYPTED) {
result =
AddCBCharacteristicProperties(result, CBCharacteristicPropertyWrite);
}
if (gatt_property &
BluetoothGattCharacteristic::PROPERTY_READ_ENCRYPTED_AUTHENTICATED) {
result =
AddCBCharacteristicProperties(result, CBCharacteristicPropertyRead);
}
if (gatt_property &
BluetoothGattCharacteristic::PROPERTY_WRITE_ENCRYPTED_AUTHENTICATED) {
result =
AddCBCharacteristicProperties(result, CBCharacteristicPropertyWrite);
}
return result;
}
} // namespace
} // device
@interface MockCBCharacteristic () {
scoped_nsobject<CBUUID> _UUID;
CBCharacteristicProperties _cb_properties;
}
@end
@implementation MockCBCharacteristic
- (instancetype)initWithCBUUID:(CBUUID*)uuid properties:(int)properties {
self = [super init];
if (self) {
_UUID.reset([uuid retain]);
_cb_properties =
device::GattCharacteristicPropertyToCBCharacteristicProperty(
properties);
}
return self;
}
- (BOOL)isKindOfClass:(Class)aClass {
if (aClass == [CBCharacteristic class] ||
[aClass isSubclassOfClass:[CBCharacteristic class]]) {
return YES;
}
return [super isKindOfClass:aClass];
}
- (BOOL)isMemberOfClass:(Class)aClass {
if (aClass == [CBCharacteristic class] ||
[aClass isSubclassOfClass:[CBCharacteristic class]]) {
return YES;
}
return [super isKindOfClass:aClass];
}
- (CBUUID*)UUID {
return _UUID.get();
}
- (CBCharacteristic*)characteristic {
return ObjCCast<CBCharacteristic>(self);
}
- (CBCharacteristicProperties)properties {
return _cb_properties;
}
@end
\ No newline at end of file
...@@ -38,6 +38,8 @@ class BluetoothTestMac; ...@@ -38,6 +38,8 @@ class BluetoothTestMac;
- (void)addServices:(NSArray*)services; - (void)addServices:(NSArray*)services;
- (void)didDiscoverServicesWithError:(NSError*)error; - (void)didDiscoverServicesWithError:(NSError*)error;
- (void)removeService:(CBService*)uuid; - (void)removeService:(CBService*)uuid;
- (void)didDiscoverCharactericsForAllServices;
- (void)didModifyServices:(NSArray*)invalidatedServices;
@end @end
......
...@@ -84,6 +84,11 @@ using base::scoped_nsobject; ...@@ -84,6 +84,11 @@ using base::scoped_nsobject;
if (_bluetoothTestMac) { if (_bluetoothTestMac) {
_bluetoothTestMac->OnFakeBluetoothServiceDiscovery(); _bluetoothTestMac->OnFakeBluetoothServiceDiscovery();
} }
[_delegate peripheral:self.peripheral didDiscoverServices:nil];
}
- (void)discoverCharacteristics:(NSArray*)characteristics
forService:(CBService*)service {
} }
- (void)removeAllServices { - (void)removeAllServices {
...@@ -95,8 +100,10 @@ using base::scoped_nsobject; ...@@ -95,8 +100,10 @@ using base::scoped_nsobject;
_services.reset([[NSMutableArray alloc] init]); _services.reset([[NSMutableArray alloc] init]);
} }
for (CBUUID* uuid in services) { for (CBUUID* uuid in services) {
base::scoped_nsobject<MockCBService> service( base::scoped_nsobject<MockCBService> service([[MockCBService alloc]
[[MockCBService alloc] initWithCBUUID:uuid primary:YES]); initWithPeripheral:self.peripheral
CBUUID:uuid
primary:YES]);
[_services.get() addObject:service.get().service]; [_services.get() addObject:service.get().service];
} }
} }
...@@ -110,6 +117,18 @@ using base::scoped_nsobject; ...@@ -110,6 +117,18 @@ using base::scoped_nsobject;
base::scoped_policy::RETAIN); base::scoped_policy::RETAIN);
DCHECK(serviceToRemove); DCHECK(serviceToRemove);
[_services.get() removeObject:serviceToRemove]; [_services.get() removeObject:serviceToRemove];
[self didModifyServices:@[ serviceToRemove ]];
}
- (void)didDiscoverCharactericsForAllServices {
for (CBService* service in _services.get()) {
[_delegate peripheral:self.peripheral
didDiscoverCharacteristicsForService:service
error:nil];
}
}
- (void)didModifyServices:(NSArray*)invalidatedServices {
// -[CBPeripheralDelegate peripheral:didModifyServices:] is only available // -[CBPeripheralDelegate peripheral:didModifyServices:] is only available
// with 10.9. It is safe to call this method (even if chrome is running on // with 10.9. It is safe to call this method (even if chrome is running on
// 10.8) since WebBluetooth is enabled only with 10.10. // 10.8) since WebBluetooth is enabled only with 10.10.
...@@ -117,7 +136,7 @@ using base::scoped_nsobject; ...@@ -117,7 +136,7 @@ using base::scoped_nsobject;
[_delegate respondsToSelector:@selector(peripheral:didModifyServices:)]); [_delegate respondsToSelector:@selector(peripheral:didModifyServices:)]);
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability" #pragma clang diagnostic ignored "-Wpartial-availability"
[_delegate peripheral:self.peripheral didModifyServices:@[ serviceToRemove ]]; [_delegate peripheral:self.peripheral didModifyServices:invalidatedServices];
#pragma clang diagnostic pop #pragma clang diagnostic pop
} }
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#import <CoreBluetooth/CoreBluetooth.h> #import <CoreBluetooth/CoreBluetooth.h>
@class MockCBCharacteristic;
// This class mocks the behavior of a CBService. // This class mocks the behavior of a CBService.
@interface MockCBService : NSObject @interface MockCBService : NSObject
...@@ -17,7 +19,13 @@ ...@@ -17,7 +19,13 @@
@property(readonly, nonatomic) BOOL isPrimary; @property(readonly, nonatomic) BOOL isPrimary;
@property(readonly, nonatomic) CBService* service; @property(readonly, nonatomic) CBService* service;
- (instancetype)initWithCBUUID:(CBUUID*)uuid primary:(BOOL)isPrimary; - (instancetype)initWithPeripheral:(CBPeripheral*)peripheral
CBUUID:(CBUUID*)uuid
primary:(BOOL)isPrimary;
// Creates and adds a mock characteristic.
- (void)addCharacteristicWithUUID:(CBUUID*)cb_uuid properties:(int)properties;
- (void)removeCharacteristicMock:(MockCBCharacteristic*)characteristic_mock;
@end @end
......
...@@ -7,13 +7,17 @@ ...@@ -7,13 +7,17 @@
#include "base/mac/foundation_util.h" #include "base/mac/foundation_util.h"
#include "base/mac/scoped_nsobject.h" #include "base/mac/scoped_nsobject.h"
#include "device/bluetooth/test/bluetooth_test.h" #include "device/bluetooth/test/bluetooth_test.h"
#include "device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h"
using base::mac::ObjCCast; using base::mac::ObjCCast;
using base::scoped_nsobject; using base::scoped_nsobject;
@interface MockCBService () { @interface MockCBService () {
// Owner of this instance.
CBPeripheral* _peripheral;
scoped_nsobject<CBUUID> _UUID; scoped_nsobject<CBUUID> _UUID;
BOOL _primary; BOOL _primary;
scoped_nsobject<NSMutableArray> _characteristics;
} }
@end @end
...@@ -22,11 +26,15 @@ using base::scoped_nsobject; ...@@ -22,11 +26,15 @@ using base::scoped_nsobject;
@synthesize isPrimary = _primary; @synthesize isPrimary = _primary;
- (instancetype)initWithCBUUID:(CBUUID*)uuid primary:(BOOL)isPrimary { - (instancetype)initWithPeripheral:(CBPeripheral*)peripheral
CBUUID:(CBUUID*)uuid
primary:(BOOL)isPrimary {
self = [super init]; self = [super init];
if (self) { if (self) {
_UUID.reset([uuid retain]); _UUID.reset([uuid retain]);
_primary = isPrimary; _primary = isPrimary;
_peripheral = peripheral;
_characteristics.reset([[NSMutableArray alloc] init]);
} }
return self; return self;
} }
...@@ -47,12 +55,31 @@ using base::scoped_nsobject; ...@@ -47,12 +55,31 @@ using base::scoped_nsobject;
return [super isKindOfClass:aClass]; return [super isKindOfClass:aClass];
} }
- (CBPeripheral*)peripheral {
return _peripheral;
}
- (CBUUID*)UUID { - (CBUUID*)UUID {
return _UUID.get(); return _UUID.get();
} }
- (void)addCharacteristicWithUUID:(CBUUID*)cb_uuid properties:(int)properties {
scoped_nsobject<MockCBCharacteristic> characteristic_mock(
[[MockCBCharacteristic alloc] initWithCBUUID:cb_uuid
properties:properties]);
[_characteristics.get() addObject:characteristic_mock];
}
- (void)removeCharacteristicMock:(MockCBCharacteristic*)characteristic_mock {
[_characteristics.get() removeObject:characteristic_mock];
}
- (CBService*)service { - (CBService*)service {
return ObjCCast<CBService>(self); return ObjCCast<CBService>(self);
} }
- (NSArray*)characteristics {
return _characteristics.get();
}
@end @end
...@@ -64,6 +64,8 @@ ...@@ -64,6 +64,8 @@
'bluetooth/test/bluetooth_test_mac.mm', 'bluetooth/test/bluetooth_test_mac.mm',
'bluetooth/test/bluetooth_test_win.h', 'bluetooth/test/bluetooth_test_win.h',
'bluetooth/test/bluetooth_test_win.cc', 'bluetooth/test/bluetooth_test_win.cc',
'bluetooth/test/mock_bluetooth_cbcharacteristic_mac.mm',
'bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h',
'bluetooth/test/mock_bluetooth_cbperipheral_mac.mm', 'bluetooth/test/mock_bluetooth_cbperipheral_mac.mm',
'bluetooth/test/mock_bluetooth_cbperipheral_mac.h', 'bluetooth/test/mock_bluetooth_cbperipheral_mac.h',
'bluetooth/test/mock_bluetooth_cbservice_mac.mm', 'bluetooth/test/mock_bluetooth_cbservice_mac.mm',
......
...@@ -1554,32 +1554,10 @@ crbug.com/617799 crbug.com/621025 [ Linux ] svg/custom/text-match-highlight.html ...@@ -1554,32 +1554,10 @@ crbug.com/617799 crbug.com/621025 [ Linux ] svg/custom/text-match-highlight.html
# TODO(jlebel): Remove when methods are implemented. # TODO(jlebel): Remove when methods are implemented.
crbug.com/609065 [ Mac ] bluetooth/characteristicProperties.html [ Skip ] crbug.com/609065 [ Mac ] bluetooth/characteristicProperties.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/connect/connect-disconnected-connect.html [ Skip ] crbug.com/609065 [ Mac ] bluetooth/connect/connect-disconnected-connect.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/gattserverdisconnected-event/disconnected.html [ Skip ] crbug.com/609065 [ Mac ] bluetooth/gattserverdisconnected-event/disconnected.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/gattserverdisconnected-event/disconnected_gc.html [ Skip ] crbug.com/609065 [ Mac ] bluetooth/getCharacteristics/correct-characteristics.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/gattserverdisconnected-event/one-event-per-disconnection.html [ Skip ] crbug.com/609065 [ Mac ] bluetooth/gattserverdisconnected-event/reconnect-during-disconnected-event.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/gattserverdisconnected-event/reconnect-during-disconnected-event.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristic/blacklisted-characteristic.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristic/characteristic-found.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristic/characteristic-not-found.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristic/device-goes-out-of-range.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristic/get-same-characteristic.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristic/invalid-characteristic-name.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristic/service-is-removed.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristics/blacklisted-characteristics-with-uuid.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristics/blacklisted-characteristics.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristics/characteristics-found-with-uuid.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristics/characteristics-found.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristics/characteristics-not-found-with-uuid.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristics/characteristics-not-found.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristics/correct-characteristics.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristics/device-goes-out-of-range-with-uuid.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristics/device-goes-out-of-range.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristics/get-same-characteristics.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristics/invalid-characteristic-name.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristics/service-is-removed-with-uuid.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getCharacteristics/service-is-removed.html [ Skip ]
crbug.com/609064 [ Mac ] bluetooth/getPrimaryServices/correct-services.html [ Skip ]
crbug.com/609068 [ Mac ] bluetooth/notifications/add-listener-after-promise.html [ Skip ] crbug.com/609068 [ Mac ] bluetooth/notifications/add-listener-after-promise.html [ Skip ]
crbug.com/609068 [ Mac ] bluetooth/notifications/add-multiple-event-listeners.html [ Skip ] crbug.com/609068 [ Mac ] bluetooth/notifications/add-multiple-event-listeners.html [ Skip ]
crbug.com/609068 [ Mac ] bluetooth/notifications/characteristic-does-not-support-notifications.html [ Skip ] crbug.com/609068 [ Mac ] bluetooth/notifications/characteristic-does-not-support-notifications.html [ Skip ]
......
...@@ -73,13 +73,6 @@ private: ...@@ -73,13 +73,6 @@ private:
ScriptPromise BluetoothRemoteGATTService::getCharacteristic(ScriptState* scriptState, const StringOrUnsignedLong& characteristic, ExceptionState& exceptionState) ScriptPromise BluetoothRemoteGATTService::getCharacteristic(ScriptState* scriptState, const StringOrUnsignedLong& characteristic, ExceptionState& exceptionState)
{ {
#if OS(MACOSX)
// TODO(jlebel): Remove when getCharacteristic is implemented.
return ScriptPromise::rejectWithDOMException(scriptState,
DOMException::create(NotSupportedError,
"getCharacteristic is not implemented yet. See https://goo.gl/J6ASzs"));
#endif // OS(MACOSX)
String characteristicUUID = BluetoothUUID::getCharacteristic(characteristic, exceptionState); String characteristicUUID = BluetoothUUID::getCharacteristic(characteristic, exceptionState);
if (exceptionState.hadException()) if (exceptionState.hadException())
return exceptionState.reject(scriptState); return exceptionState.reject(scriptState);
...@@ -103,13 +96,6 @@ ScriptPromise BluetoothRemoteGATTService::getCharacteristics(ScriptState* script ...@@ -103,13 +96,6 @@ ScriptPromise BluetoothRemoteGATTService::getCharacteristics(ScriptState* script
ScriptPromise BluetoothRemoteGATTService::getCharacteristicsImpl(ScriptState* scriptState, mojom::WebBluetoothGATTQueryQuantity quantity, String characteristicsUUID) ScriptPromise BluetoothRemoteGATTService::getCharacteristicsImpl(ScriptState* scriptState, mojom::WebBluetoothGATTQueryQuantity quantity, String characteristicsUUID)
{ {
#if OS(MACOSX)
// TODO(jlebel): Remove when getCharacteristics is implemented.
return ScriptPromise::rejectWithDOMException(scriptState,
DOMException::create(NotSupportedError,
"getCharacteristics is not implemented yet. See https://goo.gl/J6ASzs"));
#endif // OS(MACOSX)
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = resolver->promise(); ScriptPromise promise = resolver->promise();
......
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