1 // Copyright 2013 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_adapter_chromeos.h"
6
7 #include <vector>
8
9 #include "base/callback.h"
10 #include "base/logging.h"
11 #include "chromeos/dbus/dbus_thread_manager.h"
12 #include "device/nfc/nfc_peer_chromeos.h"
13 #include "device/nfc/nfc_tag_chromeos.h"
14 #include "third_party/cros_system_api/dbus/service_constants.h"
15
16 namespace chromeos {
17
18 namespace {
19
20 typedef std::vector<dbus::ObjectPath> ObjectPathVector;
21
22 } // namespace
23
NfcAdapterChromeOS()24 NfcAdapterChromeOS::NfcAdapterChromeOS()
25 : weak_ptr_factory_(this) {
26 DBusThreadManager::Get()->GetNfcAdapterClient()->AddObserver(this);
27 DBusThreadManager::Get()->GetNfcDeviceClient()->AddObserver(this);
28 DBusThreadManager::Get()->GetNfcTagClient()->AddObserver(this);
29
30 const ObjectPathVector& object_paths =
31 DBusThreadManager::Get()->GetNfcAdapterClient()->GetAdapters();
32 if (!object_paths.empty()) {
33 VLOG(1) << object_paths.size() << " NFC adapter(s) available.";
34 SetAdapter(object_paths[0]);
35 }
36 }
37
~NfcAdapterChromeOS()38 NfcAdapterChromeOS::~NfcAdapterChromeOS() {
39 DBusThreadManager::Get()->GetNfcAdapterClient()->RemoveObserver(this);
40 DBusThreadManager::Get()->GetNfcDeviceClient()->RemoveObserver(this);
41 DBusThreadManager::Get()->GetNfcTagClient()->RemoveObserver(this);
42 }
43
AddObserver(NfcAdapter::Observer * observer)44 void NfcAdapterChromeOS::AddObserver(NfcAdapter::Observer* observer) {
45 DCHECK(observer);
46 observers_.AddObserver(observer);
47 }
48
RemoveObserver(NfcAdapter::Observer * observer)49 void NfcAdapterChromeOS::RemoveObserver(NfcAdapter::Observer* observer) {
50 DCHECK(observer);
51 observers_.RemoveObserver(observer);
52 }
53
IsPresent() const54 bool NfcAdapterChromeOS::IsPresent() const {
55 return !object_path_.value().empty();
56 }
57
IsPowered() const58 bool NfcAdapterChromeOS::IsPowered() const {
59 if (!IsPresent())
60 return false;
61 return DBusThreadManager::Get()->GetNfcAdapterClient()->
62 GetProperties(object_path_)->powered.value();
63 }
64
IsPolling() const65 bool NfcAdapterChromeOS::IsPolling() const {
66 if (!IsPresent())
67 return false;
68 return DBusThreadManager::Get()->GetNfcAdapterClient()->
69 GetProperties(object_path_)->polling.value();
70 }
71
IsInitialized() const72 bool NfcAdapterChromeOS::IsInitialized() const {
73 return true;
74 }
75
SetPowered(bool powered,const base::Closure & callback,const ErrorCallback & error_callback)76 void NfcAdapterChromeOS::SetPowered(bool powered,
77 const base::Closure& callback,
78 const ErrorCallback& error_callback) {
79 if (!IsPresent()) {
80 LOG(WARNING) << "Adapter not present. Cannot power up the antenna.";
81 error_callback.Run();
82 return;
83 }
84 DBusThreadManager::Get()->GetNfcAdapterClient()->
85 GetProperties(object_path_)->powered.Set(
86 powered,
87 base::Bind(&NfcAdapterChromeOS::OnSetPowered,
88 weak_ptr_factory_.GetWeakPtr(),
89 callback,
90 error_callback));
91 }
92
StartPolling(const base::Closure & callback,const ErrorCallback & error_callback)93 void NfcAdapterChromeOS::StartPolling(const base::Closure& callback,
94 const ErrorCallback& error_callback) {
95 // Always poll in "Initiator" mode.
96 DBusThreadManager::Get()->GetNfcAdapterClient()->
97 StartPollLoop(object_path_,
98 nfc_adapter::kModeInitiator,
99 base::Bind(&NfcAdapterChromeOS::OnStartPolling,
100 weak_ptr_factory_.GetWeakPtr(),
101 callback),
102 base::Bind(&NfcAdapterChromeOS::OnStartPollingError,
103 weak_ptr_factory_.GetWeakPtr(),
104 error_callback));
105 }
106
StopPolling(const base::Closure & callback,const ErrorCallback & error_callback)107 void NfcAdapterChromeOS::StopPolling(const base::Closure& callback,
108 const ErrorCallback& error_callback) {
109 DBusThreadManager::Get()->GetNfcAdapterClient()->
110 StopPollLoop(object_path_,
111 base::Bind(&NfcAdapterChromeOS::OnStopPolling,
112 weak_ptr_factory_.GetWeakPtr(),
113 callback),
114 base::Bind(&NfcAdapterChromeOS::OnStopPollingError,
115 weak_ptr_factory_.GetWeakPtr(),
116 error_callback));
117 }
118
AdapterAdded(const dbus::ObjectPath & object_path)119 void NfcAdapterChromeOS::AdapterAdded(const dbus::ObjectPath& object_path) {
120 // Set the adapter to the newly added adapter only if no adapter is present.
121 if (!IsPresent())
122 SetAdapter(object_path);
123 }
124
AdapterRemoved(const dbus::ObjectPath & object_path)125 void NfcAdapterChromeOS::AdapterRemoved(const dbus::ObjectPath& object_path) {
126 if (object_path != object_path_)
127 return;
128
129 // The current adapter was removed, so mark us as not present and clean up
130 // peers and tags.
131 RemoveAdapter();
132
133 // There may still be other adapters present on the system. Set the next
134 // available adapter as the current one.
135 const ObjectPathVector& object_paths =
136 DBusThreadManager::Get()->GetNfcAdapterClient()->GetAdapters();
137 for (ObjectPathVector::const_iterator iter =
138 object_paths.begin();
139 iter != object_paths.end(); ++iter) {
140 // The removed object will still be available until the call to
141 // AdapterRemoved returns. Make sure that we are not re-adding the
142 // removed adapter.
143 if (*iter == object_path)
144 continue;
145 SetAdapter(*iter);
146 }
147 }
148
AdapterPropertyChanged(const dbus::ObjectPath & object_path,const std::string & property_name)149 void NfcAdapterChromeOS::AdapterPropertyChanged(
150 const dbus::ObjectPath& object_path,
151 const std::string& property_name) {
152 if (object_path != object_path_)
153 return;
154 NfcAdapterClient::Properties* properties =
155 DBusThreadManager::Get()->GetNfcAdapterClient()->
156 GetProperties(object_path_);
157 if (property_name == properties->powered.name())
158 PoweredChanged(properties->powered.value());
159 else if (property_name == properties->polling.name())
160 PollingChanged(properties->polling.value());
161 }
162
DeviceAdded(const dbus::ObjectPath & object_path)163 void NfcAdapterChromeOS::DeviceAdded(const dbus::ObjectPath& object_path) {
164 if (!IsPresent())
165 return;
166
167 if (GetPeer(object_path.value()))
168 return;
169
170 VLOG(1) << "NFC device found: " << object_path.value();
171
172 // Check to see if the device belongs to this adapter.
173 const ObjectPathVector& devices =
174 DBusThreadManager::Get()->GetNfcDeviceClient()->
175 GetDevicesForAdapter(object_path_);
176 bool device_found = false;
177 for (ObjectPathVector::const_iterator iter = devices.begin();
178 iter != devices.end(); ++iter) {
179 if (*iter == object_path) {
180 device_found = true;
181 break;
182 }
183 }
184 if (!device_found) {
185 VLOG(1) << "Found peer device does not belong to the current adapter.";
186 return;
187 }
188
189 // Create the peer object.
190 NfcPeerChromeOS* peer_chromeos = new NfcPeerChromeOS(object_path);
191 SetPeer(object_path.value(), peer_chromeos);
192 FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
193 PeerFound(this, peer_chromeos));
194 }
195
DeviceRemoved(const dbus::ObjectPath & object_path)196 void NfcAdapterChromeOS::DeviceRemoved(const dbus::ObjectPath& object_path) {
197 VLOG(1) << "NFC device lost: " << object_path.value();
198 device::NfcPeer* peer = RemovePeer(object_path.value());
199 if (!peer) {
200 VLOG(1) << "Removed peer device does not belong to the current adapter.";
201 return;
202 }
203 FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, PeerLost(this, peer));
204 delete peer;
205 }
206
TagAdded(const dbus::ObjectPath & object_path)207 void NfcAdapterChromeOS::TagAdded(const dbus::ObjectPath& object_path) {
208 if (!IsPresent())
209 return;
210
211 if (GetTag(object_path.value()))
212 return;
213
214 VLOG(1) << "NFC tag found: " << object_path.value();
215
216 // Check to see if the tag belongs to this adapter.
217 const std::vector<dbus::ObjectPath>& tags =
218 DBusThreadManager::Get()->GetNfcTagClient()->
219 GetTagsForAdapter(object_path_);
220 bool tag_found = false;
221 for (std::vector<dbus::ObjectPath>::const_iterator iter = tags.begin();
222 iter != tags.end(); ++iter) {
223 if (*iter == object_path) {
224 tag_found = true;
225 break;
226 }
227 }
228 if (!tag_found) {
229 VLOG(1) << "Found tag does not belong to the current adapter.";
230 return;
231 }
232
233 // Create the tag object.
234 NfcTagChromeOS* tag_chromeos = new NfcTagChromeOS(object_path);
235 SetTag(object_path.value(), tag_chromeos);
236 FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
237 TagFound(this, tag_chromeos));
238 }
239
TagRemoved(const dbus::ObjectPath & object_path)240 void NfcAdapterChromeOS::TagRemoved(const dbus::ObjectPath& object_path) {
241 VLOG(1) << "NFC tag lost : " << object_path.value();
242 device::NfcTag* tag = RemoveTag(object_path.value());
243 if (!tag) {
244 VLOG(1) << "Removed tag does not belong to the current adapter.";
245 return;
246 }
247 FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, TagLost(this, tag));
248 delete tag;
249 }
250
SetAdapter(const dbus::ObjectPath & object_path)251 void NfcAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) {
252 DCHECK(!IsPresent());
253 object_path_ = object_path;
254 VLOG(1) << "Using NFC adapter: " << object_path.value();
255
256 NfcAdapterClient::Properties* properties =
257 DBusThreadManager::Get()->GetNfcAdapterClient()->
258 GetProperties(object_path_);
259 PresentChanged(true);
260 if (properties->powered.value())
261 PoweredChanged(true);
262 if (properties->polling.value())
263 PollingChanged(true);
264
265 // Create peer objects for peers that were added before the adapter was set.
266 const ObjectPathVector& devices =
267 DBusThreadManager::Get()->GetNfcDeviceClient()->
268 GetDevicesForAdapter(object_path_);
269 for (ObjectPathVector::const_iterator iter = devices.begin();
270 iter != devices.end(); ++iter) {
271 const dbus::ObjectPath& object_path = *iter;
272 if (GetPeer(object_path.value()))
273 continue;
274 NfcPeerChromeOS* peer_chromeos = new NfcPeerChromeOS(object_path);
275 SetPeer(object_path.value(), peer_chromeos);
276 FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
277 PeerFound(this, peer_chromeos));
278 }
279
280 // Create tag objects for tags that were added before the adapter was set.
281 const std::vector<dbus::ObjectPath>& tags =
282 DBusThreadManager::Get()->GetNfcTagClient()->
283 GetTagsForAdapter(object_path_);
284 for (std::vector<dbus::ObjectPath>::const_iterator iter = tags.begin();
285 iter != tags.end(); ++iter) {
286 const dbus::ObjectPath& object_path = *iter;
287 if (GetTag(object_path.value()))
288 continue;
289 NfcTagChromeOS* tag_chromeos = new NfcTagChromeOS(object_path);
290 SetTag(object_path.value(), tag_chromeos);
291 FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
292 TagFound(this, tag_chromeos));
293 }
294 }
295
RemoveAdapter()296 void NfcAdapterChromeOS::RemoveAdapter() {
297 DCHECK(IsPresent());
298 VLOG(1) << "NFC adapter removed: " << object_path_.value();
299
300 NfcAdapterClient::Properties* properties =
301 DBusThreadManager::Get()->GetNfcAdapterClient()->
302 GetProperties(object_path_);
303 if (properties->powered.value())
304 PoweredChanged(false);
305 if (properties->polling.value())
306 PollingChanged(false);
307
308 // Copy the tags and peers here and clear the original containers so that
309 // GetPeers and GetTags return no values during the *Removed observer calls.
310 PeerList peers;
311 TagList tags;
312 GetPeers(&peers);
313 GetTags(&tags);
314 ClearPeers();
315 ClearTags();
316
317 for (PeerList::iterator iter = peers.begin();
318 iter != peers.end(); ++iter) {
319 device::NfcPeer* peer = *iter;
320 FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
321 PeerLost(this, peer));
322 delete peer;
323 }
324 for (TagList::iterator iter = tags.begin();
325 iter != tags.end(); ++iter) {
326 device::NfcTag* tag = *iter;
327 FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
328 TagLost(this, tag));
329 delete tag;
330 }
331
332 object_path_ = dbus::ObjectPath("");
333 PresentChanged(false);
334 }
335
PoweredChanged(bool powered)336 void NfcAdapterChromeOS::PoweredChanged(bool powered) {
337 FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
338 AdapterPoweredChanged(this, powered));
339 }
340
PollingChanged(bool polling)341 void NfcAdapterChromeOS::PollingChanged(bool polling) {
342 FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
343 AdapterPollingChanged(this, polling));
344 }
345
PresentChanged(bool present)346 void NfcAdapterChromeOS::PresentChanged(bool present) {
347 FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
348 AdapterPresentChanged(this, present));
349 }
350
OnSetPowered(const base::Closure & callback,const ErrorCallback & error_callback,bool success)351 void NfcAdapterChromeOS::OnSetPowered(const base::Closure& callback,
352 const ErrorCallback& error_callback,
353 bool success) {
354 VLOG(1) << "NfcAdapterChromeOS::OnSetPowered result: " << success;
355 if (success) {
356 // TODO(armansito): There is a bug in neard 0.13 that causes it not to emit
357 // a signal when the "Powered" property changes. Sync the properties here,
358 // but remove it in neard 0.14.
359 if (IsPresent()) {
360 DBusThreadManager::Get()->GetNfcAdapterClient()->
361 GetProperties(object_path_)->GetAll();
362 }
363 callback.Run();
364 } else {
365 LOG(ERROR) << "Failed to power up the NFC antenna radio.";
366 error_callback.Run();
367 }
368 }
369
OnStartPolling(const base::Closure & callback)370 void NfcAdapterChromeOS::OnStartPolling(const base::Closure& callback) {
371 callback.Run();
372 }
373
OnStartPollingError(const ErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)374 void NfcAdapterChromeOS::OnStartPollingError(
375 const ErrorCallback& error_callback,
376 const std::string& error_name,
377 const std::string& error_message) {
378 LOG(ERROR) << object_path_.value() << ": Failed to start polling: "
379 << error_name << ": " << error_message;
380 error_callback.Run();
381 }
382
OnStopPolling(const base::Closure & callback)383 void NfcAdapterChromeOS::OnStopPolling(const base::Closure& callback) {
384 callback.Run();
385 }
386
OnStopPollingError(const ErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)387 void NfcAdapterChromeOS::OnStopPollingError(
388 const ErrorCallback& error_callback,
389 const std::string& error_name,
390 const std::string& error_message) {
391 LOG(ERROR) << object_path_.value() << ": Failed to stop polling: "
392 << error_name << ": " << error_message;
393 error_callback.Run();
394 }
395
396 } // namespace chromeos
397