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 "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" 14 15 namespace chromeos { 16 17 namespace { 18 19 const int kStartNotifyResponseIntervalMs = 200; 20 const int kHeartRateMeasurementNotificationIntervalMs = 2000; 21 22 } // namespace 23 24 // static 25 const char FakeBluetoothGattCharacteristicClient:: 26 kHeartRateMeasurementPathComponent[] = "char0000"; 27 const char FakeBluetoothGattCharacteristicClient:: 28 kBodySensorLocationPathComponent[] = "char0001"; 29 const char FakeBluetoothGattCharacteristicClient:: 30 kHeartRateControlPointPathComponent[] = "char0002"; 31 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"; 39 40 FakeBluetoothGattCharacteristicClient::Properties::Properties( 41 const PropertyChangedCallback& callback) 42 : BluetoothGattCharacteristicClient::Properties( 43 NULL, 44 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface, 45 callback) { 46 } 47 48 FakeBluetoothGattCharacteristicClient::Properties::~Properties() { 49 } 50 51 void FakeBluetoothGattCharacteristicClient::Properties::Get( 52 dbus::PropertyBase* property, 53 dbus::PropertySet::GetCallback callback) { 54 VLOG(1) << "Get " << property->name(); 55 callback.Run(true); 56 } 57 58 void FakeBluetoothGattCharacteristicClient::Properties::GetAll() { 59 VLOG(1) << "GetAll"; 60 } 61 62 void FakeBluetoothGattCharacteristicClient::Properties::Set( 63 dbus::PropertyBase* property, 64 dbus::PropertySet::SetCallback callback) { 65 VLOG(1) << "Set " << property->name(); 66 callback.Run(false); 67 } 68 69 FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient() 70 : heart_rate_visible_(false), 71 calories_burned_(0), 72 weak_ptr_factory_(this) { 73 } 74 75 FakeBluetoothGattCharacteristicClient:: 76 ~FakeBluetoothGattCharacteristicClient() { 77 } 78 79 void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) { 80 } 81 82 void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) { 83 observers_.AddObserver(observer); 84 } 85 86 void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) { 87 observers_.RemoveObserver(observer); 88 } 89 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_)); 97 } 98 return paths; 99 } 100 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(); 107 } 108 if (object_path.value() == body_sensor_location_path_) { 109 DCHECK(body_sensor_location_properties_.get()); 110 return body_sensor_location_properties_.get(); 111 } 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(); 115 } 116 return NULL; 117 } 118 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; 126 } 127 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; 133 } 134 135 if (object_path.value() != body_sensor_location_path_) { 136 error_callback.Run(kUnknownCharacteristicError, ""); 137 return; 138 } 139 140 std::vector<uint8> value; 141 value.push_back(0x06); // Location is "foot". 142 callback.Run(value); 143 } 144 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; 153 } 154 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; 159 } 160 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; 166 } 167 168 if (value[0] == 1) 169 calories_burned_ = 0; 170 171 callback.Run(); 172 } 173 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; 181 } 182 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; 187 } 188 189 if (heart_rate_measurement_properties_->notifying.value()) { 190 error_callback.Run("org.bluez.Error.Busy", 191 "Characteristic already notifying"); 192 return; 193 } 194 195 heart_rate_measurement_properties_->notifying.ReplaceValue(true); 196 ScheduleHeartRateMeasurementValueChange(); 197 198 // Respond asynchronously. 199 base::MessageLoop::current()->PostDelayedTask( 200 FROM_HERE, 201 callback, 202 base::TimeDelta::FromMilliseconds(kStartNotifyResponseIntervalMs)); 203 } 204 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; 212 } 213 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; 218 } 219 220 if (!heart_rate_measurement_properties_->notifying.value()) { 221 error_callback.Run("org.bluez.Error.Failed", "Not notifying"); 222 return; 223 } 224 225 heart_rate_measurement_properties_->notifying.ReplaceValue(false); 226 227 callback.Run(); 228 } 229 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; 235 } 236 237 VLOG(2) << "Exposing fake Heart Rate characteristics."; 238 239 std::vector<std::string> flags; 240 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); 253 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); 266 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); 280 281 heart_rate_visible_ = true; 282 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_)); 286 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(); 297 } 298 299 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() { 300 VLOG(2) << "Hiding fake Heart Rate characteristics."; 301 302 // Hide the descriptors. 303 FakeBluetoothGattDescriptorClient* descriptor_client = 304 static_cast<FakeBluetoothGattDescriptorClient*>( 305 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()); 306 descriptor_client->HideDescriptor( 307 dbus::ObjectPath(heart_rate_measurement_ccc_desc_path_)); 308 309 // Notify the observers before deleting the properties structures so that they 310 // can be accessed from the observer method. 311 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_measurement_path_)); 312 NotifyCharacteristicRemoved(dbus::ObjectPath(body_sensor_location_path_)); 313 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_control_point_path_)); 314 315 heart_rate_measurement_properties_.reset(); 316 body_sensor_location_properties_.reset(); 317 heart_rate_control_point_properties_.reset(); 318 319 heart_rate_measurement_path_.clear(); 320 body_sensor_location_path_.clear(); 321 heart_rate_control_point_path_.clear(); 322 heart_rate_visible_ = false; 323 } 324 325 dbus::ObjectPath 326 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const { 327 return dbus::ObjectPath(heart_rate_measurement_path_); 328 } 329 330 dbus::ObjectPath 331 FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const { 332 return dbus::ObjectPath(body_sensor_location_path_); 333 } 334 335 dbus::ObjectPath 336 FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const { 337 return dbus::ObjectPath(heart_rate_control_point_path_); 338 } 339 340 void FakeBluetoothGattCharacteristicClient::OnPropertyChanged( 341 const dbus::ObjectPath& object_path, 342 const std::string& property_name) { 343 VLOG(2) << "Characteristic property changed: " << object_path.value() 344 << ": " << property_name; 345 346 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, 347 GattCharacteristicPropertyChanged( 348 object_path, property_name)); 349 } 350 351 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded( 352 const dbus::ObjectPath& object_path) { 353 VLOG(2) << "GATT characteristic added: " << object_path.value(); 354 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, 355 GattCharacteristicAdded(object_path)); 356 } 357 358 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved( 359 const dbus::ObjectPath& object_path) { 360 VLOG(2) << "GATT characteristic removed: " << object_path.value(); 361 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, 362 GattCharacteristicRemoved(object_path)); 363 } 364 365 void FakeBluetoothGattCharacteristicClient:: 366 ScheduleHeartRateMeasurementValueChange() { 367 if (!IsHeartRateVisible()) 368 return; 369 370 // Don't send updates if the characteristic is not notifying. 371 if (!heart_rate_measurement_properties_->notifying.value()) 372 return; 373 374 VLOG(2) << "Updating heart rate value."; 375 std::vector<uint8> measurement = GetHeartRateMeasurementValue(); 376 377 FOR_EACH_OBSERVER( 378 BluetoothGattCharacteristicClient::Observer, 379 observers_, 380 GattCharacteristicValueUpdated( 381 dbus::ObjectPath(heart_rate_measurement_path_), measurement)); 382 383 base::MessageLoop::current()->PostDelayedTask( 384 FROM_HERE, 385 base::Bind(&FakeBluetoothGattCharacteristicClient:: 386 ScheduleHeartRateMeasurementValueChange, 387 weak_ptr_factory_.GetWeakPtr()), 388 base::TimeDelta::FromMilliseconds( 389 kHeartRateMeasurementNotificationIntervalMs)); 390 } 391 392 std::vector<uint8> 393 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() { 394 // TODO(armansito): We should make sure to properly pack this struct to ensure 395 // correct byte alignment and endianness. It doesn't matter too much right now 396 // as this is a fake and GCC on Linux seems to do the right thing. 397 struct { 398 uint8 flags; 399 uint8 bpm; 400 uint16 energy_expanded; 401 uint16 rr_interval; 402 } value; 403 404 // Flags in LSB: 0 11 1 1 000 405 // | | | | | 406 // 8-bit bpm format -- | | | | 407 // Sensor contact supported -- | | | 408 // Energy expanded field present -- | | 409 // RR-Interval values present ------- | 410 // Reserved for future use ------------ 411 value.flags = 0x0; 412 value.flags |= (0x03 << 1); 413 value.flags |= (0x01 << 3); 414 value.flags |= (0x01 << 4); 415 416 // Pick a value between 117 bpm and 153 bpm for heart rate. 417 value.bpm = static_cast<uint8>(base::RandInt(117, 153)); 418 419 // Total calories burned in kJoules since the last reset. Increment this by 1 420 // every time. It's fine if it overflows: it becomes 0 when the user resets 421 // the heart rate monitor (or pretend that he had a lot of cheeseburgers). 422 value.energy_expanded = calories_burned_++; 423 424 // Include one RR-Interval value, in seconds. 425 value.rr_interval = 60/value.bpm; 426 427 // Return the bytes in an array. 428 uint8* bytes = reinterpret_cast<uint8*>(&value); 429 std::vector<uint8> return_value; 430 return_value.assign(bytes, bytes + sizeof(value)); 431 return return_value; 432 } 433 434 bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const { 435 DCHECK(heart_rate_visible_ != heart_rate_measurement_path_.empty()); 436 DCHECK(heart_rate_visible_ != body_sensor_location_path_.empty()); 437 DCHECK(heart_rate_visible_ != heart_rate_control_point_path_.empty()); 438 DCHECK(heart_rate_visible_ == !!heart_rate_measurement_properties_.get()); 439 DCHECK(heart_rate_visible_ == !!body_sensor_location_properties_.get()); 440 DCHECK(heart_rate_visible_ == !!heart_rate_control_point_properties_.get()); 441 return heart_rate_visible_; 442 } 443 444 } // namespace chromeos 445