blob: b1b5633f877306d44a08736200820f6cf2a55f65 [file] [log] [blame]
Christopher Wiley639477c2014-03-27 14:49:39 -07001// 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 "buffet/exported_property_set.h"
6
7#include <base/bind.h>
Christopher Wiley90016242014-04-01 17:33:29 -07008#include <dbus/bus.h> // For kPropertyInterface
Christopher Wiley639477c2014-03-27 14:49:39 -07009#include <dbus/property.h> // For kPropertyInterface
10
Christopher Wiley90016242014-04-01 17:33:29 -070011#include "buffet/async_event_sequencer.h"
Christopher Wiley639477c2014-03-27 14:49:39 -070012#include "buffet/dbus_utils.h"
13
14namespace buffet {
15
16namespace dbus_utils {
17
Christopher Wiley90016242014-04-01 17:33:29 -070018namespace {
19const char kExportFailedMessage[] = "Failed to register DBus method.";
20} // namespace
Christopher Wiley639477c2014-03-27 14:49:39 -070021
Christopher Wiley90016242014-04-01 17:33:29 -070022ExportedPropertySet::ExportedPropertySet(dbus::Bus* bus,
23 const dbus::ObjectPath& path)
24 : bus_(bus), exported_object_(bus->GetExportedObject(path)),
25 weak_ptr_factory_(this) { }
26
27void ExportedPropertySet::Init(const OnInitFinish& cb) {
28 bus_->AssertOnOriginThread();
29 scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
30 exported_object_->ExportMethod(
Christopher Wiley639477c2014-03-27 14:49:39 -070031 dbus::kPropertiesInterface, dbus::kPropertiesGetAll,
32 base::Bind(&ExportedPropertySet::HandleGetAll,
Christopher Wiley90016242014-04-01 17:33:29 -070033 weak_ptr_factory_.GetWeakPtr()),
34 sequencer->GetExportHandler(
35 dbus::kPropertiesInterface, dbus::kPropertiesGetAll,
36 kExportFailedMessage, false));
37 exported_object_->ExportMethod(
Christopher Wiley639477c2014-03-27 14:49:39 -070038 dbus::kPropertiesInterface, dbus::kPropertiesGet,
39 base::Bind(&ExportedPropertySet::HandleGet,
Christopher Wiley90016242014-04-01 17:33:29 -070040 weak_ptr_factory_.GetWeakPtr()),
41 sequencer->GetExportHandler(
42 dbus::kPropertiesInterface, dbus::kPropertiesGet,
43 kExportFailedMessage, false));
44 exported_object_->ExportMethod(
Christopher Wiley639477c2014-03-27 14:49:39 -070045 dbus::kPropertiesInterface, dbus::kPropertiesSet,
46 base::Bind(&ExportedPropertySet::HandleSet,
Christopher Wiley90016242014-04-01 17:33:29 -070047 weak_ptr_factory_.GetWeakPtr()),
48 sequencer->GetExportHandler(
49 dbus::kPropertiesInterface, dbus::kPropertiesSet,
50 kExportFailedMessage, false));
51 sequencer->OnAllTasksCompletedCall({cb});
Christopher Wiley639477c2014-03-27 14:49:39 -070052}
53
Christopher Wileyadb901d2014-05-07 09:58:45 -070054ExportedPropertySet::PropertyWriter ExportedPropertySet::GetPropertyWriter(
55 const std::string& interface) {
56 return base::Bind(&ExportedPropertySet::WritePropertiesDictToMessage,
57 weak_ptr_factory_.GetWeakPtr(),
58 interface);
59}
Christopher Wiley639477c2014-03-27 14:49:39 -070060
61void ExportedPropertySet::RegisterProperty(
62 const std::string& interface_name,
63 const std::string& property_name,
64 ExportedPropertyBase* exported_property) {
Christopher Wiley90016242014-04-01 17:33:29 -070065 bus_->AssertOnOriginThread();
Christopher Wiley639477c2014-03-27 14:49:39 -070066 properties_[interface_name][property_name] = exported_property;
67 // Technically, the property set exists longer than the properties themselves,
68 // so we could use Unretained here rather than a weak pointer.
69 ExportedPropertyBase::OnUpdateCallback cb = base::Bind(
70 &ExportedPropertySet::HandlePropertyUpdated,
71 weak_ptr_factory_.GetWeakPtr(),
72 interface_name, property_name);
73 exported_property->SetUpdateCallback(cb);
74}
75
76void ExportedPropertySet::HandleGetAll(
77 dbus::MethodCall* method_call,
78 dbus::ExportedObject::ResponseSender response_sender) {
Christopher Wiley90016242014-04-01 17:33:29 -070079 bus_->AssertOnOriginThread();
Christopher Wiley639477c2014-03-27 14:49:39 -070080 dbus::MessageReader reader(method_call);
81 std::string interface_name;
82 if (!reader.PopString(&interface_name)) {
83 response_sender.Run(
84 GetBadArgsError(method_call, "No interface name specified."));
85 return;
86 }
87 if (reader.HasMoreData()) {
88 response_sender.Run(
89 GetBadArgsError(method_call, "Too many arguments to GetAll."));
90 return;
91 }
Christopher Wiley639477c2014-03-27 14:49:39 -070092 scoped_ptr<dbus::Response> response(
93 dbus::Response::FromMethodCall(method_call));
94 dbus::MessageWriter resp_writer(response.get());
Christopher Wileycec927c2014-04-15 16:26:47 -070095 WritePropertiesDictToMessage(interface_name, &resp_writer);
Christopher Wiley639477c2014-03-27 14:49:39 -070096 response_sender.Run(response.Pass());
97}
98
Christopher Wileycec927c2014-04-15 16:26:47 -070099void ExportedPropertySet::WritePropertiesDictToMessage(
100 const std::string& interface_name,
101 dbus::MessageWriter* writer) {
102 dbus::MessageWriter dict_writer(nullptr);
103 writer->OpenArray("{sv}", &dict_writer);
104 auto property_map_itr = properties_.find(interface_name);
105 if (property_map_itr != properties_.end()) {
106 for (const auto& kv : property_map_itr->second) {
107 dbus::MessageWriter entry_writer(nullptr);
108 dict_writer.OpenDictEntry(&entry_writer);
109 entry_writer.AppendString(kv.first);
110 kv.second->AppendValueToWriter(&entry_writer);
111 dict_writer.CloseContainer(&entry_writer);
112 }
113 } else {
114 LOG(WARNING) << "No properties found for interface interface_name";
115 }
116 writer->CloseContainer(&dict_writer);
117}
118
Christopher Wiley639477c2014-03-27 14:49:39 -0700119void ExportedPropertySet::HandleGet(
120 dbus::MethodCall* method_call,
121 dbus::ExportedObject::ResponseSender response_sender) {
Christopher Wiley90016242014-04-01 17:33:29 -0700122 bus_->AssertOnOriginThread();
Christopher Wiley639477c2014-03-27 14:49:39 -0700123 dbus::MessageReader reader(method_call);
124 std::string interface_name, property_name;
125 if (!reader.PopString(&interface_name)) {
126 response_sender.Run(
127 GetBadArgsError(method_call, "No interface name specified."));
128 return;
129 }
130 if (!reader.PopString(&property_name)) {
131 response_sender.Run(
132 GetBadArgsError(method_call, "No property name specified."));
133 return;
134 }
135 if (reader.HasMoreData()) {
136 response_sender.Run(
137 GetBadArgsError(method_call, "Too many arguments to Get."));
138 return;
139 }
140 auto property_map_itr = properties_.find(interface_name);
141 if (property_map_itr == properties_.end()) {
142 response_sender.Run(
143 GetBadArgsError(method_call, "No such interface on object."));
144 return;
145 }
146 LOG(ERROR) << "Looking for " << property_name << " on " << interface_name;
147 auto property_itr = property_map_itr->second.find(property_name);
148 if (property_itr == property_map_itr->second.end()) {
149 response_sender.Run(
150 GetBadArgsError(method_call, "No such property on interface."));
151 return;
152 }
153 scoped_ptr<dbus::Response> response(
154 dbus::Response::FromMethodCall(method_call));
155 dbus::MessageWriter resp_writer(response.get());
156 property_itr->second->AppendValueToWriter(&resp_writer);
157 response_sender.Run(response.Pass());
158}
159
160void ExportedPropertySet::HandleSet(
161 dbus::MethodCall* method_call,
162 dbus::ExportedObject::ResponseSender response_sender) {
Christopher Wiley90016242014-04-01 17:33:29 -0700163 bus_->AssertOnOriginThread();
Christopher Wiley639477c2014-03-27 14:49:39 -0700164 scoped_ptr<dbus::ErrorResponse> error_resp(
165 dbus::ErrorResponse::FromMethodCall(
166 method_call, "org.freedesktop.DBus.Error.NotSupported", ""));
167 scoped_ptr<dbus::Response> response(error_resp.release());
168 response_sender.Run(response.Pass());
169}
170
171void ExportedPropertySet::HandlePropertyUpdated(
172 const std::string& interface,
173 const std::string& name,
174 const ExportedPropertyBase* property) {
Christopher Wiley90016242014-04-01 17:33:29 -0700175 bus_->AssertOnOriginThread();
Christopher Wiley0ffe3b02014-04-01 17:33:29 -0700176 dbus::Signal signal(dbus::kPropertiesInterface, dbus::kPropertiesChanged);
Christopher Wiley90016242014-04-01 17:33:29 -0700177 dbus::MessageWriter writer(&signal);
Christopher Wiley0ffe3b02014-04-01 17:33:29 -0700178 dbus::MessageWriter array_writer(nullptr);
179 dbus::MessageWriter dict_writer(nullptr);
180 writer.AppendString(interface);
181 writer.OpenArray("{sv}", &array_writer);
182 array_writer.OpenDictEntry(&dict_writer);
183 dict_writer.AppendString(name);
184 property->AppendValueToWriter(&dict_writer);
185 array_writer.CloseContainer(&dict_writer);
186 writer.CloseContainer(&array_writer);
187 // The interface specification tells us to include this list of properties
188 // which have changed, but for whom no value is conveyed. Currently, we
189 // don't do anything interesting here.
Christopher Wiley90016242014-04-01 17:33:29 -0700190 writer.OpenArray("s", &array_writer);
191 writer.CloseContainer(&array_writer);
192 // This sends the signal asyncronously. However, the raw message inside
193 // the signal object is ref-counted, so we're fine to allocate the Signal
194 // object on our local stack.
195 exported_object_->SendSignal(&signal);
Christopher Wiley639477c2014-03-27 14:49:39 -0700196}
197
198template <typename T>
199void AppendPropertyToWriter(dbus::MessageWriter* writer, const T& value);
200
201template <>
202void AppendPropertyToWriter(dbus::MessageWriter* writer, const bool& value) {
203 writer->AppendVariantOfBool(value);
204}
205
206template <>
207void AppendPropertyToWriter(dbus::MessageWriter* writer, const uint8& value) {
208 writer->AppendVariantOfByte(value);
209}
210
211template <>
212void AppendPropertyToWriter(dbus::MessageWriter* writer, const int16& value) {
213 writer->AppendVariantOfInt16(value);
214}
215
216template <>
217void AppendPropertyToWriter(dbus::MessageWriter* writer, const uint16& value) {
218 writer->AppendVariantOfUint16(value);
219}
220
221template <>
222void AppendPropertyToWriter(dbus::MessageWriter* writer, const int32& value) {
223 writer->AppendVariantOfInt32(value);
224}
225
226template <>
227void AppendPropertyToWriter(dbus::MessageWriter* writer, const uint32& value) {
228 writer->AppendVariantOfUint32(value);
229}
230
231template <>
232void AppendPropertyToWriter(dbus::MessageWriter* writer, const int64& value) {
233 writer->AppendVariantOfInt64(value);
234}
235
236template <>
237void AppendPropertyToWriter(dbus::MessageWriter* writer, const uint64& value) {
238 writer->AppendVariantOfUint64(value);
239}
240
241template <>
242void AppendPropertyToWriter(dbus::MessageWriter* writer, const double& value) {
243 writer->AppendVariantOfDouble(value);
244}
245
246template <>
247void AppendPropertyToWriter(
248 dbus::MessageWriter* writer, const std::string& value) {
249 writer->AppendVariantOfString(value);
250}
251
252template <>
253void AppendPropertyToWriter(
254 dbus::MessageWriter* writer, const dbus::ObjectPath& value) {
255 writer->AppendVariantOfObjectPath(value);
256}
257
258template <>
259void AppendPropertyToWriter(
260 dbus::MessageWriter* writer, const std::vector<std::string>& value) {
261 dbus::MessageWriter variant_writer(nullptr);
262 writer->OpenVariant("as", &variant_writer);
263 variant_writer.AppendArrayOfStrings(value);
264 writer->CloseContainer(&variant_writer);
265}
266
267template <>
268void AppendPropertyToWriter(
269 dbus::MessageWriter* writer, const std::vector<dbus::ObjectPath>& value) {
270 dbus::MessageWriter variant_writer(nullptr);
271 writer->OpenVariant("ao", &variant_writer);
272 variant_writer.AppendArrayOfObjectPaths(value);
273 writer->CloseContainer(&variant_writer);
274}
275
276template <>
277void AppendPropertyToWriter(
278 dbus::MessageWriter* writer, const std::vector<uint8>& value) {
279 dbus::MessageWriter variant_writer(nullptr);
280 writer->OpenVariant("ay", &variant_writer);
281 variant_writer.AppendArrayOfBytes(value.data(), value.size());
282 writer->CloseContainer(&variant_writer);
283}
284
285template <typename T>
286ExportedProperty<T>::ExportedProperty() {}
287
288template <typename T>
289ExportedProperty<T>::~ExportedProperty() {}
290
291template <typename T>
292const T& ExportedProperty<T>::value() const { return value_; }
293
294template <typename T>
295void ExportedProperty<T>::SetValue(const T& new_value) {
296 if (value_ == new_value) {
297 return;
298 }
299 value_ = new_value;
300 // These is a brief period after the construction of an ExportedProperty
301 // when this callback is not initialized because the property has not
302 // been registered with the parent ExportedPropertySet. During this period
303 // users should be initializing values via SetValue, and no notifications
304 // should be triggered by the ExportedPropertySet.
305 if (!on_update_.is_null()) {
306 on_update_.Run(this);
307 }
308}
309
310template <typename T>
311void ExportedProperty<T>::SetUpdateCallback(const OnUpdateCallback& cb) {
312 on_update_ = cb;
313}
314
315template <typename T>
Christopher Wiley0ffe3b02014-04-01 17:33:29 -0700316void ExportedProperty<T>::AppendValueToWriter(
317 dbus::MessageWriter* writer) const {
Christopher Wiley639477c2014-03-27 14:49:39 -0700318 AppendPropertyToWriter(writer, value_);
319}
320
321template class ExportedProperty<bool>;
322template class ExportedProperty<uint8>;
323template class ExportedProperty<int16>;
324template class ExportedProperty<uint16>;
325template class ExportedProperty<int32>;
326template class ExportedProperty<uint32>;
327template class ExportedProperty<int64>;
328template class ExportedProperty<uint64>;
329template class ExportedProperty<double>;
330template class ExportedProperty<std::string>;
331template class ExportedProperty<dbus::ObjectPath>;
332template class ExportedProperty<std::vector<std::string>>;
333template class ExportedProperty<std::vector<dbus::ObjectPath>>;
334template class ExportedProperty<std::vector<uint8>>;
335
336} // namespace dbus_utils
337
338} // namespace buffet