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/message_loop/message_loop.h" |
| 9 #include "base/rand_util.h" |
| 10 #include "base/time/time.h" |
| 11 #include "third_party/cros_system_api/dbus/service_constants.h" |
| 12 |
| 13 namespace chromeos { |
| 14 |
| 15 namespace { |
| 16 |
| 17 const int kHeartRateMeasurementNotificationIntervalMs = 2000; |
| 18 |
| 19 } // namespace |
| 20 |
| 21 // static |
| 22 const char FakeBluetoothGattCharacteristicClient:: |
| 23 kHeartRateMeasurementPathComponent[] = "char0000"; |
| 24 const char FakeBluetoothGattCharacteristicClient:: |
| 25 kBodySensorLocationPathComponent[] = "char0001"; |
| 26 const char FakeBluetoothGattCharacteristicClient:: |
| 27 kHeartRateControlPointPathComponent[] = "char0002"; |
| 28 |
| 29 // static |
| 30 const char FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID[] = |
| 31 "00002a37-0000-1000-8000-00805f9b34fb"; |
| 32 const char FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID[] = |
| 33 "00002a38-0000-1000-8000-00805f9b34fb"; |
| 34 const char FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID[] = |
| 35 "00002a39-0000-1000-8000-00805f9b34fb"; |
| 36 |
| 37 FakeBluetoothGattCharacteristicClient::Properties::Properties( |
| 38 const PropertyChangedCallback& callback) |
| 39 : BluetoothGattCharacteristicClient::Properties( |
| 40 NULL, |
| 41 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface, |
| 42 callback) { |
| 43 } |
| 44 |
| 45 FakeBluetoothGattCharacteristicClient::Properties::~Properties() { |
| 46 } |
| 47 |
| 48 void FakeBluetoothGattCharacteristicClient::Properties::Get( |
| 49 dbus::PropertyBase* property, |
| 50 dbus::PropertySet::GetCallback callback) { |
| 51 VLOG(1) << "Get " << property->name(); |
| 52 callback.Run(false); |
| 53 } |
| 54 |
| 55 void FakeBluetoothGattCharacteristicClient::Properties::GetAll() { |
| 56 VLOG(1) << "GetAll"; |
| 57 } |
| 58 |
| 59 void FakeBluetoothGattCharacteristicClient::Properties::Set( |
| 60 dbus::PropertyBase* property, |
| 61 dbus::PropertySet::SetCallback callback) { |
| 62 VLOG(1) << "Set " << property->name(); |
| 63 if (property->name() != value.name()) { |
| 64 callback.Run(false); |
| 65 return; |
| 66 } |
| 67 // Allow writing to only certain characteristics that are defined with the |
| 68 // write permission. |
| 69 // TODO(armansito): Actually check against the permissions property instead of |
| 70 // UUID, once that property is fully defined in the API. |
| 71 if (uuid.value() != kHeartRateControlPointUUID) { |
| 72 callback.Run(false); |
| 73 return; |
| 74 } |
| 75 callback.Run(true); |
| 76 property->ReplaceValueWithSetValue(); |
| 77 } |
| 78 |
| 79 FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient() |
| 80 : heart_rate_visible_(false), |
| 81 calories_burned_(0), |
| 82 weak_ptr_factory_(this) { |
| 83 } |
| 84 |
| 85 FakeBluetoothGattCharacteristicClient:: |
| 86 ~FakeBluetoothGattCharacteristicClient() { |
| 87 } |
| 88 |
| 89 void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) { |
| 90 } |
| 91 |
| 92 void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) { |
| 93 observers_.AddObserver(observer); |
| 94 } |
| 95 |
| 96 void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) { |
| 97 observers_.RemoveObserver(observer); |
| 98 } |
| 99 |
| 100 std::vector<dbus::ObjectPath> |
| 101 FakeBluetoothGattCharacteristicClient::GetCharacteristics() { |
| 102 std::vector<dbus::ObjectPath> paths; |
| 103 if (IsHeartRateVisible()) { |
| 104 paths.push_back(dbus::ObjectPath(heart_rate_measurement_path_)); |
| 105 paths.push_back(dbus::ObjectPath(body_sensor_location_path_)); |
| 106 paths.push_back(dbus::ObjectPath(heart_rate_control_point_path_)); |
| 107 } |
| 108 return paths; |
| 109 } |
| 110 |
| 111 FakeBluetoothGattCharacteristicClient::Properties* |
| 112 FakeBluetoothGattCharacteristicClient::GetProperties( |
| 113 const dbus::ObjectPath& object_path) { |
| 114 if (object_path.value() == heart_rate_measurement_path_) { |
| 115 DCHECK(heart_rate_measurement_properties_.get()); |
| 116 return heart_rate_measurement_properties_.get(); |
| 117 } |
| 118 if (object_path.value() == body_sensor_location_path_) { |
| 119 DCHECK(heart_rate_measurement_properties_.get()); |
| 120 return heart_rate_measurement_properties_.get(); |
| 121 } |
| 122 if (object_path.value() == heart_rate_control_point_path_) { |
| 123 DCHECK(heart_rate_control_point_properties_.get()); |
| 124 return heart_rate_control_point_properties_.get(); |
| 125 } |
| 126 return NULL; |
| 127 } |
| 128 |
| 129 void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics( |
| 130 const dbus::ObjectPath& service_path) { |
| 131 if (IsHeartRateVisible()) { |
| 132 VLOG(2) << "Fake Heart Rate characteristics are already visible."; |
| 133 return; |
| 134 } |
| 135 |
| 136 VLOG(2) << "Exposing fake Heart Rate characteristics."; |
| 137 |
| 138 // ==== Heart Rate Measurement Characteristic ==== |
| 139 heart_rate_measurement_path_ = |
| 140 service_path.value() + "/" + kHeartRateMeasurementPathComponent; |
| 141 heart_rate_measurement_properties_.reset(new Properties(base::Bind( |
| 142 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged, |
| 143 weak_ptr_factory_.GetWeakPtr(), |
| 144 dbus::ObjectPath(heart_rate_measurement_path_)))); |
| 145 heart_rate_measurement_properties_->uuid.ReplaceValue( |
| 146 kHeartRateMeasurementUUID); |
| 147 heart_rate_measurement_properties_->service.ReplaceValue(service_path); |
| 148 |
| 149 // TODO(armansito): Fill out the flags field once bindings for the values have |
| 150 // been added. For now, leave it empty. |
| 151 |
| 152 std::vector<uint8> measurement_value = GetHeartRateMeasurementValue(); |
| 153 heart_rate_measurement_properties_->value.ReplaceValue(measurement_value); |
| 154 |
| 155 // ==== Body Sensor Location Characteristic ==== |
| 156 body_sensor_location_path_ = |
| 157 service_path.value() + "/" + kBodySensorLocationPathComponent; |
| 158 body_sensor_location_properties_.reset(new Properties(base::Bind( |
| 159 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged, |
| 160 weak_ptr_factory_.GetWeakPtr(), |
| 161 dbus::ObjectPath(body_sensor_location_path_)))); |
| 162 body_sensor_location_properties_->uuid.ReplaceValue(kBodySensorLocationUUID); |
| 163 body_sensor_location_properties_->service.ReplaceValue(service_path); |
| 164 |
| 165 // TODO(armansito): Fill out the flags field once bindings for the values have |
| 166 // been added. For now, leave it empty. |
| 167 |
| 168 // The sensor is in the "Other" location. |
| 169 std::vector<uint8> body_sensor_location_value; |
| 170 body_sensor_location_value.push_back(0); |
| 171 body_sensor_location_properties_->value.ReplaceValue( |
| 172 body_sensor_location_value); |
| 173 |
| 174 // ==== Heart Rate Control Point Characteristic ==== |
| 175 heart_rate_control_point_path_ = |
| 176 service_path.value() + "/" + kHeartRateControlPointPathComponent; |
| 177 heart_rate_control_point_properties_.reset(new Properties(base::Bind( |
| 178 &FakeBluetoothGattCharacteristicClient::OnPropertyChanged, |
| 179 weak_ptr_factory_.GetWeakPtr(), |
| 180 dbus::ObjectPath(heart_rate_control_point_path_)))); |
| 181 heart_rate_control_point_properties_->uuid.ReplaceValue( |
| 182 kHeartRateControlPointUUID); |
| 183 heart_rate_control_point_properties_->service.ReplaceValue(service_path); |
| 184 |
| 185 // TODO(armansito): Fill out the flags field once bindings for the values have |
| 186 // been added. For now, leave it empty. |
| 187 |
| 188 // Set the initial value to 0. Whenever this gets set to 1, we will reset the |
| 189 // total calories burned and change the value back to 0. |
| 190 std::vector<uint8> heart_rate_control_point_value; |
| 191 heart_rate_control_point_value.push_back(0); |
| 192 heart_rate_control_point_properties_->value.ReplaceValue( |
| 193 heart_rate_control_point_value); |
| 194 |
| 195 heart_rate_visible_ = true; |
| 196 |
| 197 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_measurement_path_)); |
| 198 NotifyCharacteristicAdded(dbus::ObjectPath(body_sensor_location_path_)); |
| 199 NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_control_point_path_)); |
| 200 |
| 201 // Set up notifications for heart rate measurement. |
| 202 // TODO(armansito): Do this based on the value of the "client characteristic |
| 203 // configuration" descriptor. Since it's still unclear how descriptors will |
| 204 // be handled by BlueZ, automatically set up notifications for now. |
| 205 ScheduleHeartRateMeasurementValueChange(); |
| 206 |
| 207 // TODO(armansito): Add descriptors. |
| 208 } |
| 209 |
| 210 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() { |
| 211 VLOG(2) << "Hiding fake Heart Rate characteristics."; |
| 212 heart_rate_measurement_properties_.reset(); |
| 213 body_sensor_location_properties_.reset(); |
| 214 heart_rate_control_point_properties_.reset(); |
| 215 |
| 216 std::string hrm_path = heart_rate_measurement_path_; |
| 217 heart_rate_measurement_path_.clear(); |
| 218 std::string bsl_path = body_sensor_location_path_; |
| 219 body_sensor_location_path_.clear(); |
| 220 std::string hrcp_path = heart_rate_control_point_path_; |
| 221 heart_rate_control_point_path_.clear(); |
| 222 heart_rate_visible_ = false; |
| 223 |
| 224 NotifyCharacteristicRemoved(dbus::ObjectPath(hrm_path)); |
| 225 NotifyCharacteristicRemoved(dbus::ObjectPath(bsl_path)); |
| 226 NotifyCharacteristicRemoved(dbus::ObjectPath(hrcp_path)); |
| 227 } |
| 228 |
| 229 void FakeBluetoothGattCharacteristicClient::OnPropertyChanged( |
| 230 const dbus::ObjectPath& object_path, |
| 231 const std::string& property_name) { |
| 232 VLOG(2) << "Characteristic property changed: " << object_path.value() |
| 233 << ": " << property_name; |
| 234 |
| 235 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, |
| 236 GattCharacteristicPropertyChanged( |
| 237 object_path, property_name)); |
| 238 |
| 239 // If the heart rate control point was set, reset the calories burned. |
| 240 if (object_path.value() != heart_rate_control_point_path_) |
| 241 return; |
| 242 DCHECK(heart_rate_control_point_properties_.get()); |
| 243 dbus::Property<std::vector<uint8> >* value_prop = |
| 244 &heart_rate_control_point_properties_->value; |
| 245 if (property_name != value_prop->name()) |
| 246 return; |
| 247 |
| 248 std::vector<uint8> value = value_prop->value(); |
| 249 DCHECK(value.size() == 1); |
| 250 if (value[0] == 0) |
| 251 return; |
| 252 |
| 253 DCHECK(value[0] == 1); |
| 254 calories_burned_ = 0; |
| 255 value[0] = 0; |
| 256 value_prop->ReplaceValue(value); |
| 257 } |
| 258 |
| 259 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded( |
| 260 const dbus::ObjectPath& object_path) { |
| 261 VLOG(2) << "GATT characteristic added: " << object_path.value(); |
| 262 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, |
| 263 GattCharacteristicAdded(object_path)); |
| 264 } |
| 265 |
| 266 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved( |
| 267 const dbus::ObjectPath& object_path) { |
| 268 VLOG(2) << "GATT characteristic removed: " << object_path.value(); |
| 269 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, |
| 270 GattCharacteristicRemoved(object_path)); |
| 271 } |
| 272 |
| 273 void FakeBluetoothGattCharacteristicClient:: |
| 274 ScheduleHeartRateMeasurementValueChange() { |
| 275 if (!IsHeartRateVisible()) |
| 276 return; |
| 277 VLOG(2) << "Updating heart rate value."; |
| 278 std::vector<uint8> measurement = GetHeartRateMeasurementValue(); |
| 279 heart_rate_measurement_properties_->value.ReplaceValue(measurement); |
| 280 |
| 281 base::MessageLoop::current()->PostDelayedTask( |
| 282 FROM_HERE, |
| 283 base::Bind(&FakeBluetoothGattCharacteristicClient:: |
| 284 ScheduleHeartRateMeasurementValueChange, |
| 285 weak_ptr_factory_.GetWeakPtr()), |
| 286 base::TimeDelta::FromMilliseconds( |
| 287 kHeartRateMeasurementNotificationIntervalMs)); |
| 288 } |
| 289 |
| 290 std::vector<uint8> |
| 291 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() { |
| 292 // TODO(armansito): We should make sure to properly pack this struct to ensure |
| 293 // correct byte alignment and endianness. It doesn't matter too much right now |
| 294 // as this is a fake and GCC on Linux seems to do the right thing. |
| 295 struct { |
| 296 uint8 flags; |
| 297 uint8 bpm; |
| 298 uint16 energy_expanded; |
| 299 uint16 rr_interval; |
| 300 } value; |
| 301 |
| 302 // Flags in LSB: 0 11 1 1 000 |
| 303 // | | | | | |
| 304 // 8-bit bpm format -- | | | | |
| 305 // Sensor contact supported -- | | | |
| 306 // Energy expanded field present -- | | |
| 307 // RR-Interval values present ------- | |
| 308 // Reserved for future use ------------ |
| 309 value.flags = 0x0; |
| 310 value.flags |= (0x03 << 1); |
| 311 value.flags |= (0x01 << 3); |
| 312 value.flags |= (0x01 << 4); |
| 313 |
| 314 // Pick a value between 117 bpm and 153 bpm for heart rate. |
| 315 value.bpm = static_cast<uint8>(base::RandInt(117, 153)); |
| 316 |
| 317 // Total calories burned in kJoules since the last reset. Increment this by 1 |
| 318 // every time. It's fine if it overflows: it becomes 0 when the user resets |
| 319 // the heart rate monitor (or pretend that he had a lot of cheeseburgers). |
| 320 value.energy_expanded = calories_burned_++; |
| 321 |
| 322 // Include one RR-Interval value, in seconds. |
| 323 value.rr_interval = 60/value.bpm; |
| 324 |
| 325 // Return the bytes in an array. |
| 326 uint8* bytes = reinterpret_cast<uint8*>(&value); |
| 327 std::vector<uint8> return_value; |
| 328 return_value.assign(bytes, bytes + sizeof(value)); |
| 329 return return_value; |
| 330 } |
| 331 |
| 332 bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const { |
| 333 DCHECK(heart_rate_visible_ != heart_rate_measurement_path_.empty()); |
| 334 DCHECK(heart_rate_visible_ != body_sensor_location_path_.empty()); |
| 335 DCHECK(heart_rate_visible_ != heart_rate_control_point_path_.empty()); |
| 336 DCHECK(heart_rate_visible_ == !!heart_rate_measurement_properties_.get()); |
| 337 DCHECK(heart_rate_visible_ == !!body_sensor_location_properties_.get()); |
| 338 DCHECK(heart_rate_visible_ == !!heart_rate_control_point_properties_.get()); |
| 339 return heart_rate_visible_; |
| 340 } |
| 341 |
| 342 } // namespace chromeos |
OLD | NEW |