diff options
author | armansito@chromium.org <armansito@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-18 08:47:17 +0000 |
---|---|---|
committer | armansito@chromium.org <armansito@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-18 08:47:17 +0000 |
commit | 20e38b8e9730ae28f077269d67e75a231bea7ca3 (patch) | |
tree | 24416175bfded4f8df6d13d99c8713df9caea7d3 | |
parent | b826fa8f5432ba0e02306d9901aa584653130e0d (diff) | |
download | chromium_src-20e38b8e9730ae28f077269d67e75a231bea7ca3.zip chromium_src-20e38b8e9730ae28f077269d67e75a231bea7ca3.tar.gz chromium_src-20e38b8e9730ae28f077269d67e75a231bea7ca3.tar.bz2 |
nfc: Implement device::NfcTagChromeOS.
Implemented device::NfcTag for the Chrome OS platform. With this patch,
Chrome can now interact with remote NFC tags.
BUG=316471
TEST=device_unittests
Review URL: https://codereview.chromium.org/116143009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245753 0039d316-1c4b-4281-b951-d872f2087c98
29 files changed, 1563 insertions, 222 deletions
diff --git a/chromeos/dbus/fake_nfc_adapter_client.cc b/chromeos/dbus/fake_nfc_adapter_client.cc index f36c262..8da285e 100644 --- a/chromeos/dbus/fake_nfc_adapter_client.cc +++ b/chromeos/dbus/fake_nfc_adapter_client.cc @@ -7,6 +7,7 @@ #include "base/logging.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/fake_nfc_device_client.h" +#include "chromeos/dbus/fake_nfc_tag_client.h" #include "chromeos/dbus/nfc_client_helpers.h" #include "dbus/message.h" #include "dbus/object_path.h" @@ -81,7 +82,8 @@ void FakeNfcAdapterClient::Properties::Set( FakeNfcAdapterClient::FakeNfcAdapterClient() : present_(true), second_present_(false), - start_pairing_on_poll_(true) { + start_pairing_on_poll_(true), + device_pairing_(false) { VLOG(1) << "Creating FakeNfcAdapterClient"; std::vector<std::string> protocols; @@ -147,28 +149,37 @@ void FakeNfcAdapterClient::StartPollLoop( return; } if (!properties_->powered.value()) { - error_callback.Run("org.neard.Error.Failed", "Adapter not powered."); + error_callback.Run(nfc_error::kFailed, "Adapter not powered."); return; } if (properties_->polling.value()) { - error_callback.Run("org.neard.Error.Failed", "Already polling."); + error_callback.Run(nfc_error::kFailed, "Already polling."); return; } if (!properties_->devices.value().empty() || !properties_->tags.value().empty()) { - error_callback.Run("org.neard.Error.Failed", "Adapter busy."); + error_callback.Run(nfc_error::kFailed, "Adapter busy."); return; } properties_->polling.ReplaceValue(true); properties_->mode.ReplaceValue(mode); callback.Run(); - FakeNfcDeviceClient* device_client = - static_cast<FakeNfcDeviceClient*>( - DBusThreadManager::Get()->GetNfcDeviceClient()); + if (!start_pairing_on_poll_) + return; - if (start_pairing_on_poll_) + if (device_pairing_) { + FakeNfcDeviceClient* device_client = + static_cast<FakeNfcDeviceClient*>( + DBusThreadManager::Get()->GetNfcDeviceClient()); device_client->BeginPairingSimulation(3000, 2000); + } else { + FakeNfcTagClient* tag_client = + static_cast<FakeNfcTagClient*>( + DBusThreadManager::Get()->GetNfcTagClient()); + tag_client->BeginPairingSimulation(2000); + } + device_pairing_ = !device_pairing_; } void FakeNfcAdapterClient::StopPollLoop( @@ -188,6 +199,10 @@ void FakeNfcAdapterClient::StopPollLoop( static_cast<FakeNfcDeviceClient*>( DBusThreadManager::Get()->GetNfcDeviceClient()); device_client->EndPairingSimulation(); + FakeNfcTagClient* tag_client = + static_cast<FakeNfcTagClient*>( + DBusThreadManager::Get()->GetNfcTagClient()); + tag_client->EndPairingSimulation(); properties_->polling.ReplaceValue(false); callback.Run(); } @@ -221,7 +236,7 @@ void FakeNfcAdapterClient::SetSecondAdapterPresent(bool present) { void FakeNfcAdapterClient::SetDevice(const dbus::ObjectPath& device_path) { LOG(INFO) << "Add device path to the fake adapter: " << device_path.value(); if (!properties_->polling.value()) { - LOG(ERROR) << "Device not polling, cannot set device."; + LOG(ERROR) << "Adapter not polling, cannot set device."; return; } const ObjectPathVector& devices(properties_->devices.value()); @@ -240,8 +255,31 @@ void FakeNfcAdapterClient::SetDevice(const dbus::ObjectPath& device_path) { properties_->devices.ReplaceValue(new_devices); } +void FakeNfcAdapterClient::SetTag(const dbus::ObjectPath& tag_path) { + LOG(INFO) << "Add tag path to the fake adapter: " << tag_path.value(); + if (!properties_->polling.value()) { + LOG(ERROR) << "Adapter not polling, cannot set tag."; + return; + } + const ObjectPathVector& tags(properties_->tags.value()); + for (ObjectPathVector::const_iterator iter = tags.begin(); + iter != tags.end(); ++iter) { + if (*iter == tag_path) { + LOG(WARNING) << "Tag path already in list of tags."; + return; + } + } + // Mark as not polling. + properties_->polling.ReplaceValue(false); + + ObjectPathVector new_tags = tags; + new_tags.push_back(tag_path); + properties_->tags.ReplaceValue(new_tags); +} + void FakeNfcAdapterClient::UnsetDevice(const dbus::ObjectPath& device_path) { - LOG(INFO) << "Add device path to the fake adapter: " << device_path.value(); + LOG(INFO) << "Remove device path from the fake adapter: " + << device_path.value(); ObjectPathVector new_devices = properties_->devices.value(); for (ObjectPathVector::iterator iter = new_devices.begin(); iter != new_devices.end(); ++iter) { @@ -258,6 +296,24 @@ void FakeNfcAdapterClient::UnsetDevice(const dbus::ObjectPath& device_path) { LOG(WARNING) << "Device path not in list of devices."; } +void FakeNfcAdapterClient::UnsetTag(const dbus::ObjectPath& tag_path) { + LOG(INFO) << "Add tag path to the fake adapter: " << tag_path.value(); + ObjectPathVector new_tags = properties_->tags.value(); + for (ObjectPathVector::iterator iter = new_tags.begin(); + iter != new_tags.end(); ++iter) { + if (*iter == tag_path) { + new_tags.erase(iter); + properties_->tags.ReplaceValue(new_tags); + + // Mark as polling. + DCHECK(!properties_->polling.value()); + properties_->polling.ReplaceValue(true); + return; + } + } + LOG(WARNING) << "Tag path not in list of tags."; +} + void FakeNfcAdapterClient::EnablePairingOnPoll(bool enabled) { start_pairing_on_poll_ = enabled; } diff --git a/chromeos/dbus/fake_nfc_adapter_client.h b/chromeos/dbus/fake_nfc_adapter_client.h index 6752458..0bd353a 100644 --- a/chromeos/dbus/fake_nfc_adapter_client.h +++ b/chromeos/dbus/fake_nfc_adapter_client.h @@ -60,22 +60,29 @@ class CHROMEOS_EXPORT FakeNfcAdapterClient : public NfcAdapterClient { void SetAdapterPresent(bool present); void SetSecondAdapterPresent(bool present); - // Tells the FakeNfcAdapterClient to add the device with path |device_path| - // to its list of devices exposed for |kAdapterPath0|, if it is not already in + // Tells the FakeNfcAdapterClient to add the device or tag with the given path + // to its corresponding list for |kAdapterPath0|, if it is not already in // the list and promptly triggers a property changed signal. This method will // also fail, if the polling property of the adapter is false and will set it // to false on success. void SetDevice(const dbus::ObjectPath& device_path); + void SetTag(const dbus::ObjectPath& tag_path); - // Talls the FakeNfcAdapterClient to remove the device with path - // |device_path| from its list of devices exposed for |kAdapterPath0|, if it - // is in its list of devices. On success, this method will mark the polling - // property of the adapter to true. + // Tells the FakeNfcAdapterClient to remove the device or tag with the given + // path from its corresponding list exposed for |kAdapterPath0|, if it + // is in the list. On success, this method will mark the polling property of + // the adapter to true. void UnsetDevice(const dbus::ObjectPath& device_path); + void UnsetTag(const dbus::ObjectPath& tag_path); // Sets a flag that determines whether FakeNfcAdapterClient should notify - // FakeNfcDeviceClient to start a pairing simulation as a result of a call - // to StartPollLoop(). This is enabled by default. + // FakeNfcDeviceClient or FakeNfcTagClient to start a pairing simulation as a + // result of a call to StartPollLoop(). This is enabled by default. If + // enabled, the first call to StartPollLoop, will initiate a tag pairing + // simulation. The simulation will alternate between device and tag pairing on + // each successive call to StartPollLoop. This behavior, which is meant for + // feature development based on fake classes, can be disabled to allow manual + // control for unit tests. void EnablePairingOnPoll(bool enabled); private: @@ -98,6 +105,10 @@ class CHROMEOS_EXPORT FakeNfcAdapterClient : public NfcAdapterClient { // StartPollLoop(). bool start_pairing_on_poll_; + // If true, device pairing will be simulated on the next call to + // StartPollLoop. Otherwise, tag pairing will be simulated. + bool device_pairing_; + DISALLOW_COPY_AND_ASSIGN(FakeNfcAdapterClient); }; diff --git a/chromeos/dbus/fake_nfc_device_client.cc b/chromeos/dbus/fake_nfc_device_client.cc index d5f7a21..cf43f08 100644 --- a/chromeos/dbus/fake_nfc_device_client.cc +++ b/chromeos/dbus/fake_nfc_device_client.cc @@ -13,6 +13,7 @@ #include "chromeos/dbus/fake_nfc_record_client.h" #include "chromeos/dbus/nfc_client_helpers.h" #include "dbus/object_path.h" +#include "third_party/cros_system_api/dbus/service_constants.h" namespace chromeos { @@ -77,7 +78,7 @@ std::vector<dbus::ObjectPath> FakeNfcDeviceClient::GetDevicesForAdapter( const dbus::ObjectPath& adapter_path) { std::vector<dbus::ObjectPath> device_paths; if (device_visible_ && - adapter_path == dbus::ObjectPath(FakeNfcAdapterClient::kAdapterPath0)) + adapter_path.value() == FakeNfcAdapterClient::kAdapterPath0) device_paths.push_back(dbus::ObjectPath(kDevicePath)); return device_paths; } @@ -97,6 +98,11 @@ void FakeNfcDeviceClient::Push( VLOG(1) << "FakeNfcDeviceClient::Write called."; // Success! + if (!device_visible_) { + LOG(ERROR) << "Device not visible. Cannot push record."; + error_callback.Run(nfc_error::kDoesNotExist, "No such device."); + return; + } callback.Run(); } @@ -107,10 +113,7 @@ void FakeNfcDeviceClient::BeginPairingSimulation(int visibility_delay, return; } DCHECK(!device_visible_); - - // Cap the |visibility_delay| value at zero. Leave |record_push_delay| as is, - // as a negative value has a special meaning for it. - visibility_delay = visibility_delay < 0 ? 0 : visibility_delay; + DCHECK(visibility_delay >= 0); pairing_started_ = true; @@ -133,7 +136,7 @@ void FakeNfcDeviceClient::EndPairingSimulation() { FakeNfcRecordClient* record_client = static_cast<FakeNfcRecordClient*>( DBusThreadManager::Get()->GetNfcRecordClient()); - record_client->SetRecordsVisible(false); + record_client->SetDeviceRecordsVisible(false); } // Remove the device. FOR_EACH_OBSERVER(Observer, observers_, @@ -218,7 +221,7 @@ void FakeNfcDeviceClient::MakeRecordsVisible() { FakeNfcRecordClient* record_client = static_cast<FakeNfcRecordClient*>( DBusThreadManager::Get()->GetNfcRecordClient()); - record_client->SetRecordsVisible(true); + record_client->SetDeviceRecordsVisible(true); if (simulation_timeout_ < 0) return; diff --git a/chromeos/dbus/fake_nfc_device_client.h b/chromeos/dbus/fake_nfc_device_client.h index 50734e3..ab89c08 100644 --- a/chromeos/dbus/fake_nfc_device_client.h +++ b/chromeos/dbus/fake_nfc_device_client.h @@ -56,8 +56,8 @@ class CHROMEOS_EXPORT FakeNfcDeviceClient : public NfcDeviceClient { // Simulates the appearance of a device. The fake device will show up after // exactly |visibility_delay| milliseconds, and will simulate pushing a single // record to the local fake adapter after exactly |record_push_delay| - // milliseconds after the the device appears. If |visibility_delay| has a - // negative value, it will be treated as 0. If |record_push_delay| has a + // milliseconds after the the device appears. |visibility_delay| must have a + // non-negative value. |record_push_delay| CAN be negative: if it has a // negative value, the record push step will not be simulated. The // side-effects of this method occur asynchronously, i.e. even with arguments // with value 0, the pairing won't take place until after this method has @@ -72,8 +72,9 @@ class CHROMEOS_EXPORT FakeNfcDeviceClient : public NfcDeviceClient { // Enables or disables automatic unpairing. When enabled, a pairing // simulation will end |simulation_timeout| milliseconds after records have - // been exposed. This is enabled by default and the timeout is set to - // |kDefaultSimulationTimeoutMilliseconds|. + // been exposed (or after the tag has been exposed, if |record_push_delay| was + // given as a negative value to BeginPairingSimulation) This is enabled by + // default and the timeout is set to |kDefaultSimulationTimeoutMilliseconds|. void EnableSimulationTimeout(int simulation_timeout); void DisableSimulationTimeout(); diff --git a/chromeos/dbus/fake_nfc_record_client.cc b/chromeos/dbus/fake_nfc_record_client.cc index ef77aad..c16b300 100644 --- a/chromeos/dbus/fake_nfc_record_client.cc +++ b/chromeos/dbus/fake_nfc_record_client.cc @@ -7,13 +7,47 @@ #include "base/logging.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/fake_nfc_device_client.h" +#include "chromeos/dbus/fake_nfc_tag_client.h" #include "third_party/cros_system_api/dbus/service_constants.h" namespace chromeos { -const char FakeNfcRecordClient::kSmartPosterRecordPath[] = "/fake/record0"; -const char FakeNfcRecordClient::kTextRecordPath[] = "/fake/record1"; -const char FakeNfcRecordClient::kUriRecordPath[] = "/fake/record2"; +namespace { + +// Gets and returns the value for |key| in |dictionary| as a string. If |key| is +// not found, returns an empty string. +std::string GetStringValue(const base::DictionaryValue& dictionary, + const std::string& key) { + std::string value; + bool result = dictionary.GetString(key, &value); + + // Simply return |value|. |value| will remain untouched if + // base::DictionaryValue::GetString returns false. + DCHECK(result || value.empty()); + return value; +} + +// Gets and returns the value for |key| in |dictionary| as a double. If |key| is +// not found, returns 0. +double GetDoubleValue(const base::DictionaryValue& dictionary, + const std::string& key) { + double value = 0; + bool result = dictionary.GetDouble(key, &value); + + // Simply return |value|. |value| will remain untouched if + // base::DictionaryValue::GetString returns false. + DCHECK(result || !value); + return value; +} + +} // namespace + +const char FakeNfcRecordClient::kDeviceSmartPosterRecordPath[] = + "/fake/device/record0"; +const char FakeNfcRecordClient::kDeviceTextRecordPath[] = + "/fake/device/record1"; +const char FakeNfcRecordClient::kDeviceUriRecordPath[] = "/fake/device/record2"; +const char FakeNfcRecordClient::kTagRecordPath[] = "/fake/tag/record0"; FakeNfcRecordClient::Properties::Properties( const PropertyChangedCallback& callback) @@ -43,34 +77,42 @@ void FakeNfcRecordClient::Properties::Set( callback.Run(false); } -FakeNfcRecordClient::FakeNfcRecordClient() : records_visible_(false) { +FakeNfcRecordClient::FakeNfcRecordClient() + : device_records_visible_(false), + tag_records_visible_(false) { VLOG(1) << "Creating FakeNfcRecordClient"; - smart_poster_record_properties_.reset(new Properties( + + device_smart_poster_record_properties_.reset(new Properties( base::Bind(&FakeNfcRecordClient::OnPropertyChanged, base::Unretained(this), - dbus::ObjectPath(kSmartPosterRecordPath)))); - smart_poster_record_properties_->SetAllPropertiesReceivedCallback( + dbus::ObjectPath(kDeviceSmartPosterRecordPath)))); + device_smart_poster_record_properties_->SetAllPropertiesReceivedCallback( base::Bind(&FakeNfcRecordClient::OnPropertiesReceived, base::Unretained(this), - dbus::ObjectPath(kSmartPosterRecordPath))); + dbus::ObjectPath(kDeviceSmartPosterRecordPath))); - text_record_properties_.reset(new Properties( + device_text_record_properties_.reset(new Properties( base::Bind(&FakeNfcRecordClient::OnPropertyChanged, base::Unretained(this), - dbus::ObjectPath(kTextRecordPath)))); - text_record_properties_->SetAllPropertiesReceivedCallback( + dbus::ObjectPath(kDeviceTextRecordPath)))); + device_text_record_properties_->SetAllPropertiesReceivedCallback( base::Bind(&FakeNfcRecordClient::OnPropertiesReceived, base::Unretained(this), - dbus::ObjectPath(kTextRecordPath))); + dbus::ObjectPath(kDeviceTextRecordPath))); - uri_record_properties_.reset(new Properties( + device_uri_record_properties_.reset(new Properties( base::Bind(&FakeNfcRecordClient::OnPropertyChanged, base::Unretained(this), - dbus::ObjectPath(kUriRecordPath)))); - uri_record_properties_->SetAllPropertiesReceivedCallback( + dbus::ObjectPath(kDeviceUriRecordPath)))); + device_uri_record_properties_->SetAllPropertiesReceivedCallback( base::Bind(&FakeNfcRecordClient::OnPropertiesReceived, base::Unretained(this), - dbus::ObjectPath(kUriRecordPath))); + dbus::ObjectPath(kDeviceUriRecordPath))); + + tag_record_properties_.reset(new Properties( + base::Bind(&FakeNfcRecordClient::OnPropertyChanged, + base::Unretained(this), + dbus::ObjectPath(kTagRecordPath)))); } FakeNfcRecordClient::~FakeNfcRecordClient() { @@ -90,30 +132,41 @@ void FakeNfcRecordClient::RemoveObserver(Observer* observer) { std::vector<dbus::ObjectPath> FakeNfcRecordClient::GetRecordsForDevice( const dbus::ObjectPath& device_path) { std::vector<dbus::ObjectPath> record_paths; - if (records_visible_ && + if (device_records_visible_ && device_path == dbus::ObjectPath(FakeNfcDeviceClient::kDevicePath)) { - record_paths.push_back(dbus::ObjectPath(kSmartPosterRecordPath)); - record_paths.push_back(dbus::ObjectPath(kTextRecordPath)); - record_paths.push_back(dbus::ObjectPath(kUriRecordPath)); + record_paths.push_back(dbus::ObjectPath(kDeviceSmartPosterRecordPath)); + record_paths.push_back(dbus::ObjectPath(kDeviceTextRecordPath)); + record_paths.push_back(dbus::ObjectPath(kDeviceUriRecordPath)); } return record_paths; } +std::vector<dbus::ObjectPath> FakeNfcRecordClient::GetRecordsForTag( + const dbus::ObjectPath& tag_path) { + std::vector<dbus::ObjectPath> record_paths; + if (tag_records_visible_ && tag_path.value() == FakeNfcTagClient::kTagPath) + record_paths.push_back(dbus::ObjectPath(kTagRecordPath)); + return record_paths; +} + FakeNfcRecordClient::Properties* FakeNfcRecordClient::GetProperties(const dbus::ObjectPath& object_path) { - if (!records_visible_) + if (device_records_visible_) { + if (object_path.value() == kDeviceSmartPosterRecordPath) + return device_smart_poster_record_properties_.get(); + if (object_path.value() == kDeviceTextRecordPath) + return device_text_record_properties_.get(); + if (object_path.value() == kDeviceUriRecordPath) + return device_uri_record_properties_.get(); return NULL; - if (object_path.value() == kSmartPosterRecordPath) - return smart_poster_record_properties_.get(); - if (object_path.value() == kTextRecordPath) - return text_record_properties_.get(); - if (object_path.value() == kUriRecordPath) - return uri_record_properties_.get(); + } + if (tag_records_visible_ && object_path.value() == kTagRecordPath) + return tag_record_properties_.get(); return NULL; } -void FakeNfcRecordClient::SetRecordsVisible(bool visible) { - if (records_visible_ == visible) { +void FakeNfcRecordClient::SetDeviceRecordsVisible(bool visible) { + if (device_records_visible_ == visible) { VLOG(1) << "Record visibility is already: " << visible; return; } @@ -123,55 +176,132 @@ void FakeNfcRecordClient::SetRecordsVisible(bool visible) { VLOG(1) << "Cannot set records when device is not visible."; return; } - if (visible) { - records_visible_ = visible; - std::vector<dbus::ObjectPath> record_paths = - GetRecordsForDevice( - dbus::ObjectPath(FakeNfcDeviceClient::kDevicePath)); - device_client->SetRecords(record_paths); - - // Reassign each property and send signals. - FOR_EACH_OBSERVER(NfcRecordClient::Observer, observers_, - RecordAdded(dbus::ObjectPath(kSmartPosterRecordPath))); - smart_poster_record_properties_->type.ReplaceValue( - nfc_record::kTypeSmartPoster); - smart_poster_record_properties_->uri.ReplaceValue( - "http://www.fake-uri0.com"); - smart_poster_record_properties_->mime_type.ReplaceValue("text/fake"); - smart_poster_record_properties_->size.ReplaceValue(128); - smart_poster_record_properties_->representation.ReplaceValue("Fake Title"); - smart_poster_record_properties_->encoding.ReplaceValue( - nfc_record::kEncodingUtf16); - smart_poster_record_properties_->language.ReplaceValue("en"); - OnPropertiesReceived(dbus::ObjectPath(kSmartPosterRecordPath)); - - FOR_EACH_OBSERVER(NfcRecordClient::Observer, observers_, - RecordAdded(dbus::ObjectPath(kTextRecordPath))); - text_record_properties_->type.ReplaceValue(nfc_record::kTypeText); - text_record_properties_->representation.ReplaceValue( - "Sahte Ba\xC5\x9fl\xC4\xB1k"); - text_record_properties_->encoding.ReplaceValue( - nfc_record::kEncodingUtf8); - text_record_properties_->language.ReplaceValue("tr"); - OnPropertiesReceived(dbus::ObjectPath(kTextRecordPath)); - - FOR_EACH_OBSERVER(NfcRecordClient::Observer, observers_, - RecordAdded(dbus::ObjectPath(kUriRecordPath))); - uri_record_properties_->type.ReplaceValue(nfc_record::kTypeUri); - uri_record_properties_->uri.ReplaceValue("file://some/fake/path"); - uri_record_properties_->mime_type.ReplaceValue("text/fake"); - uri_record_properties_->size.ReplaceValue(512); - OnPropertiesReceived(dbus::ObjectPath(kUriRecordPath)); - } else { + if (!visible) { device_client->ClearRecords(); + FOR_EACH_OBSERVER( + NfcRecordClient::Observer, observers_, + RecordRemoved(dbus::ObjectPath(kDeviceSmartPosterRecordPath))); FOR_EACH_OBSERVER(NfcRecordClient::Observer, observers_, - RecordRemoved(dbus::ObjectPath(kSmartPosterRecordPath))); + RecordRemoved(dbus::ObjectPath(kDeviceTextRecordPath))); FOR_EACH_OBSERVER(NfcRecordClient::Observer, observers_, - RecordRemoved(dbus::ObjectPath(kTextRecordPath))); + RecordRemoved(dbus::ObjectPath(kDeviceUriRecordPath))); + device_records_visible_ = visible; + return; + } + device_records_visible_ = visible; + std::vector<dbus::ObjectPath> record_paths = + GetRecordsForDevice( + dbus::ObjectPath(FakeNfcDeviceClient::kDevicePath)); + device_client->SetRecords(record_paths); + + // Reassign each property and send signals. + FOR_EACH_OBSERVER( + NfcRecordClient::Observer, observers_, + RecordAdded(dbus::ObjectPath(kDeviceSmartPosterRecordPath))); + device_smart_poster_record_properties_->type.ReplaceValue( + nfc_record::kTypeSmartPoster); + device_smart_poster_record_properties_->uri.ReplaceValue( + "http://fake.uri0.fake"); + device_smart_poster_record_properties_->mime_type.ReplaceValue("text/fake"); + device_smart_poster_record_properties_->size.ReplaceValue(128); + device_smart_poster_record_properties_-> + representation.ReplaceValue("Fake Title"); + device_smart_poster_record_properties_->encoding.ReplaceValue( + nfc_record::kEncodingUtf16); + device_smart_poster_record_properties_->language.ReplaceValue("en"); + OnPropertiesReceived(dbus::ObjectPath(kDeviceSmartPosterRecordPath)); + + FOR_EACH_OBSERVER(NfcRecordClient::Observer, observers_, + RecordAdded(dbus::ObjectPath(kDeviceTextRecordPath))); + device_text_record_properties_->type.ReplaceValue(nfc_record::kTypeText); + device_text_record_properties_->representation.ReplaceValue( + "Kablosuz \xC4\xb0leti\xC5\x9fim"); + device_text_record_properties_->encoding.ReplaceValue( + nfc_record::kEncodingUtf8); + device_text_record_properties_->language.ReplaceValue("tr"); + OnPropertiesReceived(dbus::ObjectPath(kDeviceTextRecordPath)); + + FOR_EACH_OBSERVER(NfcRecordClient::Observer, observers_, + RecordAdded(dbus::ObjectPath(kDeviceUriRecordPath))); + device_uri_record_properties_->type.ReplaceValue(nfc_record::kTypeUri); + device_uri_record_properties_->uri.ReplaceValue("file://some/fake/path"); + device_uri_record_properties_->mime_type.ReplaceValue("text/fake"); + device_uri_record_properties_->size.ReplaceValue(512); + OnPropertiesReceived(dbus::ObjectPath(kDeviceUriRecordPath)); +} + +void FakeNfcRecordClient::SetTagRecordsVisible(bool visible) { + if (tag_records_visible_ == visible) { + VLOG(1) << "Record visibility is already: " << visible; + return; + } + FakeNfcTagClient* tag_client = static_cast<FakeNfcTagClient*>( + DBusThreadManager::Get()->GetNfcTagClient()); + if (!tag_client->tag_visible()) { + VLOG(1) << "Cannot set records when tag is not visible."; + return; + } + if (!visible) { + tag_client->ClearRecords(); FOR_EACH_OBSERVER(NfcRecordClient::Observer, observers_, - RecordRemoved(dbus::ObjectPath(kUriRecordPath))); - records_visible_ = visible; + RecordRemoved(dbus::ObjectPath(kTagRecordPath))); + tag_records_visible_ = visible; + return; } + tag_records_visible_ = visible; + std::vector<dbus::ObjectPath> record_paths = + GetRecordsForTag(dbus::ObjectPath(FakeNfcTagClient::kTagPath)); + tag_client->SetRecords(record_paths); + + // Reassign each property to its current value to trigger a property change + // signal. + FOR_EACH_OBSERVER(NfcRecordClient::Observer, observers_, + RecordAdded(dbus::ObjectPath(kTagRecordPath))); + tag_record_properties_->type.ReplaceValue( + tag_record_properties_->type.value()); + tag_record_properties_->representation.ReplaceValue( + tag_record_properties_->representation.value()); + tag_record_properties_->encoding.ReplaceValue( + tag_record_properties_->encoding.value()); + tag_record_properties_->language.ReplaceValue( + tag_record_properties_->language.value()); + tag_record_properties_->uri.ReplaceValue( + tag_record_properties_->uri.value()); + tag_record_properties_->mime_type.ReplaceValue( + tag_record_properties_->mime_type.value()); + tag_record_properties_->size.ReplaceValue( + tag_record_properties_->size.value()); + tag_record_properties_->action.ReplaceValue( + tag_record_properties_->action.value()); + OnPropertiesReceived(dbus::ObjectPath(kTagRecordPath)); +} + +bool FakeNfcRecordClient::WriteTagRecord( + const base::DictionaryValue& attributes) { + if (attributes.empty()) + return false; + + tag_record_properties_->type.ReplaceValue( + GetStringValue(attributes, nfc_record::kTypeProperty)); + tag_record_properties_->encoding.ReplaceValue( + GetStringValue(attributes, nfc_record::kEncodingProperty)); + tag_record_properties_->language.ReplaceValue( + GetStringValue(attributes, nfc_record::kLanguageProperty)); + tag_record_properties_->representation.ReplaceValue( + GetStringValue(attributes, nfc_record::kRepresentationProperty)); + tag_record_properties_->uri.ReplaceValue( + GetStringValue(attributes, nfc_record::kUriProperty)); + tag_record_properties_->mime_type.ReplaceValue( + GetStringValue(attributes, nfc_record::kMimeTypeProperty)); + tag_record_properties_->action.ReplaceValue( + GetStringValue(attributes, nfc_record::kActionProperty)); + tag_record_properties_->size.ReplaceValue(static_cast<uint32>( + GetDoubleValue(attributes, nfc_record::kSizeProperty))); + + SetTagRecordsVisible(false); + SetTagRecordsVisible(true); + + return true; } void FakeNfcRecordClient::OnPropertyChanged( diff --git a/chromeos/dbus/fake_nfc_record_client.h b/chromeos/dbus/fake_nfc_record_client.h index db53094..9af1bcc 100644 --- a/chromeos/dbus/fake_nfc_record_client.h +++ b/chromeos/dbus/fake_nfc_record_client.h @@ -7,6 +7,7 @@ #include "base/memory/scoped_ptr.h" #include "base/observer_list.h" +#include "base/values.h" #include "chromeos/chromeos_export.h" #include "chromeos/dbus/nfc_record_client.h" #include "dbus/object_path.h" @@ -18,9 +19,10 @@ namespace chromeos { class CHROMEOS_EXPORT FakeNfcRecordClient : public NfcRecordClient { public: // Paths of the records exposed. - static const char kSmartPosterRecordPath[]; - static const char kTextRecordPath[]; - static const char kUriRecordPath[]; + static const char kDeviceSmartPosterRecordPath[]; + static const char kDeviceTextRecordPath[]; + static const char kDeviceUriRecordPath[]; + static const char kTagRecordPath[]; // Properties structure that provides fake behavior for D-Bus calls. struct Properties : public NfcRecordClient::Properties { @@ -44,11 +46,22 @@ class CHROMEOS_EXPORT FakeNfcRecordClient : public NfcRecordClient { virtual void RemoveObserver(Observer* observer) OVERRIDE; virtual std::vector<dbus::ObjectPath> GetRecordsForDevice( const dbus::ObjectPath& device_path) OVERRIDE; + virtual std::vector<dbus::ObjectPath> GetRecordsForTag( + const dbus::ObjectPath& tag_path) OVERRIDE; virtual Properties* GetProperties( const dbus::ObjectPath& object_path) OVERRIDE; // Adds or removes the fake record objects and notifies the observers. - void SetRecordsVisible(bool visible); + void SetDeviceRecordsVisible(bool visible); + void SetTagRecordsVisible(bool visible); + + // Modifies the contents of the tag record. |attributes| should be the + // same as the argument to NfcTagClient::Write. Each field will be directly + // assigned to the underlying record based on the type property, with + // no validity checking. Invalid tag content can be passed here to test + // the case where the remote application returns an incorrectly formatted + // record. + bool WriteTagRecord(const base::DictionaryValue& attributes); private: // Property changed callback passed when we create Properties* structures. @@ -59,15 +72,17 @@ class CHROMEOS_EXPORT FakeNfcRecordClient : public NfcRecordClient { void OnPropertiesReceived(const dbus::ObjectPath& object_path); // If true, the records are currently visible. - bool records_visible_; + bool device_records_visible_; + bool tag_records_visible_; // List of observers interested in event notifications from us. ObserverList<Observer> observers_; // Fake properties that are returned for the fake records. - scoped_ptr<Properties> smart_poster_record_properties_; - scoped_ptr<Properties> text_record_properties_; - scoped_ptr<Properties> uri_record_properties_; + scoped_ptr<Properties> device_smart_poster_record_properties_; + scoped_ptr<Properties> device_text_record_properties_; + scoped_ptr<Properties> device_uri_record_properties_; + scoped_ptr<Properties> tag_record_properties_; DISALLOW_COPY_AND_ASSIGN(FakeNfcRecordClient); }; diff --git a/chromeos/dbus/fake_nfc_tag_client.cc b/chromeos/dbus/fake_nfc_tag_client.cc index 6875ad0..1de3efd 100644 --- a/chromeos/dbus/fake_nfc_tag_client.cc +++ b/chromeos/dbus/fake_nfc_tag_client.cc @@ -5,14 +5,22 @@ #include "chromeos/dbus/fake_nfc_tag_client.h" #include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/time/time.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_nfc_adapter_client.h" +#include "chromeos/dbus/fake_nfc_record_client.h" +#include "chromeos/dbus/nfc_client_helpers.h" #include "dbus/object_path.h" - -// TODO(armansito): For now, this class doesn't do anything. Implement fake -// behavior in conjunction with unit tests while implementing the src/device -// layer. +#include "third_party/cros_system_api/dbus/service_constants.h" namespace chromeos { +using nfc_client_helpers::ObjectPathVector; + +const char FakeNfcTagClient::kTagPath[] = "/fake/tag0"; +const int FakeNfcTagClient::kDefaultSimulationTimeoutMilliseconds = 20000; + FakeNfcTagClient::Properties::Properties( const PropertyChangedCallback& callback) : NfcTagClient::Properties(NULL, callback) { @@ -39,8 +47,15 @@ void FakeNfcTagClient::Properties::Set( callback.Run(false); } -FakeNfcTagClient::FakeNfcTagClient() { +FakeNfcTagClient::FakeNfcTagClient() + : pairing_started_(false), + tag_visible_(false), + simulation_timeout_(kDefaultSimulationTimeoutMilliseconds) { VLOG(1) << "Creating FakeNfcTagClient"; + properties_.reset(new Properties( + base::Bind(&FakeNfcTagClient::OnPropertyChanged, + base::Unretained(this), + dbus::ObjectPath(kTagPath)))); } FakeNfcTagClient::~FakeNfcTagClient() { @@ -50,14 +65,27 @@ void FakeNfcTagClient::Init(dbus::Bus* bus) { } void FakeNfcTagClient::AddObserver(Observer* observer) { + observers_.AddObserver(observer); } void FakeNfcTagClient::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +std::vector<dbus::ObjectPath> FakeNfcTagClient::GetTagsForAdapter( + const dbus::ObjectPath& adapter_path) { + std::vector<dbus::ObjectPath> tag_paths; + if (tag_visible_ && + adapter_path.value() == FakeNfcAdapterClient::kAdapterPath0) + tag_paths.push_back(dbus::ObjectPath(kTagPath)); + return tag_paths; } FakeNfcTagClient::Properties* FakeNfcTagClient::GetProperties(const dbus::ObjectPath& object_path) { - return NULL; + if (!tag_visible_) + return NULL; + return properties_.get(); } void FakeNfcTagClient::Write( @@ -66,6 +94,135 @@ void FakeNfcTagClient::Write( const base::Closure& callback, const nfc_client_helpers::ErrorCallback& error_callback) { VLOG(1) << "FakeNfcTagClient::Write called. Nothing happened."; + + if (!tag_visible_ || object_path.value() != kTagPath) { + LOG(ERROR) << "No such tag: " << object_path.value(); + error_callback.Run(nfc_error::kDoesNotExist, "No such tag."); + return; + } + + FakeNfcRecordClient* record_client = static_cast<FakeNfcRecordClient*>( + DBusThreadManager::Get()->GetNfcRecordClient()); + if (!record_client->WriteTagRecord(attributes)) { + LOG(ERROR) << "Failed to tag: " << object_path.value(); + error_callback.Run(nfc_error::kFailed, "Failed."); + return; + } + + // Success! + callback.Run(); +} + +void FakeNfcTagClient::BeginPairingSimulation(int visibility_delay) { + if (pairing_started_) { + VLOG(1) << "Simulation already started."; + return; + } + DCHECK(!tag_visible_); + DCHECK(visibility_delay >= 0); + + pairing_started_ = true; + + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeNfcTagClient::MakeTagVisible, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(visibility_delay)); +} + +void FakeNfcTagClient::EndPairingSimulation() { + if (!pairing_started_) { + VLOG(1) << "No simulation started."; + return; + } + + pairing_started_ = false; + if (!tag_visible_) + return; + + // Remove records, if they were added. + if (!properties_->records.value().empty()) { + FakeNfcRecordClient* record_client = + static_cast<FakeNfcRecordClient*>( + DBusThreadManager::Get()->GetNfcRecordClient()); + record_client->SetTagRecordsVisible(false); + } + // Remove the tag. + FOR_EACH_OBSERVER(Observer, observers_, + TagRemoved(dbus::ObjectPath(kTagPath))); + FakeNfcAdapterClient* adapter_client = + static_cast<FakeNfcAdapterClient*>( + DBusThreadManager::Get()->GetNfcAdapterClient()); + adapter_client->UnsetTag(dbus::ObjectPath(kTagPath)); + tag_visible_ = false; +} + +void FakeNfcTagClient::EnableSimulationTimeout(int simulation_timeout) { + DCHECK(simulation_timeout >= 0); + simulation_timeout_ = simulation_timeout; +} + +void FakeNfcTagClient::DisableSimulationTimeout() { + simulation_timeout_ = -1; +} + +void FakeNfcTagClient::SetRecords( + const std::vector<dbus::ObjectPath>& record_paths) { + if (!tag_visible_) { + VLOG(1) << "Tag not visible."; + return; + } + properties_->records.ReplaceValue(record_paths); +} + +void FakeNfcTagClient::ClearRecords() { + ObjectPathVector records; + SetRecords(records); +} + +void FakeNfcTagClient::OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) { + FOR_EACH_OBSERVER(Observer, observers_, + TagPropertyChanged(object_path, property_name)); +} + +void FakeNfcTagClient::MakeTagVisible() { + if (!pairing_started_) { + VLOG(1) << "Tag pairing was cancelled."; + return; + } + tag_visible_ = true; + + // Set the tag properties. + FakeNfcAdapterClient* adapter_client = + static_cast<FakeNfcAdapterClient*>( + DBusThreadManager::Get()->GetNfcAdapterClient()); + adapter_client->SetTag(dbus::ObjectPath(kTagPath)); + FOR_EACH_OBSERVER(Observer, observers_, + TagAdded(dbus::ObjectPath(kTagPath))); + + properties_->type.ReplaceValue(nfc_tag::kTagType2); + properties_->protocol.ReplaceValue(nfc_common::kProtocolNfcDep); + properties_->read_only.ReplaceValue(false); + FOR_EACH_OBSERVER(Observer, observers_, + TagPropertiesReceived(dbus::ObjectPath(kTagPath))); + + if (simulation_timeout_ >= 0) { + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeNfcTagClient::HandleSimulationTimeout, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(simulation_timeout_)); + return; + } +} + +void FakeNfcTagClient::HandleSimulationTimeout() { + if (simulation_timeout_ < 0) { + VLOG(1) << "Simulation timeout was cancelled. Nothing to do."; + return; + } + EndPairingSimulation(); } } // namespace chromeos diff --git a/chromeos/dbus/fake_nfc_tag_client.h b/chromeos/dbus/fake_nfc_tag_client.h index 90321ee..244d373 100644 --- a/chromeos/dbus/fake_nfc_tag_client.h +++ b/chromeos/dbus/fake_nfc_tag_client.h @@ -5,6 +5,7 @@ #ifndef CHROMEOS_DBUS_FAKE_NFC_TAG_CLIENT_H_ #define CHROMEOS_DBUS_FAKE_NFC_TAG_CLIENT_H_ +#include "base/observer_list.h" #include "chromeos/chromeos_export.h" #include "chromeos/dbus/nfc_client_helpers.h" #include "chromeos/dbus/nfc_tag_client.h" @@ -13,11 +14,14 @@ namespace chromeos { // FakeNfcTagClient simulates the behavior of the NFC tag objects // and is used both in test cases in place of a mock and on the Linux desktop. -// TODO(armansito): For now, this doesn't do anything. Implement fake -// behavior in conjunction with unit tests while implementing the src/device -// layer. class CHROMEOS_EXPORT FakeNfcTagClient : public NfcTagClient { public: + // The fake tag object path. + static const char kTagPath[]; + + // The default simulation timeout interval. + static const int kDefaultSimulationTimeoutMilliseconds; + struct Properties : public NfcTagClient::Properties { explicit Properties(const PropertyChangedCallback& callback); virtual ~Properties(); @@ -37,6 +41,8 @@ class CHROMEOS_EXPORT FakeNfcTagClient : public NfcTagClient { virtual void Init(dbus::Bus* bus) OVERRIDE; virtual void AddObserver(Observer* observer) OVERRIDE; virtual void RemoveObserver(Observer* observer) OVERRIDE; + virtual std::vector<dbus::ObjectPath> GetTagsForAdapter( + const dbus::ObjectPath& adapter_path) OVERRIDE; virtual Properties* GetProperties( const dbus::ObjectPath& object_path) OVERRIDE; virtual void Write( @@ -45,7 +51,70 @@ class CHROMEOS_EXPORT FakeNfcTagClient : public NfcTagClient { const base::Closure& callback, const nfc_client_helpers::ErrorCallback& error_callback) OVERRIDE; + // Simulates the appearance of a tag. The fake tag will show up after + // exactly |visibility_delay| milliseconds. |visibility_delay| must have a + // non-negative value. The side-effects of this method + // occur asynchronously, i.e. even with an argument of 0, the pairing will not + // take place until after this method has returned. + void BeginPairingSimulation(int visibility_delay); + + // If tag pairing was previously started, simulates the disappearance of + // the tag. Any tag object presented and their records will disappear + // after this call. Delayed events that were set up by a previous call to + // BeginPairing() will be canceled through a call to EndPairing(). + void EndPairingSimulation(); + + // Enables or disables automatic unpairing. When enabled, a pairing + // simulation will end |simulation_timeout| milliseconds after the tag has + // been exposed. This is enabled by default and the timeout is set to + // |kDefaultSimulationTimeoutMilliseconds|. |simulation_timeout| must be + // non-negative. + void EnableSimulationTimeout(int simulation_timeout); + void DisableSimulationTimeout(); + + // Tells the FakeNfcDeviceClient to add the records in |record_paths| to its + // list of records exposed for |kDevicePath|. This method will immediately + // assign the records and trigger a property changed signal, only if the + // tag is currently visible. + void SetRecords(const std::vector<dbus::ObjectPath>& record_paths); + + // Tells the FakeNfcDeviceClient to clear the list of records exposed for + // |kDevicePath|. This method takes effect immediately and triggers a + // property changed signal. + void ClearRecords(); + + // Returns true, if a pairing simulation is currently going on. + bool tag_visible() const { return tag_visible_; } + private: + // Property changed callback passed when we create Properties* structures. + void OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name); + + // Makes the fake tag visible if it is not already visible. + void MakeTagVisible(); + + // Called when the simulation timeout expires. + void HandleSimulationTimeout(); + + // List of observers interested in event notifications from us. + ObserverList<Observer> observers_; + + // Fake properties that are returned for the emulated tag. + scoped_ptr<Properties> properties_; + + // If true, a pairing simulation was begun using BeginPairing() and no call + // to EndPairing() has been made. + bool pairing_started_; + + // If true, observers have been notified that a tag has been created and + // the tag properties are accesible. + bool tag_visible_; + + // If non-negative, the tag will disappear this many milliseconds after + // its records have been exposed. + int simulation_timeout_; + DISALLOW_COPY_AND_ASSIGN(FakeNfcTagClient); }; diff --git a/chromeos/dbus/nfc_record_client.cc b/chromeos/dbus/nfc_record_client.cc index 7564c76..f0bbd9b 100644 --- a/chromeos/dbus/nfc_record_client.cc +++ b/chromeos/dbus/nfc_record_client.cc @@ -80,6 +80,11 @@ class NfcRecordClientImpl : public NfcRecordClient, return object_map->GetObjectPaths(); } + virtual std::vector<dbus::ObjectPath> GetRecordsForTag( + const dbus::ObjectPath& tag_path) OVERRIDE { + return GetRecordsForDevice(tag_path); + } + // NfcRecordClient override. virtual Properties* GetProperties( const dbus::ObjectPath& object_path) OVERRIDE { diff --git a/chromeos/dbus/nfc_record_client.h b/chromeos/dbus/nfc_record_client.h index 84e2ef9..37a277e 100644 --- a/chromeos/dbus/nfc_record_client.h +++ b/chromeos/dbus/nfc_record_client.h @@ -96,8 +96,9 @@ class CHROMEOS_EXPORT NfcRecordClient : public DBusClient { // have been received. This method will be called after // Observer::RecordPropertyChanged has been called for all properties that // were received through the initial property fetch that is done when the - // object proxy is first created. Observers can use this method to be - // notified when all existing properties of a record are available for use. + // object proxy is first created or after a call to + // dbus::PropertySet::GetAll Observers can use this method to be notified + // when all existing properties of a record are available for use. virtual void RecordPropertiesReceived( const dbus::ObjectPath& object_path) {} }; @@ -115,6 +116,11 @@ class CHROMEOS_EXPORT NfcRecordClient : public DBusClient { virtual std::vector<dbus::ObjectPath> GetRecordsForDevice( const dbus::ObjectPath& device_path) = 0; + // Returns the list of record object paths associated with the given tag + // identified by the D-Bus object path |tag_path|. + virtual std::vector<dbus::ObjectPath> GetRecordsForTag( + const dbus::ObjectPath& tag_path) = 0; + // Obtain the properties for the NFC record with object path |object_path|; // any values should be copied if needed. virtual Properties* GetProperties(const dbus::ObjectPath& object_path) = 0; diff --git a/chromeos/dbus/nfc_tag_client.cc b/chromeos/dbus/nfc_tag_client.cc index de7e1ef..6c439fa 100644 --- a/chromeos/dbus/nfc_tag_client.cc +++ b/chromeos/dbus/nfc_tag_client.cc @@ -63,6 +63,16 @@ class NfcTagClientImpl : public NfcTagClient, } // NfcTagClient override. + virtual std::vector<dbus::ObjectPath> GetTagsForAdapter( + const dbus::ObjectPath& adapter_path) OVERRIDE { + DBusObjectMap* object_map = + adapters_to_object_maps_.GetObjectMap(adapter_path); + if (!object_map) + return std::vector<dbus::ObjectPath>(); + return object_map->GetObjectPaths(); + } + + // NfcTagClient override. virtual Properties* GetProperties( const dbus::ObjectPath& object_path) OVERRIDE { return static_cast<Properties*>( @@ -182,11 +192,16 @@ class NfcTagClientImpl : public NfcTagClient, // nfc_client_helpers::DBusObjectMap::Delegate override. virtual NfcPropertySet* CreateProperties( dbus::ObjectProxy* object_proxy) OVERRIDE { - return new Properties( + Properties* properties = new Properties( object_proxy, base::Bind(&NfcTagClientImpl::OnPropertyChanged, weak_ptr_factory_.GetWeakPtr(), object_proxy->object_path())); + properties->SetAllPropertiesReceivedCallback( + base::Bind(&NfcTagClientImpl::OnPropertiesReceived, + weak_ptr_factory_.GetWeakPtr(), + object_proxy->object_path())); + return properties; } // nfc_client_helpers::DBusObjectMap::Delegate override. @@ -210,6 +225,14 @@ class NfcTagClientImpl : public NfcTagClient, TagPropertyChanged(object_path, property_name)); } + // Called by NfcPropertySet when all properties have been processed as a + // result of a call to GetAll. + void OnPropertiesReceived(const dbus::ObjectPath& object_path) { + VLOG(1) << "All tag properties received; Path: " << object_path.value(); + FOR_EACH_OBSERVER(NfcTagClient::Observer, observers_, + TagPropertiesReceived(object_path)); + } + // We maintain a pointer to the bus to be able to request proxies for // new NFC tags that appear. dbus::Bus* bus_; diff --git a/chromeos/dbus/nfc_tag_client.h b/chromeos/dbus/nfc_tag_client.h index 2875359..444a0ae 100644 --- a/chromeos/dbus/nfc_tag_client.h +++ b/chromeos/dbus/nfc_tag_client.h @@ -66,6 +66,15 @@ class CHROMEOS_EXPORT NfcTagClient : public DBusClient { // object path |object_path| has acquired a new value. virtual void TagPropertyChanged(const dbus::ObjectPath& object_path, const std::string& property_name) {} + + // Called when all properties for the tag with object path |object_path| + // have been received. This method will be called after + // Observer::TagPropertyChanged has been called for all properties that + // were received through the initial property fetch that is done when the + // object proxy is first created or after a call to + // dbus::PropertySet::GetAll. Observers can use this method to be notified + // when all existing properties of a tag are available for use. + virtual void TagPropertiesReceived(const dbus::ObjectPath& object_path) {} }; virtual ~NfcTagClient(); @@ -76,6 +85,11 @@ class CHROMEOS_EXPORT NfcTagClient : public DBusClient { virtual void AddObserver(Observer* observer) = 0; virtual void RemoveObserver(Observer* observer) = 0; + // Returns the list of tag object paths associated with the given adapter + // identified by the D-Bus object path |adapter_path|. + virtual std::vector<dbus::ObjectPath> GetTagsForAdapter( + const dbus::ObjectPath& adapter_path) = 0; + // Obtain the properties for the NFC tag with object path |object_path|; any // values should be copied if needed. virtual Properties* GetProperties(const dbus::ObjectPath& object_path) = 0; diff --git a/device/nfc/nfc.gyp b/device/nfc/nfc.gyp index 2495a1c..11e4308 100644 --- a/device/nfc/nfc.gyp +++ b/device/nfc/nfc.gyp @@ -31,8 +31,12 @@ 'nfc_peer_chromeos.h', 'nfc_tag.cc', 'nfc_tag.h', + 'nfc_tag_chromeos.cc', + 'nfc_tag_chromeos.h', 'nfc_tag_technology.cc', - 'nfc_tag_technology.h' + 'nfc_tag_technology.h', + 'nfc_tag_technology_chromeos.cc', + 'nfc_tag_technology_chromeos.h' ], 'conditions': [ ['chromeos==1', { diff --git a/device/nfc/nfc_adapter.cc b/device/nfc/nfc_adapter.cc index 1bef83d3..520d4f3 100644 --- a/device/nfc/nfc_adapter.cc +++ b/device/nfc/nfc_adapter.cc @@ -48,4 +48,50 @@ NfcTag* NfcAdapter::GetTag(const std::string& identifier) const { return NULL; } +void NfcAdapter::SetTag(const std::string& identifier, NfcTag* tag) { + if (GetTag(identifier)) { + VLOG(1) << "Tag object for tag \"" << identifier << "\" already exists."; + return; + } + tags_[identifier] = tag; +} + +void NfcAdapter::SetPeer(const std::string& identifier, NfcPeer* peer) { + if (GetPeer(identifier)) { + VLOG(1) << "Peer object for peer \"" << identifier << "\" already exists."; + return; + } + peers_[identifier] = peer; +} + +NfcTag* NfcAdapter::RemoveTag(const std::string& identifier) { + TagsMap::iterator iter = tags_.find(identifier); + if (iter == tags_.end()) { + VLOG(1) << "Tag with identifier \"" << identifier << "\" not found."; + return NULL; + } + NfcTag* tag = iter->second; + tags_.erase(iter); + return tag; +} + +NfcPeer* NfcAdapter::RemovePeer(const std::string& identifier) { + PeersMap::iterator iter = peers_.find(identifier); + if (iter == peers_.end()) { + VLOG(1) << "Peer object for peer \"" << identifier << "\" not found."; + return NULL; + } + NfcPeer* peer = iter->second; + peers_.erase(iter); + return peer; +} + +void NfcAdapter::ClearTags() { + tags_.clear(); +} + +void NfcAdapter::ClearPeers() { + peers_.clear(); +} + } // namespace device diff --git a/device/nfc/nfc_adapter.h b/device/nfc/nfc_adapter.h index 373f4cc..455b142 100644 --- a/device/nfc/nfc_adapter.h +++ b/device/nfc/nfc_adapter.h @@ -181,10 +181,27 @@ class NfcAdapter : public base::RefCounted<NfcAdapter> { typedef std::map<const std::string, NfcPeer*> PeersMap; typedef std::map<const std::string, NfcTag*> TagsMap; + // Set the given tag or peer for |identifier|. If a tag or peer for + // |identifier| already exists, these methods won't do anything. + void SetTag(const std::string& identifier, NfcTag* tag); + void SetPeer(const std::string& identifier, NfcPeer* peer); + + // Removes the tag or peer for |identifier| and returns the removed object. + // Returns NULL, if no tag or peer for |identifier| was found. + NfcTag* RemoveTag(const std::string& identifier); + NfcPeer* RemovePeer(const std::string& identifier); + + // Clear the peer and tag maps. These methods won't delete the tag and peer + // objects, however after the call to these methods, the peers and tags won't + // be returned via calls to GetPeers and GetTags. + void ClearTags(); + void ClearPeers(); + + private: + // Peers and tags that are managed by this adapter. PeersMap peers_; TagsMap tags_; - private: DISALLOW_COPY_AND_ASSIGN(NfcAdapter); }; diff --git a/device/nfc/nfc_adapter_chromeos.cc b/device/nfc/nfc_adapter_chromeos.cc index b78ded2..374e8ac 100644 --- a/device/nfc/nfc_adapter_chromeos.cc +++ b/device/nfc/nfc_adapter_chromeos.cc @@ -10,7 +10,7 @@ #include "base/logging.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "device/nfc/nfc_peer_chromeos.h" -#include "device/nfc/nfc_tag.h" +#include "device/nfc/nfc_tag_chromeos.h" #include "third_party/cros_system_api/dbus/service_constants.h" namespace chromeos { @@ -164,11 +164,8 @@ void NfcAdapterChromeOS::DeviceAdded(const dbus::ObjectPath& object_path) { if (!IsPresent()) return; - if (peers_.find(object_path.value()) != peers_.end()) { - VLOG(1) << "Peer object for device \"" << object_path.value() - << "\" already exists."; + if (GetPeer(object_path.value())) return; - } VLOG(1) << "NFC device found: " << object_path.value(); @@ -191,40 +188,64 @@ void NfcAdapterChromeOS::DeviceAdded(const dbus::ObjectPath& object_path) { // Create the peer object. NfcPeerChromeOS* peer_chromeos = new NfcPeerChromeOS(object_path); - peers_[object_path.value()] = peer_chromeos; + 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(); - PeersMap::iterator iter = peers_.find(object_path.value()); - if (iter == peers_.end()) { + device::NfcPeer* peer = RemovePeer(object_path.value()); + if (!peer) { VLOG(1) << "Removed peer device does not belong to the current adapter."; return; } - - // Remove the peer. - device::NfcPeer* peer = iter->second; - peers_.erase(iter); FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, PeerLost(this, peer)); delete peer; } void NfcAdapterChromeOS::TagAdded(const dbus::ObjectPath& object_path) { - VLOG(1) << "NFC tag found: " << object_path.value(); - // TODO(armansito): Implement tag logic. -} + if (!IsPresent()) + return; + + if (GetTag(object_path.value())) + return; -void NfcAdapterChromeOS::TagRemoved(const dbus::ObjectPath& object_path) { VLOG(1) << "NFC tag found: " << object_path.value(); - // TODO(armansito): Implement tag logic. + + // Check to see if the tag belongs to this adapter. + const std::vector<dbus::ObjectPath>& tags = + DBusThreadManager::Get()->GetNfcTagClient()-> + GetTagsForAdapter(object_path_); + bool tag_found = false; + for (std::vector<dbus::ObjectPath>::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::TagPropertyChanged( - const dbus::ObjectPath& object_path, - const std::string& property_name) { - // TODO(armansito): Implement tag logic. +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) { @@ -248,16 +269,28 @@ void NfcAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) { for (ObjectPathVector::const_iterator iter = devices.begin(); iter != devices.end(); ++iter) { const dbus::ObjectPath& object_path = *iter; - if (peers_.find(object_path.value()) != peers_.end()) + if (GetPeer(object_path.value())) continue; NfcPeerChromeOS* peer_chromeos = new NfcPeerChromeOS(object_path); - peers_[object_path.value()] = peer_chromeos; + SetPeer(object_path.value(), peer_chromeos); FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, PeerFound(this, peer_chromeos)); } - // TODO(armansito): Create device::NfcTag instances for all tags that exist, - // once they have been implemented for ChromeOS. + // Create tag objects for tags that were added before the adapter was set. + const std::vector<dbus::ObjectPath>& tags = + DBusThreadManager::Get()->GetNfcTagClient()-> + GetTagsForAdapter(object_path_); + for (std::vector<dbus::ObjectPath>::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() { @@ -274,22 +307,26 @@ void NfcAdapterChromeOS::RemoveAdapter() { // Copy the tags and peers here and clear the original containers so that // GetPeers and GetTags return no values during the *Removed observer calls. - PeersMap peers = peers_; - TagsMap tags = tags_; - peers_.clear(); - tags_.clear(); - - for (PeersMap::iterator iter = peers_.begin(); - iter != peers_.end(); ++iter) { + 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, iter->second)); - delete iter->second; + PeerLost(this, peer)); + delete peer; } - for (TagsMap::iterator iter = tags_.begin(); - iter != tags_.end(); ++iter) { + for (TagList::iterator iter = tags.begin(); + iter != tags.end(); ++iter) { + device::NfcTag* tag = *iter; FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, - TagLost(this, iter->second)); - delete iter->second; + TagLost(this, tag)); + delete tag; } object_path_ = dbus::ObjectPath(""); @@ -325,7 +362,7 @@ void NfcAdapterChromeOS::OnSetPowered(const base::Closure& callback, } callback.Run(); } else { - LOG(WARNING) << "Failed to power up the NFC antenna radio."; + LOG(ERROR) << "Failed to power up the NFC antenna radio."; error_callback.Run(); } } @@ -338,8 +375,8 @@ void NfcAdapterChromeOS::OnStartPollingError( const ErrorCallback& error_callback, const std::string& error_name, const std::string& error_message) { - LOG(WARNING) << object_path_.value() << ": Failed to start polling: " - << error_name << ": " << error_message; + LOG(ERROR) << object_path_.value() << ": Failed to start polling: " + << error_name << ": " << error_message; error_callback.Run(); } @@ -351,8 +388,8 @@ void NfcAdapterChromeOS::OnStopPollingError( const ErrorCallback& error_callback, const std::string& error_name, const std::string& error_message) { - LOG(WARNING) << object_path_.value() << ": Failed to stop polling: " - << error_name << ": " << error_message; + LOG(ERROR) << object_path_.value() << ": Failed to stop polling: " + << error_name << ": " << error_message; error_callback.Run(); } diff --git a/device/nfc/nfc_adapter_chromeos.h b/device/nfc/nfc_adapter_chromeos.h index 7ee99b0..3511e5d 100644 --- a/device/nfc/nfc_adapter_chromeos.h +++ b/device/nfc/nfc_adapter_chromeos.h @@ -64,8 +64,6 @@ class NfcAdapterChromeOS : public device::NfcAdapter, // NfcTagClient::Observer overrides. virtual void TagAdded(const dbus::ObjectPath& object_path) OVERRIDE; virtual void TagRemoved(const dbus::ObjectPath& object_path) OVERRIDE; - virtual void TagPropertyChanged(const dbus::ObjectPath& object_path, - const std::string& property_name) OVERRIDE; // Set the tracked adapter to the one in |object_path|, this object will // subsequently operate on that adapter until it is removed. diff --git a/device/nfc/nfc_chromeos_unittest.cc b/device/nfc/nfc_chromeos_unittest.cc index c6155e0..672780f 100644 --- a/device/nfc/nfc_chromeos_unittest.cc +++ b/device/nfc/nfc_chromeos_unittest.cc @@ -11,17 +11,22 @@ #include "chromeos/dbus/fake_nfc_adapter_client.h" #include "chromeos/dbus/fake_nfc_device_client.h" #include "chromeos/dbus/fake_nfc_record_client.h" +#include "chromeos/dbus/fake_nfc_tag_client.h" #include "device/nfc/nfc_adapter_chromeos.h" #include "device/nfc/nfc_ndef_record.h" #include "device/nfc/nfc_ndef_record_utils_chromeos.h" #include "device/nfc/nfc_peer.h" +#include "device/nfc/nfc_tag.h" +#include "device/nfc/nfc_tag_technology.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/cros_system_api/dbus/service_constants.h" using device::NfcAdapter; using device::NfcNdefMessage; using device::NfcNdefRecord; +using device::NfcNdefTagTechnology; using device::NfcPeer; +using device::NfcTag; namespace chromeos { @@ -36,14 +41,18 @@ void OnSet(bool success) { } class TestObserver : public NfcAdapter::Observer, - public NfcPeer::Observer { + public NfcPeer::Observer, + public NfcTag::Observer, + public NfcNdefTagTechnology::Observer { public: TestObserver(scoped_refptr<NfcAdapter> adapter) : present_changed_count_(0), powered_changed_count_(0), polling_changed_count_(0), - records_received_count_(0), + peer_records_received_count_(0), + tag_records_received_count_(0), peer_count_(0), + tag_count_(0), adapter_(adapter) { } @@ -80,23 +89,51 @@ class TestObserver : public NfcAdapter::Observer, // NfcAdapter::Observer override. virtual void PeerLost(NfcAdapter* adapter, NfcPeer* peer) OVERRIDE { EXPECT_EQ(adapter_, adapter); + EXPECT_EQ(peer_identifier_, peer->GetIdentifier()); peer_count_--; peer_identifier_.clear(); } + // NfcAdapter::Observer override. + virtual void TagFound(NfcAdapter* adapter, NfcTag* tag) OVERRIDE { + EXPECT_EQ(adapter_, adapter); + tag_count_++; + tag_identifier_ = tag->GetIdentifier(); + } + + // NfcAdapter::Observer override. + virtual void TagLost(NfcAdapter* adapter, NfcTag* tag) OVERRIDE { + EXPECT_EQ(adapter_, adapter); + EXPECT_EQ(tag_identifier_, tag->GetIdentifier()); + tag_count_--; + tag_identifier_.clear(); + } + // NfcPeer::Observer override. - virtual void RecordsReceived( + virtual void RecordReceived( NfcPeer* peer, const NfcNdefRecord* record) OVERRIDE { EXPECT_EQ(peer, adapter_->GetPeer(peer_identifier_)); - records_received_count_++; + EXPECT_EQ(peer_identifier_, peer->GetIdentifier()); + peer_records_received_count_++; + } + + // NfcNdefTagTechnology::Observer override. + virtual void RecordReceived( + NfcTag* tag, const NfcNdefRecord* record) OVERRIDE { + EXPECT_EQ(tag, adapter_->GetTag(tag_identifier_)); + EXPECT_EQ(tag_identifier_, tag->GetIdentifier()); + tag_records_received_count_++; } int present_changed_count_; int powered_changed_count_; int polling_changed_count_; - int records_received_count_; + int peer_records_received_count_; + int tag_records_received_count_; int peer_count_; + int tag_count_; std::string peer_identifier_; + std::string tag_identifier_; scoped_refptr<NfcAdapter> adapter_; }; @@ -112,9 +149,12 @@ class NfcChromeOSTest : public testing::Test { DBusThreadManager::Get()->GetNfcDeviceClient()); fake_nfc_record_client_ = static_cast<FakeNfcRecordClient*>( DBusThreadManager::Get()->GetNfcRecordClient()); + fake_nfc_tag_client_ = static_cast<FakeNfcTagClient*>( + DBusThreadManager::Get()->GetNfcTagClient()); fake_nfc_adapter_client_->EnablePairingOnPoll(false); fake_nfc_device_client_->DisableSimulationTimeout(); + fake_nfc_tag_client_->DisableSimulationTimeout(); success_callback_count_ = 0; error_callback_count_ = 0; } @@ -164,6 +204,7 @@ class NfcChromeOSTest : public testing::Test { FakeNfcAdapterClient* fake_nfc_adapter_client_; FakeNfcDeviceClient* fake_nfc_device_client_; FakeNfcRecordClient* fake_nfc_record_client_; + FakeNfcTagClient* fake_nfc_tag_client_; }; // Tests that the adapter updates correctly to reflect the current "default" @@ -309,8 +350,8 @@ TEST_F(NfcChromeOSTest, PeersInitializedWhenAdapterCreated) { // Observer shouldn't have received any calls, as it got created AFTER the // notifications were sent. EXPECT_EQ(0, observer.present_changed_count_); - EXPECT_EQ(0, observer.present_changed_count_); - EXPECT_EQ(0, observer.present_changed_count_); + EXPECT_EQ(0, observer.powered_changed_count_); + EXPECT_EQ(0, observer.polling_changed_count_); EXPECT_EQ(0, observer.peer_count_); EXPECT_TRUE(adapter_->IsPresent()); @@ -326,6 +367,75 @@ TEST_F(NfcChromeOSTest, PeersInitializedWhenAdapterCreated) { EXPECT_EQ(static_cast<size_t>(3), message.records().size()); } +// Tests that tag and record objects are created for all tags and records that +// already exist when the adapter is created. +TEST_F(NfcChromeOSTest, TagsInitializedWhenAdapterCreated) { + const char kTestURI[] = "fake://path/for/testing"; + + // Set up the adapter client. + NfcAdapterClient::Properties* properties = + fake_nfc_adapter_client_->GetProperties( + dbus::ObjectPath(FakeNfcAdapterClient::kAdapterPath0)); + properties->powered.Set(true, base::Bind(&OnSet)); + + fake_nfc_adapter_client_->StartPollLoop( + dbus::ObjectPath(FakeNfcAdapterClient::kAdapterPath0), + nfc_adapter::kModeInitiator, + base::Bind(&NfcChromeOSTest::SuccessCallback, + base::Unretained(this)), + base::Bind(&NfcChromeOSTest::ErrorCallbackWithParameters, + base::Unretained(this))); + EXPECT_EQ(1, success_callback_count_); + EXPECT_TRUE(properties->powered.value()); + EXPECT_TRUE(properties->polling.value()); + + // Add the fake tag. + fake_nfc_tag_client_->BeginPairingSimulation(0); + base::RunLoop().RunUntilIdle(); + + // Create a fake record. + base::DictionaryValue test_record_data; + test_record_data.SetString(nfc_record::kTypeProperty, nfc_record::kTypeUri); + test_record_data.SetString(nfc_record::kUriProperty, kTestURI); + fake_nfc_tag_client_->Write( + dbus::ObjectPath(FakeNfcTagClient::kTagPath), + test_record_data, + base::Bind(&NfcChromeOSTest::SuccessCallback, + base::Unretained(this)), + base::Bind(&NfcChromeOSTest::ErrorCallbackWithParameters, + base::Unretained(this))); + EXPECT_EQ(2, success_callback_count_); + + // Create the adapter. + SetAdapter(); + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + // Observer shouldn't have received any calls, as it got created AFTER the + // notifications were sent. + EXPECT_EQ(0, observer.present_changed_count_); + EXPECT_EQ(0, observer.powered_changed_count_); + EXPECT_EQ(0, observer.polling_changed_count_); + EXPECT_EQ(0, observer.peer_count_); + + EXPECT_TRUE(adapter_->IsPresent()); + EXPECT_TRUE(adapter_->IsPowered()); + EXPECT_FALSE(adapter_->IsPolling()); + + NfcAdapter::TagList tags; + adapter_->GetTags(&tags); + EXPECT_EQ(static_cast<size_t>(1), tags.size()); + + NfcTag* tag = tags[0]; + const NfcNdefMessage& message = tag->GetNdefTagTechnology()->GetNdefMessage(); + EXPECT_EQ(static_cast<size_t>(1), message.records().size()); + + const NfcNdefRecord* record = message.records()[0]; + std::string uri; + EXPECT_TRUE(record->data().GetString(NfcNdefRecord::kFieldURI, &uri)); + EXPECT_EQ(kTestURI, uri); +} + // Tests that the adapter correctly updates its state when polling is started // and stopped. TEST_F(NfcChromeOSTest, StartAndStopPolling) { @@ -433,14 +543,14 @@ TEST_F(NfcChromeOSTest, PeerTest) { // Peer should have no records on it. EXPECT_TRUE(peer->GetNdefMessage().records().empty()); - EXPECT_EQ(0, observer.records_received_count_); + EXPECT_EQ(0, observer.peer_records_received_count_); // Make records visible. - fake_nfc_record_client_->SetRecordsVisible(true); - EXPECT_EQ(3, observer.records_received_count_); + fake_nfc_record_client_->SetDeviceRecordsVisible(true); + EXPECT_EQ(3, observer.peer_records_received_count_); EXPECT_EQ(static_cast<size_t>(3), peer->GetNdefMessage().records().size()); - // End the simulation. Record should have been removed. + // End the simulation. Peer should get removed. fake_nfc_device_client_->EndPairingSimulation(); EXPECT_EQ(0, observer.peer_count_); EXPECT_TRUE(observer.peer_identifier_.empty()); @@ -449,7 +559,104 @@ TEST_F(NfcChromeOSTest, PeerTest) { EXPECT_FALSE(peer); // No record related notifications will be sent when a peer gets removed. - EXPECT_EQ(3, observer.records_received_count_); + EXPECT_EQ(3, observer.peer_records_received_count_); +} + +// Tests a simple tag pairing simulation. +TEST_F(NfcChromeOSTest, TagTest) { + const char kTestURI[] = "fake://path/for/testing"; + + SetAdapter(); + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + adapter_->SetPowered( + true, + base::Bind(&NfcChromeOSTest::SuccessCallback, + base::Unretained(this)), + base::Bind(&NfcChromeOSTest::ErrorCallback, + base::Unretained(this))); + adapter_->StartPolling( + base::Bind(&NfcChromeOSTest::SuccessCallback, + base::Unretained(this)), + base::Bind(&NfcChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(2, success_callback_count_); + + EXPECT_TRUE(adapter_->IsPowered()); + EXPECT_TRUE(adapter_->IsPolling()); + EXPECT_EQ(0, observer.tag_count_); + + // Add the fake tag. + fake_nfc_tag_client_->BeginPairingSimulation(0); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, observer.tag_count_); + EXPECT_EQ(FakeNfcTagClient::kTagPath, observer.tag_identifier_); + + NfcTag* tag = adapter_->GetTag(observer.tag_identifier_); + CHECK(tag); + tag->AddObserver(&observer); + EXPECT_TRUE(tag->IsReady()); + CHECK(tag->GetNdefTagTechnology()); + tag->GetNdefTagTechnology()->AddObserver(&observer); + + NfcNdefTagTechnology* tag_technology = tag->GetNdefTagTechnology(); + EXPECT_TRUE(tag_technology->IsSupportedByTag()); + + // Tag should have no records on it. + EXPECT_TRUE(tag_technology->GetNdefMessage().records().empty()); + EXPECT_EQ(0, observer.tag_records_received_count_); + + // Set the tag record visible. By default the record has no content, so no + // NfcNdefMessage should be received. + fake_nfc_record_client_->SetTagRecordsVisible(true); + EXPECT_TRUE(tag_technology->GetNdefMessage().records().empty()); + EXPECT_EQ(0, observer.tag_records_received_count_); + fake_nfc_record_client_->SetTagRecordsVisible(false); + + // Write an NDEF record to the tag. + EXPECT_EQ(2, success_callback_count_); // 2 for SetPowered and StartPolling. + EXPECT_EQ(0, error_callback_count_); + + base::DictionaryValue record_data; + record_data.SetString(NfcNdefRecord::kFieldURI, kTestURI); + NfcNdefRecord written_record; + written_record.Populate(NfcNdefRecord::kTypeURI, &record_data); + NfcNdefMessage written_message; + written_message.AddRecord(&written_record); + + tag_technology->WriteNdef( + written_message, + base::Bind(&NfcChromeOSTest::SuccessCallback, + base::Unretained(this)), + base::Bind(&NfcChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(3, success_callback_count_); + EXPECT_EQ(0, error_callback_count_); + + EXPECT_EQ(static_cast<size_t>(1), + tag_technology->GetNdefMessage().records().size()); + EXPECT_EQ(1, observer.tag_records_received_count_); + + NfcNdefRecord* received_record = + tag_technology->GetNdefMessage().records()[0]; + EXPECT_EQ(NfcNdefRecord::kTypeURI, received_record->type()); + std::string uri; + EXPECT_TRUE(received_record->data().GetString( + NfcNdefRecord::kFieldURI, &uri)); + EXPECT_EQ(kTestURI, uri); + + // End the simulation. Tag should get removed. + fake_nfc_tag_client_->EndPairingSimulation(); + EXPECT_EQ(0, observer.tag_count_); + EXPECT_TRUE(observer.tag_identifier_.empty()); + + tag = adapter_->GetTag(observer.tag_identifier_); + EXPECT_FALSE(tag); + + // No record related notifications will be sent when a tag gets removed. + EXPECT_EQ(1, observer.tag_records_received_count_); } // Unit tests for nfc_ndef_record_utils methods. diff --git a/device/nfc/nfc_ndef_record_utils_chromeos.cc b/device/nfc/nfc_ndef_record_utils_chromeos.cc index 3bc6f95..466cf38 100644 --- a/device/nfc/nfc_ndef_record_utils_chromeos.cc +++ b/device/nfc/nfc_ndef_record_utils_chromeos.cc @@ -219,12 +219,14 @@ bool RecordPropertiesToNfcNdefRecord( text_attributes->SetString(NfcNdefRecord::kFieldLanguageCode, properties->language.value()); } - if (type == NfcNdefRecord::kTypeSmartPoster) { - base::ListValue* titles = new base::ListValue(); - titles->Append(text_attributes.release()); - attributes.Set(NfcNdefRecord::kFieldTitles, titles); - } else { - attributes.MergeDictionary(text_attributes.get()); + if (!text_attributes->empty()) { + if (type == NfcNdefRecord::kTypeSmartPoster) { + base::ListValue* titles = new base::ListValue(); + titles->Append(text_attributes.release()); + attributes.Set(NfcNdefRecord::kFieldTitles, titles); + } else { + attributes.MergeDictionary(text_attributes.get()); + } } } diff --git a/device/nfc/nfc_peer.h b/device/nfc/nfc_peer.h index 710641e..a38192f 100644 --- a/device/nfc/nfc_peer.h +++ b/device/nfc/nfc_peer.h @@ -42,7 +42,7 @@ class NfcPeer { // new records on the device and when the initial set of records are // received from it, if any. All records received from |peer| can be // accessed by calling |peer->GetNdefMessage()|. - virtual void RecordsReceived(NfcPeer* peer, const NfcNdefRecord* record) {} + virtual void RecordReceived(NfcPeer* peer, const NfcNdefRecord* record) {} }; // The ErrorCallback is used by methods to asynchronously report errors. diff --git a/device/nfc/nfc_peer_chromeos.cc b/device/nfc/nfc_peer_chromeos.cc index a6e864c..4ef1804 100644 --- a/device/nfc/nfc_peer_chromeos.cc +++ b/device/nfc/nfc_peer_chromeos.cc @@ -131,12 +131,10 @@ void NfcPeerChromeOS::RecordPropertiesReceived( VLOG(1) << "Record properties received for: " << object_path.value(); // Check if the found record belongs to this device. - NfcDeviceClient::Properties* device_properties = - DBusThreadManager::Get()->GetNfcDeviceClient()-> - GetProperties(object_path_); - DCHECK(device_properties); bool record_found = false; - const ObjectPathVector& records = device_properties->records.value(); + const ObjectPathVector& records = + DBusThreadManager::Get()->GetNfcRecordClient()-> + GetRecordsForDevice(object_path_); for (ObjectPathVector::const_iterator iter = records.begin(); iter != records.end(); ++iter) { if (*iter == object_path) { @@ -190,7 +188,7 @@ void NfcPeerChromeOS::AddRecord(const dbus::ObjectPath& object_path) { message_.AddRecord(record); records_[object_path] = record; FOR_EACH_OBSERVER(NfcPeer::Observer, observers_, - RecordsReceived(this, record)); + RecordReceived(this, record)); } } // namespace chromeos diff --git a/device/nfc/nfc_peer_chromeos.h b/device/nfc/nfc_peer_chromeos.h index 2bd664a..eec8e4b 100644 --- a/device/nfc/nfc_peer_chromeos.h +++ b/device/nfc/nfc_peer_chromeos.h @@ -38,7 +38,7 @@ class NfcPeerChromeOS : public device::NfcPeer, // Mapping from D-Bus object paths to NfcNdefRecord objects. typedef std::map<dbus::ObjectPath, device::NfcNdefRecord*> NdefRecordMap; - NfcPeerChromeOS(const dbus::ObjectPath& object_path); + explicit NfcPeerChromeOS(const dbus::ObjectPath& object_path); virtual ~NfcPeerChromeOS(); // NfcRecordClient::Observer overrides. diff --git a/device/nfc/nfc_tag.h b/device/nfc/nfc_tag.h index 650f37b..039fc78 100644 --- a/device/nfc/nfc_tag.h +++ b/device/nfc/nfc_tag.h @@ -31,7 +31,8 @@ class NfcTag { kTagType1, kTagType2, kTagType3, - kTagType4 + kTagType4, + kTagTypeUnknown, }; // NFC protocols that a tag can support. A tag will usually support only one @@ -41,7 +42,8 @@ class NfcTag { kProtocolIsoDep, kProtocolJewel, kProtocolMifare, - kProtocolNfcDep + kProtocolNfcDep, + kProtocolUnknown }; // Interface for observing changes from NFC tags. @@ -49,12 +51,18 @@ class NfcTag { public: virtual ~Observer() {} - // This method will be called when an NDEF message |message|, stored on the - // NFC tag |tag| has been read. Although NDEF is the most common record - // storage format for NFC tags, not all tags support it. This method won't - // be called if there are no records on an NDEF compliant tag or if the tag - // doesn't support NDEF. - virtual void RecordsReceived(NfcTag* tag, const NfcNdefMessage& message) {} + // Called when the tag type has been determined. + virtual void TagTypeChanged(NfcTag* tag, TagType type) {} + + // Called when the write access to the tag has been determined or changed. + virtual void TagWritePermissionChanged(NfcTag* tag, bool read_only) {} + + // Called when the underlying NFC protocol has been determined. + virtual void TagSupportedProtocolChanged(NfcTag* tag, Protocol protocol) {} + + // Called when all initial values of the tag properties have been received + // from the remote tag and |tag| is ready to use. + virtual void TagReady(NfcTag* tag) {} }; virtual ~NfcTag(); @@ -81,6 +89,17 @@ class NfcTag { virtual NfcTagTechnology::TechnologyTypeMask GetSupportedTechnologies() const = 0; + // Returns true, if all tag properties have been received from the remote tag + // and this object is ready to use. + virtual bool IsReady() const = 0; + + // Returns a pointer to the NDEF technology object that allows I/O on NDEF + // records. If NDEF is not supported by this tag, operations that are + // performed on the returned instance may not succeed. Users can determine + // support by calling NfcTagTechnology::IsSupportedByTag. The returned + // instance is owned by this tag. + virtual NfcNdefTagTechnology* GetNdefTagTechnology() = 0; + protected: NfcTag(); diff --git a/device/nfc/nfc_tag_chromeos.cc b/device/nfc/nfc_tag_chromeos.cc new file mode 100644 index 0000000..e2390fa --- /dev/null +++ b/device/nfc/nfc_tag_chromeos.cc @@ -0,0 +1,164 @@ +// Copyright 2014 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_tag_chromeos.h" + +#include "chromeos/dbus/dbus_thread_manager.h" +#include "device/nfc/nfc_tag_technology_chromeos.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +using device::NfcTag; +using device::NfcTagTechnology; +using device::NfcNdefTagTechnology; + +namespace chromeos { + +namespace { + +// Converts an NFC tag type value returned by neard to a NfcTag::TagType enum +// value. +NfcTag::TagType DBusTypePropertyToTagType(const std::string& type) { + if (type == nfc_tag::kTagType1) + return NfcTag::kTagType1; + if (type == nfc_tag::kTagType2) + return NfcTag::kTagType2; + if (type == nfc_tag::kTagType3) + return NfcTag::kTagType3; + if (type == nfc_tag::kTagType4) + return NfcTag::kTagType4; + return NfcTag::kTagTypeUnknown; +} + +// Converts an NFC tag protocol value returned by neard to a NfcTag::Protocol +// enum value. +NfcTag::Protocol DBusProtocolPropertyToTagProtocol( + const std::string& protocol) { + if (protocol == nfc_common::kProtocolFelica) + return NfcTag::kProtocolFelica; + if (protocol == nfc_common::kProtocolIsoDep) + return NfcTag::kProtocolIsoDep; + if (protocol == nfc_common::kProtocolJewel) + return NfcTag::kProtocolJewel; + if (protocol == nfc_common::kProtocolMifare) + return NfcTag::kProtocolMifare; + if (protocol == nfc_common::kProtocolNfcDep) + return NfcTag::kProtocolNfcDep; + return NfcTag::kProtocolUnknown; +} + +} // namespace + +NfcTagChromeOS::NfcTagChromeOS(const dbus::ObjectPath& object_path) + : object_path_(object_path), + is_ready_(false), + ndef_technology_(new NfcNdefTagTechnologyChromeOS(this)) { + DBusThreadManager::Get()->GetNfcTagClient()->AddObserver(this); +} + +NfcTagChromeOS::~NfcTagChromeOS() { + DBusThreadManager::Get()->GetNfcTagClient()->RemoveObserver(this); +} + +void NfcTagChromeOS::AddObserver(NfcTag::Observer* observer) { + observers_.AddObserver(observer); +} + +void NfcTagChromeOS::RemoveObserver(NfcTag::Observer* observer) { + observers_.RemoveObserver(observer); +} + +std::string NfcTagChromeOS::GetIdentifier() const { + return object_path_.value(); +} + +NfcTag::TagType NfcTagChromeOS::GetType() const { + DCHECK(object_path_.IsValid()); + return DBusTypePropertyToTagType( + DBusThreadManager::Get()->GetNfcTagClient()-> + GetProperties(object_path_)->type.value()); +} + +bool NfcTagChromeOS::IsReadOnly() const { + DCHECK(object_path_.IsValid()); + return DBusThreadManager::Get()->GetNfcTagClient()-> + GetProperties(object_path_)->read_only.value(); +} + +NfcTag::Protocol NfcTagChromeOS::GetSupportedProtocol() const { + DCHECK(object_path_.IsValid()); + return DBusProtocolPropertyToTagProtocol( + DBusThreadManager::Get()->GetNfcTagClient()-> + GetProperties(object_path_)->protocol.value()); +} + +NfcTagTechnology::TechnologyTypeMask +NfcTagChromeOS::GetSupportedTechnologies() const { + // Determine supported technologies based on the tag's protocol and + // type. + NfcTag::TagType type = GetType(); + NfcTag::Protocol protocol = GetSupportedProtocol(); + if (type == NfcTag::kTagTypeUnknown || protocol == kProtocolUnknown) { + VLOG(1) << "Tag type and protocol unknown."; + return 0; + } + + NfcTagTechnology::TechnologyTypeMask technologies = 0; + technologies |= NfcTagTechnology::kTechnologyTypeNdef; + if (type == NfcTag::kTagType3) { + DCHECK(protocol == NfcTag::kProtocolFelica); + return technologies | NfcTagTechnology::kTechnologyTypeNfcF; + } + + if (protocol == NfcTag::kProtocolIsoDep) { + DCHECK(type == NfcTag::kTagType4); + technologies |= NfcTagTechnology::kTechnologyTypeIsoDep; + // TODO(armansito): Neard doesn't provide enough information to determine + // if the underlying wave-form is type A or type B. For now, report + // neither. + return technologies; + } + + return technologies | NfcTagTechnology::kTechnologyTypeNfcA; +} + +bool NfcTagChromeOS::IsReady() const { + return is_ready_; +} + +NfcNdefTagTechnology* NfcTagChromeOS::GetNdefTagTechnology() { + return ndef_technology_.get(); +} + +void NfcTagChromeOS::TagPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) { + if (object_path != object_path_) + return; + + NfcTagClient::Properties* properties = + DBusThreadManager::Get()->GetNfcTagClient()->GetProperties(object_path_); + DCHECK(properties); + + if (property_name == properties->type.name()) { + FOR_EACH_OBSERVER(NfcTag::Observer, observers_, + TagTypeChanged(this, GetType())); + } else if (property_name == properties->read_only.name()) { + FOR_EACH_OBSERVER(NfcTag::Observer, observers_, + TagWritePermissionChanged(this, IsReadOnly())); + } else if (property_name == properties->protocol.name()) { + FOR_EACH_OBSERVER( + NfcTag::Observer, observers_, + TagSupportedProtocolChanged(this, GetSupportedProtocol())); + } +} + +void NfcTagChromeOS::TagPropertiesReceived( + const dbus::ObjectPath& object_path) { + if (is_ready_ || object_path != object_path_) + return; + + is_ready_ = true; + FOR_EACH_OBSERVER(NfcTag::Observer, observers_, TagReady(this)); +} + +} // namespace chromeos diff --git a/device/nfc/nfc_tag_chromeos.h b/device/nfc/nfc_tag_chromeos.h new file mode 100644 index 0000000..5e4a723 --- /dev/null +++ b/device/nfc/nfc_tag_chromeos.h @@ -0,0 +1,70 @@ +// Copyright 2014 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. + +#ifndef DEVICE_NFC_NFC_TAG_CHROMEOS_H_ +#define DEVICE_NFC_NFC_TAG_CHROMEOS_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/observer_list.h" +#include "chromeos/dbus/nfc_tag_client.h" +#include "dbus/object_path.h" +#include "device/nfc/nfc_tag.h" + +namespace chromeos { + +class NfcNdefTagTechnologyChromeOS; + +// The NfcTagChromeOS class implements device::NfcTag for the Chrome OS +// platform. +class NfcTagChromeOS : public device::NfcTag, + public NfcTagClient::Observer { + public: + // device::NfcTag overrides. + virtual void AddObserver(device::NfcTag::Observer* observer) OVERRIDE; + virtual void RemoveObserver(device::NfcTag::Observer* observer) OVERRIDE; + virtual std::string GetIdentifier() const OVERRIDE; + virtual TagType GetType() const OVERRIDE; + virtual bool IsReadOnly() const OVERRIDE; + virtual device::NfcTag::Protocol GetSupportedProtocol() const OVERRIDE; + virtual device::NfcTagTechnology::TechnologyTypeMask + GetSupportedTechnologies() const OVERRIDE; + virtual bool IsReady() const OVERRIDE; + virtual device::NfcNdefTagTechnology* GetNdefTagTechnology() OVERRIDE; + + // NfcTagClient::Observer overrides. + virtual void TagPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) OVERRIDE; + virtual void TagPropertiesReceived( + const dbus::ObjectPath& object_path) OVERRIDE; + + // Object path representing the remote tag object. + const dbus::ObjectPath& object_path() const { return object_path_; } + + private: + friend class NfcAdapterChromeOS; + + explicit NfcTagChromeOS(const dbus::ObjectPath& object_path); + virtual ~NfcTagChromeOS(); + + // Object path of the tag that we are currently tracking. + dbus::ObjectPath object_path_; + + // Stores whether or not the initial set of properties have been received. + bool is_ready_; + + // The NfcNdefTagTechnology instance that allows users to perform NDEF + // read and write on this tag. + scoped_ptr<NfcNdefTagTechnologyChromeOS> ndef_technology_; + + // List of observers interested in event notifications from us. + ObserverList<device::NfcTag::Observer> observers_; + + DISALLOW_COPY_AND_ASSIGN(NfcTagChromeOS); +}; + +} // namespace chromeos + +#endif // DEVICE_NFC_NFC_TAG_CHROMEOS_H_ diff --git a/device/nfc/nfc_tag_technology.cc b/device/nfc/nfc_tag_technology.cc index fa2b66a..b07fb52 100644 --- a/device/nfc/nfc_tag_technology.cc +++ b/device/nfc/nfc_tag_technology.cc @@ -29,11 +29,4 @@ NfcNdefTagTechnology::NfcNdefTagTechnology(NfcTag* tag) NfcNdefTagTechnology::~NfcNdefTagTechnology() { } -// static -NfcNdefTagTechnology* NfcNdefTagTechnology::Create(NfcTag* tag) { - // TODO(armansito): Create and return platform-specific implementation - // instances here. - return NULL; -} - } // namespace device diff --git a/device/nfc/nfc_tag_technology.h b/device/nfc/nfc_tag_technology.h index c5d1c19..35def12 100644 --- a/device/nfc/nfc_tag_technology.h +++ b/device/nfc/nfc_tag_technology.h @@ -15,7 +15,8 @@ class NfcTag; // NfcTagTechnology represents an NFC technology that allows a certain type of // I/O operation on an NFC tag. NFC tags can support a wide array of protocols. // The NfcTagTechnology hierarchy allows both raw and high-level I/O operations -// on NFC tags. +// on NFC tags. Do not create instances of these objects directly. Instead, +// obtain a handle directly from an NfcTag object. class NfcTagTechnology { public: // The various I/O technologies that an NFC tag can support. @@ -56,7 +57,15 @@ class NfcTagTechnology { }; // NfcNdefTagTechnology allows reading and writing NDEF messages to a tag. This -// is the most commonly used data exchange format in NFC. +// is the most commonly used data exchange format in NFC. NDEF is a data +// exchange format and is the top most layer of the protocol stack. NDEF itself +// is not a protocol; data is typically formatted in a way that is defined by +// the NDEF format and then transmitted via one of the underlying protocols. +// Hence all tags are capable of NDEF data exchange, however, all tags don't +// necessarily use NDEF to operate (e.g. a tag may contain a smart chip that +// does data processing on ISO-DEP based APDUs and ignores NDEF). This is why, +// even if a tag inherently supports NDEF, operations done via this class may +// not necessarily succeed. class NfcNdefTagTechnology : public NfcTagTechnology { public: // The ErrorCallback is used by methods to asynchronously report errors. @@ -64,6 +73,25 @@ class NfcNdefTagTechnology : public NfcTagTechnology { virtual ~NfcNdefTagTechnology(); + // Interface for observing changes from NFC tags related to NDEF records. + class Observer { + public: + virtual ~Observer() {} + + // This method will be called when an NDEF record |record|, stored on the + // NFC tag |tag| has been read. This method will be called multiple times + // as records are read from the tag or when the tag's records change (e.g. + // when the tag has been rewritten). All received records can be accessed by + // calling GetNdefMessage(). + virtual void RecordReceived(NfcTag* tag, const NfcNdefRecord* record) {} + }; + + // Adds and removes observers for events on this NFC tag. If monitoring + // multiple tags, check the |tag| parameter of observer methods to determine + // which tag is issuing the event. + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + // NfcTagTechnology override. virtual bool IsSupportedByTag() const OVERRIDE; @@ -72,22 +100,17 @@ class NfcNdefTagTechnology : public NfcTagTechnology { // means that no records have yet been received from the tag. Users should // use this method in conjunction with the NfcTag::Observer::RecordsReceived // method to be notified when the records are ready. - virtual NfcNdefMessage GetNdefMessage() const = 0; + virtual const NfcNdefMessage& GetNdefMessage() const = 0; // Writes the given NDEF message to the underlying tag, overwriting any // existing NDEF message on it. On success, |callback| will be invoked. On // failure, |error_callback| will be invoked. This method can fail, if the // underlying tag does not support NDEF as a technology. - virtual void WriteNdefMessage(const NfcNdefMessage& message, - const base::Closure& callback, - const ErrorCallback& error_callback) = 0; + virtual void WriteNdef(const NfcNdefMessage& message, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; - // Static factory method for constructing an instance. The ownership of the - // returned instance belongs to the caller. Returns NULL, if NFC is not - // supported on the current platform. - static NfcNdefTagTechnology* Create(NfcTag* tag); - - private: + protected: // Constructs a technology instance, where |tag| is the NFC tag that this // instance will operate on. explicit NfcNdefTagTechnology(NfcTag* tag); diff --git a/device/nfc/nfc_tag_technology_chromeos.cc b/device/nfc/nfc_tag_technology_chromeos.cc new file mode 100644 index 0000000..25d1b24 --- /dev/null +++ b/device/nfc/nfc_tag_technology_chromeos.cc @@ -0,0 +1,186 @@ +// Copyright 2014 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_tag_technology_chromeos.h" + +#include "base/stl_util.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "device/nfc/nfc_ndef_record_utils_chromeos.h" +#include "device/nfc/nfc_tag_chromeos.h" + +using device::NfcNdefMessage; +using device::NfcNdefRecord; + +namespace chromeos { + +namespace { + +typedef std::vector<dbus::ObjectPath> ObjectPathVector; + +} // namespace + +NfcNdefTagTechnologyChromeOS::NfcNdefTagTechnologyChromeOS(NfcTagChromeOS* tag) + : NfcNdefTagTechnology(tag), + object_path_(tag->object_path()), + weak_ptr_factory_(this) { + DCHECK(tag); + // Create record objects for all records that were received before. + const ObjectPathVector& records = + DBusThreadManager::Get()->GetNfcRecordClient()-> + GetRecordsForTag(object_path_); + for (ObjectPathVector::const_iterator iter = records.begin(); + iter != records.end(); ++iter) { + AddRecord(*iter); + } + DBusThreadManager::Get()->GetNfcRecordClient()->AddObserver(this); +} + +NfcNdefTagTechnologyChromeOS::~NfcNdefTagTechnologyChromeOS() { + DBusThreadManager::Get()->GetNfcRecordClient()->RemoveObserver(this); + STLDeleteValues(&records_); +} + +void NfcNdefTagTechnologyChromeOS::AddObserver( + NfcNdefTagTechnology::Observer* observer) { + observers_.AddObserver(observer); +} + +void NfcNdefTagTechnologyChromeOS::RemoveObserver( + NfcNdefTagTechnology::Observer* observer) { + observers_.RemoveObserver(observer); +} + +const NfcNdefMessage& NfcNdefTagTechnologyChromeOS::GetNdefMessage() const { + return message_; +} + +void NfcNdefTagTechnologyChromeOS::WriteNdef( + const device::NfcNdefMessage& message, + const base::Closure& callback, + const ErrorCallback& error_callback) { + if (message.records().empty()) { + LOG(ERROR) << "Given NDEF message is empty. Cannot write it."; + error_callback.Run(); + return; + } + if (!tag()->IsReady()) { + LOG(ERROR) << "The tag is not ready yet: " << tag()->GetIdentifier(); + error_callback.Run(); + return; + } + // TODO(armansito): neard currently supports writing only one NDEF record + // to a tag and won't support multiple records until 0.15. Until then, report + // failure if |message| contains more than one record. + if (message.records().size() > 1) { + LOG(ERROR) << "Currently, writing only 1 NDEF record is supported."; + error_callback.Run(); + return; + } + const NfcNdefRecord* record = message.records()[0]; + base::DictionaryValue attributes; + if (!nfc_ndef_record_utils::NfcNdefRecordToDBusAttributes( + record, &attributes)) { + LOG(ERROR) << "Failed to extract NDEF record fields for NDEF push."; + error_callback.Run(); + return; + } + DBusThreadManager::Get()->GetNfcTagClient()->Write( + object_path_, + attributes, + base::Bind(&NfcNdefTagTechnologyChromeOS::OnWriteNdefMessage, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&NfcNdefTagTechnologyChromeOS::OnWriteNdefMessageError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); +} + +void NfcNdefTagTechnologyChromeOS::RecordAdded( + const dbus::ObjectPath& object_path) { + // Don't create the record object yet. Instead, wait until all record + // properties have been received and construct the object and notify observers + // then. + VLOG(1) << "Record added: " << object_path.value() << ". Waiting until " + "all properties have been fetched to create record object."; +} + +void NfcNdefTagTechnologyChromeOS::RecordRemoved( + const dbus::ObjectPath& object_path) { + NdefRecordMap::iterator iter = records_.find(object_path); + if (iter == records_.end()) + return; + VLOG(1) << "Lost remote NDEF record object: " << object_path.value() + << ", removing record."; + NfcNdefRecord* record = iter->second; + message_.RemoveRecord(record); + delete record; + records_.erase(iter); +} + +void NfcNdefTagTechnologyChromeOS::RecordPropertiesReceived( + const dbus::ObjectPath& object_path) { + VLOG(1) << "Record properties received for: " << object_path.value(); + + // Check if the found record belongs to this tag. + bool record_found = false; + const ObjectPathVector& records = + DBusThreadManager::Get()->GetNfcRecordClient()-> + GetRecordsForTag(object_path_); + for (ObjectPathVector::const_iterator iter = records.begin(); + iter != records.end(); ++iter) { + if (*iter == object_path) { + record_found = true; + break; + } + } + if (!record_found) { + VLOG(1) << "Record \"" << object_path.value() << "\" doesn't belong to this" + << " tag. Ignoring."; + return; + } + AddRecord(object_path); +} + +void NfcNdefTagTechnologyChromeOS::OnWriteNdefMessage( + const base::Closure& callback) { + callback.Run(); +} + +void NfcNdefTagTechnologyChromeOS::OnWriteNdefMessageError( + const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message) { + LOG(ERROR) << object_path_.value() << ": Failed to Push NDEF message: " + << error_name << ": " << error_message; + error_callback.Run(); +} + +void NfcNdefTagTechnologyChromeOS::AddRecord( + const dbus::ObjectPath& object_path) { + // Ignore this call if an entry for this record already exists. + if (records_.find(object_path) != records_.end()) { + VLOG(1) << "Record object for remote \"" << object_path.value() + << "\" already exists."; + return; + } + + NfcRecordClient::Properties* record_properties = + DBusThreadManager::Get()->GetNfcRecordClient()-> + GetProperties(object_path); + DCHECK(record_properties); + + NfcNdefRecord* record = new NfcNdefRecord(); + if (!nfc_ndef_record_utils::RecordPropertiesToNfcNdefRecord( + record_properties, record)) { + LOG(ERROR) << "Failed to create record object for record with object " + << "path \"" << object_path.value() << "\""; + delete record; + return; + } + + message_.AddRecord(record); + records_[object_path] = record; + FOR_EACH_OBSERVER(NfcNdefTagTechnology::Observer, observers_, + RecordReceived(tag(), record)); +} + +} // namespace chromeos diff --git a/device/nfc/nfc_tag_technology_chromeos.h b/device/nfc/nfc_tag_technology_chromeos.h new file mode 100644 index 0000000..71b070e --- /dev/null +++ b/device/nfc/nfc_tag_technology_chromeos.h @@ -0,0 +1,87 @@ +// Copyright 2014 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. + +#ifndef DEVICE_NFC_NFC_TAG_TECHNOLOGY_CHROMEOS_H_ +#define DEVICE_NFC_NFC_TAG_TECHNOLOGY_CHROMEOS_H_ + +#include <map> + +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "chromeos/dbus/nfc_record_client.h" +#include "device/nfc/nfc_tag_technology.h" + +namespace chromeos { + +class NfcTagChromeOS; + +// The NfcNdefTagTechnologyChromeOS class implements +// device::NfcNdefTagTechnology for the Chrome OS platform. The lifetime of an +// instance of this class must be tied to an instance of NfcTagChromeOS. +// Instances of this class must never outlast the owning NfcTagChromeOS +// instance. +class NfcNdefTagTechnologyChromeOS : public device::NfcNdefTagTechnology, + public NfcRecordClient::Observer { + public: + virtual ~NfcNdefTagTechnologyChromeOS(); + + // device::NfcNdefTagTechnology overrides. + virtual void AddObserver(device::NfcNdefTagTechnology::Observer* observer) + OVERRIDE; + virtual void RemoveObserver(device::NfcNdefTagTechnology::Observer* observer) + OVERRIDE; + virtual const device::NfcNdefMessage& GetNdefMessage() const OVERRIDE; + virtual void WriteNdef(const device::NfcNdefMessage& message, + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE; + + // NfcRecordClient::Observer overrides. + virtual void RecordAdded(const dbus::ObjectPath& object_path) OVERRIDE; + virtual void RecordRemoved(const dbus::ObjectPath& object_path) OVERRIDE; + virtual void RecordPropertiesReceived( + const dbus::ObjectPath& object_path) OVERRIDE; + + private: + friend class NfcTagChromeOS; + + // Mapping from D-Bus object paths to NfcNdefRecord objects. + typedef std::map<dbus::ObjectPath, device::NfcNdefRecord*> NdefRecordMap; + + explicit NfcNdefTagTechnologyChromeOS(NfcTagChromeOS* tag); + + // Called by dbus:: on completion of the D-Bus method call to write an NDEF. + void OnWriteNdefMessage(const base::Closure& callback); + void OnWriteNdefMessageError(const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message); + + // Creates a record object for the record with object path |object_path| and + // notifies the observers, if a record object did not already exist for it. + void AddRecord(const dbus::ObjectPath& object_path); + + // A map containing the NDEF records that were received from the tag. + NdefRecordMap records_; + + // Message instance that contains pointers to all created records that are + // in |records_|. This is mainly used as the cached return value for + // GetNdefMessage(). + device::NfcNdefMessage message_; + + // List of observers interested in event notifications from us. + ObserverList<device::NfcNdefTagTechnology::Observer> observers_; + + // D-Bus object path of the remote tag or device that this object operates + // on. + dbus::ObjectPath object_path_; + + // 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<NfcNdefTagTechnologyChromeOS> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(NfcNdefTagTechnologyChromeOS); +}; + +} // namespace chromeos + +#endif // DEVICE_NFC_NFC_TAG_TECHNOLOGY_CHROMEOS_H_ |