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
16namespace chromeos {
17
18namespace {
19
20typedef std::vector<dbus::ObjectPath> ObjectPathVector;
21
22}  // namespace
23
24NfcAdapterChromeOS::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
38NfcAdapterChromeOS::~NfcAdapterChromeOS() {
39  DBusThreadManager::Get()->GetNfcAdapterClient()->RemoveObserver(this);
40  DBusThreadManager::Get()->GetNfcDeviceClient()->RemoveObserver(this);
41  DBusThreadManager::Get()->GetNfcTagClient()->RemoveObserver(this);
42}
43
44void NfcAdapterChromeOS::AddObserver(NfcAdapter::Observer* observer) {
45  DCHECK(observer);
46  observers_.AddObserver(observer);
47}
48
49void NfcAdapterChromeOS::RemoveObserver(NfcAdapter::Observer* observer) {
50  DCHECK(observer);
51  observers_.RemoveObserver(observer);
52}
53
54bool NfcAdapterChromeOS::IsPresent() const {
55  return !object_path_.value().empty();
56}
57
58bool NfcAdapterChromeOS::IsPowered() const {
59  if (!IsPresent())
60    return false;
61  return DBusThreadManager::Get()->GetNfcAdapterClient()->
62      GetProperties(object_path_)->powered.value();
63}
64
65bool NfcAdapterChromeOS::IsPolling() const {
66  if (!IsPresent())
67    return false;
68  return DBusThreadManager::Get()->GetNfcAdapterClient()->
69      GetProperties(object_path_)->polling.value();
70}
71
72bool NfcAdapterChromeOS::IsInitialized() const {
73  return true;
74}
75
76void 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
93void 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
107void 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
119void 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
125void 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
149void 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
163void 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
196void 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
207void 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
240void 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
251void 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
296void 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
336void NfcAdapterChromeOS::PoweredChanged(bool powered) {
337  FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
338                    AdapterPoweredChanged(this, powered));
339}
340
341void NfcAdapterChromeOS::PollingChanged(bool polling) {
342  FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
343                    AdapterPollingChanged(this, polling));
344}
345
346void NfcAdapterChromeOS::PresentChanged(bool present) {
347  FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
348                    AdapterPresentChanged(this, present));
349}
350
351void 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
370void NfcAdapterChromeOS::OnStartPolling(const base::Closure& callback) {
371  callback.Run();
372}
373
374void 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
383void NfcAdapterChromeOS::OnStopPolling(const base::Closure& callback) {
384  callback.Run();
385}
386
387void 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