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 #include <brillo/dbus/exported_property_set.h>
6
7 #include <base/bind.h>
8 #include <dbus/bus.h>
9 #include <dbus/property.h> // For kPropertyInterface
10
11 #include <brillo/dbus/async_event_sequencer.h>
12 #include <brillo/dbus/dbus_object.h>
13 #include <brillo/errors/error_codes.h>
14
15 using brillo::dbus_utils::AsyncEventSequencer;
16
17 namespace brillo {
18
19 namespace dbus_utils {
20
ExportedPropertySet(dbus::Bus * bus)21 ExportedPropertySet::ExportedPropertySet(dbus::Bus* bus)
22 : bus_(bus), weak_ptr_factory_(this) {
23 }
24
OnPropertiesInterfaceExported(DBusInterface * prop_interface)25 void ExportedPropertySet::OnPropertiesInterfaceExported(
26 DBusInterface* prop_interface) {
27 signal_properties_changed_ =
28 prop_interface->RegisterSignalOfType<SignalPropertiesChanged>(
29 dbus::kPropertiesChanged);
30 }
31
GetPropertyWriter(const std::string & interface_name)32 ExportedPropertySet::PropertyWriter ExportedPropertySet::GetPropertyWriter(
33 const std::string& interface_name) {
34 return base::Bind(&ExportedPropertySet::WritePropertiesToDict,
35 weak_ptr_factory_.GetWeakPtr(),
36 interface_name);
37 }
38
RegisterProperty(const std::string & interface_name,const std::string & property_name,ExportedPropertyBase * exported_property)39 void ExportedPropertySet::RegisterProperty(
40 const std::string& interface_name,
41 const std::string& property_name,
42 ExportedPropertyBase* exported_property) {
43 bus_->AssertOnOriginThread();
44 auto& prop_map = properties_[interface_name];
45 auto res = prop_map.insert(std::make_pair(property_name, exported_property));
46 CHECK(res.second) << "Property '" << property_name << "' already exists";
47 // Technically, the property set exists longer than the properties themselves,
48 // so we could use Unretained here rather than a weak pointer.
49 ExportedPropertyBase::OnUpdateCallback cb =
50 base::Bind(&ExportedPropertySet::HandlePropertyUpdated,
51 weak_ptr_factory_.GetWeakPtr(),
52 interface_name,
53 property_name);
54 exported_property->SetUpdateCallback(cb);
55 }
56
HandleGetAll(const std::string & interface_name)57 VariantDictionary ExportedPropertySet::HandleGetAll(
58 const std::string& interface_name) {
59 bus_->AssertOnOriginThread();
60 return GetInterfaceProperties(interface_name);
61 }
62
GetInterfaceProperties(const std::string & interface_name) const63 VariantDictionary ExportedPropertySet::GetInterfaceProperties(
64 const std::string& interface_name) const {
65 VariantDictionary properties;
66 auto property_map_itr = properties_.find(interface_name);
67 if (property_map_itr != properties_.end()) {
68 for (const auto& kv : property_map_itr->second)
69 properties.insert(std::make_pair(kv.first, kv.second->GetValue()));
70 }
71 return properties;
72 }
73
WritePropertiesToDict(const std::string & interface_name,VariantDictionary * dict)74 void ExportedPropertySet::WritePropertiesToDict(
75 const std::string& interface_name,
76 VariantDictionary* dict) {
77 *dict = GetInterfaceProperties(interface_name);
78 }
79
HandleGet(brillo::ErrorPtr * error,const std::string & interface_name,const std::string & property_name,brillo::Any * result)80 bool ExportedPropertySet::HandleGet(brillo::ErrorPtr* error,
81 const std::string& interface_name,
82 const std::string& property_name,
83 brillo::Any* result) {
84 bus_->AssertOnOriginThread();
85 auto property_map_itr = properties_.find(interface_name);
86 if (property_map_itr == properties_.end()) {
87 brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
88 DBUS_ERROR_UNKNOWN_INTERFACE,
89 "No such interface on object.");
90 return false;
91 }
92 LOG(INFO) << "Looking for " << property_name << " on " << interface_name;
93 auto property_itr = property_map_itr->second.find(property_name);
94 if (property_itr == property_map_itr->second.end()) {
95 brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
96 DBUS_ERROR_UNKNOWN_PROPERTY,
97 "No such property on interface.");
98 return false;
99 }
100 *result = property_itr->second->GetValue();
101 return true;
102 }
103
HandleSet(brillo::ErrorPtr * error,const std::string & interface_name,const std::string & property_name,const brillo::Any & value)104 bool ExportedPropertySet::HandleSet(brillo::ErrorPtr* error,
105 const std::string& interface_name,
106 const std::string& property_name,
107 const brillo::Any& value) {
108 bus_->AssertOnOriginThread();
109 auto property_map_itr = properties_.find(interface_name);
110 if (property_map_itr == properties_.end()) {
111 brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
112 DBUS_ERROR_UNKNOWN_INTERFACE,
113 "No such interface on object.");
114 return false;
115 }
116 LOG(INFO) << "Looking for " << property_name << " on " << interface_name;
117 auto property_itr = property_map_itr->second.find(property_name);
118 if (property_itr == property_map_itr->second.end()) {
119 brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
120 DBUS_ERROR_UNKNOWN_PROPERTY,
121 "No such property on interface.");
122 return false;
123 }
124
125 return property_itr->second->SetValue(error, value);
126 }
127
HandlePropertyUpdated(const std::string & interface_name,const std::string & property_name,const ExportedPropertyBase * exported_property)128 void ExportedPropertySet::HandlePropertyUpdated(
129 const std::string& interface_name,
130 const std::string& property_name,
131 const ExportedPropertyBase* exported_property) {
132 bus_->AssertOnOriginThread();
133 // Send signal only if the object has been exported successfully.
134 // This could happen when a property value is changed (which triggers
135 // the notification) before D-Bus interface is completely exported/claimed.
136 auto signal = signal_properties_changed_.lock();
137 if (!signal)
138 return;
139 VariantDictionary changed_properties{
140 {property_name, exported_property->GetValue()}};
141 // The interface specification tells us to include this list of properties
142 // which have changed, but for whom no value is conveyed. Currently, we
143 // don't do anything interesting here.
144 std::vector<std::string> invalidated_properties; // empty.
145 signal->Send(interface_name, changed_properties, invalidated_properties);
146 }
147
NotifyPropertyChanged()148 void ExportedPropertyBase::NotifyPropertyChanged() {
149 // These is a brief period after the construction of an ExportedProperty
150 // when this callback is not initialized because the property has not
151 // been registered with the parent ExportedPropertySet. During this period
152 // users should be initializing values via SetValue, and no notifications
153 // should be triggered by the ExportedPropertySet.
154 if (!on_update_callback_.is_null()) {
155 on_update_callback_.Run(this);
156 }
157 }
158
SetUpdateCallback(const OnUpdateCallback & cb)159 void ExportedPropertyBase::SetUpdateCallback(const OnUpdateCallback& cb) {
160 on_update_callback_ = cb;
161 }
162
SetAccessMode(ExportedPropertyBase::Access access_mode)163 void ExportedPropertyBase::SetAccessMode(
164 ExportedPropertyBase::Access access_mode) {
165 access_mode_ = access_mode;
166 }
167
GetAccessMode() const168 ExportedPropertyBase::Access ExportedPropertyBase::GetAccessMode() const {
169 return access_mode_;
170 }
171
172 } // namespace dbus_utils
173
174 } // namespace brillo
175