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/bluetooth_gatt_characteristic_service_provider.h" 6 7 #include "base/bind.h" 8 #include "base/logging.h" 9 #include "base/memory/weak_ptr.h" 10 #include "base/strings/string_util.h" 11 #include "base/sys_info.h" 12 #include "base/threading/platform_thread.h" 13 #include "chromeos/dbus/fake_bluetooth_gatt_characteristic_service_provider.h" 14 #include "dbus/exported_object.h" 15 #include "dbus/message.h" 16 #include "third_party/cros_system_api/dbus/service_constants.h" 17 18 namespace chromeos { 19 namespace { 20 const char kErrorInvalidArgs[] = 21 "org.freedesktop.DBus.Error.InvalidArgs"; 22 const char kErrorPropertyReadOnly[] = 23 "org.freedesktop.DBus.Error.PropertyReadOnly"; 24 const char kErrorFailed[] = 25 "org.freedesktop.DBus.Error.Failed"; 26 } // namespace 27 28 // The BluetoothGattCharacteristicServiceProvider implementation used in 29 // production. 30 class BluetoothGattCharacteristicServiceProviderImpl 31 : public BluetoothGattCharacteristicServiceProvider { 32 public: 33 BluetoothGattCharacteristicServiceProviderImpl( 34 dbus::Bus* bus, 35 const dbus::ObjectPath& object_path, 36 Delegate* delegate, 37 const std::string& uuid, 38 const std::vector<std::string>& flags, 39 const std::vector<std::string>& permissions, 40 const dbus::ObjectPath& service_path) 41 : origin_thread_id_(base::PlatformThread::CurrentId()), 42 uuid_(uuid), 43 bus_(bus), 44 delegate_(delegate), 45 object_path_(object_path), 46 service_path_(service_path), 47 weak_ptr_factory_(this) { 48 VLOG(1) << "Created Bluetooth GATT characteristic: " << object_path.value() 49 << " UUID: " << uuid; 50 DCHECK(bus_); 51 DCHECK(delegate_); 52 DCHECK(!uuid_.empty()); 53 DCHECK(object_path_.IsValid()); 54 DCHECK(service_path_.IsValid()); 55 DCHECK(StartsWithASCII( 56 object_path_.value(), service_path_.value() + "/", true)); 57 58 exported_object_ = bus_->GetExportedObject(object_path_); 59 60 exported_object_->ExportMethod( 61 dbus::kDBusPropertiesInterface, 62 dbus::kDBusPropertiesGet, 63 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::Get, 64 weak_ptr_factory_.GetWeakPtr()), 65 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported, 66 weak_ptr_factory_.GetWeakPtr())); 67 68 exported_object_->ExportMethod( 69 dbus::kDBusPropertiesInterface, 70 dbus::kDBusPropertiesSet, 71 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::Set, 72 weak_ptr_factory_.GetWeakPtr()), 73 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported, 74 weak_ptr_factory_.GetWeakPtr())); 75 76 exported_object_->ExportMethod( 77 dbus::kDBusPropertiesInterface, 78 dbus::kDBusPropertiesGetAll, 79 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::GetAll, 80 weak_ptr_factory_.GetWeakPtr()), 81 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported, 82 weak_ptr_factory_.GetWeakPtr())); 83 } 84 85 virtual ~BluetoothGattCharacteristicServiceProviderImpl() { 86 VLOG(1) << "Cleaning up Bluetooth GATT characteristic: " 87 << object_path_.value(); 88 bus_->UnregisterExportedObject(object_path_); 89 } 90 91 // BluetoothGattCharacteristicServiceProvider override. 92 virtual void SendValueChanged(const std::vector<uint8>& value) OVERRIDE { 93 VLOG(2) << "Emitting a PropertiesChanged signal for characteristic value."; 94 dbus::Signal signal( 95 dbus::kDBusPropertiesInterface, 96 dbus::kDBusPropertiesChangedSignal); 97 dbus::MessageWriter writer(&signal); 98 dbus::MessageWriter array_writer(NULL); 99 dbus::MessageWriter dict_entry_writer(NULL); 100 dbus::MessageWriter variant_writer(NULL); 101 102 // interface_name 103 writer.AppendString( 104 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface); 105 106 // changed_properties 107 writer.OpenArray("{sv}", &array_writer); 108 array_writer.OpenDictEntry(&dict_entry_writer); 109 dict_entry_writer.AppendString( 110 bluetooth_gatt_characteristic::kValueProperty); 111 dict_entry_writer.OpenVariant("ay", &variant_writer); 112 variant_writer.AppendArrayOfBytes(value.data(), value.size()); 113 dict_entry_writer.CloseContainer(&variant_writer); 114 array_writer.CloseContainer(&dict_entry_writer); 115 writer.CloseContainer(&array_writer); 116 117 // invalidated_properties. 118 writer.OpenArray("s", &array_writer); 119 writer.CloseContainer(&array_writer); 120 121 exported_object_->SendSignal(&signal); 122 } 123 124 private: 125 // Returns true if the current thread is on the origin thread. 126 bool OnOriginThread() { 127 return base::PlatformThread::CurrentId() == origin_thread_id_; 128 } 129 130 // Called by dbus:: when the Bluetooth daemon fetches a single property of 131 // the characteristic. 132 void Get(dbus::MethodCall* method_call, 133 dbus::ExportedObject::ResponseSender response_sender) { 134 VLOG(2) << "BluetoothGattCharacteristicServiceProvider::Get: " 135 << object_path_.value(); 136 DCHECK(OnOriginThread()); 137 138 dbus::MessageReader reader(method_call); 139 140 std::string interface_name; 141 std::string property_name; 142 if (!reader.PopString(&interface_name) || 143 !reader.PopString(&property_name) || 144 reader.HasMoreData()) { 145 scoped_ptr<dbus::ErrorResponse> error_response = 146 dbus::ErrorResponse::FromMethodCall( 147 method_call, kErrorInvalidArgs, "Expected 'ss'."); 148 response_sender.Run(error_response.PassAs<dbus::Response>()); 149 return; 150 } 151 152 // Only the GATT characteristic interface is supported. 153 if (interface_name != 154 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface) { 155 scoped_ptr<dbus::ErrorResponse> error_response = 156 dbus::ErrorResponse::FromMethodCall( 157 method_call, kErrorInvalidArgs, 158 "No such interface: '" + interface_name + "'."); 159 response_sender.Run(error_response.PassAs<dbus::Response>()); 160 return; 161 } 162 163 // If getting the "Value" property, obtain the value from the delegate. 164 if (property_name == bluetooth_gatt_characteristic::kValueProperty) { 165 DCHECK(delegate_); 166 delegate_->GetCharacteristicValue( 167 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnGet, 168 weak_ptr_factory_.GetWeakPtr(), 169 method_call, response_sender), 170 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure, 171 weak_ptr_factory_.GetWeakPtr(), 172 method_call, response_sender)); 173 return; 174 } 175 176 scoped_ptr<dbus::Response> response = 177 dbus::Response::FromMethodCall(method_call); 178 dbus::MessageWriter writer(response.get()); 179 dbus::MessageWriter variant_writer(NULL); 180 181 // TODO(armansito): Process the "Flags" and "Permissions" properties below. 182 if (property_name == bluetooth_gatt_characteristic::kUUIDProperty) { 183 writer.OpenVariant("s", &variant_writer); 184 variant_writer.AppendString(uuid_); 185 writer.CloseContainer(&variant_writer); 186 } else if (property_name == 187 bluetooth_gatt_characteristic::kServiceProperty) { 188 writer.OpenVariant("o", &variant_writer); 189 variant_writer.AppendObjectPath(service_path_); 190 writer.CloseContainer(&variant_writer); 191 } else { 192 response = dbus::ErrorResponse::FromMethodCall( 193 method_call, kErrorInvalidArgs, 194 "No such property: '" + property_name + "'.") 195 .PassAs<dbus::Response>(); 196 } 197 198 response_sender.Run(response.Pass()); 199 } 200 201 // Called by dbus:: when the Bluetooth daemon sets a single property of the 202 // characteristic. 203 void Set(dbus::MethodCall* method_call, 204 dbus::ExportedObject::ResponseSender response_sender) { 205 VLOG(2) << "BluetoothGattCharacteristicServiceProvider::Set: " 206 << object_path_.value(); 207 DCHECK(OnOriginThread()); 208 209 dbus::MessageReader reader(method_call); 210 211 std::string interface_name; 212 std::string property_name; 213 dbus::MessageReader variant_reader(NULL); 214 if (!reader.PopString(&interface_name) || 215 !reader.PopString(&property_name) || 216 !reader.PopVariant(&variant_reader) || 217 reader.HasMoreData()) { 218 scoped_ptr<dbus::ErrorResponse> error_response = 219 dbus::ErrorResponse::FromMethodCall( 220 method_call, kErrorInvalidArgs, "Expected 'ssv'."); 221 response_sender.Run(error_response.PassAs<dbus::Response>()); 222 return; 223 } 224 225 // Only the GATT characteristic interface is allowed. 226 if (interface_name != 227 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface) { 228 scoped_ptr<dbus::ErrorResponse> error_response = 229 dbus::ErrorResponse::FromMethodCall( 230 method_call, kErrorInvalidArgs, 231 "No such interface: '" + interface_name + "'."); 232 response_sender.Run(error_response.PassAs<dbus::Response>()); 233 return; 234 } 235 236 // Only the "Value" property is writeable. 237 if (property_name != bluetooth_gatt_characteristic::kValueProperty) { 238 std::string error_name; 239 std::string error_message; 240 if (property_name == bluetooth_gatt_characteristic::kUUIDProperty || 241 property_name == bluetooth_gatt_characteristic::kServiceProperty) { 242 error_name = kErrorPropertyReadOnly; 243 error_message = "Read-only property: '" + property_name + "'."; 244 } else { 245 error_name = kErrorInvalidArgs; 246 error_message = "No such property: '" + property_name + "'."; 247 } 248 scoped_ptr<dbus::ErrorResponse> error_response = 249 dbus::ErrorResponse::FromMethodCall( 250 method_call, error_name, error_message); 251 response_sender.Run(error_response.PassAs<dbus::Response>()); 252 return; 253 } 254 255 // Obtain the value. 256 const uint8* bytes = NULL; 257 size_t length = 0; 258 if (!variant_reader.PopArrayOfBytes(&bytes, &length)) { 259 scoped_ptr<dbus::ErrorResponse> error_response = 260 dbus::ErrorResponse::FromMethodCall( 261 method_call, kErrorInvalidArgs, 262 "Property '" + property_name + "' has type 'ay'."); 263 response_sender.Run(error_response.PassAs<dbus::Response>()); 264 return; 265 } 266 267 // Pass the set request onto the delegate. 268 std::vector<uint8> value(bytes, bytes + length); 269 DCHECK(delegate_); 270 delegate_->SetCharacteristicValue( 271 value, 272 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnSet, 273 weak_ptr_factory_.GetWeakPtr(), 274 method_call, response_sender), 275 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure, 276 weak_ptr_factory_.GetWeakPtr(), 277 method_call, response_sender)); 278 } 279 280 // Called by dbus:: when the Bluetooth daemon fetches all properties of the 281 // characteristic. 282 void GetAll(dbus::MethodCall* method_call, 283 dbus::ExportedObject::ResponseSender response_sender) { 284 VLOG(2) << "BluetoothGattCharacteristicServiceProvider::GetAll: " 285 << object_path_.value(); 286 DCHECK(OnOriginThread()); 287 288 dbus::MessageReader reader(method_call); 289 290 std::string interface_name; 291 if (!reader.PopString(&interface_name) || reader.HasMoreData()) { 292 scoped_ptr<dbus::ErrorResponse> error_response = 293 dbus::ErrorResponse::FromMethodCall( 294 method_call, kErrorInvalidArgs, "Expected 's'."); 295 response_sender.Run(error_response.PassAs<dbus::Response>()); 296 return; 297 } 298 299 // Only the GATT characteristic interface is supported. 300 if (interface_name != 301 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface) { 302 scoped_ptr<dbus::ErrorResponse> error_response = 303 dbus::ErrorResponse::FromMethodCall( 304 method_call, kErrorInvalidArgs, 305 "No such interface: '" + interface_name + "'."); 306 response_sender.Run(error_response.PassAs<dbus::Response>()); 307 return; 308 } 309 310 // Try to obtain the value from the delegate. We will construct the 311 // response in the success callback. 312 DCHECK(delegate_); 313 delegate_->GetCharacteristicValue( 314 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnGetAll, 315 weak_ptr_factory_.GetWeakPtr(), 316 method_call, response_sender), 317 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure, 318 weak_ptr_factory_.GetWeakPtr(), 319 method_call, response_sender)); 320 } 321 322 // Called by dbus:: when a method is exported. 323 void OnExported(const std::string& interface_name, 324 const std::string& method_name, 325 bool success) { 326 LOG_IF(WARNING, !success) << "Failed to export " 327 << interface_name << "." << method_name; 328 } 329 330 // Called by the Delegate in response to a method to call to get all 331 // properties, in which the delegate has successfully returned the 332 // characteristic value. 333 void OnGetAll(dbus::MethodCall* method_call, 334 dbus::ExportedObject::ResponseSender response_sender, 335 const std::vector<uint8>& value) { 336 VLOG(2) << "Characteristic value obtained from delegate. Responding to " 337 << "GetAll."; 338 339 scoped_ptr<dbus::Response> response = 340 dbus::Response::FromMethodCall(method_call); 341 dbus::MessageWriter writer(response.get()); 342 dbus::MessageWriter array_writer(NULL); 343 dbus::MessageWriter dict_entry_writer(NULL); 344 dbus::MessageWriter variant_writer(NULL); 345 346 writer.OpenArray("{sv}", &array_writer); 347 348 array_writer.OpenDictEntry(&dict_entry_writer); 349 dict_entry_writer.AppendString( 350 bluetooth_gatt_characteristic::kUUIDProperty); 351 dict_entry_writer.AppendVariantOfString(uuid_); 352 array_writer.CloseContainer(&dict_entry_writer); 353 354 array_writer.OpenDictEntry(&dict_entry_writer); 355 dict_entry_writer.AppendString( 356 bluetooth_gatt_characteristic::kServiceProperty); 357 dict_entry_writer.AppendVariantOfObjectPath(service_path_); 358 array_writer.CloseContainer(&dict_entry_writer); 359 360 array_writer.OpenDictEntry(&dict_entry_writer); 361 dict_entry_writer.AppendString( 362 bluetooth_gatt_characteristic::kValueProperty); 363 dict_entry_writer.OpenVariant("ay", &variant_writer); 364 variant_writer.AppendArrayOfBytes(value.data(), value.size()); 365 dict_entry_writer.CloseContainer(&variant_writer); 366 array_writer.CloseContainer(&dict_entry_writer); 367 368 // TODO(armansito): Process Flags & Permissions properties. 369 370 writer.CloseContainer(&array_writer); 371 372 response_sender.Run(response.Pass()); 373 } 374 375 // Called by the Delegate in response to a successful method call to get the 376 // characteristic value. 377 void OnGet(dbus::MethodCall* method_call, 378 dbus::ExportedObject::ResponseSender response_sender, 379 const std::vector<uint8>& value) { 380 VLOG(2) << "Returning characteristic value obtained from delegate."; 381 scoped_ptr<dbus::Response> response = 382 dbus::Response::FromMethodCall(method_call); 383 dbus::MessageWriter writer(response.get()); 384 dbus::MessageWriter variant_writer(NULL); 385 386 writer.OpenVariant("ay", &variant_writer); 387 variant_writer.AppendArrayOfBytes(value.data(), value.size()); 388 writer.CloseContainer(&variant_writer); 389 390 response_sender.Run(response.Pass()); 391 } 392 393 // Called by the Delegate in response to a successful method call to set the 394 // characteristic value. 395 void OnSet(dbus::MethodCall* method_call, 396 dbus::ExportedObject::ResponseSender response_sender) { 397 VLOG(2) << "Successfully set characteristic value. Return success."; 398 response_sender.Run(dbus::Response::FromMethodCall(method_call)); 399 } 400 401 // Called by the Delegate in response to a failed method call to get or set 402 // the characteristic value. 403 void OnFailure(dbus::MethodCall* method_call, 404 dbus::ExportedObject::ResponseSender response_sender) { 405 VLOG(2) << "Failed to get/set characteristic value. Report error."; 406 scoped_ptr<dbus::ErrorResponse> error_response = 407 dbus::ErrorResponse::FromMethodCall( 408 method_call, kErrorFailed, 409 "Failed to get/set characteristic value."); 410 response_sender.Run(error_response.PassAs<dbus::Response>()); 411 } 412 413 // Origin thread (i.e. the UI thread in production). 414 base::PlatformThreadId origin_thread_id_; 415 416 // 128-bit characteristic UUID of this object. 417 std::string uuid_; 418 419 // D-Bus bus object is exported on, not owned by this object and must 420 // outlive it. 421 dbus::Bus* bus_; 422 423 // Incoming methods to get and set the "Value" property are passed on to the 424 // delegate and callbacks passed to generate a reply. |delegate_| is generally 425 // the object that owns this one and must outlive it. 426 Delegate* delegate_; 427 428 // D-Bus object path of object we are exporting, kept so we can unregister 429 // again in our destructor. 430 dbus::ObjectPath object_path_; 431 432 // Object path of the GATT service that the exported characteristic belongs 433 // to. 434 dbus::ObjectPath service_path_; 435 436 // D-Bus object we are exporting, owned by this object. 437 scoped_refptr<dbus::ExportedObject> exported_object_; 438 439 // Weak pointer factory for generating 'this' pointers that might live longer 440 // than we do. 441 // Note: This should remain the last member so it'll be destroyed and 442 // invalidate its weak pointers before any other members are destroyed. 443 base::WeakPtrFactory<BluetoothGattCharacteristicServiceProviderImpl> 444 weak_ptr_factory_; 445 446 DISALLOW_COPY_AND_ASSIGN(BluetoothGattCharacteristicServiceProviderImpl); 447 }; 448 449 BluetoothGattCharacteristicServiceProvider:: 450 BluetoothGattCharacteristicServiceProvider() { 451 } 452 453 BluetoothGattCharacteristicServiceProvider:: 454 ~BluetoothGattCharacteristicServiceProvider() { 455 } 456 457 // static 458 BluetoothGattCharacteristicServiceProvider* 459 BluetoothGattCharacteristicServiceProvider::Create( 460 dbus::Bus* bus, 461 const dbus::ObjectPath& object_path, 462 Delegate* delegate, 463 const std::string& uuid, 464 const std::vector<std::string>& flags, 465 const std::vector<std::string>& permissions, 466 const dbus::ObjectPath& service_path) { 467 if (base::SysInfo::IsRunningOnChromeOS()) { 468 return new BluetoothGattCharacteristicServiceProviderImpl( 469 bus, object_path, delegate, uuid, flags, permissions, service_path); 470 } 471 return new FakeBluetoothGattCharacteristicServiceProvider( 472 object_path, delegate, uuid, flags, permissions, service_path); 473 } 474 475 } // namespace chromeos 476