diff options
3 files changed, 239 insertions, 25 deletions
diff --git a/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc index 63f6b49..ec04474 100644 --- a/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc +++ b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc @@ -21,6 +21,15 @@ const int kHeartRateMeasurementNotificationIntervalMs = 2000; } // namespace +FakeBluetoothGattCharacteristicClient::DelayedCallback::DelayedCallback( + base::Closure callback, + size_t delay) + : callback_(callback), delay_(delay) { +} + +FakeBluetoothGattCharacteristicClient::DelayedCallback::~DelayedCallback() { +} + // static const char FakeBluetoothGattCharacteristicClient:: kHeartRateMeasurementPathComponent[] = "char0000"; @@ -68,12 +77,19 @@ void FakeBluetoothGattCharacteristicClient::Properties::Set( FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient() : heart_rate_visible_(false), + authorized_(true), + authenticated_(true), calories_burned_(0), + extra_requests_(0), weak_ptr_factory_(this) { } FakeBluetoothGattCharacteristicClient:: ~FakeBluetoothGattCharacteristicClient() { + for (const auto& it : action_extra_requests_) { + delete it.second; + } + action_extra_requests_.clear(); } void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) { @@ -120,26 +136,62 @@ void FakeBluetoothGattCharacteristicClient::ReadValue( const dbus::ObjectPath& object_path, const ValueCallback& callback, const ErrorCallback& error_callback) { - if (!IsHeartRateVisible()) { - error_callback.Run(kUnknownCharacteristicError, ""); + if (!authenticated_) { + error_callback.Run("org.bluez.Error.NotPaired", "Please login"); return; } - if (object_path.value() == heart_rate_measurement_path_ || - object_path.value() == heart_rate_control_point_path_) { + if (!authorized_) { + error_callback.Run("org.bluez.Error.NotAuthorized", "Authorize first"); + return; + } + + if (object_path.value() == heart_rate_control_point_path_) { error_callback.Run("org.bluez.Error.ReadNotPermitted", "Reads of this value are not allowed"); return; } + if (object_path.value() == heart_rate_measurement_path_) { + error_callback.Run("org.bluez.Error.NotSupported", + "Action not supported on this characteristic"); + return; + } + if (object_path.value() != body_sensor_location_path_) { error_callback.Run(kUnknownCharacteristicError, ""); return; } - std::vector<uint8> value; - value.push_back(0x06); // Location is "foot". - callback.Run(value); + if (action_extra_requests_.find("ReadValue") != + action_extra_requests_.end()) { + DelayedCallback* delayed = action_extra_requests_["ReadValue"]; + delayed->delay_--; + error_callback.Run("org.bluez.Error.InProgress", + "Another read is currenty in progress"); + if (delayed->delay_ == 0) { + delayed->callback_.Run(); + action_extra_requests_.erase("ReadValue"); + delete delayed; + } + return; + } + base::Closure completed_callback; + if (!IsHeartRateVisible()) { + completed_callback = + base::Bind(error_callback, kUnknownCharacteristicError, ""); + } else { + std::vector<uint8> value; + value.push_back(0x06); // Location is "foot". + completed_callback = base::Bind(callback, value); + } + + if (extra_requests_ > 0) { + action_extra_requests_["ReadValue"] = + new DelayedCallback(completed_callback, extra_requests_); + return; + } + completed_callback.Run(); } void FakeBluetoothGattCharacteristicClient::WriteValue( @@ -147,11 +199,27 @@ void FakeBluetoothGattCharacteristicClient::WriteValue( const std::vector<uint8>& value, const base::Closure& callback, const ErrorCallback& error_callback) { + if (!authenticated_) { + error_callback.Run("org.bluez.Error.NotPaired", "Please login"); + return; + } + + if (!authorized_) { + error_callback.Run("org.bluez.Error.NotAuthorized", "Authorize first"); + return; + } + if (!IsHeartRateVisible()) { error_callback.Run(kUnknownCharacteristicError, ""); return; } + if (object_path.value() == heart_rate_measurement_path_) { + error_callback.Run("org.bluez.Error.NotSupported", + "Action not supported on this characteristic"); + return; + } + if (object_path.value() != heart_rate_control_point_path_) { error_callback.Run("org.bluez.Error.WriteNotPermitted", "Writes of this value are not allowed"); @@ -159,21 +227,40 @@ void FakeBluetoothGattCharacteristicClient::WriteValue( } DCHECK(heart_rate_control_point_properties_.get()); - if (value.size() != 1) { - error_callback.Run("org.bluez.Error.InvalidValueLength", - "Invalid length for write"); - return; - } - if (value[0] > 1) { - error_callback.Run("org.bluez.Error.Failed", - "Invalid value given for write"); + if (action_extra_requests_.find("WriteValue") != + action_extra_requests_.end()) { + DelayedCallback* delayed = action_extra_requests_["WriteValue"]; + delayed->delay_--; + error_callback.Run("org.bluez.Error.InProgress", + "Another write is in progress"); + if (delayed->delay_ == 0) { + delayed->callback_.Run(); + action_extra_requests_.erase("WriteValue"); + delete delayed; + } return; } - - if (value[0] == 1) + base::Closure completed_callback; + if (value.size() != 1) { + completed_callback = base::Bind(error_callback, + "org.bluez.Error.InvalidValueLength", + "Invalid length for write"); + } else if (value[0] > 1) { + completed_callback = base::Bind(error_callback, + "org.bluez.Error.Failed", + "Invalid value given for write"); + } else if (value[0] == 1) { + // TODO(jamuraa): make this happen when the callback happens calories_burned_ = 0; + completed_callback = callback; + } - callback.Run(); + if (extra_requests_ > 0) { + action_extra_requests_["WriteValue"] = + new DelayedCallback(completed_callback, extra_requests_); + return; + } + completed_callback.Run(); } void FakeBluetoothGattCharacteristicClient::StartNotify( @@ -332,6 +419,24 @@ void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() { heart_rate_visible_ = false; } +void FakeBluetoothGattCharacteristicClient::SetExtraProcessing( + size_t requests) { + extra_requests_ = requests; + if (extra_requests_ == 0) { + for (const auto& it : action_extra_requests_) { + it.second->callback_.Run(); + delete it.second; + } + action_extra_requests_.clear(); + return; + } + VLOG(2) << "Requests SLOW now, " << requests << " InProgress errors each."; +} + +size_t FakeBluetoothGattCharacteristicClient::GetExtraProcessing() const { + return extra_requests_; +} + dbus::ObjectPath FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const { return dbus::ObjectPath(heart_rate_measurement_path_); diff --git a/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h index 12be69d..fc17614 100644 --- a/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h +++ b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h @@ -73,6 +73,29 @@ class CHROMEOS_EXPORT FakeBluetoothGattCharacteristicClient // performs the appropriate assertions. bool IsHeartRateVisible() const; + // Makes this characteristic client really slow. + // So slow, that it is guaranteed that |requests| requests will + // come in while the client is doing the previous request. + // Setting |requests| to zero will cause all delayed actions to + // complete immediately. + void SetExtraProcessing(size_t requests); + + size_t GetExtraProcessing() const; + + // Sets whether the client is authorized or not. + // Defaults to authorized. + void SetAuthorized(bool authorized) { authorized_ = authorized; } + + // Get the current Authorization state. + bool IsAuthorized() const { return authorized_; } + + // Whether the client is Authenticated + // Defaults to authenticated. + void SetAuthenticated(bool authenticated) { authenticated_ = authenticated; } + + // Get the current Authenticated state. + bool IsAuthenticated() const { return authenticated_; } + // Returns the current object paths of exposed characteristics. If the // characteristic is not visible, returns an invalid empty path. dbus::ObjectPath GetHeartRateMeasurementPath() const; @@ -110,6 +133,12 @@ class CHROMEOS_EXPORT FakeBluetoothGattCharacteristicClient // IsHeartRateVisible() to check the value. bool heart_rate_visible_; + // If true, the client is authorized to read and write. + bool authorized_; + + // If true, the client is authenticated. + bool authenticated_; + // Total calories burned, used for the Heart Rate Measurement characteristic. uint16 calories_burned_; @@ -127,6 +156,22 @@ class CHROMEOS_EXPORT FakeBluetoothGattCharacteristicClient std::string body_sensor_location_path_; std::string heart_rate_control_point_path_; + // Number of extra requests that need to come in simulating slowness. + size_t extra_requests_; + + // Current countdowns for extra requests for various actions. + struct DelayedCallback { + public: + DelayedCallback(base::Closure callback, size_t delay); + ~DelayedCallback(); + + base::Closure callback_; + size_t delay_; + }; + + // Map of delayed callbacks. + std::map<std::string, DelayedCallback*> action_extra_requests_; + // List of observers interested in event notifications from us. ObserverList<Observer> observers_; diff --git a/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc b/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc index 2947f16..60cd54c 100644 --- a/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc +++ b/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc @@ -398,7 +398,7 @@ class BluetoothGattChromeOSTest : public testing::Test { int success_callback_count_; int error_callback_count_; std::vector<uint8> last_read_value_; - enum BluetoothGattService::GattErrorCode last_service_error_; + BluetoothGattService::GattErrorCode last_service_error_; }; TEST_F(BluetoothGattChromeOSTest, GattConnection) { @@ -852,7 +852,7 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicValue) { EXPECT_FALSE(observer.last_gatt_characteristic_uuid_.IsValid()); EXPECT_EQ(0, success_callback_count_); EXPECT_EQ(1, error_callback_count_); - EXPECT_EQ(BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_PERMITTED, + EXPECT_EQ(BluetoothGattService::GATT_ERROR_NOT_SUPPORTED, last_service_error_); EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count_); @@ -874,7 +874,7 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicValue) { EXPECT_FALSE(observer.last_gatt_characteristic_uuid_.IsValid()); EXPECT_EQ(0, success_callback_count_); EXPECT_EQ(2, error_callback_count_); - EXPECT_EQ(BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_PERMITTED, + EXPECT_EQ(BluetoothGattService::GATT_ERROR_NOT_PERMITTED, last_service_error_); EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count_); @@ -916,7 +916,7 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicValue) { base::Unretained(this))); EXPECT_EQ(1, success_callback_count_); EXPECT_EQ(3, error_callback_count_); - EXPECT_EQ(BluetoothGattService::GattErrorCode::GATT_ERROR_INVALID_LENGTH, + EXPECT_EQ(BluetoothGattService::GATT_ERROR_INVALID_LENGTH, last_service_error_); EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count_); @@ -930,8 +930,7 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicValue) { base::Unretained(this))); EXPECT_EQ(1, success_callback_count_); EXPECT_EQ(4, error_callback_count_); - EXPECT_EQ(BluetoothGattService::GattErrorCode::GATT_ERROR_FAILED, - last_service_error_); + EXPECT_EQ(BluetoothGattService::GATT_ERROR_FAILED, last_service_error_); EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count_); // Issue a read request. A successful read results in a @@ -953,6 +952,71 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicValue) { EXPECT_EQ(4, error_callback_count_); EXPECT_EQ(1, observer.gatt_characteristic_value_changed_count_); EXPECT_TRUE(ValuesEqual(characteristic->GetValue(), last_read_value_)); + + // Test long-running actions. + fake_bluetooth_gatt_characteristic_client_->SetExtraProcessing(1); + characteristic = service->GetCharacteristic( + fake_bluetooth_gatt_characteristic_client_->GetBodySensorLocationPath() + .value()); + ASSERT_TRUE(characteristic); + EXPECT_EQ( + fake_bluetooth_gatt_characteristic_client_->GetBodySensorLocationPath() + .value(), + characteristic->GetIdentifier()); + EXPECT_EQ(kBodySensorLocationUUID, characteristic->GetUUID()); + characteristic->ReadRemoteCharacteristic( + base::Bind(&BluetoothGattChromeOSTest::ValueCallback, + base::Unretained(this)), + base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback, + base::Unretained(this))); + + // Callback counts shouldn't change, this one will be delayed until after + // tne next one. + EXPECT_EQ(2, success_callback_count_); + EXPECT_EQ(4, error_callback_count_); + EXPECT_EQ(1, observer.gatt_characteristic_value_changed_count_); + + // Next read should error because IN_PROGRESS + characteristic->ReadRemoteCharacteristic( + base::Bind(&BluetoothGattChromeOSTest::ValueCallback, + base::Unretained(this)), + base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback, + base::Unretained(this))); + EXPECT_EQ(5, error_callback_count_); + EXPECT_EQ(BluetoothGattService::GATT_ERROR_IN_PROGRESS, last_service_error_); + + // But previous call finished. + EXPECT_EQ(3, success_callback_count_); + EXPECT_EQ(2, observer.gatt_characteristic_value_changed_count_); + EXPECT_TRUE(ValuesEqual(characteristic->GetValue(), last_read_value_)); + fake_bluetooth_gatt_characteristic_client_->SetExtraProcessing(0); + + // Test unauthorized actions. + fake_bluetooth_gatt_characteristic_client_->SetAuthorized(false); + characteristic->ReadRemoteCharacteristic( + base::Bind(&BluetoothGattChromeOSTest::ValueCallback, + base::Unretained(this)), + base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback, + base::Unretained(this))); + EXPECT_EQ(3, success_callback_count_); + EXPECT_EQ(6, error_callback_count_); + EXPECT_EQ(BluetoothGattService::GATT_ERROR_NOT_AUTHORIZED, + last_service_error_); + EXPECT_EQ(2, observer.gatt_characteristic_value_changed_count_); + fake_bluetooth_gatt_characteristic_client_->SetAuthorized(true); + + // Test unauthenticated / needs login. + fake_bluetooth_gatt_characteristic_client_->SetAuthenticated(false); + characteristic->ReadRemoteCharacteristic( + base::Bind(&BluetoothGattChromeOSTest::ValueCallback, + base::Unretained(this)), + base::Bind(&BluetoothGattChromeOSTest::ServiceErrorCallback, + base::Unretained(this))); + EXPECT_EQ(3, success_callback_count_); + EXPECT_EQ(7, error_callback_count_); + EXPECT_EQ(BluetoothGattService::GATT_ERROR_NOT_PAIRED, last_service_error_); + EXPECT_EQ(2, observer.gatt_characteristic_value_changed_count_); + fake_bluetooth_gatt_characteristic_client_->SetAuthenticated(true); } TEST_F(BluetoothGattChromeOSTest, GattCharacteristicProperties) { @@ -1074,7 +1138,7 @@ TEST_F(BluetoothGattChromeOSTest, GattDescriptorValue) { base::Unretained(this))); EXPECT_EQ(1, success_callback_count_); EXPECT_EQ(1, error_callback_count_); - EXPECT_EQ(BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_PERMITTED, + EXPECT_EQ(BluetoothGattService::GATT_ERROR_NOT_PERMITTED, last_service_error_); EXPECT_TRUE(ValuesEqual(last_read_value_, descriptor->GetValue())); EXPECT_FALSE(ValuesEqual(desc_value, descriptor->GetValue())); |