Added unit test for DevTools' ephemeral port support.
[chromium-blink-merge.git] / chromeos / dbus / fake_bluetooth_gatt_characteristic_client.cc
blob0010ace4d77757b06c22a0cf33e08934153adc7f
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 kHeartRateMeasurementNotificationIntervalMs = 2000;
21 } // namespace
23 // static
24 const char FakeBluetoothGattCharacteristicClient::
25 kHeartRateMeasurementPathComponent[] = "char0000";
26 const char FakeBluetoothGattCharacteristicClient::
27 kBodySensorLocationPathComponent[] = "char0001";
28 const char FakeBluetoothGattCharacteristicClient::
29 kHeartRateControlPointPathComponent[] = "char0002";
31 // static
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(
42 NULL,
43 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
44 callback) {
47 FakeBluetoothGattCharacteristicClient::Properties::~Properties() {
50 void FakeBluetoothGattCharacteristicClient::Properties::Get(
51 dbus::PropertyBase* property,
52 dbus::PropertySet::GetCallback callback) {
53 VLOG(1) << "Get " << property->name();
54 callback.Run(true);
57 void FakeBluetoothGattCharacteristicClient::Properties::GetAll() {
58 VLOG(1) << "GetAll";
61 void FakeBluetoothGattCharacteristicClient::Properties::Set(
62 dbus::PropertyBase* property,
63 dbus::PropertySet::SetCallback callback) {
64 VLOG(1) << "Set " << property->name();
65 callback.Run(false);
68 FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient()
69 : heart_rate_visible_(false),
70 calories_burned_(0),
71 weak_ptr_factory_(this) {
74 FakeBluetoothGattCharacteristicClient::
75 ~FakeBluetoothGattCharacteristicClient() {
78 void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) {
81 void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) {
82 observers_.AddObserver(observer);
85 void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) {
86 observers_.RemoveObserver(observer);
89 std::vector<dbus::ObjectPath>
90 FakeBluetoothGattCharacteristicClient::GetCharacteristics() {
91 std::vector<dbus::ObjectPath> paths;
92 if (IsHeartRateVisible()) {
93 paths.push_back(dbus::ObjectPath(heart_rate_measurement_path_));
94 paths.push_back(dbus::ObjectPath(body_sensor_location_path_));
95 paths.push_back(dbus::ObjectPath(heart_rate_control_point_path_));
97 return paths;
100 FakeBluetoothGattCharacteristicClient::Properties*
101 FakeBluetoothGattCharacteristicClient::GetProperties(
102 const dbus::ObjectPath& object_path) {
103 if (object_path.value() == heart_rate_measurement_path_) {
104 DCHECK(heart_rate_measurement_properties_.get());
105 return heart_rate_measurement_properties_.get();
107 if (object_path.value() == body_sensor_location_path_) {
108 DCHECK(body_sensor_location_properties_.get());
109 return body_sensor_location_properties_.get();
111 if (object_path.value() == heart_rate_control_point_path_) {
112 DCHECK(heart_rate_control_point_properties_.get());
113 return heart_rate_control_point_properties_.get();
115 return NULL;
118 void FakeBluetoothGattCharacteristicClient::ReadValue(
119 const dbus::ObjectPath& object_path,
120 const ValueCallback& callback,
121 const ErrorCallback& error_callback) {
122 if (!IsHeartRateVisible()) {
123 error_callback.Run(kUnknownCharacteristicError, "");
124 return;
127 if (object_path.value() == heart_rate_measurement_path_ ||
128 object_path.value() == heart_rate_control_point_path_) {
129 error_callback.Run("org.bluez.Error.ReadNotPermitted",
130 "Reads of this value are not allowed");
131 return;
134 if (object_path.value() != body_sensor_location_path_) {
135 error_callback.Run(kUnknownCharacteristicError, "");
136 return;
139 std::vector<uint8> value;
140 value.push_back(0x06); // Location is "foot".
141 callback.Run(value);
144 void FakeBluetoothGattCharacteristicClient::WriteValue(
145 const dbus::ObjectPath& object_path,
146 const std::vector<uint8>& value,
147 const base::Closure& callback,
148 const ErrorCallback& error_callback) {
149 if (!IsHeartRateVisible()) {
150 error_callback.Run(kUnknownCharacteristicError, "");
151 return;
154 if (object_path.value() != heart_rate_control_point_path_) {
155 error_callback.Run("org.bluez.Error.WriteNotPermitted",
156 "Writes of this value are not allowed");
157 return;
160 DCHECK(heart_rate_control_point_properties_.get());
161 if (value.size() != 1 || value[0] > 1) {
162 error_callback.Run("org.bluez.Error.Failed",
163 "Invalid value given for write");
164 return;
167 if (value[0] == 1)
168 calories_burned_ = 0;
170 callback.Run();
173 void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics(
174 const dbus::ObjectPath& service_path) {
175 if (IsHeartRateVisible()) {
176 VLOG(2) << "Fake Heart Rate characteristics are already visible.";
177 return;
180 VLOG(2) << "Exposing fake Heart Rate characteristics.";
182 std::vector<std::string> flags;
184 // ==== Heart Rate Measurement Characteristic ====
185 heart_rate_measurement_path_ =
186 service_path.value() + "/" + kHeartRateMeasurementPathComponent;
187 heart_rate_measurement_properties_.reset(new Properties(base::Bind(
188 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
189 weak_ptr_factory_.GetWeakPtr(),
190 dbus::ObjectPath(heart_rate_measurement_path_))));
191 heart_rate_measurement_properties_->uuid.ReplaceValue(
192 kHeartRateMeasurementUUID);
193 heart_rate_measurement_properties_->service.ReplaceValue(service_path);
194 flags.push_back(bluetooth_gatt_characteristic::kFlagNotify);
195 heart_rate_measurement_properties_->flags.ReplaceValue(flags);
197 // ==== Body Sensor Location Characteristic ====
198 body_sensor_location_path_ =
199 service_path.value() + "/" + kBodySensorLocationPathComponent;
200 body_sensor_location_properties_.reset(new Properties(base::Bind(
201 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
202 weak_ptr_factory_.GetWeakPtr(),
203 dbus::ObjectPath(body_sensor_location_path_))));
204 body_sensor_location_properties_->uuid.ReplaceValue(kBodySensorLocationUUID);
205 body_sensor_location_properties_->service.ReplaceValue(service_path);
206 flags.clear();
207 flags.push_back(bluetooth_gatt_characteristic::kFlagRead);
208 body_sensor_location_properties_->flags.ReplaceValue(flags);
210 // ==== Heart Rate Control Point Characteristic ====
211 heart_rate_control_point_path_ =
212 service_path.value() + "/" + kHeartRateControlPointPathComponent;
213 heart_rate_control_point_properties_.reset(new Properties(base::Bind(
214 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged,
215 weak_ptr_factory_.GetWeakPtr(),
216 dbus::ObjectPath(heart_rate_control_point_path_))));
217 heart_rate_control_point_properties_->uuid.ReplaceValue(
218 kHeartRateControlPointUUID);
219 heart_rate_control_point_properties_->service.ReplaceValue(service_path);
220 flags.clear();
221 flags.push_back(bluetooth_gatt_characteristic::kFlagWrite);
222 heart_rate_control_point_properties_->flags.ReplaceValue(flags);
224 heart_rate_visible_ = true;
226 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_measurement_path_));
227 NotifyCharacteristicAdded(dbus::ObjectPath(body_sensor_location_path_));
228 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_control_point_path_));
230 // Set up notifications for heart rate measurement.
231 // TODO(armansito): Do this based on the value of the "client characteristic
232 // configuration" descriptor. Since it's still unclear how descriptors will
233 // be handled by BlueZ, automatically set up notifications for now.
234 ScheduleHeartRateMeasurementValueChange();
236 // Expose CCC descriptor for Heart Rate Measurement characteristic.
237 FakeBluetoothGattDescriptorClient* descriptor_client =
238 static_cast<FakeBluetoothGattDescriptorClient*>(
239 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
240 dbus::ObjectPath ccc_path(descriptor_client->ExposeDescriptor(
241 dbus::ObjectPath(heart_rate_measurement_path_),
242 FakeBluetoothGattDescriptorClient::
243 kClientCharacteristicConfigurationUUID));
244 DCHECK(ccc_path.IsValid());
245 heart_rate_measurement_ccc_desc_path_ = ccc_path.value();
248 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() {
249 VLOG(2) << "Hiding fake Heart Rate characteristics.";
251 // Hide the descriptors.
252 FakeBluetoothGattDescriptorClient* descriptor_client =
253 static_cast<FakeBluetoothGattDescriptorClient*>(
254 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient());
255 descriptor_client->HideDescriptor(
256 dbus::ObjectPath(heart_rate_measurement_ccc_desc_path_));
258 // Notify the observers before deleting the properties structures so that they
259 // can be accessed from the observer method.
260 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_measurement_path_));
261 NotifyCharacteristicRemoved(dbus::ObjectPath(body_sensor_location_path_));
262 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_control_point_path_));
264 heart_rate_measurement_properties_.reset();
265 body_sensor_location_properties_.reset();
266 heart_rate_control_point_properties_.reset();
268 heart_rate_measurement_path_.clear();
269 body_sensor_location_path_.clear();
270 heart_rate_control_point_path_.clear();
271 heart_rate_visible_ = false;
274 dbus::ObjectPath
275 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const {
276 return dbus::ObjectPath(heart_rate_measurement_path_);
279 dbus::ObjectPath
280 FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const {
281 return dbus::ObjectPath(body_sensor_location_path_);
284 dbus::ObjectPath
285 FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const {
286 return dbus::ObjectPath(heart_rate_control_point_path_);
289 void FakeBluetoothGattCharacteristicClient::OnPropertyChanged(
290 const dbus::ObjectPath& object_path,
291 const std::string& property_name) {
292 VLOG(2) << "Characteristic property changed: " << object_path.value()
293 << ": " << property_name;
295 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
296 GattCharacteristicPropertyChanged(
297 object_path, property_name));
300 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded(
301 const dbus::ObjectPath& object_path) {
302 VLOG(2) << "GATT characteristic added: " << object_path.value();
303 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
304 GattCharacteristicAdded(object_path));
307 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved(
308 const dbus::ObjectPath& object_path) {
309 VLOG(2) << "GATT characteristic removed: " << object_path.value();
310 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_,
311 GattCharacteristicRemoved(object_path));
314 void FakeBluetoothGattCharacteristicClient::
315 ScheduleHeartRateMeasurementValueChange() {
316 if (!IsHeartRateVisible())
317 return;
318 VLOG(2) << "Updating heart rate value.";
319 std::vector<uint8> measurement = GetHeartRateMeasurementValue();
321 FOR_EACH_OBSERVER(
322 BluetoothGattCharacteristicClient::Observer,
323 observers_,
324 GattCharacteristicValueUpdated(
325 dbus::ObjectPath(heart_rate_measurement_path_), measurement));
327 base::MessageLoop::current()->PostDelayedTask(
328 FROM_HERE,
329 base::Bind(&FakeBluetoothGattCharacteristicClient::
330 ScheduleHeartRateMeasurementValueChange,
331 weak_ptr_factory_.GetWeakPtr()),
332 base::TimeDelta::FromMilliseconds(
333 kHeartRateMeasurementNotificationIntervalMs));
336 std::vector<uint8>
337 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() {
338 // TODO(armansito): We should make sure to properly pack this struct to ensure
339 // correct byte alignment and endianness. It doesn't matter too much right now
340 // as this is a fake and GCC on Linux seems to do the right thing.
341 struct {
342 uint8 flags;
343 uint8 bpm;
344 uint16 energy_expanded;
345 uint16 rr_interval;
346 } value;
348 // Flags in LSB: 0 11 1 1 000
349 // | | | | |
350 // 8-bit bpm format -- | | | |
351 // Sensor contact supported -- | | |
352 // Energy expanded field present -- | |
353 // RR-Interval values present ------- |
354 // Reserved for future use ------------
355 value.flags = 0x0;
356 value.flags |= (0x03 << 1);
357 value.flags |= (0x01 << 3);
358 value.flags |= (0x01 << 4);
360 // Pick a value between 117 bpm and 153 bpm for heart rate.
361 value.bpm = static_cast<uint8>(base::RandInt(117, 153));
363 // Total calories burned in kJoules since the last reset. Increment this by 1
364 // every time. It's fine if it overflows: it becomes 0 when the user resets
365 // the heart rate monitor (or pretend that he had a lot of cheeseburgers).
366 value.energy_expanded = calories_burned_++;
368 // Include one RR-Interval value, in seconds.
369 value.rr_interval = 60/value.bpm;
371 // Return the bytes in an array.
372 uint8* bytes = reinterpret_cast<uint8*>(&value);
373 std::vector<uint8> return_value;
374 return_value.assign(bytes, bytes + sizeof(value));
375 return return_value;
378 bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const {
379 DCHECK(heart_rate_visible_ != heart_rate_measurement_path_.empty());
380 DCHECK(heart_rate_visible_ != body_sensor_location_path_.empty());
381 DCHECK(heart_rate_visible_ != heart_rate_control_point_path_.empty());
382 DCHECK(heart_rate_visible_ == !!heart_rate_measurement_properties_.get());
383 DCHECK(heart_rate_visible_ == !!body_sensor_location_properties_.get());
384 DCHECK(heart_rate_visible_ == !!heart_rate_control_point_properties_.get());
385 return heart_rate_visible_;
388 } // namespace chromeos