// 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 "device/nfc/nfc_adapter_chromeos.h" #include #include "base/callback.h" #include "base/logging.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "device/nfc/nfc_peer_chromeos.h" #include "device/nfc/nfc_tag_chromeos.h" #include "third_party/cros_system_api/dbus/service_constants.h" namespace chromeos { namespace { typedef std::vector ObjectPathVector; } // namespace NfcAdapterChromeOS::NfcAdapterChromeOS() : weak_ptr_factory_(this) { DBusThreadManager::Get()->GetNfcAdapterClient()->AddObserver(this); DBusThreadManager::Get()->GetNfcDeviceClient()->AddObserver(this); DBusThreadManager::Get()->GetNfcTagClient()->AddObserver(this); const ObjectPathVector& object_paths = DBusThreadManager::Get()->GetNfcAdapterClient()->GetAdapters(); if (!object_paths.empty()) { VLOG(1) << object_paths.size() << " NFC adapter(s) available."; SetAdapter(object_paths[0]); } } NfcAdapterChromeOS::~NfcAdapterChromeOS() { DBusThreadManager::Get()->GetNfcAdapterClient()->RemoveObserver(this); DBusThreadManager::Get()->GetNfcDeviceClient()->RemoveObserver(this); DBusThreadManager::Get()->GetNfcTagClient()->RemoveObserver(this); } void NfcAdapterChromeOS::AddObserver(NfcAdapter::Observer* observer) { DCHECK(observer); observers_.AddObserver(observer); } void NfcAdapterChromeOS::RemoveObserver(NfcAdapter::Observer* observer) { DCHECK(observer); observers_.RemoveObserver(observer); } bool NfcAdapterChromeOS::IsPresent() const { return !object_path_.value().empty(); } bool NfcAdapterChromeOS::IsPowered() const { if (!IsPresent()) return false; return DBusThreadManager::Get()->GetNfcAdapterClient()-> GetProperties(object_path_)->powered.value(); } bool NfcAdapterChromeOS::IsPolling() const { if (!IsPresent()) return false; return DBusThreadManager::Get()->GetNfcAdapterClient()-> GetProperties(object_path_)->polling.value(); } bool NfcAdapterChromeOS::IsInitialized() const { return true; } void NfcAdapterChromeOS::SetPowered(bool powered, const base::Closure& callback, const ErrorCallback& error_callback) { if (!IsPresent()) { LOG(WARNING) << "Adapter not present. Cannot power up the antenna."; error_callback.Run(); return; } DBusThreadManager::Get()->GetNfcAdapterClient()-> GetProperties(object_path_)->powered.Set( powered, base::Bind(&NfcAdapterChromeOS::OnSetPowered, weak_ptr_factory_.GetWeakPtr(), callback, error_callback)); } void NfcAdapterChromeOS::StartPolling(const base::Closure& callback, const ErrorCallback& error_callback) { // Always poll in "Initiator" mode. DBusThreadManager::Get()->GetNfcAdapterClient()-> StartPollLoop(object_path_, nfc_adapter::kModeInitiator, base::Bind(&NfcAdapterChromeOS::OnStartPolling, weak_ptr_factory_.GetWeakPtr(), callback), base::Bind(&NfcAdapterChromeOS::OnStartPollingError, weak_ptr_factory_.GetWeakPtr(), error_callback)); } void NfcAdapterChromeOS::StopPolling(const base::Closure& callback, const ErrorCallback& error_callback) { DBusThreadManager::Get()->GetNfcAdapterClient()-> StopPollLoop(object_path_, base::Bind(&NfcAdapterChromeOS::OnStopPolling, weak_ptr_factory_.GetWeakPtr(), callback), base::Bind(&NfcAdapterChromeOS::OnStopPollingError, weak_ptr_factory_.GetWeakPtr(), error_callback)); } void NfcAdapterChromeOS::AdapterAdded(const dbus::ObjectPath& object_path) { // Set the adapter to the newly added adapter only if no adapter is present. if (!IsPresent()) SetAdapter(object_path); } void NfcAdapterChromeOS::AdapterRemoved(const dbus::ObjectPath& object_path) { if (object_path != object_path_) return; // The current adapter was removed, so mark us as not present and clean up // peers and tags. RemoveAdapter(); // There may still be other adapters present on the system. Set the next // available adapter as the current one. const ObjectPathVector& object_paths = DBusThreadManager::Get()->GetNfcAdapterClient()->GetAdapters(); for (ObjectPathVector::const_iterator iter = object_paths.begin(); iter != object_paths.end(); ++iter) { // The removed object will still be available until the call to // AdapterRemoved returns. Make sure that we are not re-adding the // removed adapter. if (*iter == object_path) continue; SetAdapter(*iter); } } void NfcAdapterChromeOS::AdapterPropertyChanged( const dbus::ObjectPath& object_path, const std::string& property_name) { if (object_path != object_path_) return; NfcAdapterClient::Properties* properties = DBusThreadManager::Get()->GetNfcAdapterClient()-> GetProperties(object_path_); if (property_name == properties->powered.name()) PoweredChanged(properties->powered.value()); else if (property_name == properties->polling.name()) PollingChanged(properties->polling.value()); } void NfcAdapterChromeOS::DeviceAdded(const dbus::ObjectPath& object_path) { if (!IsPresent()) return; if (GetPeer(object_path.value())) return; VLOG(1) << "NFC device found: " << object_path.value(); // Check to see if the device belongs to this adapter. const ObjectPathVector& devices = DBusThreadManager::Get()->GetNfcDeviceClient()-> GetDevicesForAdapter(object_path_); bool device_found = false; for (ObjectPathVector::const_iterator iter = devices.begin(); iter != devices.end(); ++iter) { if (*iter == object_path) { device_found = true; break; } } if (!device_found) { VLOG(1) << "Found peer device does not belong to the current adapter."; return; } // Create the peer object. NfcPeerChromeOS* peer_chromeos = new NfcPeerChromeOS(object_path); SetPeer(object_path.value(), peer_chromeos); FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, PeerFound(this, peer_chromeos)); } void NfcAdapterChromeOS::DeviceRemoved(const dbus::ObjectPath& object_path) { VLOG(1) << "NFC device lost: " << object_path.value(); device::NfcPeer* peer = RemovePeer(object_path.value()); if (!peer) { VLOG(1) << "Removed peer device does not belong to the current adapter."; return; } FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, PeerLost(this, peer)); delete peer; } void NfcAdapterChromeOS::TagAdded(const dbus::ObjectPath& object_path) { if (!IsPresent()) return; if (GetTag(object_path.value())) return; VLOG(1) << "NFC tag found: " << object_path.value(); // Check to see if the tag belongs to this adapter. const std::vector& tags = DBusThreadManager::Get()->GetNfcTagClient()-> GetTagsForAdapter(object_path_); bool tag_found = false; for (std::vector::const_iterator iter = tags.begin(); iter != tags.end(); ++iter) { if (*iter == object_path) { tag_found = true; break; } } if (!tag_found) { VLOG(1) << "Found tag does not belong to the current adapter."; return; } // Create the tag object. NfcTagChromeOS* tag_chromeos = new NfcTagChromeOS(object_path); SetTag(object_path.value(), tag_chromeos); FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, TagFound(this, tag_chromeos)); } void NfcAdapterChromeOS::TagRemoved(const dbus::ObjectPath& object_path) { VLOG(1) << "NFC tag lost : " << object_path.value(); device::NfcTag* tag = RemoveTag(object_path.value()); if (!tag) { VLOG(1) << "Removed tag does not belong to the current adapter."; return; } FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, TagLost(this, tag)); delete tag; } void NfcAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) { DCHECK(!IsPresent()); object_path_ = object_path; VLOG(1) << "Using NFC adapter: " << object_path.value(); NfcAdapterClient::Properties* properties = DBusThreadManager::Get()->GetNfcAdapterClient()-> GetProperties(object_path_); PresentChanged(true); if (properties->powered.value()) PoweredChanged(true); if (properties->polling.value()) PollingChanged(true); // Create peer objects for peers that were added before the adapter was set. const ObjectPathVector& devices = DBusThreadManager::Get()->GetNfcDeviceClient()-> GetDevicesForAdapter(object_path_); for (ObjectPathVector::const_iterator iter = devices.begin(); iter != devices.end(); ++iter) { const dbus::ObjectPath& object_path = *iter; if (GetPeer(object_path.value())) continue; NfcPeerChromeOS* peer_chromeos = new NfcPeerChromeOS(object_path); SetPeer(object_path.value(), peer_chromeos); FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, PeerFound(this, peer_chromeos)); } // Create tag objects for tags that were added before the adapter was set. const std::vector& tags = DBusThreadManager::Get()->GetNfcTagClient()-> GetTagsForAdapter(object_path_); for (std::vector::const_iterator iter = tags.begin(); iter != tags.end(); ++iter) { const dbus::ObjectPath& object_path = *iter; if (GetTag(object_path.value())) continue; NfcTagChromeOS* tag_chromeos = new NfcTagChromeOS(object_path); SetTag(object_path.value(), tag_chromeos); FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, TagFound(this, tag_chromeos)); } } void NfcAdapterChromeOS::RemoveAdapter() { DCHECK(IsPresent()); VLOG(1) << "NFC adapter removed: " << object_path_.value(); NfcAdapterClient::Properties* properties = DBusThreadManager::Get()->GetNfcAdapterClient()-> GetProperties(object_path_); if (properties->powered.value()) PoweredChanged(false); if (properties->polling.value()) PollingChanged(false); // Copy the tags and peers here and clear the original containers so that // GetPeers and GetTags return no values during the *Removed observer calls. PeerList peers; TagList tags; GetPeers(&peers); GetTags(&tags); ClearPeers(); ClearTags(); for (PeerList::iterator iter = peers.begin(); iter != peers.end(); ++iter) { device::NfcPeer* peer = *iter; FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, PeerLost(this, peer)); delete peer; } for (TagList::iterator iter = tags.begin(); iter != tags.end(); ++iter) { device::NfcTag* tag = *iter; FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, TagLost(this, tag)); delete tag; } object_path_ = dbus::ObjectPath(""); PresentChanged(false); } void NfcAdapterChromeOS::PoweredChanged(bool powered) { FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, AdapterPoweredChanged(this, powered)); } void NfcAdapterChromeOS::PollingChanged(bool polling) { FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, AdapterPollingChanged(this, polling)); } void NfcAdapterChromeOS::PresentChanged(bool present) { FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, AdapterPresentChanged(this, present)); } void NfcAdapterChromeOS::OnSetPowered(const base::Closure& callback, const ErrorCallback& error_callback, bool success) { VLOG(1) << "NfcAdapterChromeOS::OnSetPowered result: " << success; if (success) { // TODO(armansito): There is a bug in neard 0.13 that causes it not to emit // a signal when the "Powered" property changes. Sync the properties here, // but remove it in neard 0.14. if (IsPresent()) { DBusThreadManager::Get()->GetNfcAdapterClient()-> GetProperties(object_path_)->GetAll(); } callback.Run(); } else { LOG(ERROR) << "Failed to power up the NFC antenna radio."; error_callback.Run(); } } void NfcAdapterChromeOS::OnStartPolling(const base::Closure& callback) { callback.Run(); } void NfcAdapterChromeOS::OnStartPollingError( const ErrorCallback& error_callback, const std::string& error_name, const std::string& error_message) { LOG(ERROR) << object_path_.value() << ": Failed to start polling: " << error_name << ": " << error_message; error_callback.Run(); } void NfcAdapterChromeOS::OnStopPolling(const base::Closure& callback) { callback.Run(); } void NfcAdapterChromeOS::OnStopPollingError( const ErrorCallback& error_callback, const std::string& error_name, const std::string& error_message) { LOG(ERROR) << object_path_.value() << ": Failed to stop polling: " << error_name << ": " << error_message; error_callback.Run(); } } // namespace chromeos