// 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); } 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) { // Don't remove the object proxy. There is a bug in dbus::Bus that causes a // crash when object proxies are removed, due to the proxy objects not being // properly reference counted. (See crbug.com/170182 and crbug.com/328264). NfcPropertySet* properties = pair.second; 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; paths_to_object_maps_.erase(iter); delete object_map; } 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