1// Copyright 2013 The Chromium 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 "chromeos/dbus/nfc_client_helpers.h"
6
7#include "base/stl_util.h"
8#include "dbus/values_util.h"
9
10namespace chromeos {
11namespace nfc_client_helpers {
12
13const char kNoResponseError[] = "org.chromium.Error.NoResponse";
14const char kUnknownObjectError[] = "org.chromium.Error.UnknownObject";
15
16void OnSuccess(const base::Closure& callback, dbus::Response* response) {
17  DCHECK(response);
18  callback.Run();
19}
20
21void OnError(const ErrorCallback& error_callback,
22             dbus::ErrorResponse* response) {
23  // Error response has optional error message argument.
24  std::string error_name;
25  std::string error_message;
26  if (response) {
27    dbus::MessageReader reader(response);
28    error_name = response->GetErrorName();
29    reader.PopString(&error_message);
30  } else {
31    error_name = kNoResponseError;
32    error_message = "";
33  }
34  error_callback.Run(error_name, error_message);
35}
36
37void AppendValueDataAsVariant(dbus::MessageWriter* writer,
38                              const base::Value& value) {
39  switch (value.GetType()) {
40    case base::Value::TYPE_DICTIONARY: {
41      const base::DictionaryValue* dictionary = NULL;
42      value.GetAsDictionary(&dictionary);
43      dbus::MessageWriter variant_writer(NULL);
44      dbus::MessageWriter array_writer(NULL);
45      writer->OpenVariant("a{sv}", &variant_writer);
46      variant_writer.OpenArray("{sv}", &array_writer);
47      for (base::DictionaryValue::Iterator iter(*dictionary);
48           !iter.IsAtEnd(); iter.Advance()) {
49        dbus::MessageWriter entry_writer(NULL);
50        array_writer.OpenDictEntry(&entry_writer);
51        entry_writer.AppendString(iter.key());
52        AppendValueDataAsVariant(&entry_writer, iter.value());
53        array_writer.CloseContainer(&entry_writer);
54      }
55      variant_writer.CloseContainer(&array_writer);
56      writer->CloseContainer(&variant_writer);
57      break;
58    }
59    case base::Value::TYPE_LIST: {
60      const base::ListValue* list = NULL;
61      value.GetAsList(&list);
62      dbus::MessageWriter variant_writer(NULL);
63      dbus::MessageWriter array_writer(NULL);
64      writer->OpenVariant("av", &variant_writer);
65      variant_writer.OpenArray("v", &array_writer);
66      for (base::ListValue::const_iterator iter = list->begin();
67           iter != list->end(); ++iter) {
68        const base::Value* value = *iter;
69        AppendValueDataAsVariant(&array_writer, *value);
70      }
71      variant_writer.CloseContainer(&array_writer);
72      writer->CloseContainer(&variant_writer);
73      break;
74    }
75    case base::Value::TYPE_BOOLEAN:
76    case base::Value::TYPE_INTEGER:
77    case base::Value::TYPE_DOUBLE:
78    case base::Value::TYPE_STRING:
79      dbus::AppendBasicTypeValueDataAsVariant(writer, value);
80      break;
81    default:
82      DLOG(ERROR) << "Unexpected type: " << value.GetType();
83  }
84}
85
86DBusObjectMap::DBusObjectMap(const std::string& service_name,
87                             Delegate* delegate,
88                             dbus::Bus* bus)
89    : bus_(bus),
90      service_name_(service_name),
91      delegate_(delegate) {
92  DCHECK(bus_);
93  DCHECK(delegate_);
94}
95
96DBusObjectMap::~DBusObjectMap() {
97  // Clean up the Properties structures. We don't explicitly delete the object
98  // proxies, as they are owned by dbus::Bus.
99  for (ObjectMap::iterator iter = object_map_.begin();
100       iter != object_map_.end(); ++iter) {
101    const dbus::ObjectPath& object_path = iter->first;
102    ObjectPropertyPair pair = iter->second;
103    delegate_->ObjectRemoved(object_path);
104    CleanUpObjectPropertyPair(pair);
105  }
106}
107
108dbus::ObjectProxy* DBusObjectMap::GetObjectProxy(
109    const dbus::ObjectPath& object_path) {
110  return GetObjectPropertyPair(object_path).first;
111}
112
113NfcPropertySet* DBusObjectMap::GetObjectProperties(
114    const dbus::ObjectPath& object_path) {
115  return GetObjectPropertyPair(object_path).second;
116}
117
118void DBusObjectMap::UpdateObjects(const ObjectPathVector& object_paths) {
119  // This set is used to query if an object path was removed, while updating
120  // the removed paths below. The iterator is used as a hint to accelerate
121  // insertion.
122  std::set<dbus::ObjectPath> object_path_set;
123  std::set<dbus::ObjectPath>::iterator object_path_set_iter =
124      object_path_set.begin();
125
126  // Add all objects.
127  for (ObjectPathVector::const_iterator iter = object_paths.begin();
128       iter != object_paths.end(); ++iter) {
129    const dbus::ObjectPath &object_path = *iter;
130    AddObject(object_path);
131    // Neard usually returns object paths in ascending order. Give a hint here
132    // to make insertion amortized constant.
133    object_path_set_iter =
134        object_path_set.insert(object_path_set_iter, object_path);
135  }
136
137  // Remove all objects that are not in |object_paths|.
138  ObjectMap::const_iterator iter = object_map_.begin();
139  while (iter != object_map_.end()) {
140    // It is safe to use a const reference here, as DBusObjectMap::RemoveObject
141    // won't access it after the iterator becomes invalidated.
142    const dbus::ObjectPath &object_path = iter->first;
143    ++iter;
144    if (!ContainsKey(object_path_set, object_path))
145      RemoveObject(object_path);
146  }
147}
148
149bool DBusObjectMap::AddObject(const dbus::ObjectPath& object_path) {
150  ObjectMap::iterator iter = object_map_.find(object_path);
151  if (iter != object_map_.end())
152    return false;
153
154  DCHECK(bus_);
155  DCHECK(delegate_);
156  dbus::ObjectProxy* object_proxy = bus_->GetObjectProxy(service_name_,
157                                                         object_path);
158
159  // Create the properties structure.
160  NfcPropertySet* properties = delegate_->CreateProperties(object_proxy);
161  properties->ConnectSignals();
162  properties->GetAll();
163  ObjectPropertyPair object = std::make_pair(object_proxy, properties);
164  object_map_[object_path] = object;
165  VLOG(1) << "Created proxy for object with Path: " << object_path.value()
166          << ", Service: " << service_name_;
167  delegate_->ObjectAdded(object_path);
168  return true;
169}
170
171void DBusObjectMap::RemoveObject(const dbus::ObjectPath& object_path) {
172  DCHECK(bus_);
173  DCHECK(delegate_);
174  ObjectMap::iterator iter = object_map_.find(object_path);
175  if (iter == object_map_.end())
176    return;
177
178  // Notify the delegate, so that it can perform any clean up that is
179  // necessary.
180  delegate_->ObjectRemoved(object_path);
181
182  // Clean up the object proxy and the properties structure.
183  ObjectPropertyPair pair = iter->second;
184  CleanUpObjectPropertyPair(pair);
185  object_map_.erase(iter);
186  VLOG(1) << "Object proxy removed for object path: "
187          << object_path.value();
188}
189
190void DBusObjectMap::RefreshProperties(const dbus::ObjectPath& object_path) {
191  NfcPropertySet* properties = GetObjectProperties(object_path);
192  if (properties)
193    properties->GetAll();
194}
195
196void DBusObjectMap::RefreshAllProperties() {
197  for (ObjectMap::const_iterator iter = object_map_.begin();
198       iter != object_map_.end(); ++iter) {
199    const dbus::ObjectPath& object_path = iter->first;
200    RefreshProperties(object_path);
201  }
202}
203
204ObjectPathVector DBusObjectMap::GetObjectPaths() {
205  std::vector<dbus::ObjectPath> object_paths;
206  for (ObjectMap::const_iterator iter = object_map_.begin();
207       iter != object_map_.end(); ++iter) {
208    const dbus::ObjectPath& object_path = iter->first;
209    object_paths.push_back(object_path);
210  }
211  return object_paths;
212}
213
214DBusObjectMap::ObjectPropertyPair DBusObjectMap::GetObjectPropertyPair(
215    const dbus::ObjectPath& object_path) {
216  ObjectMap::iterator iter = object_map_.find(object_path);
217  if (iter != object_map_.end())
218    return iter->second;
219  return std::make_pair(static_cast<dbus::ObjectProxy*>(NULL),
220                        static_cast<NfcPropertySet*>(NULL));
221}
222
223void DBusObjectMap::CleanUpObjectPropertyPair(const ObjectPropertyPair& pair) {
224  dbus::ObjectProxy* object_proxy = pair.first;
225  NfcPropertySet* properties = pair.second;
226  bus_->RemoveObjectProxy(service_name_,
227                          object_proxy->object_path(),
228                          base::Bind(&base::DoNothing));
229  delete properties;
230}
231
232ObjectProxyTree::ObjectProxyTree() {
233}
234
235ObjectProxyTree::~ObjectProxyTree() {
236  for (PathsToObjectMapsType::iterator iter = paths_to_object_maps_.begin();
237       iter != paths_to_object_maps_.end(); ++iter) {
238    DBusObjectMap* object_map = iter->second;
239    delete object_map;
240  }
241}
242
243bool ObjectProxyTree::CreateObjectMap(const dbus::ObjectPath& object_path,
244                                      const std::string& service_name,
245                                      DBusObjectMap::Delegate* delegate,
246                                      dbus::Bus* bus) {
247  if (ContainsKey(paths_to_object_maps_, object_path)) {
248    LOG(ERROR) << "Mapping already exists for object path: "
249               << object_path.value();
250    return false;
251  }
252  paths_to_object_maps_[object_path] =
253      new DBusObjectMap(service_name, delegate, bus);
254  return true;
255}
256
257void ObjectProxyTree::RemoveObjectMap(const dbus::ObjectPath& object_path) {
258  PathsToObjectMapsType::iterator iter =
259      paths_to_object_maps_.find(object_path);
260  if (iter == paths_to_object_maps_.end())
261    return;
262  DBusObjectMap* object_map = iter->second;
263  delete object_map;
264  paths_to_object_maps_.erase(iter);
265}
266
267DBusObjectMap* ObjectProxyTree::GetObjectMap(
268    const dbus::ObjectPath& object_path) {
269  PathsToObjectMapsType::iterator iter =
270      paths_to_object_maps_.find(object_path);
271  if (iter == paths_to_object_maps_.end())
272    return NULL;
273  return iter->second;
274}
275
276dbus::ObjectProxy* ObjectProxyTree::FindObjectProxy(
277    const dbus::ObjectPath& object_proxy_path) {
278  for (PathsToObjectMapsType::iterator iter = paths_to_object_maps_.begin();
279       iter != paths_to_object_maps_.end(); ++iter) {
280    DBusObjectMap* object_map = iter->second;
281    dbus::ObjectProxy* object_proxy =
282        object_map->GetObjectProxy(object_proxy_path);
283    if (object_proxy)
284      return object_proxy;
285  }
286  return NULL;
287}
288
289NfcPropertySet* ObjectProxyTree::FindObjectProperties(
290    const dbus::ObjectPath& object_proxy_path) {
291  for (PathsToObjectMapsType::iterator iter = paths_to_object_maps_.begin();
292       iter != paths_to_object_maps_.end(); ++iter) {
293    DBusObjectMap* object_map = iter->second;
294    NfcPropertySet* properties =
295        object_map->GetObjectProperties(object_proxy_path);
296    if (properties)
297      return properties;
298  }
299  return NULL;
300}
301
302}  // namespace nfc_client_helpers
303}  // namespace chromeos
304