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 std::vector<dbus::ObjectPath> desc_paths; 299 desc_paths.push_back(ccc_path); 300 301 heart_rate_measurement_properties_->descriptors.ReplaceValue(desc_paths); 302 } 303 304 void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() { 305 VLOG(2) << "Hiding fake Heart Rate characteristics."; 306 307 // Hide the descriptors. 308 FakeBluetoothGattDescriptorClient* descriptor_client = 309 static_cast<FakeBluetoothGattDescriptorClient*>( 310 DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()); 311 descriptor_client->HideDescriptor( 312 dbus::ObjectPath(heart_rate_measurement_ccc_desc_path_)); 313 314 // Notify the observers before deleting the properties structures so that they 315 // can be accessed from the observer method. 316 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_measurement_path_)); 317 NotifyCharacteristicRemoved(dbus::ObjectPath(body_sensor_location_path_)); 318 NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_control_point_path_)); 319 320 heart_rate_measurement_properties_.reset(); 321 body_sensor_location_properties_.reset(); 322 heart_rate_control_point_properties_.reset(); 323 324 heart_rate_measurement_path_.clear(); 325 body_sensor_location_path_.clear(); 326 heart_rate_control_point_path_.clear(); 327 heart_rate_visible_ = false; 328 } 329 330 dbus::ObjectPath 331 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const { 332 return dbus::ObjectPath(heart_rate_measurement_path_); 333 } 334 335 dbus::ObjectPath 336 FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const { 337 return dbus::ObjectPath(body_sensor_location_path_); 338 } 339 340 dbus::ObjectPath 341 FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const { 342 return dbus::ObjectPath(heart_rate_control_point_path_); 343 } 344 345 void FakeBluetoothGattCharacteristicClient::OnPropertyChanged( 346 const dbus::ObjectPath& object_path, 347 const std::string& property_name) { 348 VLOG(2) << "Characteristic property changed: " << object_path.value() 349 << ": " << property_name; 350 351 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, 352 GattCharacteristicPropertyChanged( 353 object_path, property_name)); 354 } 355 356 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded( 357 const dbus::ObjectPath& object_path) { 358 VLOG(2) << "GATT characteristic added: " << object_path.value(); 359 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, 360 GattCharacteristicAdded(object_path)); 361 } 362 363 void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved( 364 const dbus::ObjectPath& object_path) { 365 VLOG(2) << "GATT characteristic removed: " << object_path.value(); 366 FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, 367 GattCharacteristicRemoved(object_path)); 368 } 369 370 void FakeBluetoothGattCharacteristicClient:: 371 ScheduleHeartRateMeasurementValueChange() { 372 if (!IsHeartRateVisible()) 373 return; 374 375 // Don't send updates if the characteristic is not notifying. 376 if (!heart_rate_measurement_properties_->notifying.value()) 377 return; 378 379 VLOG(2) << "Updating heart rate value."; 380 std::vector<uint8> measurement = GetHeartRateMeasurementValue(); 381 382 FOR_EACH_OBSERVER( 383 BluetoothGattCharacteristicClient::Observer, 384 observers_, 385 GattCharacteristicValueUpdated( 386 dbus::ObjectPath(heart_rate_measurement_path_), measurement)); 387 388 base::MessageLoop::current()->PostDelayedTask( 389 FROM_HERE, 390 base::Bind(&FakeBluetoothGattCharacteristicClient:: 391 ScheduleHeartRateMeasurementValueChange, 392 weak_ptr_factory_.GetWeakPtr()), 393 base::TimeDelta::FromMilliseconds( 394 kHeartRateMeasurementNotificationIntervalMs)); 395 } 396 397 std::vector<uint8> 398 FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() { 399 // TODO(armansito): We should make sure to properly pack this struct to ensure 400 // correct byte alignment and endianness. It doesn't matter too much right now 401 // as this is a fake and GCC on Linux seems to do the right thing. 402 struct { 403 uint8 flags; 404 uint8 bpm; 405 uint16 energy_expanded; 406 uint16 rr_interval; 407 } value; 408 409 // Flags in LSB: 0 11 1 1 000 410 // | | | | | 411 // 8-bit bpm format -- | | | | 412 // Sensor contact supported -- | | | 413 // Energy expanded field present -- | | 414 // RR-Interval values present ------- | 415 // Reserved for future use ------------ 416 value.flags = 0x0; 417 value.flags |= (0x03 << 1); 418 value.flags |= (0x01 << 3); 419 value.flags |= (0x01 << 4); 420 421 // Pick a value between 117 bpm and 153 bpm for heart rate. 422 value.bpm = static_cast<uint8>(base::RandInt(117, 153)); 423 424 // Total calories burned in kJoules since the last reset. Increment this by 1 425 // every time. It's fine if it overflows: it becomes 0 when the user resets 426 // the heart rate monitor (or pretend that he had a lot of cheeseburgers). 427 value.energy_expanded = calories_burned_++; 428 429 // Include one RR-Interval value, in seconds. 430 value.rr_interval = 60/value.bpm; 431 432 // Return the bytes in an array. 433 uint8* bytes = reinterpret_cast<uint8*>(&value); 434 std::vector<uint8> return_value; 435 return_value.assign(bytes, bytes + sizeof(value)); 436 return return_value; 437 } 438 439 bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const { 440 DCHECK(heart_rate_visible_ != heart_rate_measurement_path_.empty()); 441 DCHECK(heart_rate_visible_ != body_sensor_location_path_.empty()); 442 DCHECK(heart_rate_visible_ != heart_rate_control_point_path_.empty()); 443 DCHECK(heart_rate_visible_ == !!heart_rate_measurement_properties_.get()); 444 DCHECK(heart_rate_visible_ == !!body_sensor_location_properties_.get()); 445 DCHECK(heart_rate_visible_ == !!heart_rate_control_point_properties_.get()); 446 return heart_rate_visible_; 447 } 448 449 } // namespace chromeos 450