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