1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "device/nfc/nfc_tag_technology_chromeos.h"
6
7#include "base/stl_util.h"
8#include "chromeos/dbus/dbus_thread_manager.h"
9#include "device/nfc/nfc_ndef_record_utils_chromeos.h"
10#include "device/nfc/nfc_tag_chromeos.h"
11
12using device::NfcNdefMessage;
13using device::NfcNdefRecord;
14
15namespace chromeos {
16
17namespace {
18
19typedef std::vector<dbus::ObjectPath> ObjectPathVector;
20
21}  // namespace
22
23NfcNdefTagTechnologyChromeOS::NfcNdefTagTechnologyChromeOS(NfcTagChromeOS* tag)
24    : NfcNdefTagTechnology(tag),
25      object_path_(tag->object_path()),
26      weak_ptr_factory_(this) {
27  DCHECK(tag);
28  // Create record objects for all records that were received before.
29  const ObjectPathVector& records =
30      DBusThreadManager::Get()->GetNfcRecordClient()->
31          GetRecordsForTag(object_path_);
32  for (ObjectPathVector::const_iterator iter = records.begin();
33       iter != records.end(); ++iter) {
34    AddRecord(*iter);
35  }
36  DBusThreadManager::Get()->GetNfcRecordClient()->AddObserver(this);
37}
38
39NfcNdefTagTechnologyChromeOS::~NfcNdefTagTechnologyChromeOS() {
40  DBusThreadManager::Get()->GetNfcRecordClient()->RemoveObserver(this);
41  STLDeleteValues(&records_);
42}
43
44void NfcNdefTagTechnologyChromeOS::AddObserver(
45    NfcNdefTagTechnology::Observer* observer) {
46  observers_.AddObserver(observer);
47}
48
49void NfcNdefTagTechnologyChromeOS::RemoveObserver(
50    NfcNdefTagTechnology::Observer* observer) {
51  observers_.RemoveObserver(observer);
52}
53
54const NfcNdefMessage& NfcNdefTagTechnologyChromeOS::GetNdefMessage() const {
55  return message_;
56}
57
58void NfcNdefTagTechnologyChromeOS::WriteNdef(
59    const device::NfcNdefMessage& message,
60    const base::Closure& callback,
61    const ErrorCallback& error_callback) {
62  if (message.records().empty()) {
63    LOG(ERROR) << "Given NDEF message is empty. Cannot write it.";
64    error_callback.Run();
65    return;
66  }
67  if (!tag()->IsReady()) {
68    LOG(ERROR) << "The tag is not ready yet: " << tag()->GetIdentifier();
69    error_callback.Run();
70    return;
71  }
72  // TODO(armansito): neard currently supports writing only one NDEF record
73  // to a tag and won't support multiple records until 0.15. Until then, report
74  // failure if |message| contains more than one record.
75  if (message.records().size() > 1) {
76    LOG(ERROR) << "Currently, writing only 1 NDEF record is supported.";
77    error_callback.Run();
78    return;
79  }
80  const NfcNdefRecord* record = message.records()[0];
81  base::DictionaryValue attributes;
82  if (!nfc_ndef_record_utils::NfcNdefRecordToDBusAttributes(
83          record, &attributes)) {
84    LOG(ERROR) << "Failed to extract NDEF record fields for NDEF push.";
85    error_callback.Run();
86    return;
87  }
88  DBusThreadManager::Get()->GetNfcTagClient()->Write(
89      object_path_,
90      attributes,
91      base::Bind(&NfcNdefTagTechnologyChromeOS::OnWriteNdefMessage,
92                 weak_ptr_factory_.GetWeakPtr(), callback),
93      base::Bind(&NfcNdefTagTechnologyChromeOS::OnWriteNdefMessageError,
94                 weak_ptr_factory_.GetWeakPtr(), error_callback));
95}
96
97void NfcNdefTagTechnologyChromeOS::RecordAdded(
98    const dbus::ObjectPath& object_path) {
99  // Don't create the record object yet. Instead, wait until all record
100  // properties have been received and construct the object and notify observers
101  // then.
102  VLOG(1) << "Record added: " << object_path.value() << ". Waiting until "
103          "all properties have been fetched to create record object.";
104}
105
106void NfcNdefTagTechnologyChromeOS::RecordRemoved(
107    const dbus::ObjectPath& object_path) {
108  NdefRecordMap::iterator iter = records_.find(object_path);
109  if (iter == records_.end())
110    return;
111  VLOG(1) << "Lost remote NDEF record object: " << object_path.value()
112          << ", removing record.";
113  NfcNdefRecord* record = iter->second;
114  message_.RemoveRecord(record);
115  delete record;
116  records_.erase(iter);
117}
118
119void NfcNdefTagTechnologyChromeOS::RecordPropertiesReceived(
120    const dbus::ObjectPath& object_path) {
121  VLOG(1) << "Record properties received for: " << object_path.value();
122
123  // Check if the found record belongs to this tag.
124  bool record_found = false;
125  const ObjectPathVector& records =
126      DBusThreadManager::Get()->GetNfcRecordClient()->
127          GetRecordsForTag(object_path_);
128  for (ObjectPathVector::const_iterator iter = records.begin();
129       iter != records.end(); ++iter) {
130    if (*iter == object_path) {
131      record_found = true;
132      break;
133    }
134  }
135  if (!record_found) {
136    VLOG(1) << "Record \"" << object_path.value() << "\" doesn't belong to this"
137            << " tag. Ignoring.";
138    return;
139  }
140  AddRecord(object_path);
141}
142
143void NfcNdefTagTechnologyChromeOS::OnWriteNdefMessage(
144    const base::Closure& callback) {
145  callback.Run();
146}
147
148void NfcNdefTagTechnologyChromeOS::OnWriteNdefMessageError(
149    const ErrorCallback& error_callback,
150    const std::string& error_name,
151    const std::string& error_message) {
152  LOG(ERROR) << object_path_.value() << ": Failed to Push NDEF message: "
153             << error_name << ": " << error_message;
154  error_callback.Run();
155}
156
157void NfcNdefTagTechnologyChromeOS::AddRecord(
158    const dbus::ObjectPath& object_path) {
159  // Ignore this call if an entry for this record already exists.
160  if (records_.find(object_path) != records_.end()) {
161    VLOG(1) << "Record object for remote \"" << object_path.value()
162            << "\" already exists.";
163    return;
164  }
165
166  NfcRecordClient::Properties* record_properties =
167      DBusThreadManager::Get()->GetNfcRecordClient()->
168          GetProperties(object_path);
169  DCHECK(record_properties);
170
171  NfcNdefRecord* record = new NfcNdefRecord();
172  if (!nfc_ndef_record_utils::RecordPropertiesToNfcNdefRecord(
173          record_properties, record)) {
174    LOG(ERROR) << "Failed to create record object for record with object "
175               << "path \"" << object_path.value() << "\"";
176    delete record;
177    return;
178  }
179
180  message_.AddRecord(record);
181  records_[object_path] = record;
182  FOR_EACH_OBSERVER(NfcNdefTagTechnology::Observer, observers_,
183                    RecordReceived(tag(), record));
184}
185
186}  // namespace chromeos
187