Home | History | Annotate | Download | only in dbus
      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