summaryrefslogtreecommitdiffstats
path: root/device
diff options
context:
space:
mode:
authorarmansito@chromium.org <armansito@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-20 23:24:19 +0000
committerarmansito@chromium.org <armansito@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-20 23:24:19 +0000
commit13aa72068571a09f84d6bd2524cf6d9ca190f214 (patch)
tree77b4587e072bdfdde5ac0e95db8292acbf0211f9 /device
parent57afa12e2f3d503b2cea20ecd50a47930156305a (diff)
downloadchromium_src-13aa72068571a09f84d6bd2524cf6d9ca190f214.zip
chromium_src-13aa72068571a09f84d6bd2524cf6d9ca190f214.tar.gz
chromium_src-13aa72068571a09f84d6bd2524cf6d9ca190f214.tar.bz2
nfc: Implement device::NfcPeerChromeOS.
Implemented device::NfcPeer for Chrome OS. With this, Chrome can now interact with remote NFC adapters. BUG=316471 TEST=device_unittests Review URL: https://codereview.chromium.org/112183002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@242205 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'device')
-rw-r--r--device/nfc/nfc.gyp5
-rw-r--r--device/nfc/nfc_adapter_chromeos.cc83
-rw-r--r--device/nfc/nfc_adapter_chromeos.h2
-rw-r--r--device/nfc/nfc_chromeos_unittest.cc466
-rw-r--r--device/nfc/nfc_ndef_record.cc55
-rw-r--r--device/nfc/nfc_ndef_record.h8
-rw-r--r--device/nfc/nfc_ndef_record_unittest.cc39
-rw-r--r--device/nfc/nfc_ndef_record_utils_chromeos.cc236
-rw-r--r--device/nfc/nfc_ndef_record_utils_chromeos.h36
-rw-r--r--device/nfc/nfc_peer.h12
-rw-r--r--device/nfc/nfc_peer_chromeos.cc196
-rw-r--r--device/nfc/nfc_peer_chromeos.h81
12 files changed, 1171 insertions, 48 deletions
diff --git a/device/nfc/nfc.gyp b/device/nfc/nfc.gyp
index d5e519b..2495a1c 100644
--- a/device/nfc/nfc.gyp
+++ b/device/nfc/nfc.gyp
@@ -12,6 +12,7 @@
'type': 'static_library',
'dependencies': [
'../../base/base.gyp:base',
+ '../../url/url.gyp:url_lib',
],
'sources': [
'nfc_adapter.cc',
@@ -22,8 +23,12 @@
'nfc_adapter_factory.h',
'nfc_ndef_record.cc',
'nfc_ndef_record.h',
+ 'nfc_ndef_record_utils_chromeos.cc',
+ 'nfc_ndef_record_utils_chromeos.h',
'nfc_peer.cc',
'nfc_peer.h',
+ 'nfc_peer_chromeos.cc',
+ 'nfc_peer_chromeos.h',
'nfc_tag.cc',
'nfc_tag.h',
'nfc_tag_technology.cc',
diff --git a/device/nfc/nfc_adapter_chromeos.cc b/device/nfc/nfc_adapter_chromeos.cc
index 071b080..b78ded2 100644
--- a/device/nfc/nfc_adapter_chromeos.cc
+++ b/device/nfc/nfc_adapter_chromeos.cc
@@ -9,19 +9,25 @@
#include "base/callback.h"
#include "base/logging.h"
#include "chromeos/dbus/dbus_thread_manager.h"
-#include "device/nfc/nfc_peer.h"
+#include "device/nfc/nfc_peer_chromeos.h"
#include "device/nfc/nfc_tag.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
+namespace {
+
+typedef std::vector<dbus::ObjectPath> ObjectPathVector;
+
+} // namespace
+
NfcAdapterChromeOS::NfcAdapterChromeOS()
: weak_ptr_factory_(this) {
DBusThreadManager::Get()->GetNfcAdapterClient()->AddObserver(this);
DBusThreadManager::Get()->GetNfcDeviceClient()->AddObserver(this);
DBusThreadManager::Get()->GetNfcTagClient()->AddObserver(this);
- const std::vector<dbus::ObjectPath>& object_paths =
+ const ObjectPathVector& object_paths =
DBusThreadManager::Get()->GetNfcAdapterClient()->GetAdapters();
if (!object_paths.empty()) {
VLOG(1) << object_paths.size() << " NFC adapter(s) available.";
@@ -126,9 +132,9 @@ void NfcAdapterChromeOS::AdapterRemoved(const dbus::ObjectPath& object_path) {
// There may still be other adapters present on the system. Set the next
// available adapter as the current one.
- const std::vector<dbus::ObjectPath>& object_paths =
+ const ObjectPathVector& object_paths =
DBusThreadManager::Get()->GetNfcAdapterClient()->GetAdapters();
- for (std::vector<dbus::ObjectPath>::const_iterator iter =
+ for (ObjectPathVector::const_iterator iter =
object_paths.begin();
iter != object_paths.end(); ++iter) {
// The removed object will still be available until the call to
@@ -155,19 +161,54 @@ void NfcAdapterChromeOS::AdapterPropertyChanged(
}
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.";
+ return;
+ }
+
VLOG(1) << "NFC device found: " << object_path.value();
- // TODO(armansito): Implement device logic.
+
+ // Check to see if the device belongs to this adapter.
+ const ObjectPathVector& devices =
+ DBusThreadManager::Get()->GetNfcDeviceClient()->
+ GetDevicesForAdapter(object_path_);
+ bool device_found = false;
+ for (ObjectPathVector::const_iterator iter = devices.begin();
+ iter != devices.end(); ++iter) {
+ if (*iter == object_path) {
+ device_found = true;
+ break;
+ }
+ }
+ if (!device_found) {
+ VLOG(1) << "Found peer device does not belong to the current adapter.";
+ return;
+ }
+
+ // Create the peer object.
+ NfcPeerChromeOS* peer_chromeos = new NfcPeerChromeOS(object_path);
+ peers_[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();
- // TODO(armansito): Implement device logic.
-}
+ PeersMap::iterator iter = peers_.find(object_path.value());
+ if (iter == peers_.end()) {
+ VLOG(1) << "Removed peer device does not belong to the current adapter.";
+ return;
+ }
-void NfcAdapterChromeOS::DevicePropertyChanged(
- const dbus::ObjectPath& object_path,
- const std::string& property_name) {
- // TODO(armansito): Implement device logic.
+ // 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) {
@@ -200,9 +241,23 @@ void NfcAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) {
if (properties->polling.value())
PollingChanged(true);
- // TODO(armansito): Create device::NfcPeer and device::NfcTag instances for
- // all peers and tags that exist, once they have been implemented for
- // ChromeOS.
+ // Create peer objects for peers that were added before the adapter was set.
+ const ObjectPathVector& devices =
+ DBusThreadManager::Get()->GetNfcDeviceClient()->
+ GetDevicesForAdapter(object_path_);
+ for (ObjectPathVector::const_iterator iter = devices.begin();
+ iter != devices.end(); ++iter) {
+ const dbus::ObjectPath& object_path = *iter;
+ if (peers_.find(object_path.value()) != peers_.end())
+ continue;
+ NfcPeerChromeOS* peer_chromeos = new NfcPeerChromeOS(object_path);
+ peers_[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.
}
void NfcAdapterChromeOS::RemoveAdapter() {
diff --git a/device/nfc/nfc_adapter_chromeos.h b/device/nfc/nfc_adapter_chromeos.h
index d70a98d..7ee99b0 100644
--- a/device/nfc/nfc_adapter_chromeos.h
+++ b/device/nfc/nfc_adapter_chromeos.h
@@ -60,8 +60,6 @@ class NfcAdapterChromeOS : public device::NfcAdapter,
// NfcDeviceClient::Observer overrides.
virtual void DeviceAdded(const dbus::ObjectPath& object_path) OVERRIDE;
virtual void DeviceRemoved(const dbus::ObjectPath& object_path) OVERRIDE;
- virtual void DevicePropertyChanged(const dbus::ObjectPath& object_path,
- const std::string& property_name) OVERRIDE;
// NfcTagClient::Observer overrides.
virtual void TagAdded(const dbus::ObjectPath& object_path) OVERRIDE;
diff --git a/device/nfc/nfc_chromeos_unittest.cc b/device/nfc/nfc_chromeos_unittest.cc
index dd94beb..c6155e0 100644
--- a/device/nfc/nfc_chromeos_unittest.cc
+++ b/device/nfc/nfc_chromeos_unittest.cc
@@ -2,23 +2,48 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/values.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#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 "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 "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::NfcPeer;
namespace chromeos {
namespace {
-class TestObserver : public NfcAdapter::Observer {
+// Callback passed to property structures.
+void OnPropertyChangedCallback(const std::string& property_name) {
+}
+
+// Callback passed to dbus::PropertyBase::Set.
+void OnSet(bool success) {
+}
+
+class TestObserver : public NfcAdapter::Observer,
+ public NfcPeer::Observer {
public:
TestObserver(scoped_refptr<NfcAdapter> adapter)
: present_changed_count_(0),
powered_changed_count_(0),
+ polling_changed_count_(0),
+ records_received_count_(0),
+ peer_count_(0),
adapter_(adapter) {
}
@@ -38,8 +63,40 @@ class TestObserver : public NfcAdapter::Observer {
powered_changed_count_++;
}
+ // NfcAdapter::Observer override.
+ virtual void AdapterPollingChanged(NfcAdapter* adapter,
+ bool powered) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+ polling_changed_count_++;
+ }
+
+ // NfcAdapter::Observer override.
+ virtual void PeerFound(NfcAdapter* adapter, NfcPeer* peer) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+ peer_count_++;
+ peer_identifier_ = peer->GetIdentifier();
+ }
+
+ // NfcAdapter::Observer override.
+ virtual void PeerLost(NfcAdapter* adapter, NfcPeer* peer) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+ peer_count_--;
+ peer_identifier_.clear();
+ }
+
+ // NfcPeer::Observer override.
+ virtual void RecordsReceived(
+ NfcPeer* peer, const NfcNdefRecord* record) OVERRIDE {
+ EXPECT_EQ(peer, adapter_->GetPeer(peer_identifier_));
+ records_received_count_++;
+ }
+
int present_changed_count_;
int powered_changed_count_;
+ int polling_changed_count_;
+ int records_received_count_;
+ int peer_count_;
+ std::string peer_identifier_;
scoped_refptr<NfcAdapter> adapter_;
};
@@ -51,9 +108,13 @@ class NfcChromeOSTest : public testing::Test {
DBusThreadManager::InitializeWithStub();
fake_nfc_adapter_client_ = static_cast<FakeNfcAdapterClient*>(
DBusThreadManager::Get()->GetNfcAdapterClient());
- SetAdapter();
- message_loop_.RunUntilIdle();
+ fake_nfc_device_client_ = static_cast<FakeNfcDeviceClient*>(
+ DBusThreadManager::Get()->GetNfcDeviceClient());
+ fake_nfc_record_client_ = static_cast<FakeNfcRecordClient*>(
+ DBusThreadManager::Get()->GetNfcRecordClient());
+ fake_nfc_adapter_client_->EnablePairingOnPoll(false);
+ fake_nfc_device_client_->DisableSimulationTimeout();
success_callback_count_ = 0;
error_callback_count_ = 0;
}
@@ -68,6 +129,7 @@ class NfcChromeOSTest : public testing::Test {
adapter_ = new NfcAdapterChromeOS();
ASSERT_TRUE(adapter_.get() != NULL);
ASSERT_TRUE(adapter_->IsInitialized());
+ base::RunLoop().RunUntilIdle();
}
// Generic callbacks for success and error.
@@ -79,23 +141,35 @@ class NfcChromeOSTest : public testing::Test {
error_callback_count_++;
}
+ void ErrorCallbackWithParameters(const std::string& error_name,
+ const std::string& error_message) {
+ LOG(INFO) << "Error callback called: " << error_name << ", "
+ << error_message;
+ error_callback_count_++;
+ }
+
protected:
+ // MessageLoop instance, used to simulate asynchronous behavior.
+ base::MessageLoop message_loop_;
+
// Fields for storing the number of times SuccessCallback and ErrorCallback
// have been called.
int success_callback_count_;
int error_callback_count_;
- // A message loop to emulate asynchronous behavior.
- base::MessageLoop message_loop_;
-
// The NfcAdapter instance under test.
scoped_refptr<NfcAdapter> adapter_;
// The fake D-Bus client instances used for testing.
FakeNfcAdapterClient* fake_nfc_adapter_client_;
+ FakeNfcDeviceClient* fake_nfc_device_client_;
+ FakeNfcRecordClient* fake_nfc_record_client_;
};
+// Tests that the adapter updates correctly to reflect the current "default"
+// adapter, when multiple adapters appear and disappear.
TEST_F(NfcChromeOSTest, PresentChanged) {
+ SetAdapter();
EXPECT_TRUE(adapter_->IsPresent());
TestObserver observer(adapter_);
@@ -122,7 +196,9 @@ TEST_F(NfcChromeOSTest, PresentChanged) {
EXPECT_FALSE(adapter_->IsPresent());
}
+// Tests that the adapter correctly reflects the power state.
TEST_F(NfcChromeOSTest, SetPowered) {
+ SetAdapter();
TestObserver observer(adapter_);
adapter_->AddObserver(&observer);
@@ -177,7 +253,9 @@ TEST_F(NfcChromeOSTest, SetPowered) {
EXPECT_EQ(2, error_callback_count_);
}
+// Tests that the power state updates correctly when the adapter disappears.
TEST_F(NfcChromeOSTest, PresentChangedWhilePowered) {
+ SetAdapter();
TestObserver observer(adapter_);
adapter_->AddObserver(&observer);
@@ -199,4 +277,380 @@ TEST_F(NfcChromeOSTest, PresentChangedWhilePowered) {
EXPECT_FALSE(adapter_->IsPresent());
}
+// Tests that peer and record objects are created for all peers and records
+// that already exist when the adapter is created.
+TEST_F(NfcChromeOSTest, PeersInitializedWhenAdapterCreated) {
+ // 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());
+
+ // Start pairing simulation, which will add a fake device and fake records.
+ fake_nfc_device_client_->BeginPairingSimulation(0, 0);
+ base::RunLoop().RunUntilIdle();
+
+ // 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.present_changed_count_);
+ EXPECT_EQ(0, observer.present_changed_count_);
+ EXPECT_EQ(0, observer.peer_count_);
+
+ EXPECT_TRUE(adapter_->IsPresent());
+ EXPECT_TRUE(adapter_->IsPowered());
+ EXPECT_FALSE(adapter_->IsPolling());
+
+ NfcAdapter::PeerList peers;
+ adapter_->GetPeers(&peers);
+ EXPECT_EQ(static_cast<size_t>(1), peers.size());
+
+ NfcPeer* peer = peers[0];
+ const NfcNdefMessage& message = peer->GetNdefMessage();
+ EXPECT_EQ(static_cast<size_t>(3), message.records().size());
+}
+
+// Tests that the adapter correctly updates its state when polling is started
+// and stopped.
+TEST_F(NfcChromeOSTest, StartAndStopPolling) {
+ SetAdapter();
+ EXPECT_TRUE(adapter_->IsPresent());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ // Start polling while not powered. Should fail.
+ EXPECT_FALSE(adapter_->IsPowered());
+ adapter_->StartPolling(
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_FALSE(adapter_->IsPolling());
+
+ // Start polling while powered. Should succeed.
+ adapter_->SetPowered(
+ true,
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_TRUE(adapter_->IsPowered());
+
+ adapter_->StartPolling(
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_TRUE(adapter_->IsPolling());
+
+ // Start polling while already polling. Should fail.
+ adapter_->StartPolling(
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+ EXPECT_EQ(2, error_callback_count_);
+ EXPECT_TRUE(adapter_->IsPolling());
+
+ // Stop polling. Should succeed.
+ adapter_->StopPolling(
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(3, success_callback_count_);
+ EXPECT_EQ(2, error_callback_count_);
+ EXPECT_FALSE(adapter_->IsPolling());
+
+ // Stop polling while not polling. Should fail.
+ adapter_->StopPolling(
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(3, success_callback_count_);
+ EXPECT_EQ(3, error_callback_count_);
+ EXPECT_FALSE(adapter_->IsPolling());
+}
+
+// Tests a simple peer pairing simulation.
+TEST_F(NfcChromeOSTest, PeerTest) {
+ 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.peer_count_);
+
+ // Add the fake device.
+ fake_nfc_device_client_->BeginPairingSimulation(0, -1);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, observer.peer_count_);
+ EXPECT_EQ(FakeNfcDeviceClient::kDevicePath, observer.peer_identifier_);
+
+ NfcPeer* peer = adapter_->GetPeer(observer.peer_identifier_);
+ CHECK(peer);
+ peer->AddObserver(&observer);
+
+ // Peer should have no records on it.
+ EXPECT_TRUE(peer->GetNdefMessage().records().empty());
+ EXPECT_EQ(0, observer.records_received_count_);
+
+ // Make records visible.
+ fake_nfc_record_client_->SetRecordsVisible(true);
+ EXPECT_EQ(3, observer.records_received_count_);
+ EXPECT_EQ(static_cast<size_t>(3), peer->GetNdefMessage().records().size());
+
+ // End the simulation. Record should have been removed.
+ fake_nfc_device_client_->EndPairingSimulation();
+ EXPECT_EQ(0, observer.peer_count_);
+ EXPECT_TRUE(observer.peer_identifier_.empty());
+
+ peer = adapter_->GetPeer(observer.peer_identifier_);
+ EXPECT_FALSE(peer);
+
+ // No record related notifications will be sent when a peer gets removed.
+ EXPECT_EQ(3, observer.records_received_count_);
+}
+
+// Unit tests for nfc_ndef_record_utils methods.
+TEST_F(NfcChromeOSTest, NfcNdefRecordToDBusAttributes) {
+ const char kText[] = "text";
+ const char kURI[] = "test://uri";
+ const char kEncoding[] = "encoding";
+ const char kLanguageCode[] = "en";
+ const char kMimeType[] = "mime-type";
+ const double kSize = 5;
+
+ // Text record.
+ base::DictionaryValue data;
+ data.SetString(NfcNdefRecord::kFieldText, kText);
+ data.SetString(NfcNdefRecord::kFieldLanguageCode, kLanguageCode);
+ data.SetString(NfcNdefRecord::kFieldEncoding, kEncoding);
+
+ scoped_ptr<NfcNdefRecord> record(new NfcNdefRecord());
+ ASSERT_TRUE(record->Populate(NfcNdefRecord::kTypeText, &data));
+
+ base::DictionaryValue result;
+ EXPECT_TRUE(nfc_ndef_record_utils::NfcNdefRecordToDBusAttributes(
+ record.get(), &result));
+
+ std::string string_value;
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kTypeProperty, &string_value));
+ EXPECT_EQ(nfc_record::kTypeText, string_value);
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kRepresentationProperty, &string_value));
+ EXPECT_EQ(kText, string_value);
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kLanguageProperty, &string_value));
+ EXPECT_EQ(kLanguageCode, string_value);
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kEncodingProperty, &string_value));
+ EXPECT_EQ(kEncoding, string_value);
+
+ // URI record.
+ data.Clear();
+ data.SetString(NfcNdefRecord::kFieldURI, kURI);
+ data.SetString(NfcNdefRecord::kFieldMimeType, kMimeType);
+ data.SetDouble(NfcNdefRecord::kFieldTargetSize, kSize);
+
+ record.reset(new NfcNdefRecord());
+ ASSERT_TRUE(record->Populate(NfcNdefRecord::kTypeURI, &data));
+
+ result.Clear();
+ EXPECT_TRUE(nfc_ndef_record_utils::NfcNdefRecordToDBusAttributes(
+ record.get(), &result));
+
+ EXPECT_TRUE(result.GetString(nfc_record::kTypeProperty, &string_value));
+ EXPECT_EQ(nfc_record::kTypeUri, string_value);
+ EXPECT_TRUE(result.GetString(nfc_record::kUriProperty, &string_value));
+ EXPECT_EQ(kURI, string_value);
+ EXPECT_TRUE(result.GetString(nfc_record::kMimeTypeProperty, &string_value));
+ EXPECT_EQ(kMimeType, string_value);
+ double double_value;
+ EXPECT_TRUE(result.GetDouble(nfc_record::kSizeProperty, &double_value));
+ EXPECT_EQ(kSize, double_value);
+
+ // SmartPoster record.
+ base::DictionaryValue* title = new base::DictionaryValue();
+ title->SetString(NfcNdefRecord::kFieldText, kText);
+ title->SetString(NfcNdefRecord::kFieldLanguageCode, kLanguageCode);
+ title->SetString(NfcNdefRecord::kFieldEncoding, kEncoding);
+
+ base::ListValue* titles = new base::ListValue();
+ titles->Append(title);
+ data.Set(NfcNdefRecord::kFieldTitles, titles);
+
+ record.reset(new NfcNdefRecord());
+ ASSERT_TRUE(record->Populate(NfcNdefRecord::kTypeSmartPoster, &data));
+
+ result.Clear();
+ EXPECT_TRUE(nfc_ndef_record_utils::NfcNdefRecordToDBusAttributes(
+ record.get(), &result));
+
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kTypeProperty, &string_value));
+ EXPECT_EQ(nfc_record::kTypeSmartPoster, string_value);
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kRepresentationProperty, &string_value));
+ EXPECT_EQ(kText, string_value);
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kLanguageProperty, &string_value));
+ EXPECT_EQ(kLanguageCode, string_value);
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kEncodingProperty, &string_value));
+ EXPECT_EQ(kEncoding, string_value);
+ EXPECT_TRUE(result.GetString(nfc_record::kUriProperty, &string_value));
+ EXPECT_EQ(kURI, string_value);
+ EXPECT_TRUE(result.GetString(nfc_record::kMimeTypeProperty, &string_value));
+ EXPECT_EQ(kMimeType, string_value);
+ EXPECT_TRUE(result.GetDouble(nfc_record::kSizeProperty, &double_value));
+ EXPECT_EQ(kSize, double_value);
+}
+
+TEST_F(NfcChromeOSTest, RecordPropertiesToNfcNdefRecord) {
+ const char kText[] = "text";
+ const char kURI[] = "test://uri";
+ const char kEncoding[] = "encoding";
+ const char kLanguageCode[] = "en";
+ const char kMimeType[] = "mime-type";
+ const uint32 kSize = 5;
+
+ FakeNfcRecordClient::Properties record_properties(
+ base::Bind(&OnPropertyChangedCallback));
+
+ // Text record.
+ record_properties.type.ReplaceValue(nfc_record::kTypeText);
+ record_properties.representation.ReplaceValue(kText);
+ record_properties.language.ReplaceValue(kLanguageCode);
+ record_properties.encoding.ReplaceValue(kEncoding);
+
+ scoped_ptr<NfcNdefRecord> record(new NfcNdefRecord());
+ EXPECT_TRUE(nfc_ndef_record_utils::RecordPropertiesToNfcNdefRecord(
+ &record_properties, record.get()));
+ EXPECT_TRUE(record->IsPopulated());
+
+ std::string string_value;
+ EXPECT_EQ(NfcNdefRecord::kTypeText, record->type());
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldText, &string_value));
+ EXPECT_EQ(kText, string_value);
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldLanguageCode, &string_value));
+ EXPECT_EQ(kLanguageCode, string_value);
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldEncoding, &string_value));
+ EXPECT_EQ(kEncoding, string_value);
+
+ // URI record.
+ record_properties.representation.ReplaceValue("");
+ record_properties.language.ReplaceValue("");
+ record_properties.encoding.ReplaceValue("");
+
+ record_properties.type.ReplaceValue(nfc_record::kTypeUri);
+ record_properties.uri.ReplaceValue(kURI);
+ record_properties.mime_type.ReplaceValue(kMimeType);
+ record_properties.size.ReplaceValue(kSize);
+
+ record.reset(new NfcNdefRecord());
+ EXPECT_TRUE(nfc_ndef_record_utils::RecordPropertiesToNfcNdefRecord(
+ &record_properties, record.get()));
+ EXPECT_TRUE(record->IsPopulated());
+
+ EXPECT_EQ(NfcNdefRecord::kTypeURI, record->type());
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldURI, &string_value));
+ EXPECT_EQ(kURI, string_value);
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldMimeType, &string_value));
+ EXPECT_EQ(kMimeType, string_value);
+ double double_value;
+ EXPECT_TRUE(record->data().GetDouble(
+ NfcNdefRecord::kFieldTargetSize, &double_value));
+ EXPECT_EQ(kSize, double_value);
+
+ // Contents not matching type.
+ record_properties.representation.ReplaceValue(kText);
+ record_properties.language.ReplaceValue(kLanguageCode);
+ record_properties.encoding.ReplaceValue(kEncoding);
+
+ record.reset(new NfcNdefRecord());
+ EXPECT_FALSE(nfc_ndef_record_utils::RecordPropertiesToNfcNdefRecord(
+ &record_properties, record.get()));
+ EXPECT_FALSE(record->IsPopulated());
+
+ // SmartPoster record.
+ record_properties.type.ReplaceValue(nfc_record::kTypeSmartPoster);
+ EXPECT_TRUE(nfc_ndef_record_utils::RecordPropertiesToNfcNdefRecord(
+ &record_properties, record.get()));
+ EXPECT_TRUE(record->IsPopulated());
+
+ EXPECT_EQ(NfcNdefRecord::kTypeSmartPoster, record->type());
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldURI, &string_value));
+ EXPECT_EQ(kURI, string_value);
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldMimeType, &string_value));
+ EXPECT_EQ(kMimeType, string_value);
+ EXPECT_TRUE(record->data().GetDouble(
+ NfcNdefRecord::kFieldTargetSize, &double_value));
+ EXPECT_EQ(kSize, double_value);
+
+ const base::ListValue* titles = NULL;
+ EXPECT_TRUE(record->data().GetList(NfcNdefRecord::kFieldTitles, &titles));
+ EXPECT_EQ(static_cast<size_t>(1), titles->GetSize());
+ ASSERT_TRUE(titles);
+ const base::DictionaryValue* title = NULL;
+ EXPECT_TRUE(titles->GetDictionary(0, &title));
+ CHECK(title);
+
+ EXPECT_TRUE(title->GetString(NfcNdefRecord::kFieldText, &string_value));
+ EXPECT_EQ(kText, string_value);
+ EXPECT_TRUE(title->GetString(
+ NfcNdefRecord::kFieldLanguageCode, &string_value));
+ EXPECT_EQ(kLanguageCode, string_value);
+ EXPECT_TRUE(title->GetString(NfcNdefRecord::kFieldEncoding, &string_value));
+ EXPECT_EQ(kEncoding, string_value);
+}
+
} // namespace chromeos
diff --git a/device/nfc/nfc_ndef_record.cc b/device/nfc/nfc_ndef_record.cc
index ec13c2f..5b8bb99 100644
--- a/device/nfc/nfc_ndef_record.cc
+++ b/device/nfc/nfc_ndef_record.cc
@@ -7,6 +7,7 @@
#include <map>
#include "base/logging.h"
+#include "url/gurl.h"
using base::DictionaryValue;
using base::ListValue;
@@ -17,6 +18,23 @@ namespace {
typedef std::map<std::string, base::Value::Type> FieldValueMap;
+bool ValidateURI(const DictionaryValue* data) {
+ std::string uri;
+ if (!data->GetString(NfcNdefRecord::kFieldURI, &uri)) {
+ VLOG(1) << "No URI entry in data.";
+ return false;
+ }
+ DCHECK(!uri.empty());
+
+ // Use GURL to check validity.
+ GURL url(uri);
+ if (!url.is_valid()) {
+ LOG(ERROR) << "Invalid URI given: " << uri;
+ return false;
+ }
+ return true;
+}
+
bool CheckFieldsAreValid(
const FieldValueMap& required_fields,
const FieldValueMap& optional_fields,
@@ -47,6 +65,12 @@ bool CheckFieldsAreValid(
<< field_iter->second;
return false;
}
+ // Make sure that the value is non-empty, if the value is a string.
+ std::string string_value;
+ if (iter.value().GetAsString(&string_value) && string_value.empty()) {
+ VLOG(1) << "Empty value given for field of type string: " << iter.key();
+ return false;
+ }
}
// Check for required fields.
if (required_count != required_fields.size()) {
@@ -63,13 +87,23 @@ bool HandleTypeText(const DictionaryValue* data) {
VLOG(1) << "Populating record with type \"Text\".";
FieldValueMap required_fields;
required_fields[NfcNdefRecord::kFieldText] = base::Value::TYPE_STRING;
+ required_fields[NfcNdefRecord::kFieldEncoding] = base::Value::TYPE_STRING;
+ required_fields[NfcNdefRecord::kFieldLanguageCode] = base::Value::TYPE_STRING;
FieldValueMap optional_fields;
- optional_fields[NfcNdefRecord::kFieldEncoding] = base::Value::TYPE_STRING;
- optional_fields[NfcNdefRecord::kFieldLanguageCode] = base::Value::TYPE_STRING;
if (!CheckFieldsAreValid(required_fields, optional_fields, data)) {
VLOG(1) << "Failed to populate record.";
return false;
}
+
+ // Verify that the "Encoding" property has valid values.
+ std::string encoding;
+ if (!data->GetString(NfcNdefRecord::kFieldEncoding, &encoding)) {
+ if (encoding != NfcNdefRecord::kEncodingUtf8 ||
+ encoding != NfcNdefRecord::kEncodingUtf16) {
+ VLOG(1) << "Invalid \"Encoding\" value:" << encoding;
+ return false;
+ }
+ }
return true;
}
@@ -113,7 +147,7 @@ bool HandleTypeSmartPoster(const DictionaryValue* data) {
}
}
}
- return true;
+ return ValidateURI(data);
}
// Verifies that the contents of |data| conform to the fields of NDEF type
@@ -125,11 +159,13 @@ bool HandleTypeUri(const DictionaryValue* data) {
FieldValueMap optional_fields;
optional_fields[NfcNdefRecord::kFieldMimeType] = base::Value::TYPE_STRING;
optional_fields[NfcNdefRecord::kFieldTargetSize] = base::Value::TYPE_DOUBLE;
+
+ // Allow passing TargetSize as an integer, but convert it to a double.
if (!CheckFieldsAreValid(required_fields, optional_fields, data)) {
VLOG(1) << "Failed to populate record.";
return false;
}
- return true;
+ return ValidateURI(data);
}
} // namespace
@@ -210,4 +246,15 @@ void NfcNdefMessage::AddRecord(NfcNdefRecord* record) {
records_.push_back(record);
}
+bool NfcNdefMessage::RemoveRecord(NfcNdefRecord* record) {
+ for (RecordList::iterator iter = records_.begin();
+ iter != records_.end(); ++iter) {
+ if (*iter == record) {
+ records_.erase(iter);
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace device
diff --git a/device/nfc/nfc_ndef_record.h b/device/nfc/nfc_ndef_record.h
index 2be66b1..24cbc12 100644
--- a/device/nfc/nfc_ndef_record.h
+++ b/device/nfc/nfc_ndef_record.h
@@ -146,9 +146,15 @@ class NfcNdefMessage {
const RecordList& records() const { return records_; }
// Adds the NDEF record |record| to the sequence of records that this
- // NdefMessage contains.
+ // NfcNdefMessage contains. This method simply adds the record to this message
+ // and does NOT take ownership of it.
void AddRecord(NfcNdefRecord* record);
+ // Removes the NDEF record |record| from this message. Returns true, if the
+ // record was removed, otherwise returns false if |record| was not contained
+ // in this message.
+ bool RemoveRecord(NfcNdefRecord* record);
+
private:
// The NDEF records that are contained by this message.
RecordList records_;
diff --git a/device/nfc/nfc_ndef_record_unittest.cc b/device/nfc/nfc_ndef_record_unittest.cc
index 00c2d2f5..2b6ef48 100644
--- a/device/nfc/nfc_ndef_record_unittest.cc
+++ b/device/nfc/nfc_ndef_record_unittest.cc
@@ -21,7 +21,7 @@ const char kTestLanguageCode[] = "test-language-code";
const char kTestMimeType[] = "test-mime-type";
const uint32 kTestTargetSize = 0;
const char kTestText[] = "test-text";
-const char kTestURI[] = "test-uri";
+const char kTestURI[] = "test://uri";
} // namespace
@@ -33,35 +33,32 @@ TEST(NfcNdefRecordTest, PopulateTextRecord) {
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeText, &data));
EXPECT_FALSE(record->IsPopulated());
- // Text field with incorrect entry. Should fail.
+ // Text field with incorrect type. Should fail.
data.SetInteger(NfcNdefRecord::kFieldText, 0);
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeText, &data));
EXPECT_FALSE(record->IsPopulated());
- // Text field with correct entry. Should succeed.
+ // Text field with correct type but missing encoding and language.
+ // Should fail.
data.SetString(NfcNdefRecord::kFieldText, kTestText);
- EXPECT_TRUE(record->Populate(NfcNdefRecord::kTypeText, &data));
- EXPECT_TRUE(record->IsPopulated());
- EXPECT_EQ(NfcNdefRecord::kTypeText, record->type());
+ EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeText, &data));
+ EXPECT_FALSE(record->IsPopulated());
// Populating a successfully populated record should fail.
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeText, &data));
- // Recycle the record.
- record.reset(new NfcNdefRecord());
- EXPECT_FALSE(record->IsPopulated());
-
- // Incorrect optional fields. Should fail.
+ // Incorrect type for language code.
data.SetInteger(NfcNdefRecord::kFieldLanguageCode, 0);
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeText, &data));
EXPECT_FALSE(record->IsPopulated());
+ // Correct type for language code, invalid encoding.
data.SetString(NfcNdefRecord::kFieldLanguageCode, kTestLanguageCode);
data.SetInteger(NfcNdefRecord::kFieldEncoding, 0);
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeText, &data));
EXPECT_FALSE(record->IsPopulated());
- // Optional fields are correct. Should succeed.
+ // All entries valid. Should succeed.
data.SetString(NfcNdefRecord::kFieldEncoding, kTestEncoding);
EXPECT_TRUE(record->Populate(NfcNdefRecord::kTypeText, &data));
EXPECT_TRUE(record->IsPopulated());
@@ -88,12 +85,16 @@ TEST(NfcNdefRecordTest, PopulateUriRecord) {
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeURI, &data));
EXPECT_FALSE(record->IsPopulated());
- // URI field with incorrect entry. Should fail.
+ // URI field with incorrect type. Should fail.
data.SetInteger(NfcNdefRecord::kFieldURI, 0);
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeURI, &data));
EXPECT_FALSE(record->IsPopulated());
- // URI field with correct entry. Should succeed.
+ // URI field with correct type but invalid format.
+ data.SetString(NfcNdefRecord::kFieldURI, "test.uri");
+ EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeURI, &data));
+ EXPECT_FALSE(record->IsPopulated());
+
data.SetString(NfcNdefRecord::kFieldURI, kTestURI);
EXPECT_TRUE(record->Populate(NfcNdefRecord::kTypeURI, &data));
EXPECT_TRUE(record->IsPopulated());
@@ -203,8 +204,10 @@ TEST(NfcNdefRecordTest, PopulateSmartPoster) {
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeSmartPoster, &data));
EXPECT_FALSE(record->IsPopulated());
- // Title value with valid "text" field.
+ // Title value with valid entries.
title_value->SetString(NfcNdefRecord::kFieldText, kTestText);
+ title_value->SetString(NfcNdefRecord::kFieldLanguageCode, kTestLanguageCode);
+ title_value->SetString(NfcNdefRecord::kFieldEncoding, kTestEncoding);
EXPECT_TRUE(record->Populate(NfcNdefRecord::kTypeSmartPoster, &data));
EXPECT_TRUE(record->IsPopulated());
@@ -233,6 +236,12 @@ TEST(NfcNdefRecordTest, PopulateSmartPoster) {
EXPECT_TRUE(const_dictionary_value->GetString(
NfcNdefRecord::kFieldText, &string_value));
EXPECT_EQ(kTestText, string_value);
+ EXPECT_TRUE(const_dictionary_value->GetString(
+ NfcNdefRecord::kFieldLanguageCode, &string_value));
+ EXPECT_EQ(kTestLanguageCode, string_value);
+ EXPECT_TRUE(const_dictionary_value->GetString(
+ NfcNdefRecord::kFieldEncoding, &string_value));
+ EXPECT_EQ(kTestEncoding, string_value);
}
} // namespace device
diff --git a/device/nfc/nfc_ndef_record_utils_chromeos.cc b/device/nfc/nfc_ndef_record_utils_chromeos.cc
new file mode 100644
index 0000000..3bc6f95
--- /dev/null
+++ b/device/nfc/nfc_ndef_record_utils_chromeos.cc
@@ -0,0 +1,236 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/nfc/nfc_ndef_record_utils_chromeos.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "device/nfc/nfc_ndef_record.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+using device::NfcNdefRecord;
+
+namespace chromeos {
+namespace nfc_ndef_record_utils {
+
+namespace {
+
+// Maps the NDEF type |type| as returned by neard to the corresponding
+// device::NfcNdefRecord::Type value.
+NfcNdefRecord::Type DBusRecordTypeValueToNfcNdefRecordType(
+ const std::string& type) {
+ if (type == nfc_record::kTypeSmartPoster)
+ return NfcNdefRecord::kTypeSmartPoster;
+ if (type == nfc_record::kTypeText)
+ return NfcNdefRecord::kTypeText;
+ if (type == nfc_record::kTypeUri)
+ return NfcNdefRecord::kTypeURI;
+ if (type == nfc_record::kTypeHandoverRequest)
+ return NfcNdefRecord::kTypeHandoverRequest;
+ if (type == nfc_record::kTypeHandoverSelect)
+ return NfcNdefRecord::kTypeHandoverSelect;
+ if (type == nfc_record::kTypeHandoverCarrier)
+ return NfcNdefRecord::kTypeHandoverCarrier;
+ return NfcNdefRecord::kTypeUnknown;
+}
+
+// Maps the NDEF type |type| given as a NFC C++ API enumeration to the
+// corresponding string value defined by neard.
+std::string NfcRecordTypeEnumToPropertyValue(NfcNdefRecord::Type type) {
+ switch (type) {
+ case NfcNdefRecord::kTypeSmartPoster:
+ return nfc_record::kTypeSmartPoster;
+ case NfcNdefRecord::kTypeText:
+ return nfc_record::kTypeText;
+ case NfcNdefRecord::kTypeURI:
+ return nfc_record::kTypeUri;
+ case NfcNdefRecord::kTypeHandoverRequest:
+ return nfc_record::kTypeHandoverRequest;
+ case NfcNdefRecord::kTypeHandoverSelect:
+ return nfc_record::kTypeHandoverSelect;
+ case NfcNdefRecord::kTypeHandoverCarrier:
+ return nfc_record::kTypeHandoverCarrier;
+ default:
+ return "";
+ }
+}
+
+// Maps the field name |field_name| given as defined in NfcNdefRecord to the
+// Neard Record D-Bus property name. This handles all fields except for
+// NfcNdefRecord::kFieldTitles and NfcNdefRecord::kFieldAction, which need
+// special handling.
+std::string NdefRecordFieldToDBusProperty(const std::string& field_name) {
+ if (field_name == NfcNdefRecord::kFieldEncoding)
+ return nfc_record::kEncodingProperty;
+ if (field_name == NfcNdefRecord::kFieldLanguageCode)
+ return nfc_record::kLanguageProperty;
+ if (field_name == NfcNdefRecord::kFieldText)
+ return nfc_record::kRepresentationProperty;
+ if (field_name == NfcNdefRecord::kFieldURI)
+ return nfc_record::kUriProperty;
+ if (field_name == NfcNdefRecord::kFieldMimeType)
+ return nfc_record::kMimeTypeProperty;
+ if (field_name == NfcNdefRecord::kFieldTargetSize)
+ return nfc_record::kSizeProperty;
+ return "";
+}
+
+std::string NfcNdefRecordActionValueToDBusActionValue(
+ const std::string& action) {
+ // TODO(armansito): Add bindings for values returned by neard to
+ // service_constants.h.
+ if (action == device::NfcNdefRecord::kSmartPosterActionDo)
+ return "Do";
+ if (action == device::NfcNdefRecord::kSmartPosterActionSave)
+ return "Save";
+ if (action == device::NfcNdefRecord::kSmartPosterActionOpen)
+ return "Edit";
+ return "";
+}
+
+std::string DBusActionValueToNfcNdefRecordActionValue(
+ const std::string& action) {
+ // TODO(armansito): Add bindings for values returned by neard to
+ // service_constants.h.
+ if (action == "Do")
+ return device::NfcNdefRecord::kSmartPosterActionDo;
+ if (action == "Save")
+ return device::NfcNdefRecord::kSmartPosterActionSave;
+ if (action == "Edit")
+ return device::NfcNdefRecord::kSmartPosterActionOpen;
+ return "";
+}
+
+
+// Translates the given dictionary of NDEF fields by recursively converting
+// each key in |record_data| to its corresponding Record property name defined
+// by the Neard D-Bus API. The output is stored in |out|. Returns false if an
+// error occurs.
+bool ConvertNdefFieldsToDBusAttributes(
+ const base::DictionaryValue& fields,
+ base::DictionaryValue* out) {
+ DCHECK(out);
+ for (base::DictionaryValue::Iterator iter(fields);
+ !iter.IsAtEnd(); iter.Advance()) {
+ // Special case the "titles" and "action" fields.
+ if (iter.key() == NfcNdefRecord::kFieldTitles) {
+ const base::ListValue* titles = NULL;
+ bool value_result = iter.value().GetAsList(&titles);
+ DCHECK(value_result);
+ DCHECK(titles->GetSize() != 0);
+ // TODO(armansito): For now, pick the first title in the list and write
+ // its contents directly to the top level of the field. This is due to an
+ // error in the Neard D-Bus API design. This code will need to be updated
+ // if the neard API changes to correct this.
+ const base::DictionaryValue* first_title = NULL;
+ value_result = titles->GetDictionary(0, &first_title);
+ DCHECK(value_result);
+ if (!ConvertNdefFieldsToDBusAttributes(*first_title, out)) {
+ LOG(ERROR) << "Invalid title field.";
+ return false;
+ }
+ } else if (iter.key() == NfcNdefRecord::kFieldAction) {
+ // The value of the action field needs to be translated.
+ std::string action_value;
+ bool value_result = iter.value().GetAsString(&action_value);
+ DCHECK(value_result);
+ std::string action =
+ NfcNdefRecordActionValueToDBusActionValue(action_value);
+ if (action.empty()) {
+ VLOG(1) << "Invalid action value: \"" << action_value << "\"";
+ return false;
+ }
+ out->SetString(nfc_record::kActionProperty, action);
+ } else {
+ std::string dbus_property = NdefRecordFieldToDBusProperty(iter.key());
+ if (dbus_property.empty()) {
+ LOG(ERROR) << "Invalid field: " << iter.key();
+ return false;
+ }
+ out->Set(dbus_property, iter.value().DeepCopy());
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+bool NfcNdefRecordToDBusAttributes(
+ const NfcNdefRecord* record,
+ base::DictionaryValue* out) {
+ DCHECK(record);
+ DCHECK(out);
+ if (!record->IsPopulated()) {
+ LOG(ERROR) << "Record is not populated.";
+ return false;
+ }
+ out->SetString(nfc_record::kTypeProperty,
+ NfcRecordTypeEnumToPropertyValue(record->type()));
+ return ConvertNdefFieldsToDBusAttributes(record->data(), out);
+}
+
+bool RecordPropertiesToNfcNdefRecord(
+ const NfcRecordClient::Properties* properties,
+ device::NfcNdefRecord* out) {
+ if (out->IsPopulated()) {
+ LOG(ERROR) << "Record is already populated!";
+ return false;
+ }
+ NfcNdefRecord::Type type =
+ DBusRecordTypeValueToNfcNdefRecordType(properties->type.value());
+ if (type == NfcNdefRecord::kTypeUnknown) {
+ LOG(ERROR) << "Record type is unknown.";
+ return false;
+ }
+
+ // Extract each property.
+ base::DictionaryValue attributes;
+ if (!properties->uri.value().empty())
+ attributes.SetString(NfcNdefRecord::kFieldURI, properties->uri.value());
+ if (!properties->mime_type.value().empty()) {
+ attributes.SetString(NfcNdefRecord::kFieldMimeType,
+ properties->mime_type.value());
+ }
+ if (properties->size.value() != 0) {
+ attributes.SetDouble(NfcNdefRecord::kFieldTargetSize,
+ static_cast<double>(properties->size.value()));
+ }
+ std::string action_value =
+ DBusActionValueToNfcNdefRecordActionValue(properties->action.value());
+ if (!action_value.empty())
+ attributes.SetString(NfcNdefRecord::kFieldAction, action_value);
+
+ // The "representation", "encoding", and "language" properties will be stored
+ // differently, depending on whether the record type is "SmartPoster" or
+ // "Text".
+ {
+ scoped_ptr<base::DictionaryValue> text_attributes(
+ new base::DictionaryValue());
+ if (!properties->representation.value().empty()) {
+ text_attributes->SetString(NfcNdefRecord::kFieldText,
+ properties->representation.value());
+ }
+ if (!properties->encoding.value().empty()) {
+ text_attributes->SetString(NfcNdefRecord::kFieldEncoding,
+ properties->encoding.value());
+ }
+ if (!properties->language.value().empty()) {
+ 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());
+ }
+ }
+
+ // Populate the given record.
+ return out->Populate(type, &attributes);
+}
+
+} // namespace nfc_ndef_record_utils
+} // namespace chromeos
diff --git a/device/nfc/nfc_ndef_record_utils_chromeos.h b/device/nfc/nfc_ndef_record_utils_chromeos.h
new file mode 100644
index 0000000..2d96b97a
--- /dev/null
+++ b/device/nfc/nfc_ndef_record_utils_chromeos.h
@@ -0,0 +1,36 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/values.h"
+#include "chromeos/dbus/nfc_record_client.h"
+
+#ifndef DEVICE_NFC_CHROMEOS_NDEF_RECORD_UTILS_CHROMEOS_H_
+#define DEVICE_NFC_CHROMEOS_NDEF_RECORD_UTILS_CHROMEOS_H_
+
+namespace device {
+class NfcNdefRecord;
+} // namespace device;
+
+namespace chromeos {
+namespace nfc_ndef_record_utils {
+
+// Converts the NfcNdefRecord |record| to a dictionary that can be passed to
+// NfcDeviceClient::Push and NfcTagClient::Write and stores it in |out|.
+// Returns false, if an error occurs during conversion.
+bool NfcNdefRecordToDBusAttributes(
+ const device::NfcNdefRecord* record,
+ base::DictionaryValue* out);
+
+// Converts an NDEF record D-Bus properties structure to an NfcNdefRecord
+// instance by populating the instance passed in |out|. |out| must not be NULL
+// and must not be already populated. Returns false, if an error occurs during
+// conversion.
+bool RecordPropertiesToNfcNdefRecord(
+ const NfcRecordClient::Properties* properties,
+ device::NfcNdefRecord* out);
+
+} // namespace nfc_ndef_record_utils
+} // namespace chromeos
+
+#endif // DEVICE_NFC_CHROMEOS_NDEF_RECORD_UTILS_CHROMEOS_H_
diff --git a/device/nfc/nfc_peer.h b/device/nfc/nfc_peer.h
index 1578738..710641e 100644
--- a/device/nfc/nfc_peer.h
+++ b/device/nfc/nfc_peer.h
@@ -37,12 +37,12 @@ class NfcPeer {
public:
virtual ~Observer() {}
- // This method will be called when an NDEF message |message| from the peer
+ // This method will be called when an NDEF record |record| from the peer
// device |peer| is received. Users can use this method to be notified of
// new records on the device and when the initial set of records are
- // received from it, if any.
- virtual void RecordsReceived(NfcPeer* peer,
- const NfcNdefMessage& message) {}
+ // 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) {}
};
// The ErrorCallback is used by methods to asynchronously report errors.
@@ -64,12 +64,12 @@ class NfcPeer {
// this only means that no records have yet been received from the device.
// Users should use this method in conjunction with the Observer methods
// to be notified when the records are ready.
- virtual NfcNdefMessage GetNdefMessage() const = 0;
+ virtual const NfcNdefMessage& GetNdefMessage() const = 0;
// Sends the NDEF records contained in |message| to the peer device. On
// success, |callback| will be invoked. On failure, |error_callback| will be
// invoked.
- virtual void PushNdef(NfcNdefMessage* message,
+ virtual void PushNdef(const NfcNdefMessage& message,
const base::Closure& callback,
const ErrorCallback& error_callback) = 0;
diff --git a/device/nfc/nfc_peer_chromeos.cc b/device/nfc/nfc_peer_chromeos.cc
new file mode 100644
index 0000000..a6e864c
--- /dev/null
+++ b/device/nfc/nfc_peer_chromeos.cc
@@ -0,0 +1,196 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/nfc/nfc_peer_chromeos.h"
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/nfc_device_client.h"
+#include "device/nfc/nfc_ndef_record_utils_chromeos.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+using device::NfcNdefMessage;
+using device::NfcNdefRecord;
+
+namespace chromeos {
+
+namespace {
+
+typedef std::vector<dbus::ObjectPath> ObjectPathVector;
+
+} // namespace
+
+NfcPeerChromeOS::NfcPeerChromeOS(const dbus::ObjectPath& object_path)
+ : object_path_(object_path),
+ weak_ptr_factory_(this) {
+ // Create record objects for all records that were received before.
+ const ObjectPathVector& records =
+ DBusThreadManager::Get()->GetNfcRecordClient()->
+ GetRecordsForDevice(object_path_);
+ for (ObjectPathVector::const_iterator iter = records.begin();
+ iter != records.end(); ++iter) {
+ AddRecord(*iter);
+ }
+ DBusThreadManager::Get()->GetNfcRecordClient()->AddObserver(this);
+}
+
+NfcPeerChromeOS::~NfcPeerChromeOS() {
+ DBusThreadManager::Get()->GetNfcRecordClient()->RemoveObserver(this);
+ STLDeleteValues(&records_);
+}
+
+void NfcPeerChromeOS::AddObserver(device::NfcPeer::Observer* observer) {
+ DCHECK(observer);
+ observers_.AddObserver(observer);
+}
+
+void NfcPeerChromeOS::RemoveObserver(device::NfcPeer::Observer* observer) {
+ DCHECK(observer);
+ observers_.RemoveObserver(observer);
+}
+
+std::string NfcPeerChromeOS::GetIdentifier() const {
+ return object_path_.value();
+}
+
+const NfcNdefMessage& NfcPeerChromeOS::GetNdefMessage() const {
+ return message_;
+}
+
+void NfcPeerChromeOS::PushNdef(const NfcNdefMessage& message,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (message.records().empty()) {
+ LOG(ERROR) << "Given NDEF message is empty. Cannot push it.";
+ error_callback.Run();
+ return;
+ }
+ // TODO(armansito): neard currently supports pushing only one NDEF record
+ // to a remote device 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, pushing 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()->GetNfcDeviceClient()->Push(
+ object_path_,
+ attributes,
+ base::Bind(&NfcPeerChromeOS::OnPushNdef,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&NfcPeerChromeOS::OnPushNdefError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+}
+
+void NfcPeerChromeOS::StartHandover(HandoverType handover_type,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ // TODO(armansito): Initiating handover with a peer is currently not
+ // supported. For now, return an error immediately.
+ LOG(ERROR) << "NFC Handover currently not supported.";
+ error_callback.Run();
+}
+
+void NfcPeerChromeOS::RecordAdded(const dbus::ObjectPath& object_path) {
+ // Don't create the record object yet. Instead, wait until all record
+ // properties have been received and contruct 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 NfcPeerChromeOS::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 NfcPeerChromeOS::RecordPropertiesReceived(
+ const dbus::ObjectPath& object_path) {
+ 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();
+ 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"
+ << " device. Ignoring.";
+ return;
+ }
+
+ AddRecord(object_path);
+}
+
+void NfcPeerChromeOS::OnPushNdef(const base::Closure& callback) {
+ callback.Run();
+}
+
+void NfcPeerChromeOS::OnPushNdefError(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 NfcPeerChromeOS::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(NfcPeer::Observer, observers_,
+ RecordsReceived(this, record));
+}
+
+} // namespace chromeos
diff --git a/device/nfc/nfc_peer_chromeos.h b/device/nfc/nfc_peer_chromeos.h
new file mode 100644
index 0000000..2bd664a
--- /dev/null
+++ b/device/nfc/nfc_peer_chromeos.h
@@ -0,0 +1,81 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_NFC_NFC_PEER_CHROMEOS_H_
+#define DEVICE_NFC_NFC_PEER_CHROMEOS_H_
+
+#include <map>
+
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "chromeos/dbus/nfc_record_client.h"
+#include "dbus/object_path.h"
+#include "device/nfc/nfc_ndef_record.h"
+#include "device/nfc/nfc_peer.h"
+
+namespace chromeos {
+
+// The NfcPeerChromeOS class implements NfcPeer for the Chrome OS platform.
+class NfcPeerChromeOS : public device::NfcPeer,
+ public NfcRecordClient::Observer {
+ public:
+ // NfcPeer overrides.
+ virtual void AddObserver(device::NfcPeer::Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(device::NfcPeer::Observer* observer) OVERRIDE;
+ virtual std::string GetIdentifier() const OVERRIDE;
+ virtual const device::NfcNdefMessage& GetNdefMessage() const OVERRIDE;
+ virtual void PushNdef(const device::NfcNdefMessage& message,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual void StartHandover(HandoverType handover_type,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+
+ private:
+ friend class NfcAdapterChromeOS;
+
+ // Mapping from D-Bus object paths to NfcNdefRecord objects.
+ typedef std::map<dbus::ObjectPath, device::NfcNdefRecord*> NdefRecordMap;
+
+ NfcPeerChromeOS(const dbus::ObjectPath& object_path);
+ virtual ~NfcPeerChromeOS();
+
+ // 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;
+
+ // Called by dbus:: on completion of the D-Bus method call to push an NDEF.
+ void OnPushNdef(const base::Closure& callback);
+ void OnPushNdefError(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);
+
+ // Object path of the peer that we are currently tracking.
+ dbus::ObjectPath object_path_;
+
+ // A map containing the NDEF records that were received from the peer.
+ NdefRecordMap records_;
+
+ // Message instance that contains pointers to all created records.
+ device::NfcNdefMessage message_;
+
+ // List of observers interested in event notifications from us.
+ ObserverList<device::NfcPeer::Observer> observers_;
+
+ // 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<NfcPeerChromeOS> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(NfcPeerChromeOS);
+};
+
+} // namespace chromeos
+
+#endif // DEVICE_NFC_NFC_PEER_CHROMEOS_H_