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
UnregisterProperty(const std::string & interface_name,const std::string & property_name)57 void ExportedPropertySet::UnregisterProperty(const std::string& interface_name,
58 const std::string& property_name) {
59 bus_->AssertOnOriginThread();
60 auto& prop_map = properties_[interface_name];
61 auto prop_iter = prop_map.find(property_name);
62 CHECK(prop_iter != prop_map.end())
63 << "Property '" << property_name << "' doesn't exist";
64 prop_iter->second->ClearUpdateCallback();
65 prop_map.erase(prop_iter);
66 }
67
HandleGetAll(const std::string & interface_name)68 VariantDictionary ExportedPropertySet::HandleGetAll(
69 const std::string& interface_name) {
70 bus_->AssertOnOriginThread();
71 return GetInterfaceProperties(interface_name);
72 }
73
GetInterfaceProperties(const std::string & interface_name) const74 VariantDictionary ExportedPropertySet::GetInterfaceProperties(
75 const std::string& interface_name) const {
76 VariantDictionary properties;
77 auto property_map_itr = properties_.find(interface_name);
78 if (property_map_itr != properties_.end()) {
79 for (const auto& kv : property_map_itr->second)
80 properties.insert(std::make_pair(kv.first, kv.second->GetValue()));
81 }
82 return properties;
83 }
84
WritePropertiesToDict(const std::string & interface_name,VariantDictionary * dict)85 void ExportedPropertySet::WritePropertiesToDict(
86 const std::string& interface_name,
87 VariantDictionary* dict) {
88 *dict = GetInterfaceProperties(interface_name);
89 }
90
HandleGet(brillo::ErrorPtr * error,const std::string & interface_name,const std::string & property_name,brillo::Any * result)91 bool ExportedPropertySet::HandleGet(brillo::ErrorPtr* error,
92 const std::string& interface_name,
93 const std::string& property_name,
94 brillo::Any* result) {
95 bus_->AssertOnOriginThread();
96 auto property_map_itr = properties_.find(interface_name);
97 if (property_map_itr == properties_.end()) {
98 brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
99 DBUS_ERROR_UNKNOWN_INTERFACE,
100 "No such interface on object.");
101 return false;
102 }
103 LOG(INFO) << "Looking for " << property_name << " on " << interface_name;
104 auto property_itr = property_map_itr->second.find(property_name);
105 if (property_itr == property_map_itr->second.end()) {
106 brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
107 DBUS_ERROR_UNKNOWN_PROPERTY,
108 "No such property on interface.");
109 return false;
110 }
111 *result = property_itr->second->GetValue();
112 return true;
113 }
114
HandleSet(brillo::ErrorPtr * error,const std::string & interface_name,const std::string & property_name,const brillo::Any & value)115 bool ExportedPropertySet::HandleSet(brillo::ErrorPtr* error,
116 const std::string& interface_name,
117 const std::string& property_name,
118 const brillo::Any& value) {
119 bus_->AssertOnOriginThread();
120 auto property_map_itr = properties_.find(interface_name);
121 if (property_map_itr == properties_.end()) {
122 brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
123 DBUS_ERROR_UNKNOWN_INTERFACE,
124 "No such interface on object.");
125 return false;
126 }
127 LOG(INFO) << "Looking for " << property_name << " on " << interface_name;
128 auto property_itr = property_map_itr->second.find(property_name);
129 if (property_itr == property_map_itr->second.end()) {
130 brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
131 DBUS_ERROR_UNKNOWN_PROPERTY,
132 "No such property on interface.");
133 return false;
134 }
135
136 return property_itr->second->SetValue(error, value);
137 }
138
HandlePropertyUpdated(const std::string & interface_name,const std::string & property_name,const ExportedPropertyBase * exported_property)139 void ExportedPropertySet::HandlePropertyUpdated(
140 const std::string& interface_name,
141 const std::string& property_name,
142 const ExportedPropertyBase* exported_property) {
143 bus_->AssertOnOriginThread();
144 // Send signal only if the object has been exported successfully.
145 // This could happen when a property value is changed (which triggers
146 // the notification) before D-Bus interface is completely exported/claimed.
147 auto signal = signal_properties_changed_.lock();
148 if (!signal)
149 return;
150 VariantDictionary changed_properties{
151 {property_name, exported_property->GetValue()}};
152 // The interface specification tells us to include this list of properties
153 // which have changed, but for whom no value is conveyed. Currently, we
154 // don't do anything interesting here.
155 std::vector<std::string> invalidated_properties; // empty.
156 signal->Send(interface_name, changed_properties, invalidated_properties);
157 }
158
NotifyPropertyChanged()159 void ExportedPropertyBase::NotifyPropertyChanged() {
160 // These is a brief period after the construction of an ExportedProperty
161 // when this callback is not initialized because the property has not
162 // been registered with the parent ExportedPropertySet. During this period
163 // users should be initializing values via SetValue, and no notifications
164 // should be triggered by the ExportedPropertySet.
165 if (!on_update_callback_.is_null()) {
166 on_update_callback_.Run(this);
167 }
168 }
169
SetUpdateCallback(const OnUpdateCallback & cb)170 void ExportedPropertyBase::SetUpdateCallback(const OnUpdateCallback& cb) {
171 on_update_callback_ = cb;
172 }
173
ClearUpdateCallback()174 void ExportedPropertyBase::ClearUpdateCallback() {
175 on_update_callback_.Reset();
176 }
177
SetAccessMode(ExportedPropertyBase::Access access_mode)178 void ExportedPropertyBase::SetAccessMode(
179 ExportedPropertyBase::Access access_mode) {
180 access_mode_ = access_mode;
181 }
182
GetAccessMode() const183 ExportedPropertyBase::Access ExportedPropertyBase::GetAccessMode() const {
184 return access_mode_;
185 }
186
187 } // namespace dbus_utils
188
189 } // namespace brillo
190