Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / chromeos / dbus / fake_bluetooth_gatt_characteristic_client.cc
blob3567567271c9dcebd7addb5b2ed61f7e67803c39
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h"
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/rand_util.h"
10 #include "base/time/time.h"
11 #include "chromeos/dbus/dbus_thread_manager.h"
12 #include "chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h"
13 #include "third_party/cros_system_api/dbus/service_constants.h"
15 namespace chromeos {
17 namespace {
19 const int kStartNotifyResponseIntervalMs = 200;
20 const int kHeartRateMeasurementNotificationIntervalMs = 2000;
22 } // namespace
24 // static
25 const char FakeBluetoothGattCharacteristicClient::
26 kHeartRateMeasurementPathComponent[] = "char0000";
27 const char FakeBluetoothGattCharacteristicClient::
28 kBodySensorLocationPathComponent[] = "char0001";
29 const char FakeBluetoothGattCharacteristicClient::
30 kHeartRateControlPointPathComponent[] = "char0002";
32 // static
33 const char FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID[] =
34 "00002a37-0000-1000-8000-00805f9b34fb";
35 const char FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID[] =
36 "00002a38-0000-1000-8000-00805f9b34fb";
37 const char FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID[] =
38 "00002a39-0000-1000-8000-00805f9b34fb";
40 FakeBluetoothGattCharacteristicClient::Properties::Properties(
41 const PropertyChangedCallback& callback)
42 : BluetoothGattCharacteristicClient::Properties(
43 NULL,
44 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
45 callback) {
48 FakeBluetoothGattCharacteristicClient::Properties::~Properties() {
51 void FakeBluetoothGattCharacteristicClient::Properties::Get(
52 dbus::PropertyBase* property,
53 dbus::PropertySet::GetCallback callback) {
54 VLOG(1) << "Get " << property->name();
55 callback.Run(true);
58 void FakeBluetoothGattCharacteristicClient::Properties::GetAll() {
59 VLOG(1) << "GetAll";
62 void FakeBluetoothGattCharacteristicClient::Properties::Set(
63 dbus::PropertyBase* property,
64 dbus::PropertySet::SetCallback callback) {
65 VLOG(1) << "Set " << property->name();
66 callback.Run(false);
69 FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient()
70 : heart_rate_visible_(false),
71 calories_burned_(0),
72 weak_ptr_factory_(this) {
75 FakeBluetoothGattCharacteristicClient::
76 ~FakeBluetoothGattCharacteristicClient() {
79 void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) {
82 void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) {
83 observers_.AddObserver(observer);
86 void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) {
87 observers_.RemoveObserver(observer);
90 std::vector<dbus::ObjectPath>
91 FakeBluetoothGattCharacteristicClient::GetCharacteristics() {
92 std::vector<dbus::ObjectPath> paths;
93 if (IsHeartRateVisible()) {
94 paths.push_back(dbus::ObjectPath(heart_rate_measurement_path_));
95 paths.push_back(dbus::ObjectPath(body_sensor_location_path_));
96 paths.push_back(dbus::ObjectPath(heart_rate_control_point_path_));
98 return paths;
101 FakeBluetoothGattCharacteristicClient::Properties*
102 FakeBluetoothGattCharacteristicClient::GetProperties(
103 const dbus::ObjectPath& object_path) {
104 if (object_path.value() == heart_rate_measurement_path_) {
105 DCHECK(heart_rate_measurement_properties_.get());
106 return heart_rate_measurement_properties_.get();
108 if (object_path.value() == body_sensor_location_path_) {
109 DCHECK(body_sensor_location_properties_.get());
110 return body_sensor_location_properties_.get();
112 if (object_path.value() == heart_rate_control_point_path_) {
113 DCHECK(heart_rate_control_point_properties_.get());
114 return heart_rate_control_point_properties_.get();
116 return NULL;
119 void FakeBluetoothGattCharacteristicClient::ReadValue(
120 const dbus::ObjectPath& object_path,
121 const ValueCallback& callback,
122 const ErrorCallback& error_callback) {
123 if (!IsHeartRateVisible()) {
124 error_callback.Run(kUnknownCharacteristicError, "");
125 return;
128 if (object_path.value() == heart_rate_measurement_path_ ||
129 object_path.value() == heart_rate_control_point_path_) {
130 error_callback.Run("org.bluez.Error.ReadNotPermitted",
131 "Reads of this value are not allowed");
132 return;
135 if (object_path.value() != body_sensor_location_path_) {
136 error_callback.Run(kUnknownCharacteristicError, "");
137 return;
140 std::vector<uint8> value;
141 value.push_back(0x06); // Location is "foot".
142 callback.Run(value);
145 void FakeBluetoothGattCharacteristicClient::WriteValue(
146 const dbus::ObjectPath& object_path,
147 const std::vector<uint8>& value,
148 const base::Closure& callback,
149 const ErrorCallback& error_callback) {
150 if (!IsHeartRateVisible()) {
151 error_callback.Run(kUnknownCharacteristicError, "");
152 return;
155 if (object_path.value() != heart_rate_control_point_path_) {
156 error_callback.Run("org.bluez.Error.WriteNotPermitted",
157 "Writes of this value are not allowed");
158 return;
161 DCHECK(heart_rate_control_point_properties_.get());
162 if (value.size() != 1 || value[0] > 1) {
163 error_callback.Run("org.bluez.Error.Failed",
164 "Invalid value given for write");
165 return;
168 if (value[0] == 1)
169 calories_burned_ = 0;
171 callback.Run();
174 void FakeBluetoothGattCharacteristicClient::StartNotify(
175 const dbus::ObjectPath& object_path,
176 const base::Closure& callback,
177 const ErrorCallback& error_callback) {
178 if (!IsHeartRateVisible()) {
179 error_callback.Run(kUnknownCharacteristicError, "");
180 return;
183 if (object_path.value() != heart_rate_measurement_path_) {
184 error_callback.Run("org.bluez.Error.NotSupported",
185 "This characteristic does not support notifications");
186 return;
189 if (heart_rate_measurement_properties_->notifying.value()) {
190 error_callback.Run("org.bluez.Error.Busy",
191 "Characteristic already notifying");
192 return;
195 heart_rate_measurement_properties_->notifying.ReplaceValue(true);
196 ScheduleHeartRateMeasurementValueChange();
198 // Respond asynchronously.
199 base::MessageLoop::current()->PostDelayedTask(
200 FROM_HERE,
201 callback,
202 base::TimeDelta::FromMilliseconds(kStartNotifyResponseIntervalMs));
205 void FakeBluetoothGattCharacteristicClient::StopNotify(
206 const dbus::ObjectPath& object_path,
207 const base::Closure& callback,
208 const ErrorCallback& error_callback) {
209 if (!IsHeartRateVisible()) {
210 error_callback.Run(kUnknownCharacteristicError, "");
211 return;
214 if (object_path.value() != heart_rate_measurement_path_) {
215 error_callback.Run("org.bluez.Error.NotSupported",
216 "This characteristic does not support notifications");
217 return;
220 if (!heart_rate_measurement_properties_->notifying.value()) {
221 error_callback.Run("org.bluez.Error.Failed", "Not notifying");
222 return;
225 heart_rate_measurement_properties_->notifying.ReplaceValue(false);
227 callback.Run();
230 void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics(
231 const dbus::ObjectPath& service_path) {
232 if (IsHeartRateVisible()) {
233 VLOG(2) << "Fake Heart Rate characteristics are already visible.";
234 return;
237 VLOG(2) << "Exposing fake Heart Rate characteristics.";
239 std::vector<std::string> flags;
241 // ==== Heart Rate Measurement Characteristic ====
242 heart_rate_measurement_path_ =
243 service_path.value() + "/" + kHeartRateMeasurementPathComponent;
244 heart_rate_measurement_properties_.reset(new Properties(base::Bind(
245 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
246 weak_ptr_factory_.GetWeakPtr(),
247 dbus::ObjectPath(heart_rate_measurement_path_))));
248 heart_rate_measurement_properties_->uuid.ReplaceValue(
249 kHeartRateMeasurementUUID);
250 heart_rate_measurement_properties_->service.ReplaceValue(service_path);
251 flags.push_back(bluetooth_gatt_characteristic::kFlagNotify);
252 heart_rate_measurement_properties_->flags.ReplaceValue(flags);
254 // ==== Body Sensor Location Characteristic ====
255 body_sensor_location_path_ =
256 service_path.value() + "/" + kBodySensorLocationPathComponent;
257 body_sensor_location_properties_.reset(new Properties(base::Bind(
258 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
259 weak_ptr_factory_.GetWeakPtr(),
260 dbus::ObjectPath(body_sensor_location_path_))));
261 body_sensor_location_properties_->uuid.ReplaceValue(kBodySensorLocationUUID);
262 body_sensor_location_properties_->service.ReplaceValue(service_path);
263 flags.clear();
264 flags.push_back(bluetooth_gatt_characteristic::kFlagRead);
265 body_sensor_location_properties_->flags.ReplaceValue(flags);
267 // ==== Heart Rate Control Point Characteristic ====
268 heart_rate_control_point_path_ =
269 service_path.value() + "/" + kHeartRateControlPointPathComponent;
270 heart_rate_control_point_properties_.reset(new Properties(base::Bind(
271 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
272 weak_ptr_factory_.GetWeakPtr(),
273 dbus::ObjectPath(heart_rate_control_point_path_))));
274 heart_rate_control_point_properties_->uuid.ReplaceValue(
275 kHeartRateControlPointUUID);
276 heart_rate_control_point_properties_->service.ReplaceValue(service_path);
277 flags.clear();
278 flags.push_back(bluetooth_gatt_characteristic::kFlagWrite);
279 heart_rate_control_point_properties_->flags.ReplaceValue(flags);
281 heart_rate_visible_ = true;
283 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_measurement_path_));
284 NotifyCharacteristicAdded(dbus::ObjectPath(body_sensor_location_path_));
285 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_control_point_path_));
287 // Expose CCC descriptor for Heart Rate Measurement characteristic.
288 FakeBluetoothGattDescriptorClient* descriptor_client =
289 static_cast<FakeBluetoothGattDescriptorClient*>(
290 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
291 dbus::ObjectPath ccc_path(descriptor_client->ExposeDescriptor(
292 dbus::ObjectPath(heart_rate_measurement_path_),
293 FakeBluetoothGattDescriptorClient::
294 kClientCharacteristicConfigurationUUID));
295 DCHECK(ccc_path.IsValid());
296 heart_rate_measurement_ccc_desc_path_ = ccc_path.value();
298 std::vector<dbus::ObjectPath> desc_paths;
299 desc_paths.push_back(ccc_path);
301 heart_rate_measurement_properties_->descriptors.ReplaceValue(desc_paths);
304 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() {
305 VLOG(2) << "Hiding fake Heart Rate characteristics.";
307 // Hide the descriptors.
308 FakeBluetoothGattDescriptorClient* descriptor_client =
309 static_cast<FakeBluetoothGattDescriptorClient*>(
310 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
311 descriptor_client->HideDescriptor(
312 dbus::ObjectPath(heart_rate_measurement_ccc_desc_path_));
314 // Notify the observers before deleting the properties structures so that they
315 // can be accessed from the observer method.
316 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_measurement_path_));
317 NotifyCharacteristicRemoved(dbus::ObjectPath(body_sensor_location_path_));
318 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_control_point_path_));
320 heart_rate_measurement_properties_.reset();
321 body_sensor_location_properties_.reset();
322 heart_rate_control_point_properties_.reset();
324 heart_rate_measurement_path_.clear();
325 body_sensor_location_path_.clear();
326 heart_rate_control_point_path_.clear();
327 heart_rate_visible_ = false;
330 dbus::ObjectPath
331 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const {
332 return dbus::ObjectPath(heart_rate_measurement_path_);
335 dbus::ObjectPath
336 FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const {
337 return dbus::ObjectPath(body_sensor_location_path_);
340 dbus::ObjectPath
341 FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const {
342 return dbus::ObjectPath(heart_rate_control_point_path_);
345 void FakeBluetoothGattCharacteristicClient::OnPropertyChanged(
346 const dbus::ObjectPath& object_path,
347 const std::string& property_name) {
348 VLOG(2) << "Characteristic property changed: " << object_path.value()
349 << ": " << property_name;
351 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
352 GattCharacteristicPropertyChanged(
353 object_path, property_name));
356 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded(
357 const dbus::ObjectPath& object_path) {
358 VLOG(2) << "GATT characteristic added: " << object_path.value();
359 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
360 GattCharacteristicAdded(object_path));
363 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved(
364 const dbus::ObjectPath& object_path) {
365 VLOG(2) << "GATT characteristic removed: " << object_path.value();
366 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
367 GattCharacteristicRemoved(object_path));
370 void FakeBluetoothGattCharacteristicClient::
371 ScheduleHeartRateMeasurementValueChange() {
372 if (!IsHeartRateVisible())
373 return;
375 // Don't send updates if the characteristic is not notifying.
376 if (!heart_rate_measurement_properties_->notifying.value())
377 return;
379 VLOG(2) << "Updating heart rate value.";
380 std::vector<uint8> measurement = GetHeartRateMeasurementValue();
382 FOR_EACH_OBSERVER(
383 BluetoothGattCharacteristicClient::Observer,
384 observers_,
385 GattCharacteristicValueUpdated(
386 dbus::ObjectPath(heart_rate_measurement_path_), measurement));
388 base::MessageLoop::current()->PostDelayedTask(
389 FROM_HERE,
390 base::Bind(&FakeBluetoothGattCharacteristicClient::
391 ScheduleHeartRateMeasurementValueChange,
392 weak_ptr_factory_.GetWeakPtr()),
393 base::TimeDelta::FromMilliseconds(
394 kHeartRateMeasurementNotificationIntervalMs));
397 std::vector<uint8>
398 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() {
399 // TODO(armansito): We should make sure to properly pack this struct to ensure
400 // correct byte alignment and endianness. It doesn't matter too much right now
401 // as this is a fake and GCC on Linux seems to do the right thing.
402 struct {
403 uint8 flags;
404 uint8 bpm;
405 uint16 energy_expanded;
406 uint16 rr_interval;
407 } value;
409 // Flags in LSB: 0 11 1 1 000
410 // | | | | |
411 // 8-bit bpm format -- | | | |
412 // Sensor contact supported -- | | |
413 // Energy expanded field present -- | |
414 // RR-Interval values present ------- |
415 // Reserved for future use ------------
416 value.flags = 0x0;
417 value.flags |= (0x03 << 1);
418 value.flags |= (0x01 << 3);
419 value.flags |= (0x01 << 4);
421 // Pick a value between 117 bpm and 153 bpm for heart rate.
422 value.bpm = static_cast<uint8>(base::RandInt(117, 153));
424 // Total calories burned in kJoules since the last reset. Increment this by 1
425 // every time. It's fine if it overflows: it becomes 0 when the user resets
426 // the heart rate monitor (or pretend that he had a lot of cheeseburgers).
427 value.energy_expanded = calories_burned_++;
429 // Include one RR-Interval value, in seconds.
430 value.rr_interval = 60/value.bpm;
432 // Return the bytes in an array.
433 uint8* bytes = reinterpret_cast<uint8*>(&value);
434 std::vector<uint8> return_value;
435 return_value.assign(bytes, bytes + sizeof(value));
436 return return_value;
439 bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const {
440 DCHECK(heart_rate_visible_ != heart_rate_measurement_path_.empty());
441 DCHECK(heart_rate_visible_ != body_sensor_location_path_.empty());
442 DCHECK(heart_rate_visible_ != heart_rate_control_point_path_.empty());
443 DCHECK(heart_rate_visible_ == !!heart_rate_measurement_properties_.get());
444 DCHECK(heart_rate_visible_ == !!body_sensor_location_properties_.get());
445 DCHECK(heart_rate_visible_ == !!heart_rate_control_point_properties_.get());
446 return heart_rate_visible_;
449 } // namespace chromeos