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 /device | |
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
Diffstat (limited to 'device')
-rw-r--r-- | device/nfc/nfc.gyp | 6 | ||||
-rw-r--r-- | device/nfc/nfc_adapter.cc | 46 | ||||
-rw-r--r-- | device/nfc/nfc_adapter.h | 19 | ||||
-rw-r--r-- | device/nfc/nfc_adapter_chromeos.cc | 123 | ||||
-rw-r--r-- | device/nfc/nfc_adapter_chromeos.h | 2 | ||||
-rw-r--r-- | device/nfc/nfc_chromeos_unittest.cc | 231 | ||||
-rw-r--r-- | device/nfc/nfc_ndef_record_utils_chromeos.cc | 14 | ||||
-rw-r--r-- | device/nfc/nfc_peer.h | 2 | ||||
-rw-r--r-- | device/nfc/nfc_peer_chromeos.cc | 10 | ||||
-rw-r--r-- | device/nfc/nfc_peer_chromeos.h | 2 | ||||
-rw-r--r-- | device/nfc/nfc_tag.h | 35 | ||||
-rw-r--r-- | device/nfc/nfc_tag_chromeos.cc | 164 | ||||
-rw-r--r-- | device/nfc/nfc_tag_chromeos.h | 70 | ||||
-rw-r--r-- | device/nfc/nfc_tag_technology.cc | 7 | ||||
-rw-r--r-- | device/nfc/nfc_tag_technology.h | 47 | ||||
-rw-r--r-- | device/nfc/nfc_tag_technology_chromeos.cc | 186 | ||||
-rw-r--r-- | device/nfc/nfc_tag_technology_chromeos.h | 87 |
17 files changed, 951 insertions, 100 deletions
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_ |