Home | History | Annotate | Download | only in dbus
      1 // Copyright 2014 The Chromium OS 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 #ifndef LIBBRILLO_BRILLO_DBUS_EXPORTED_PROPERTY_SET_H_
      6 #define LIBBRILLO_BRILLO_DBUS_EXPORTED_PROPERTY_SET_H_
      7 
      8 #include <stdint.h>
      9 
     10 #include <map>
     11 #include <string>
     12 #include <vector>
     13 
     14 #include <base/memory/weak_ptr.h>
     15 #include <brillo/any.h>
     16 #include <brillo/brillo_export.h>
     17 #include <brillo/dbus/dbus_signal.h>
     18 #include <brillo/errors/error.h>
     19 #include <brillo/errors/error_codes.h>
     20 #include <brillo/variant_dictionary.h>
     21 #include <dbus/exported_object.h>
     22 #include <dbus/message.h>
     23 
     24 namespace brillo {
     25 
     26 namespace dbus_utils {
     27 
     28 // This class may be used to implement the org.freedesktop.DBus.Properties
     29 // interface.  It sends the update signal on property updates:
     30 //
     31 //   org.freedesktop.DBus.Properties.PropertiesChanged (
     32 //       STRING interface_name,
     33 //       DICT<STRING,VARIANT> changed_properties,
     34 //       ARRAY<STRING> invalidated_properties);
     35 //
     36 //
     37 // and implements the required methods of the interface:
     38 //
     39 //   org.freedesktop.DBus.Properties.Get(in STRING interface_name,
     40 //                                       in STRING property_name,
     41 //                                       out VARIANT value);
     42 //   org.freedesktop.DBus.Properties.Set(in STRING interface_name,
     43 //                                       in STRING property_name,
     44 //                                       in VARIANT value);
     45 //   org.freedesktop.DBus.Properties.GetAll(in STRING interface_name,
     46 //                                          out DICT<STRING,VARIANT> props);
     47 //
     48 //  This class is very similar to the PropertySet class in Chrome, except that
     49 //  it allows objects to expose properties rather than to consume them.
     50 //  It is used as part of DBusObject to implement D-Bus object properties on
     51 //  registered interfaces. See description of DBusObject class for more details.
     52 
     53 class DBusInterface;
     54 class DBusObject;
     55 
     56 class BRILLO_EXPORT ExportedPropertyBase {
     57  public:
     58   enum class Access {
     59     kReadOnly,
     60     kWriteOnly,
     61     kReadWrite,
     62   };
     63 
     64   ExportedPropertyBase() = default;
     65   virtual ~ExportedPropertyBase() = default;
     66 
     67   using OnUpdateCallback = base::Callback<void(const ExportedPropertyBase*)>;
     68 
     69   // Called by ExportedPropertySet to register a callback.  This callback
     70   // triggers ExportedPropertySet to send a signal from the properties
     71   // interface of the exported object.
     72   virtual void SetUpdateCallback(const OnUpdateCallback& cb);
     73 
     74   // Returns the contained value as Any.
     75   virtual brillo::Any GetValue() const = 0;
     76 
     77   virtual bool SetValue(brillo::ErrorPtr* error,
     78                         const brillo::Any& value) = 0;
     79 
     80   void SetAccessMode(Access access_mode);
     81   Access GetAccessMode() const;
     82 
     83  protected:
     84   // Notify the listeners of OnUpdateCallback that the property has changed.
     85   void NotifyPropertyChanged();
     86 
     87  private:
     88   OnUpdateCallback on_update_callback_;
     89   // Default to read-only.
     90   Access access_mode_{Access::kReadOnly};
     91 };
     92 
     93 class BRILLO_EXPORT ExportedPropertySet {
     94  public:
     95   using PropertyWriter = base::Callback<void(VariantDictionary* dict)>;
     96 
     97   explicit ExportedPropertySet(dbus::Bus* bus);
     98   virtual ~ExportedPropertySet() = default;
     99 
    100   // Called to notify ExportedPropertySet that the Properties interface of the
    101   // D-Bus object has been exported successfully and property notification
    102   // signals can be sent out.
    103   void OnPropertiesInterfaceExported(DBusInterface* prop_interface);
    104 
    105   // Return a callback that knows how to write this property set's properties
    106   // to a message.  This writer retains a weak pointer to this, and must
    107   // only be invoked on the same thread as the rest of ExportedPropertySet.
    108   PropertyWriter GetPropertyWriter(const std::string& interface_name);
    109 
    110   void RegisterProperty(const std::string& interface_name,
    111                         const std::string& property_name,
    112                         ExportedPropertyBase* exported_property);
    113 
    114   // D-Bus methods for org.freedesktop.DBus.Properties interface.
    115   VariantDictionary HandleGetAll(const std::string& interface_name);
    116   bool HandleGet(brillo::ErrorPtr* error,
    117                  const std::string& interface_name,
    118                  const std::string& property_name,
    119                  brillo::Any* result);
    120   // While Properties.Set has a handler to complete the interface,  we don't
    121   // support writable properties.  This is almost a feature, since bindings for
    122   // many languages don't support errors coming back from invalid writes.
    123   // Instead, use setters in exposed interfaces.
    124   bool HandleSet(brillo::ErrorPtr* error,
    125                  const std::string& interface_name,
    126                  const std::string& property_name,
    127                  const brillo::Any& value);
    128   // Returns a string-to-variant map of all the properties for the given
    129   // interface and their values.
    130   VariantDictionary GetInterfaceProperties(
    131       const std::string& interface_name) const;
    132 
    133  private:
    134   // Used to write the dictionary of string->variant to a message.
    135   // This dictionary represents the property name/value pairs for the
    136   // given interface.
    137   BRILLO_PRIVATE void WritePropertiesToDict(const std::string& interface_name,
    138                                             VariantDictionary* dict);
    139   BRILLO_PRIVATE void HandlePropertyUpdated(
    140       const std::string& interface_name,
    141       const std::string& property_name,
    142       const ExportedPropertyBase* exported_property);
    143 
    144   dbus::Bus* bus_;  // weak; owned by outer DBusObject containing this object.
    145   // This is a map from interface name -> property name -> pointer to property.
    146   std::map<std::string, std::map<std::string, ExportedPropertyBase*>>
    147       properties_;
    148 
    149   // D-Bus callbacks may last longer the property set exporting those methods.
    150   base::WeakPtrFactory<ExportedPropertySet> weak_ptr_factory_;
    151 
    152   using SignalPropertiesChanged =
    153       DBusSignal<std::string, VariantDictionary, std::vector<std::string>>;
    154 
    155   std::weak_ptr<SignalPropertiesChanged> signal_properties_changed_;
    156 
    157   friend class DBusObject;
    158   friend class ExportedPropertySetTest;
    159   DISALLOW_COPY_AND_ASSIGN(ExportedPropertySet);
    160 };
    161 
    162 template<typename T>
    163 class ExportedProperty : public ExportedPropertyBase {
    164  public:
    165   ExportedProperty() = default;
    166   ~ExportedProperty() override = default;
    167 
    168   // Retrieves the current value.
    169   const T& value() const { return value_; }
    170 
    171   // Set the value exposed to remote applications.  This triggers notifications
    172   // of changes over the Properties interface.
    173   void SetValue(const T& new_value) {
    174     if (value_ != new_value) {
    175       value_ = new_value;
    176       this->NotifyPropertyChanged();
    177     }
    178   }
    179 
    180   // Set the validator for value checking when setting the property by remote
    181   // application.
    182   void SetValidator(
    183       const base::Callback<bool(brillo::ErrorPtr*, const T&)>& validator) {
    184     validator_ = validator;
    185   }
    186 
    187   // Implementation provided by specialization.
    188   brillo::Any GetValue() const override { return value_; }
    189 
    190   bool SetValue(brillo::ErrorPtr* error,
    191                 const brillo::Any& value) override {
    192     if (GetAccessMode() == ExportedPropertyBase::Access::kReadOnly) {
    193       brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
    194                            DBUS_ERROR_PROPERTY_READ_ONLY,
    195                            "Property is read-only.");
    196       return false;
    197     }
    198     if (!value.IsTypeCompatible<T>()) {
    199       brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
    200                            DBUS_ERROR_INVALID_ARGS,
    201                            "Argument type mismatched.");
    202       return false;
    203     }
    204     if (value_ == value.Get<T>()) {
    205       // No change to the property value, nothing to be done.
    206       return true;
    207     }
    208     if (!validator_.is_null() && !validator_.Run(error, value.Get<T>())) {
    209       return false;
    210     }
    211     value_ = value.Get<T>();
    212     return true;
    213   }
    214 
    215  private:
    216   T value_{};
    217   base::Callback<bool(brillo::ErrorPtr*, const T&)> validator_;
    218 
    219   DISALLOW_COPY_AND_ASSIGN(ExportedProperty);
    220 };
    221 
    222 }  // namespace dbus_utils
    223 
    224 }  // namespace brillo
    225 
    226 #endif  // LIBBRILLO_BRILLO_DBUS_EXPORTED_PROPERTY_SET_H_
    227