// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chromeos/dbus/nfc_client_helpers.h" #include "base/stl_util.h" #include "dbus/values_util.h" namespace chromeos { namespace nfc_client_helpers { const char kNoResponseError[] = "org.chromium.Error.NoResponse"; const char kUnknownObjectError[] = "org.chromium.Error.UnknownObject"; void OnSuccess(const base::Closure& callback, dbus::Response* response) { DCHECK(response); callback.Run(); } void OnError(const ErrorCallback& error_callback, dbus::ErrorResponse* response) { // Error response has optional error message argument. std::string error_name; std::string error_message; if (response) { dbus::MessageReader reader(response); error_name = response->GetErrorName(); reader.PopString(&error_message); } else { error_name = kNoResponseError; error_message = ""; } error_callback.Run(error_name, error_message); } void AppendValueDataAsVariant(dbus::MessageWriter* writer, const base::Value& value) { switch (value.GetType()) { case base::Value::TYPE_DICTIONARY: { const base::DictionaryValue* dictionary = NULL; value.GetAsDictionary(&dictionary); dbus::MessageWriter variant_writer(NULL); dbus::MessageWriter array_writer(NULL); writer->OpenVariant("a{sv}", &variant_writer); variant_writer.OpenArray("{sv}", &array_writer); for (base::DictionaryValue::Iterator iter(*dictionary); !iter.IsAtEnd(); iter.Advance()) { dbus::MessageWriter entry_writer(NULL); array_writer.OpenDictEntry(&entry_writer); entry_writer.AppendString(iter.key()); AppendValueDataAsVariant(&entry_writer, iter.value()); array_writer.CloseContainer(&entry_writer); } variant_writer.CloseContainer(&array_writer); writer->CloseContainer(&variant_writer); break; } case base::Value::TYPE_LIST: { const base::ListValue* list = NULL; value.GetAsList(&list); dbus::MessageWriter variant_writer(NULL); dbus::MessageWriter array_writer(NULL); writer->OpenVariant("av", &variant_writer); variant_writer.OpenArray("v", &array_writer); for (base::ListValue::const_iterator iter = list->begin(); iter != list->end(); ++iter) { const base::Value* value = *iter; AppendValueDataAsVariant(&array_writer, *value); } variant_writer.CloseContainer(&array_writer); writer->CloseContainer(&variant_writer); break; } case base::Value::TYPE_BOOLEAN: case base::Value::TYPE_INTEGER: case base::Value::TYPE_DOUBLE: case base::Value::TYPE_STRING: dbus::AppendBasicTypeValueDataAsVariant(writer, value); break; default: DLOG(ERROR) << "Unexpected type: " << value.GetType(); } } DBusObjectMap::DBusObjectMap(const std::string& service_name, Delegate* delegate, dbus::Bus* bus) : bus_(bus), service_name_(service_name), delegate_(delegate) { DCHECK(bus_); DCHECK(delegate_); } DBusObjectMap::~DBusObjectMap() { // Clean up the Properties structures. We don't explicitly delete the object // proxies, as they are owned by dbus::Bus. for (ObjectMap::iterator iter = object_map_.begin(); iter != object_map_.end(); ++iter) { const dbus::ObjectPath& object_path = iter->first; ObjectPropertyPair pair = iter->second; delegate_->ObjectRemoved(object_path); CleanUpObjectPropertyPair(pair); } } dbus::ObjectProxy* DBusObjectMap::GetObjectProxy( const dbus::ObjectPath& object_path) { return GetObjectPropertyPair(object_path).first; } NfcPropertySet* DBusObjectMap::GetObjectProperties( const dbus::ObjectPath& object_path) { return GetObjectPropertyPair(object_path).second; } void DBusObjectMap::UpdateObjects(const ObjectPathVector& object_paths) { // This set is used to query if an object path was removed, while updating // the removed paths below. The iterator is used as a hint to accelerate // insertion. std::set<dbus::ObjectPath> object_path_set; std::set<dbus::ObjectPath>::iterator object_path_set_iter = object_path_set.begin(); // Add all objects. for (ObjectPathVector::const_iterator iter = object_paths.begin(); iter != object_paths.end(); ++iter) { const dbus::ObjectPath &object_path = *iter; AddObject(object_path); // Neard usually returns object paths in ascending order. Give a hint here // to make insertion amortized constant. object_path_set_iter = object_path_set.insert(object_path_set_iter, object_path); } // Remove all objects that are not in |object_paths|. ObjectMap::const_iterator iter = object_map_.begin(); while (iter != object_map_.end()) { // It is safe to use a const reference here, as DBusObjectMap::RemoveObject // won't access it after the iterator becomes invalidated. const dbus::ObjectPath &object_path = iter->first; ++iter; if (!ContainsKey(object_path_set, object_path)) RemoveObject(object_path); } } bool DBusObjectMap::AddObject(const dbus::ObjectPath& object_path) { ObjectMap::iterator iter = object_map_.find(object_path); if (iter != object_map_.end()) return false; DCHECK(bus_); DCHECK(delegate_); dbus::ObjectProxy* object_proxy = bus_->GetObjectProxy(service_name_, object_path); // Create the properties structure. NfcPropertySet* properties = delegate_->CreateProperties(object_proxy); properties->ConnectSignals(); properties->GetAll(); ObjectPropertyPair object = std::make_pair(object_proxy, properties); object_map_[object_path] = object; VLOG(1) << "Created proxy for object with Path: " << object_path.value() << ", Service: " << service_name_; delegate_->ObjectAdded(object_path); return true; } void DBusObjectMap::RemoveObject(const dbus::ObjectPath& object_path) { DCHECK(bus_); DCHECK(delegate_); ObjectMap::iterator iter = object_map_.find(object_path); if (iter == object_map_.end()) return; // Notify the delegate, so that it can perform any clean up that is // necessary. delegate_->ObjectRemoved(object_path); // Clean up the object proxy and the properties structure. ObjectPropertyPair pair = iter->second; CleanUpObjectPropertyPair(pair); object_map_.erase(iter); VLOG(1) << "Object proxy removed for object path: " << object_path.value(); } void DBusObjectMap::RefreshProperties(const dbus::ObjectPath& object_path) { NfcPropertySet* properties = GetObjectProperties(object_path); if (properties) properties->GetAll(); } void DBusObjectMap::RefreshAllProperties() { for (ObjectMap::const_iterator iter = object_map_.begin(); iter != object_map_.end(); ++iter) { const dbus::ObjectPath& object_path = iter->first; RefreshProperties(object_path); } } ObjectPathVector DBusObjectMap::GetObjectPaths() { std::vector<dbus::ObjectPath> object_paths; for (ObjectMap::const_iterator iter = object_map_.begin(); iter != object_map_.end(); ++iter) { const dbus::ObjectPath& object_path = iter->first; object_paths.push_back(object_path); } return object_paths; } DBusObjectMap::ObjectPropertyPair DBusObjectMap::GetObjectPropertyPair( const dbus::ObjectPath& object_path) { ObjectMap::iterator iter = object_map_.find(object_path); if (iter != object_map_.end()) return iter->second; return std::make_pair(static_cast<dbus::ObjectProxy*>(NULL), static_cast<NfcPropertySet*>(NULL)); } void DBusObjectMap::CleanUpObjectPropertyPair(const ObjectPropertyPair& pair) { dbus::ObjectProxy* object_proxy = pair.first; NfcPropertySet* properties = pair.second; bus_->RemoveObjectProxy(service_name_, object_proxy->object_path(), base::Bind(&base::DoNothing)); delete properties; } ObjectProxyTree::ObjectProxyTree() { } ObjectProxyTree::~ObjectProxyTree() { for (PathsToObjectMapsType::iterator iter = paths_to_object_maps_.begin(); iter != paths_to_object_maps_.end(); ++iter) { DBusObjectMap* object_map = iter->second; delete object_map; } } bool ObjectProxyTree::CreateObjectMap(const dbus::ObjectPath& object_path, const std::string& service_name, DBusObjectMap::Delegate* delegate, dbus::Bus* bus) { if (ContainsKey(paths_to_object_maps_, object_path)) { LOG(ERROR) << "Mapping already exists for object path: " << object_path.value(); return false; } paths_to_object_maps_[object_path] = new DBusObjectMap(service_name, delegate, bus); return true; } void ObjectProxyTree::RemoveObjectMap(const dbus::ObjectPath& object_path) { PathsToObjectMapsType::iterator iter = paths_to_object_maps_.find(object_path); if (iter == paths_to_object_maps_.end()) return; DBusObjectMap* object_map = iter->second; delete object_map; paths_to_object_maps_.erase(iter); } DBusObjectMap* ObjectProxyTree::GetObjectMap( const dbus::ObjectPath& object_path) { PathsToObjectMapsType::iterator iter = paths_to_object_maps_.find(object_path); if (iter == paths_to_object_maps_.end()) return NULL; return iter->second; } dbus::ObjectProxy* ObjectProxyTree::FindObjectProxy( const dbus::ObjectPath& object_proxy_path) { for (PathsToObjectMapsType::iterator iter = paths_to_object_maps_.begin(); iter != paths_to_object_maps_.end(); ++iter) { DBusObjectMap* object_map = iter->second; dbus::ObjectProxy* object_proxy = object_map->GetObjectProxy(object_proxy_path); if (object_proxy) return object_proxy; } return NULL; } NfcPropertySet* ObjectProxyTree::FindObjectProperties( const dbus::ObjectPath& object_proxy_path) { for (PathsToObjectMapsType::iterator iter = paths_to_object_maps_.begin(); iter != paths_to_object_maps_.end(); ++iter) { DBusObjectMap* object_map = iter->second; NfcPropertySet* properties = object_map->GetObjectProperties(object_proxy_path); if (properties) return properties; } return NULL; } } // namespace nfc_client_helpers } // namespace chromeos