OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/location.h" | |
9 #include "base/rand_util.h" | |
10 #include "base/single_thread_task_runner.h" | |
11 #include "base/thread_task_runner_handle.h" | |
12 #include "base/time/time.h" | |
13 #include "chromeos/dbus/dbus_thread_manager.h" | |
14 #include "chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h" | |
15 #include "third_party/cros_system_api/dbus/service_constants.h" | |
16 | |
17 namespace chromeos { | |
18 | |
19 namespace { | |
20 | |
21 const int kStartNotifyResponseIntervalMs = 200; | |
22 const int kHeartRateMeasurementNotificationIntervalMs = 2000; | |
23 | |
24 } // namespace | |
25 | |
26 FakeBluetoothGattCharacteristicClient::DelayedCallback::DelayedCallback( | |
27 base::Closure callback, | |
28 size_t delay) | |
29 : callback_(callback), delay_(delay) { | |
30 } | |
31 | |
32 FakeBluetoothGattCharacteristicClient::DelayedCallback::~DelayedCallback() { | |
33 } | |
34 | |
35 // static | |
36 const char FakeBluetoothGattCharacteristicClient:: | |
37 kHeartRateMeasurementPathComponent[] = "char0000"; | |
38 const char FakeBluetoothGattCharacteristicClient:: | |
39 kBodySensorLocationPathComponent[] = "char0001"; | |
40 const char FakeBluetoothGattCharacteristicClient:: | |
41 kHeartRateControlPointPathComponent[] = "char0002"; | |
42 | |
43 // static | |
44 const char FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID[] = | |
45 "00002a37-0000-1000-8000-00805f9b34fb"; | |
46 const char FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID[] = | |
47 "00002a38-0000-1000-8000-00805f9b34fb"; | |
48 const char FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID[] = | |
49 "00002a39-0000-1000-8000-00805f9b34fb"; | |
50 | |
51 FakeBluetoothGattCharacteristicClient::Properties::Properties( | |
52 const PropertyChangedCallback& callback) | |
53 : BluetoothGattCharacteristicClient::Properties( | |
54 NULL, | |
55 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface, | |
56 callback) { | |
57 } | |
58 | |
59 FakeBluetoothGattCharacteristicClient::Properties::~Properties() { | |
60 } | |
61 | |
62 void FakeBluetoothGattCharacteristicClient::Properties::Get( | |
63 dbus::PropertyBase* property, | |
64 dbus::PropertySet::GetCallback callback) { | |
65 VLOG(1) << "Get " << property->name(); | |
66 callback.Run(true); | |
67 } | |
68 | |
69 void FakeBluetoothGattCharacteristicClient::Properties::GetAll() { | |
70 VLOG(1) << "GetAll"; | |
71 } | |
72 | |
73 void FakeBluetoothGattCharacteristicClient::Properties::Set( | |
74 dbus::PropertyBase* property, | |
75 dbus::PropertySet::SetCallback callback) { | |
76 VLOG(1) << "Set " << property->name(); | |
77 callback.Run(false); | |
78 } | |
79 | |
80 FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient() | |
81 : heart_rate_visible_(false), | |
82 authorized_(true), | |
83 authenticated_(true), | |
84 calories_burned_(0), | |
85 extra_requests_(0), | |
86 weak_ptr_factory_(this) { | |
87 } | |
88 | |
89 FakeBluetoothGattCharacteristicClient:: | |
90 ~FakeBluetoothGattCharacteristicClient() { | |
91 for (const auto& it : action_extra_requests_) { | |
92 delete it.second; | |
93 } | |
94 action_extra_requests_.clear(); | |
95 } | |
96 | |
97 void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) { | |
98 } | |
99 | |
100 void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) { | |
101 observers_.AddObserver(observer); | |
102 } | |
103 | |
104 void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) { | |
105 observers_.RemoveObserver(observer); | |
106 } | |
107 | |
108 std::vector<dbus::ObjectPath> | |
109 FakeBluetoothGattCharacteristicClient::GetCharacteristics() { | |
110 std::vector<dbus::ObjectPath> paths; | |
111 if (IsHeartRateVisible()) { | |
112 paths.push_back(dbus::ObjectPath(heart_rate_measurement_path_)); | |
113 paths.push_back(dbus::ObjectPath(body_sensor_location_path_)); | |
114 paths.push_back(dbus::ObjectPath(heart_rate_control_point_path_)); | |
115 } | |
116 return paths; | |
117 } | |
118 | |
119 FakeBluetoothGattCharacteristicClient::Properties* | |
120 FakeBluetoothGattCharacteristicClient::GetProperties( | |
121 const dbus::ObjectPath& object_path) { | |
122 if (object_path.value() == heart_rate_measurement_path_) { | |
123 DCHECK(heart_rate_measurement_properties_.get()); | |
124 return heart_rate_measurement_properties_.get(); | |
125 } | |
126 if (object_path.value() == body_sensor_location_path_) { | |
127 DCHECK(body_sensor_location_properties_.get()); | |
128 return body_sensor_location_properties_.get(); | |
129 } | |
130 if (object_path.value() == heart_rate_control_point_path_) { | |
131 DCHECK(heart_rate_control_point_properties_.get()); | |
132 return heart_rate_control_point_properties_.get(); | |
133 } | |
134 return NULL; | |
135 } | |
136 | |
137 void FakeBluetoothGattCharacteristicClient::ReadValue( | |
138 const dbus::ObjectPath& object_path, | |
139 const ValueCallback& callback, | |
140 const ErrorCallback& error_callback) { | |
141 if (!authenticated_) { | |
142 error_callback.Run("org.bluez.Error.NotPaired", "Please login"); | |
143 return; | |
144 } | |
145 | |
146 if (!authorized_) { | |
147 error_callback.Run("org.bluez.Error.NotAuthorized", "Authorize first"); | |
148 return; | |
149 } | |
150 | |
151 if (object_path.value() == heart_rate_control_point_path_) { | |
152 error_callback.Run("org.bluez.Error.NotPermitted", | |
153 "Reads of this value are not allowed"); | |
154 return; | |
155 } | |
156 | |
157 if (object_path.value() == heart_rate_measurement_path_) { | |
158 error_callback.Run("org.bluez.Error.NotSupported", | |
159 "Action not supported on this characteristic"); | |
160 return; | |
161 } | |
162 | |
163 if (object_path.value() != body_sensor_location_path_) { | |
164 error_callback.Run(kUnknownCharacteristicError, ""); | |
165 return; | |
166 } | |
167 | |
168 if (action_extra_requests_.find("ReadValue") != | |
169 action_extra_requests_.end()) { | |
170 DelayedCallback* delayed = action_extra_requests_["ReadValue"]; | |
171 delayed->delay_--; | |
172 error_callback.Run("org.bluez.Error.InProgress", | |
173 "Another read is currenty in progress"); | |
174 if (delayed->delay_ == 0) { | |
175 delayed->callback_.Run(); | |
176 action_extra_requests_.erase("ReadValue"); | |
177 delete delayed; | |
178 } | |
179 return; | |
180 } | |
181 | |
182 base::Closure completed_callback; | |
183 if (!IsHeartRateVisible()) { | |
184 completed_callback = | |
185 base::Bind(error_callback, kUnknownCharacteristicError, ""); | |
186 } else { | |
187 std::vector<uint8> value = {0x06}; // Location is "foot". | |
188 completed_callback = base::Bind( | |
189 &FakeBluetoothGattCharacteristicClient::DelayedReadValueCallback, | |
190 weak_ptr_factory_.GetWeakPtr(), object_path, callback, value); | |
191 } | |
192 | |
193 if (extra_requests_ > 0) { | |
194 action_extra_requests_["ReadValue"] = | |
195 new DelayedCallback(completed_callback, extra_requests_); | |
196 return; | |
197 } | |
198 | |
199 completed_callback.Run(); | |
200 } | |
201 | |
202 void FakeBluetoothGattCharacteristicClient::WriteValue( | |
203 const dbus::ObjectPath& object_path, | |
204 const std::vector<uint8>& value, | |
205 const base::Closure& callback, | |
206 const ErrorCallback& error_callback) { | |
207 if (!authenticated_) { | |
208 error_callback.Run("org.bluez.Error.NotPaired", "Please login"); | |
209 return; | |
210 } | |
211 | |
212 if (!authorized_) { | |
213 error_callback.Run("org.bluez.Error.NotAuthorized", "Authorize first"); | |
214 return; | |
215 } | |
216 | |
217 if (!IsHeartRateVisible()) { | |
218 error_callback.Run(kUnknownCharacteristicError, ""); | |
219 return; | |
220 } | |
221 | |
222 if (object_path.value() == heart_rate_measurement_path_) { | |
223 error_callback.Run("org.bluez.Error.NotSupported", | |
224 "Action not supported on this characteristic"); | |
225 return; | |
226 } | |
227 | |
228 if (object_path.value() != heart_rate_control_point_path_) { | |
229 error_callback.Run("org.bluez.Error.NotPermitted", | |
230 "Writes of this value are not allowed"); | |
231 return; | |
232 } | |
233 | |
234 DCHECK(heart_rate_control_point_properties_.get()); | |
235 if (action_extra_requests_.find("WriteValue") != | |
236 action_extra_requests_.end()) { | |
237 DelayedCallback* delayed = action_extra_requests_["WriteValue"]; | |
238 delayed->delay_--; | |
239 error_callback.Run("org.bluez.Error.InProgress", | |
240 "Another write is in progress"); | |
241 if (delayed->delay_ == 0) { | |
242 delayed->callback_.Run(); | |
243 action_extra_requests_.erase("WriteValue"); | |
244 delete delayed; | |
245 } | |
246 return; | |
247 } | |
248 base::Closure completed_callback; | |
249 if (value.size() != 1) { | |
250 completed_callback = base::Bind(error_callback, | |
251 "org.bluez.Error.InvalidValueLength", | |
252 "Invalid length for write"); | |
253 } else if (value[0] > 1) { | |
254 completed_callback = base::Bind(error_callback, | |
255 "org.bluez.Error.Failed", | |
256 "Invalid value given for write"); | |
257 } else if (value[0] == 1) { | |
258 // TODO(jamuraa): make this happen when the callback happens | |
259 calories_burned_ = 0; | |
260 completed_callback = callback; | |
261 } | |
262 | |
263 if (extra_requests_ > 0) { | |
264 action_extra_requests_["WriteValue"] = | |
265 new DelayedCallback(completed_callback, extra_requests_); | |
266 return; | |
267 } | |
268 completed_callback.Run(); | |
269 } | |
270 | |
271 void FakeBluetoothGattCharacteristicClient::StartNotify( | |
272 const dbus::ObjectPath& object_path, | |
273 const base::Closure& callback, | |
274 const ErrorCallback& error_callback) { | |
275 if (!IsHeartRateVisible()) { | |
276 error_callback.Run(kUnknownCharacteristicError, ""); | |
277 return; | |
278 } | |
279 | |
280 if (object_path.value() != heart_rate_measurement_path_) { | |
281 error_callback.Run("org.bluez.Error.NotSupported", | |
282 "This characteristic does not support notifications"); | |
283 return; | |
284 } | |
285 | |
286 if (heart_rate_measurement_properties_->notifying.value()) { | |
287 error_callback.Run("org.bluez.Error.InProgress", | |
288 "Characteristic already notifying"); | |
289 return; | |
290 } | |
291 | |
292 heart_rate_measurement_properties_->notifying.ReplaceValue(true); | |
293 ScheduleHeartRateMeasurementValueChange(); | |
294 | |
295 // Respond asynchronously. | |
296 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
297 FROM_HERE, callback, | |
298 base::TimeDelta::FromMilliseconds(kStartNotifyResponseIntervalMs)); | |
299 } | |
300 | |
301 void FakeBluetoothGattCharacteristicClient::StopNotify( | |
302 const dbus::ObjectPath& object_path, | |
303 const base::Closure& callback, | |
304 const ErrorCallback& error_callback) { | |
305 if (!IsHeartRateVisible()) { | |
306 error_callback.Run(kUnknownCharacteristicError, ""); | |
307 return; | |
308 } | |
309 | |
310 if (object_path.value() != heart_rate_measurement_path_) { | |
311 error_callback.Run("org.bluez.Error.NotSupported", | |
312 "This characteristic does not support notifications"); | |
313 return; | |
314 } | |
315 | |
316 if (!heart_rate_measurement_properties_->notifying.value()) { | |
317 error_callback.Run("org.bluez.Error.Failed", "Not notifying"); | |
318 return; | |
319 } | |
320 | |
321 heart_rate_measurement_properties_->notifying.ReplaceValue(false); | |
322 | |
323 callback.Run(); | |
324 } | |
325 | |
326 void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics( | |
327 const dbus::ObjectPath& service_path) { | |
328 if (IsHeartRateVisible()) { | |
329 VLOG(2) << "Fake Heart Rate characteristics are already visible."; | |
330 return; | |
331 } | |
332 | |
333 VLOG(2) << "Exposing fake Heart Rate characteristics."; | |
334 | |
335 std::vector<std::string> flags; | |
336 | |
337 // ==== Heart Rate Measurement Characteristic ==== | |
338 heart_rate_measurement_path_ = | |
339 service_path.value() + "/" + kHeartRateMeasurementPathComponent; | |
340 heart_rate_measurement_properties_.reset(new Properties(base::Bind( | |
341 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged, | |
342 weak_ptr_factory_.GetWeakPtr(), | |
343 dbus::ObjectPath(heart_rate_measurement_path_)))); | |
344 heart_rate_measurement_properties_->uuid.ReplaceValue( | |
345 kHeartRateMeasurementUUID); | |
346 heart_rate_measurement_properties_->service.ReplaceValue(service_path); | |
347 flags.push_back(bluetooth_gatt_characteristic::kFlagNotify); | |
348 heart_rate_measurement_properties_->flags.ReplaceValue(flags); | |
349 | |
350 // ==== Body Sensor Location Characteristic ==== | |
351 body_sensor_location_path_ = | |
352 service_path.value() + "/" + kBodySensorLocationPathComponent; | |
353 body_sensor_location_properties_.reset(new Properties(base::Bind( | |
354 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged, | |
355 weak_ptr_factory_.GetWeakPtr(), | |
356 dbus::ObjectPath(body_sensor_location_path_)))); | |
357 body_sensor_location_properties_->uuid.ReplaceValue(kBodySensorLocationUUID); | |
358 body_sensor_location_properties_->service.ReplaceValue(service_path); | |
359 flags.clear(); | |
360 flags.push_back(bluetooth_gatt_characteristic::kFlagRead); | |
361 body_sensor_location_properties_->flags.ReplaceValue(flags); | |
362 | |
363 // ==== Heart Rate Control Point Characteristic ==== | |
364 heart_rate_control_point_path_ = | |
365 service_path.value() + "/" + kHeartRateControlPointPathComponent; | |
366 heart_rate_control_point_properties_.reset(new Properties(base::Bind( | |
367 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged, | |
368 weak_ptr_factory_.GetWeakPtr(), | |
369 dbus::ObjectPath(heart_rate_control_point_path_)))); | |
370 heart_rate_control_point_properties_->uuid.ReplaceValue( | |
371 kHeartRateControlPointUUID); | |
372 heart_rate_control_point_properties_->service.ReplaceValue(service_path); | |
373 flags.clear(); | |
374 flags.push_back(bluetooth_gatt_characteristic::kFlagWrite); | |
375 heart_rate_control_point_properties_->flags.ReplaceValue(flags); | |
376 | |
377 heart_rate_visible_ = true; | |
378 | |
379 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_measurement_path_)); | |
380 NotifyCharacteristicAdded(dbus::ObjectPath(body_sensor_location_path_)); | |
381 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_control_point_path_)); | |
382 | |
383 // Expose CCC descriptor for Heart Rate Measurement characteristic. | |
384 FakeBluetoothGattDescriptorClient* descriptor_client = | |
385 static_cast<FakeBluetoothGattDescriptorClient*>( | |
386 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()); | |
387 dbus::ObjectPath ccc_path(descriptor_client->ExposeDescriptor( | |
388 dbus::ObjectPath(heart_rate_measurement_path_), | |
389 FakeBluetoothGattDescriptorClient:: | |
390 kClientCharacteristicConfigurationUUID)); | |
391 DCHECK(ccc_path.IsValid()); | |
392 heart_rate_measurement_ccc_desc_path_ = ccc_path.value(); | |
393 | |
394 std::vector<dbus::ObjectPath> desc_paths; | |
395 desc_paths.push_back(ccc_path); | |
396 | |
397 heart_rate_measurement_properties_->descriptors.ReplaceValue(desc_paths); | |
398 } | |
399 | |
400 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() { | |
401 VLOG(2) << "Hiding fake Heart Rate characteristics."; | |
402 | |
403 // Hide the descriptors. | |
404 FakeBluetoothGattDescriptorClient* descriptor_client = | |
405 static_cast<FakeBluetoothGattDescriptorClient*>( | |
406 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()); | |
407 descriptor_client->HideDescriptor( | |
408 dbus::ObjectPath(heart_rate_measurement_ccc_desc_path_)); | |
409 | |
410 // Notify the observers before deleting the properties structures so that they | |
411 // can be accessed from the observer method. | |
412 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_measurement_path_)); | |
413 NotifyCharacteristicRemoved(dbus::ObjectPath(body_sensor_location_path_)); | |
414 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_control_point_path_)); | |
415 | |
416 heart_rate_measurement_properties_.reset(); | |
417 body_sensor_location_properties_.reset(); | |
418 heart_rate_control_point_properties_.reset(); | |
419 | |
420 heart_rate_measurement_path_.clear(); | |
421 body_sensor_location_path_.clear(); | |
422 heart_rate_control_point_path_.clear(); | |
423 heart_rate_visible_ = false; | |
424 } | |
425 | |
426 void FakeBluetoothGattCharacteristicClient::SetExtraProcessing( | |
427 size_t requests) { | |
428 extra_requests_ = requests; | |
429 if (extra_requests_ == 0) { | |
430 for (const auto& it : action_extra_requests_) { | |
431 it.second->callback_.Run(); | |
432 delete it.second; | |
433 } | |
434 action_extra_requests_.clear(); | |
435 return; | |
436 } | |
437 VLOG(2) << "Requests SLOW now, " << requests << " InProgress errors each."; | |
438 } | |
439 | |
440 size_t FakeBluetoothGattCharacteristicClient::GetExtraProcessing() const { | |
441 return extra_requests_; | |
442 } | |
443 | |
444 dbus::ObjectPath | |
445 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const { | |
446 return dbus::ObjectPath(heart_rate_measurement_path_); | |
447 } | |
448 | |
449 dbus::ObjectPath | |
450 FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const { | |
451 return dbus::ObjectPath(body_sensor_location_path_); | |
452 } | |
453 | |
454 dbus::ObjectPath | |
455 FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const { | |
456 return dbus::ObjectPath(heart_rate_control_point_path_); | |
457 } | |
458 | |
459 void FakeBluetoothGattCharacteristicClient::OnPropertyChanged( | |
460 const dbus::ObjectPath& object_path, | |
461 const std::string& property_name) { | |
462 VLOG(2) << "Characteristic property changed: " << object_path.value() | |
463 << ": " << property_name; | |
464 | |
465 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, | |
466 GattCharacteristicPropertyChanged( | |
467 object_path, property_name)); | |
468 } | |
469 | |
470 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded( | |
471 const dbus::ObjectPath& object_path) { | |
472 VLOG(2) << "GATT characteristic added: " << object_path.value(); | |
473 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, | |
474 GattCharacteristicAdded(object_path)); | |
475 } | |
476 | |
477 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved( | |
478 const dbus::ObjectPath& object_path) { | |
479 VLOG(2) << "GATT characteristic removed: " << object_path.value(); | |
480 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, | |
481 GattCharacteristicRemoved(object_path)); | |
482 } | |
483 | |
484 void FakeBluetoothGattCharacteristicClient:: | |
485 ScheduleHeartRateMeasurementValueChange() { | |
486 if (!IsHeartRateVisible()) | |
487 return; | |
488 | |
489 // Don't send updates if the characteristic is not notifying. | |
490 if (!heart_rate_measurement_properties_->notifying.value()) | |
491 return; | |
492 | |
493 VLOG(2) << "Updating heart rate value."; | |
494 std::vector<uint8> measurement = GetHeartRateMeasurementValue(); | |
495 heart_rate_measurement_properties_->value.ReplaceValue(measurement); | |
496 | |
497 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
498 FROM_HERE, base::Bind(&FakeBluetoothGattCharacteristicClient:: | |
499 ScheduleHeartRateMeasurementValueChange, | |
500 weak_ptr_factory_.GetWeakPtr()), | |
501 base::TimeDelta::FromMilliseconds( | |
502 kHeartRateMeasurementNotificationIntervalMs)); | |
503 } | |
504 | |
505 void FakeBluetoothGattCharacteristicClient::DelayedReadValueCallback( | |
506 const dbus::ObjectPath& object_path, | |
507 const ValueCallback& callback, | |
508 const std::vector<uint8_t>& value) { | |
509 Properties* properties = GetProperties(object_path); | |
510 DCHECK(properties); | |
511 | |
512 properties->value.ReplaceValue(value); | |
513 callback.Run(value); | |
514 } | |
515 | |
516 std::vector<uint8> | |
517 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() { | |
518 // TODO(armansito): We should make sure to properly pack this struct to ensure | |
519 // correct byte alignment and endianness. It doesn't matter too much right now | |
520 // as this is a fake and GCC on Linux seems to do the right thing. | |
521 struct { | |
522 uint8 flags; | |
523 uint8 bpm; | |
524 uint16 energy_expanded; | |
525 uint16 rr_interval; | |
526 } value; | |
527 | |
528 // Flags in LSB: 0 11 1 1 000 | |
529 // | | | | | | |
530 // 8-bit bpm format -- | | | | | |
531 // Sensor contact supported -- | | | | |
532 // Energy expanded field present -- | | | |
533 // RR-Interval values present ------- | | |
534 // Reserved for future use ------------ | |
535 value.flags = 0x0; | |
536 value.flags |= (0x03 << 1); | |
537 value.flags |= (0x01 << 3); | |
538 value.flags |= (0x01 << 4); | |
539 | |
540 // Pick a value between 117 bpm and 153 bpm for heart rate. | |
541 value.bpm = static_cast<uint8>(base::RandInt(117, 153)); | |
542 | |
543 // Total calories burned in kJoules since the last reset. Increment this by 1 | |
544 // every time. It's fine if it overflows: it becomes 0 when the user resets | |
545 // the heart rate monitor (or pretend that he had a lot of cheeseburgers). | |
546 value.energy_expanded = calories_burned_++; | |
547 | |
548 // Include one RR-Interval value, in seconds. | |
549 value.rr_interval = 60/value.bpm; | |
550 | |
551 // Return the bytes in an array. | |
552 uint8* bytes = reinterpret_cast<uint8*>(&value); | |
553 std::vector<uint8> return_value; | |
554 return_value.assign(bytes, bytes + sizeof(value)); | |
555 return return_value; | |
556 } | |
557 | |
558 bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const { | |
559 DCHECK(heart_rate_visible_ != heart_rate_measurement_path_.empty()); | |
560 DCHECK(heart_rate_visible_ != body_sensor_location_path_.empty()); | |
561 DCHECK(heart_rate_visible_ != heart_rate_control_point_path_.empty()); | |
562 DCHECK(heart_rate_visible_ == !!heart_rate_measurement_properties_.get()); | |
563 DCHECK(heart_rate_visible_ == !!body_sensor_location_properties_.get()); | |
564 DCHECK(heart_rate_visible_ == !!heart_rate_control_point_properties_.get()); | |
565 return heart_rate_visible_; | |
566 } | |
567 | |
568 } // namespace chromeos | |
OLD | NEW |