// 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_device_client.h"
#include "base/bind.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/strings/stringprintf.h"
#include "chromeos/dbus/nfc_adapter_client.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
using chromeos::nfc_client_helpers::DBusObjectMap;
using chromeos::nfc_client_helpers::ObjectProxyTree;
namespace chromeos {
NfcDeviceClient::Properties::Properties(
dbus::ObjectProxy* object_proxy,
const PropertyChangedCallback& callback)
: NfcPropertySet(object_proxy,
nfc_device::kNfcDeviceInterface,
callback) {
RegisterProperty(nfc_device::kRecordsProperty, &records);
}
NfcDeviceClient::Properties::~Properties() {
}
// The NfcDeviceClient implementation used in production.
class NfcDeviceClientImpl : public NfcDeviceClient,
public NfcAdapterClient::Observer,
public DBusObjectMap::Delegate {
public:
explicit NfcDeviceClientImpl(NfcAdapterClient* adapter_client)
: bus_(NULL),
adapter_client_(adapter_client),
weak_ptr_factory_(this) {
DCHECK(adapter_client);
}
virtual ~NfcDeviceClientImpl() {
DCHECK(adapter_client_);
adapter_client_->RemoveObserver(this);
}
// NfcDeviceClient override.
virtual void AddObserver(NfcDeviceClient::Observer* observer) OVERRIDE {
DCHECK(observer);
observers_.AddObserver(observer);
}
// NfcDeviceClient override.
virtual void RemoveObserver(NfcDeviceClient::Observer* observer) OVERRIDE {
DCHECK(observer);
observers_.RemoveObserver(observer);
}
// NfcDeviceClient override.
virtual Properties* GetProperties(
const dbus::ObjectPath& object_path) OVERRIDE {
return static_cast<Properties*>(
adapters_to_object_maps_.FindObjectProperties(object_path));
}
// NfcDeviceClient override.
virtual void Push(
const dbus::ObjectPath& object_path,
const base::DictionaryValue& attributes,
const base::Closure& callback,
const nfc_client_helpers::ErrorCallback& error_callback) OVERRIDE {
dbus::ObjectProxy* object_proxy =
adapters_to_object_maps_.FindObjectProxy(object_path);
if (!object_proxy) {
std::string error_message =
base::StringPrintf(
"NFC device with object path \"%s\" does not exist.",
object_path.value().c_str());
LOG(ERROR) << error_message;
error_callback.Run(nfc_client_helpers::kUnknownObjectError,
error_message);
return;
}
// |attributes| should not be empty.
if (attributes.empty()) {
std::string error_message =
"Cannot push data to device with empty arguments.";
LOG(ERROR) << error_message;
error_callback.Run(nfc_error::kInvalidArguments, error_message);
return;
}
// Create the arguments.
dbus::MethodCall method_call(nfc_device::kNfcDeviceInterface,
nfc_device::kPush);
dbus::MessageWriter writer(&method_call);
dbus::MessageWriter array_writer(NULL);
dbus::MessageWriter dict_entry_writer(NULL);
writer.OpenArray("{sv}", &array_writer);
for (DictionaryValue::Iterator iter(attributes);
!iter.IsAtEnd(); iter.Advance()) {
array_writer.OpenDictEntry(&dict_entry_writer);
dict_entry_writer.AppendString(iter.key());
nfc_client_helpers::AppendValueDataAsVariant(&dict_entry_writer,
iter.value());
array_writer.CloseContainer(&dict_entry_writer);
}
writer.CloseContainer(&array_writer);
object_proxy->CallMethodWithErrorCallback(
&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::Bind(&nfc_client_helpers::OnSuccess, callback),
base::Bind(&nfc_client_helpers::OnError, error_callback));
}
protected:
// DBusClient override.
virtual void Init(dbus::Bus* bus) OVERRIDE {
VLOG(1) << "Creating NfcDeviceClientImpl";
DCHECK(bus);
bus_ = bus;
DCHECK(adapter_client_);
adapter_client_->AddObserver(this);
}
private:
// NfcAdapterClient::Observer override.
virtual void AdapterAdded(const dbus::ObjectPath& object_path) OVERRIDE {
VLOG(1) << "Adapter added. Creating map for device proxies belonging to "
<< "adapter: " << object_path.value();
adapters_to_object_maps_.CreateObjectMap(
object_path, nfc_device::kNfcDeviceServiceName, this, bus_);
}
// NfcAdapterClient::Observer override.
virtual void AdapterRemoved(const dbus::ObjectPath& object_path) OVERRIDE {
// Neard doesn't send out property changed signals for the devices that
// are removed when the adapter they belong to is removed. Clean up the
// object proxies for devices that are managed by the removed adapter.
// Note: DBusObjectMap guarantees that the Properties structure for the
// removed adapter will be valid before this method returns.
VLOG(1) << "Adapter removed. Cleaning up device proxies belonging to "
<< "adapter: " << object_path.value();
adapters_to_object_maps_.RemoveObjectMap(object_path);
}
// NfcAdapterClient::Observer override.
virtual void AdapterPropertyChanged(
const dbus::ObjectPath& object_path,
const std::string& property_name) OVERRIDE {
DCHECK(adapter_client_);
NfcAdapterClient::Properties *adapter_properties =
adapter_client_->GetProperties(object_path);
DCHECK(adapter_properties);
// Ignore changes to properties other than "Devices".
if (property_name != adapter_properties->devices.name())
return;
// Update the known devices.
VLOG(1) << "NFC devices changed.";
const std::vector<dbus::ObjectPath>& received_devices =
adapter_properties->devices.value();
DBusObjectMap* object_map =
adapters_to_object_maps_.GetObjectMap(object_path);
DCHECK(object_map);
object_map->UpdateObjects(received_devices);
}
// nfc_client_helpers::DBusObjectMap::Delegate override.
virtual NfcPropertySet* CreateProperties(
dbus::ObjectProxy* object_proxy) OVERRIDE {
return new Properties(
object_proxy,
base::Bind(&NfcDeviceClientImpl::OnPropertyChanged,
weak_ptr_factory_.GetWeakPtr(),
object_proxy->object_path()));
}
// nfc_client_helpers::DBusObjectMap::Delegate override.
virtual void ObjectAdded(const dbus::ObjectPath& object_path) OVERRIDE {
FOR_EACH_OBSERVER(NfcDeviceClient::Observer, observers_,
DeviceAdded(object_path));
}
virtual void ObjectRemoved(const dbus::ObjectPath& object_path) OVERRIDE {
FOR_EACH_OBSERVER(NfcDeviceClient::Observer, observers_,
DeviceRemoved(object_path));
}
// Called by NfcPropertySet when a property value is changed, either by
// result of a signal or response to a GetAll() or Get() call.
void OnPropertyChanged(const dbus::ObjectPath& object_path,
const std::string& property_name) {
VLOG(1) << "Device property changed; Path: " << object_path.value()
<< " Property: " << property_name;
FOR_EACH_OBSERVER(NfcDeviceClient::Observer, observers_,
DevicePropertyChanged(object_path, property_name));
}
// We maintain a pointer to the bus to be able to request proxies for
// new NFC devices that appear.
dbus::Bus* bus_;
// List of observers interested in event notifications.
ObserverList<NfcDeviceClient::Observer> observers_;
// Mapping from object paths to object proxies and properties structures that
// were already created by us. This stucture stores a different DBusObjectMap
// for each known NFC adapter object path.
ObjectProxyTree adapters_to_object_maps_;
// The adapter client that we listen to events notifications from.
NfcAdapterClient* adapter_client_;
// Weak pointer factory for generating 'this' pointers that might live longer
// than we do.
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
base::WeakPtrFactory<NfcDeviceClientImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(NfcDeviceClientImpl);
};
NfcDeviceClient::NfcDeviceClient() {
}
NfcDeviceClient::~NfcDeviceClient() {
}
NfcDeviceClient* NfcDeviceClient::Create(NfcAdapterClient* adapter_client) {
return new NfcDeviceClientImpl(adapter_client);
}
} // namespace chromeos