// Copyright 2014 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef LIBBRILLO_BRILLO_DBUS_EXPORTED_PROPERTY_SET_H_ #define LIBBRILLO_BRILLO_DBUS_EXPORTED_PROPERTY_SET_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace brillo { namespace dbus_utils { // This class may be used to implement the org.freedesktop.DBus.Properties // interface. It sends the update signal on property updates: // // org.freedesktop.DBus.Properties.PropertiesChanged ( // STRING interface_name, // DICT changed_properties, // ARRAY invalidated_properties); // // // and implements the required methods of the interface: // // org.freedesktop.DBus.Properties.Get(in STRING interface_name, // in STRING property_name, // out VARIANT value); // org.freedesktop.DBus.Properties.Set(in STRING interface_name, // in STRING property_name, // in VARIANT value); // org.freedesktop.DBus.Properties.GetAll(in STRING interface_name, // out DICT props); // // This class is very similar to the PropertySet class in Chrome, except that // it allows objects to expose properties rather than to consume them. // It is used as part of DBusObject to implement D-Bus object properties on // registered interfaces. See description of DBusObject class for more details. class DBusInterface; class DBusObject; class BRILLO_EXPORT ExportedPropertyBase { public: enum class Access { kReadOnly, kWriteOnly, kReadWrite, }; ExportedPropertyBase() = default; virtual ~ExportedPropertyBase() = default; using OnUpdateCallback = base::Callback; // Called by ExportedPropertySet to register a callback. This callback // triggers ExportedPropertySet to send a signal from the properties // interface of the exported object. virtual void SetUpdateCallback(const OnUpdateCallback& cb); // Clears the update callback that was previously set with SetUpdateCallback. virtual void ClearUpdateCallback(); // Returns the contained value as Any. virtual brillo::Any GetValue() const = 0; virtual bool SetValue(brillo::ErrorPtr* error, const brillo::Any& value) = 0; void SetAccessMode(Access access_mode); Access GetAccessMode() const; protected: // Notify the listeners of OnUpdateCallback that the property has changed. void NotifyPropertyChanged(); private: OnUpdateCallback on_update_callback_; // Default to read-only. Access access_mode_{Access::kReadOnly}; }; class BRILLO_EXPORT ExportedPropertySet { public: using PropertyWriter = base::Callback; explicit ExportedPropertySet(dbus::Bus* bus); virtual ~ExportedPropertySet() = default; // Called to notify ExportedPropertySet that the Properties interface of the // D-Bus object has been exported successfully and property notification // signals can be sent out. void OnPropertiesInterfaceExported(DBusInterface* prop_interface); // Return a callback that knows how to write this property set's properties // to a message. This writer retains a weak pointer to this, and must // only be invoked on the same thread as the rest of ExportedPropertySet. PropertyWriter GetPropertyWriter(const std::string& interface_name); void RegisterProperty(const std::string& interface_name, const std::string& property_name, ExportedPropertyBase* exported_property); // Unregisters a property from this exported property set. void UnregisterProperty(const std::string& interface_name, const std::string& property_name); // D-Bus methods for org.freedesktop.DBus.Properties interface. VariantDictionary HandleGetAll(const std::string& interface_name); bool HandleGet(brillo::ErrorPtr* error, const std::string& interface_name, const std::string& property_name, brillo::Any* result); // While Properties.Set has a handler to complete the interface, we don't // support writable properties. This is almost a feature, since bindings for // many languages don't support errors coming back from invalid writes. // Instead, use setters in exposed interfaces. bool HandleSet(brillo::ErrorPtr* error, const std::string& interface_name, const std::string& property_name, const brillo::Any& value); // Returns a string-to-variant map of all the properties for the given // interface and their values. VariantDictionary GetInterfaceProperties( const std::string& interface_name) const; private: // Used to write the dictionary of string->variant to a message. // This dictionary represents the property name/value pairs for the // given interface. BRILLO_PRIVATE void WritePropertiesToDict(const std::string& interface_name, VariantDictionary* dict); BRILLO_PRIVATE void HandlePropertyUpdated( const std::string& interface_name, const std::string& property_name, const ExportedPropertyBase* exported_property); dbus::Bus* bus_; // weak; owned by outer DBusObject containing this object. // This is a map from interface name -> property name -> pointer to property. std::map> properties_; // D-Bus callbacks may last longer the property set exporting those methods. base::WeakPtrFactory weak_ptr_factory_; using SignalPropertiesChanged = DBusSignal>; std::weak_ptr signal_properties_changed_; friend class DBusObject; friend class ExportedPropertySetTest; DISALLOW_COPY_AND_ASSIGN(ExportedPropertySet); }; template class ExportedProperty : public ExportedPropertyBase { public: ExportedProperty() = default; ~ExportedProperty() override = default; // Retrieves the current value. const T& value() const { return value_; } // Set the value exposed to remote applications. This triggers notifications // of changes over the Properties interface. void SetValue(const T& new_value) { if (value_ != new_value) { value_ = new_value; this->NotifyPropertyChanged(); } } // Set the validator for value checking when setting the property by remote // application. void SetValidator( const base::Callback& validator) { validator_ = validator; } // Implementation provided by specialization. brillo::Any GetValue() const override { return value_; } bool SetValue(brillo::ErrorPtr* error, const brillo::Any& value) override { if (GetAccessMode() == ExportedPropertyBase::Access::kReadOnly) { brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, DBUS_ERROR_PROPERTY_READ_ONLY, "Property is read-only."); return false; } if (!value.IsTypeCompatible()) { brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, DBUS_ERROR_INVALID_ARGS, "Argument type mismatched."); return false; } if (value_ == value.Get()) { // No change to the property value, nothing to be done. return true; } if (!validator_.is_null() && !validator_.Run(error, value.Get())) { return false; } SetValue(value.Get()); return true; } private: T value_{}; base::Callback validator_; DISALLOW_COPY_AND_ASSIGN(ExportedProperty); }; } // namespace dbus_utils } // namespace brillo #endif // LIBBRILLO_BRILLO_DBUS_EXPORTED_PROPERTY_SET_H_