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"
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"
19 const int kHeartRateMeasurementNotificationIntervalMs = 2000;
24 const char FakeBluetoothGattCharacteristicClient::
25 kHeartRateMeasurementPathComponent[] = "char0000";
26 const char FakeBluetoothGattCharacteristicClient::
27 kBodySensorLocationPathComponent[] = "char0001";
28 const char FakeBluetoothGattCharacteristicClient::
29 kHeartRateControlPointPathComponent[] = "char0002";
32 const char FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID[] =
33 "00002a37-0000-1000-8000-00805f9b34fb";
34 const char FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID[] =
35 "00002a38-0000-1000-8000-00805f9b34fb";
36 const char FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID[] =
37 "00002a39-0000-1000-8000-00805f9b34fb";
39 FakeBluetoothGattCharacteristicClient::Properties::Properties(
40 const PropertyChangedCallback& callback)
41 : BluetoothGattCharacteristicClient::Properties(
43 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
47 FakeBluetoothGattCharacteristicClient::Properties::~Properties() {
50 void FakeBluetoothGattCharacteristicClient::Properties::Get(
51 dbus::PropertyBase* property,
52 dbus::PropertySet::GetCallback callback) {
53 VLOG(1) << "Get " << property->name();
55 // TODO(armansito): Return success or failure here based on characteristic
60 void FakeBluetoothGattCharacteristicClient::Properties::GetAll() {
64 void FakeBluetoothGattCharacteristicClient::Properties::Set(
65 dbus::PropertyBase* property,
66 dbus::PropertySet::SetCallback callback) {
67 VLOG(1) << "Set " << property->name();
68 if (property->name() != value.name()) {
72 // Allow writing to only certain characteristics that are defined with the
74 // TODO(armansito): Actually check against the permissions property instead of
75 // UUID, once that property is fully defined in the API.
76 if (uuid.value() != kHeartRateControlPointUUID) {
81 property->ReplaceValueWithSetValue();
84 FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient()
85 : heart_rate_visible_(false),
87 weak_ptr_factory_(this) {
90 FakeBluetoothGattCharacteristicClient::
91 ~FakeBluetoothGattCharacteristicClient() {
94 void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) {
97 void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) {
98 observers_.AddObserver(observer);
101 void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) {
102 observers_.RemoveObserver(observer);
105 std::vector<dbus::ObjectPath>
106 FakeBluetoothGattCharacteristicClient::GetCharacteristics() {
107 std::vector<dbus::ObjectPath> paths;
108 if (IsHeartRateVisible()) {
109 paths.push_back(dbus::ObjectPath(heart_rate_measurement_path_));
110 paths.push_back(dbus::ObjectPath(body_sensor_location_path_));
111 paths.push_back(dbus::ObjectPath(heart_rate_control_point_path_));
116 FakeBluetoothGattCharacteristicClient::Properties*
117 FakeBluetoothGattCharacteristicClient::GetProperties(
118 const dbus::ObjectPath& object_path) {
119 if (object_path.value() == heart_rate_measurement_path_) {
120 DCHECK(heart_rate_measurement_properties_.get());
121 return heart_rate_measurement_properties_.get();
123 if (object_path.value() == body_sensor_location_path_) {
124 DCHECK(body_sensor_location_properties_.get());
125 return body_sensor_location_properties_.get();
127 if (object_path.value() == heart_rate_control_point_path_) {
128 DCHECK(heart_rate_control_point_properties_.get());
129 return heart_rate_control_point_properties_.get();
134 void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics(
135 const dbus::ObjectPath& service_path) {
136 if (IsHeartRateVisible()) {
137 VLOG(2) << "Fake Heart Rate characteristics are already visible.";
141 VLOG(2) << "Exposing fake Heart Rate characteristics.";
143 // ==== Heart Rate Measurement Characteristic ====
144 heart_rate_measurement_path_ =
145 service_path.value() + "/" + kHeartRateMeasurementPathComponent;
146 heart_rate_measurement_properties_.reset(new Properties(base::Bind(
147 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
148 weak_ptr_factory_.GetWeakPtr(),
149 dbus::ObjectPath(heart_rate_measurement_path_))));
150 heart_rate_measurement_properties_->uuid.ReplaceValue(
151 kHeartRateMeasurementUUID);
152 heart_rate_measurement_properties_->service.ReplaceValue(service_path);
154 // TODO(armansito): Fill out the flags field once bindings for the values have
155 // been added. For now, leave it empty.
157 std::vector<uint8> measurement_value = GetHeartRateMeasurementValue();
158 heart_rate_measurement_properties_->value.ReplaceValue(measurement_value);
160 // ==== Body Sensor Location Characteristic ====
161 body_sensor_location_path_ =
162 service_path.value() + "/" + kBodySensorLocationPathComponent;
163 body_sensor_location_properties_.reset(new Properties(base::Bind(
164 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
165 weak_ptr_factory_.GetWeakPtr(),
166 dbus::ObjectPath(body_sensor_location_path_))));
167 body_sensor_location_properties_->uuid.ReplaceValue(kBodySensorLocationUUID);
168 body_sensor_location_properties_->service.ReplaceValue(service_path);
170 // TODO(armansito): Fill out the flags field once bindings for the values have
171 // been added. For now, leave it empty.
173 // The sensor is in the "Other" location.
174 std::vector<uint8> body_sensor_location_value;
175 body_sensor_location_value.push_back(0);
176 body_sensor_location_properties_->value.ReplaceValue(
177 body_sensor_location_value);
179 // ==== Heart Rate Control Point Characteristic ====
180 heart_rate_control_point_path_ =
181 service_path.value() + "/" + kHeartRateControlPointPathComponent;
182 heart_rate_control_point_properties_.reset(new Properties(base::Bind(
183 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
184 weak_ptr_factory_.GetWeakPtr(),
185 dbus::ObjectPath(heart_rate_control_point_path_))));
186 heart_rate_control_point_properties_->uuid.ReplaceValue(
187 kHeartRateControlPointUUID);
188 heart_rate_control_point_properties_->service.ReplaceValue(service_path);
190 // TODO(armansito): Fill out the flags field once bindings for the values have
191 // been added. For now, leave it empty.
193 // Set the initial value to 0. Whenever this gets set to 1, we will reset the
194 // total calories burned and change the value back to 0.
195 std::vector<uint8> heart_rate_control_point_value;
196 heart_rate_control_point_value.push_back(0);
197 heart_rate_control_point_properties_->value.ReplaceValue(
198 heart_rate_control_point_value);
200 heart_rate_visible_ = true;
202 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_measurement_path_));
203 NotifyCharacteristicAdded(dbus::ObjectPath(body_sensor_location_path_));
204 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_control_point_path_));
206 // Set up notifications for heart rate measurement.
207 // TODO(armansito): Do this based on the value of the "client characteristic
208 // configuration" descriptor. Since it's still unclear how descriptors will
209 // be handled by BlueZ, automatically set up notifications for now.
210 ScheduleHeartRateMeasurementValueChange();
212 // Expose CCC descriptor for Heart Rate Measurement characteristic.
213 FakeBluetoothGattDescriptorClient* descriptor_client =
214 static_cast<FakeBluetoothGattDescriptorClient*>(
215 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
216 dbus::ObjectPath ccc_path(descriptor_client->ExposeDescriptor(
217 dbus::ObjectPath(heart_rate_measurement_path_),
218 FakeBluetoothGattDescriptorClient::
219 kClientCharacteristicConfigurationUUID));
220 DCHECK(ccc_path.IsValid());
221 heart_rate_measurement_ccc_desc_path_ = ccc_path.value();
224 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() {
225 VLOG(2) << "Hiding fake Heart Rate characteristics.";
227 // Hide the descriptors.
228 FakeBluetoothGattDescriptorClient* descriptor_client =
229 static_cast<FakeBluetoothGattDescriptorClient*>(
230 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
231 descriptor_client->HideDescriptor(
232 dbus::ObjectPath(heart_rate_measurement_ccc_desc_path_));
234 // Notify the observers before deleting the properties structures so that they
235 // can be accessed from the observer method.
236 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_measurement_path_));
237 NotifyCharacteristicRemoved(dbus::ObjectPath(body_sensor_location_path_));
238 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_control_point_path_));
240 heart_rate_measurement_properties_.reset();
241 body_sensor_location_properties_.reset();
242 heart_rate_control_point_properties_.reset();
244 heart_rate_measurement_path_.clear();
245 body_sensor_location_path_.clear();
246 heart_rate_control_point_path_.clear();
247 heart_rate_visible_ = false;
251 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const {
252 return dbus::ObjectPath(heart_rate_measurement_path_);
256 FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const {
257 return dbus::ObjectPath(body_sensor_location_path_);
261 FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const {
262 return dbus::ObjectPath(heart_rate_control_point_path_);
265 void FakeBluetoothGattCharacteristicClient::OnPropertyChanged(
266 const dbus::ObjectPath& object_path,
267 const std::string& property_name) {
268 VLOG(2) << "Characteristic property changed: " << object_path.value()
269 << ": " << property_name;
271 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
272 GattCharacteristicPropertyChanged(
273 object_path, property_name));
275 // If the heart rate control point was set, reset the calories burned.
276 if (object_path.value() != heart_rate_control_point_path_)
278 DCHECK(heart_rate_control_point_properties_.get());
279 dbus::Property<std::vector<uint8> >* value_prop =
280 &heart_rate_control_point_properties_->value;
281 if (property_name != value_prop->name())
284 std::vector<uint8> value = value_prop->value();
285 DCHECK(value.size() == 1);
289 DCHECK(value[0] == 1);
290 calories_burned_ = 0;
292 value_prop->ReplaceValue(value);
295 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded(
296 const dbus::ObjectPath& object_path) {
297 VLOG(2) << "GATT characteristic added: " << object_path.value();
298 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
299 GattCharacteristicAdded(object_path));
302 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved(
303 const dbus::ObjectPath& object_path) {
304 VLOG(2) << "GATT characteristic removed: " << object_path.value();
305 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
306 GattCharacteristicRemoved(object_path));
309 void FakeBluetoothGattCharacteristicClient::
310 ScheduleHeartRateMeasurementValueChange() {
311 if (!IsHeartRateVisible())
313 VLOG(2) << "Updating heart rate value.";
314 std::vector<uint8> measurement = GetHeartRateMeasurementValue();
315 heart_rate_measurement_properties_->value.ReplaceValue(measurement);
317 base::MessageLoop::current()->PostDelayedTask(
319 base::Bind(&FakeBluetoothGattCharacteristicClient::
320 ScheduleHeartRateMeasurementValueChange,
321 weak_ptr_factory_.GetWeakPtr()),
322 base::TimeDelta::FromMilliseconds(
323 kHeartRateMeasurementNotificationIntervalMs));
327 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() {
328 // TODO(armansito): We should make sure to properly pack this struct to ensure
329 // correct byte alignment and endianness. It doesn't matter too much right now
330 // as this is a fake and GCC on Linux seems to do the right thing.
334 uint16 energy_expanded;
338 // Flags in LSB: 0 11 1 1 000
340 // 8-bit bpm format -- | | | |
341 // Sensor contact supported -- | | |
342 // Energy expanded field present -- | |
343 // RR-Interval values present ------- |
344 // Reserved for future use ------------
346 value.flags |= (0x03 << 1);
347 value.flags |= (0x01 << 3);
348 value.flags |= (0x01 << 4);
350 // Pick a value between 117 bpm and 153 bpm for heart rate.
351 value.bpm = static_cast<uint8>(base::RandInt(117, 153));
353 // Total calories burned in kJoules since the last reset. Increment this by 1
354 // every time. It's fine if it overflows: it becomes 0 when the user resets
355 // the heart rate monitor (or pretend that he had a lot of cheeseburgers).
356 value.energy_expanded = calories_burned_++;
358 // Include one RR-Interval value, in seconds.
359 value.rr_interval = 60/value.bpm;
361 // Return the bytes in an array.
362 uint8* bytes = reinterpret_cast<uint8*>(&value);
363 std::vector<uint8> return_value;
364 return_value.assign(bytes, bytes + sizeof(value));
368 bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const {
369 DCHECK(heart_rate_visible_ != heart_rate_measurement_path_.empty());
370 DCHECK(heart_rate_visible_ != body_sensor_location_path_.empty());
371 DCHECK(heart_rate_visible_ != heart_rate_control_point_path_.empty());
372 DCHECK(heart_rate_visible_ == !!heart_rate_measurement_properties_.get());
373 DCHECK(heart_rate_visible_ == !!body_sensor_location_properties_.get());
374 DCHECK(heart_rate_visible_ == !!heart_rate_control_point_properties_.get());
375 return heart_rate_visible_;
378 } // namespace chromeos