summaryrefslogtreecommitdiffstats
path: root/device
diff options
context:
space:
mode:
authorarmansito@chromium.org <armansito@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-18 08:47:17 +0000
committerarmansito@chromium.org <armansito@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-18 08:47:17 +0000
commit20e38b8e9730ae28f077269d67e75a231bea7ca3 (patch)
tree24416175bfded4f8df6d13d99c8713df9caea7d3 /device
parentb826fa8f5432ba0e02306d9901aa584653130e0d (diff)
downloadchromium_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.gyp6
-rw-r--r--device/nfc/nfc_adapter.cc46
-rw-r--r--device/nfc/nfc_adapter.h19
-rw-r--r--device/nfc/nfc_adapter_chromeos.cc123
-rw-r--r--device/nfc/nfc_adapter_chromeos.h2
-rw-r--r--device/nfc/nfc_chromeos_unittest.cc231
-rw-r--r--device/nfc/nfc_ndef_record_utils_chromeos.cc14
-rw-r--r--device/nfc/nfc_peer.h2
-rw-r--r--device/nfc/nfc_peer_chromeos.cc10
-rw-r--r--device/nfc/nfc_peer_chromeos.h2
-rw-r--r--device/nfc/nfc_tag.h35
-rw-r--r--device/nfc/nfc_tag_chromeos.cc164
-rw-r--r--device/nfc/nfc_tag_chromeos.h70
-rw-r--r--device/nfc/nfc_tag_technology.cc7
-rw-r--r--device/nfc/nfc_tag_technology.h47
-rw-r--r--device/nfc/nfc_tag_technology_chromeos.cc186
-rw-r--r--device/nfc/nfc_tag_technology_chromeos.h87
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_