fake_bluetooth_gatt_characteristic_client.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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 13namespace chromeos { 14 15namespace { 16 17const int kHeartRateMeasurementNotificationIntervalMs = 2000; 18 19} // namespace 20 21// static 22const char FakeBluetoothGattCharacteristicClient:: 23 kHeartRateMeasurementPathComponent[] = "char0000"; 24const char FakeBluetoothGattCharacteristicClient:: 25 kBodySensorLocationPathComponent[] = "char0001"; 26const char FakeBluetoothGattCharacteristicClient:: 27 kHeartRateControlPointPathComponent[] = "char0002"; 28 29// static 30const char FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID[] = 31 "00002a37-0000-1000-8000-00805f9b34fb"; 32const char FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID[] = 33 "00002a38-0000-1000-8000-00805f9b34fb"; 34const char FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID[] = 35 "00002a39-0000-1000-8000-00805f9b34fb"; 36 37FakeBluetoothGattCharacteristicClient::Properties::Properties( 38 const PropertyChangedCallback& callback) 39 : BluetoothGattCharacteristicClient::Properties( 40 NULL, 41 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface, 42 callback) { 43} 44 45FakeBluetoothGattCharacteristicClient::Properties::~Properties() { 46} 47 48void FakeBluetoothGattCharacteristicClient::Properties::Get( 49 dbus::PropertyBase* property, 50 dbus::PropertySet::GetCallback callback) { 51 VLOG(1) << "Get " << property->name(); 52 callback.Run(false); 53} 54 55void FakeBluetoothGattCharacteristicClient::Properties::GetAll() { 56 VLOG(1) << "GetAll"; 57} 58 59void 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 79FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient() 80 : heart_rate_visible_(false), 81 calories_burned_(0), 82 weak_ptr_factory_(this) { 83} 84 85FakeBluetoothGattCharacteristicClient:: 86 ~FakeBluetoothGattCharacteristicClient() { 87} 88 89void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) { 90} 91 92void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) { 93 observers_.AddObserver(observer); 94} 95 96void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) { 97 observers_.RemoveObserver(observer); 98} 99 100std::vector<dbus::ObjectPath> 101FakeBluetoothGattCharacteristicClient::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 111FakeBluetoothGattCharacteristicClient::Properties* 112FakeBluetoothGattCharacteristicClient::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 129void 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 210void 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 229void 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 259void 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 266void 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 273void 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 290std::vector<uint8> 291FakeBluetoothGattCharacteristicClient::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 332bool 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 343