普通文本  |  397行  |  13.17 KB

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "device/nfc/nfc_adapter_chromeos.h"

#include <vector>

#include "base/callback.h"
#include "base/logging.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "device/nfc/nfc_peer_chromeos.h"
#include "device/nfc/nfc_tag_chromeos.h"
#include "third_party/cros_system_api/dbus/service_constants.h"

namespace chromeos {

namespace {

typedef std::vector<dbus::ObjectPath> ObjectPathVector;

}  // namespace

NfcAdapterChromeOS::NfcAdapterChromeOS()
    : weak_ptr_factory_(this) {
  DBusThreadManager::Get()->GetNfcAdapterClient()->AddObserver(this);
  DBusThreadManager::Get()->GetNfcDeviceClient()->AddObserver(this);
  DBusThreadManager::Get()->GetNfcTagClient()->AddObserver(this);

  const ObjectPathVector& object_paths =
      DBusThreadManager::Get()->GetNfcAdapterClient()->GetAdapters();
  if (!object_paths.empty()) {
    VLOG(1) << object_paths.size() << " NFC adapter(s) available.";
    SetAdapter(object_paths[0]);
  }
}

NfcAdapterChromeOS::~NfcAdapterChromeOS() {
  DBusThreadManager::Get()->GetNfcAdapterClient()->RemoveObserver(this);
  DBusThreadManager::Get()->GetNfcDeviceClient()->RemoveObserver(this);
  DBusThreadManager::Get()->GetNfcTagClient()->RemoveObserver(this);
}

void NfcAdapterChromeOS::AddObserver(NfcAdapter::Observer* observer) {
  DCHECK(observer);
  observers_.AddObserver(observer);
}

void NfcAdapterChromeOS::RemoveObserver(NfcAdapter::Observer* observer) {
  DCHECK(observer);
  observers_.RemoveObserver(observer);
}

bool NfcAdapterChromeOS::IsPresent() const {
  return !object_path_.value().empty();
}

bool NfcAdapterChromeOS::IsPowered() const {
  if (!IsPresent())
    return false;
  return DBusThreadManager::Get()->GetNfcAdapterClient()->
      GetProperties(object_path_)->powered.value();
}

bool NfcAdapterChromeOS::IsPolling() const {
  if (!IsPresent())
    return false;
  return DBusThreadManager::Get()->GetNfcAdapterClient()->
      GetProperties(object_path_)->polling.value();
}

bool NfcAdapterChromeOS::IsInitialized() const {
  return true;
}

void NfcAdapterChromeOS::SetPowered(bool powered,
                                    const base::Closure& callback,
                                    const ErrorCallback& error_callback) {
  if (!IsPresent()) {
    LOG(WARNING) << "Adapter not present. Cannot power up the antenna.";
    error_callback.Run();
    return;
  }
  DBusThreadManager::Get()->GetNfcAdapterClient()->
      GetProperties(object_path_)->powered.Set(
          powered,
          base::Bind(&NfcAdapterChromeOS::OnSetPowered,
                     weak_ptr_factory_.GetWeakPtr(),
                     callback,
                     error_callback));
}

void NfcAdapterChromeOS::StartPolling(const base::Closure& callback,
                                      const ErrorCallback& error_callback) {
  // Always poll in "Initiator" mode.
  DBusThreadManager::Get()->GetNfcAdapterClient()->
      StartPollLoop(object_path_,
                    nfc_adapter::kModeInitiator,
                    base::Bind(&NfcAdapterChromeOS::OnStartPolling,
                               weak_ptr_factory_.GetWeakPtr(),
                               callback),
                    base::Bind(&NfcAdapterChromeOS::OnStartPollingError,
                               weak_ptr_factory_.GetWeakPtr(),
                               error_callback));
}

void NfcAdapterChromeOS::StopPolling(const base::Closure& callback,
                                     const ErrorCallback& error_callback) {
  DBusThreadManager::Get()->GetNfcAdapterClient()->
      StopPollLoop(object_path_,
                   base::Bind(&NfcAdapterChromeOS::OnStopPolling,
                              weak_ptr_factory_.GetWeakPtr(),
                              callback),
                   base::Bind(&NfcAdapterChromeOS::OnStopPollingError,
                              weak_ptr_factory_.GetWeakPtr(),
                              error_callback));
}

void NfcAdapterChromeOS::AdapterAdded(const dbus::ObjectPath& object_path) {
  // Set the adapter to the newly added adapter only if no adapter is present.
  if (!IsPresent())
    SetAdapter(object_path);
}

void NfcAdapterChromeOS::AdapterRemoved(const dbus::ObjectPath& object_path) {
  if (object_path != object_path_)
    return;

  // The current adapter was removed, so mark us as not present and clean up
  // peers and tags.
  RemoveAdapter();

  // There may still be other adapters present on the system. Set the next
  // available adapter as the current one.
  const ObjectPathVector& object_paths =
      DBusThreadManager::Get()->GetNfcAdapterClient()->GetAdapters();
  for (ObjectPathVector::const_iterator iter =
          object_paths.begin();
       iter != object_paths.end(); ++iter) {
    // The removed object will still be available until the call to
    // AdapterRemoved returns. Make sure that we are not re-adding the
    // removed adapter.
    if (*iter == object_path)
      continue;
    SetAdapter(*iter);
  }
}

void NfcAdapterChromeOS::AdapterPropertyChanged(
    const dbus::ObjectPath& object_path,
    const std::string& property_name) {
  if (object_path != object_path_)
    return;
  NfcAdapterClient::Properties* properties =
      DBusThreadManager::Get()->GetNfcAdapterClient()->
          GetProperties(object_path_);
  if (property_name == properties->powered.name())
    PoweredChanged(properties->powered.value());
  else if (property_name == properties->polling.name())
    PollingChanged(properties->polling.value());
}

void NfcAdapterChromeOS::DeviceAdded(const dbus::ObjectPath& object_path) {
  if (!IsPresent())
    return;

  if (GetPeer(object_path.value()))
    return;

  VLOG(1) << "NFC device found: " << object_path.value();

  // Check to see if the device belongs to this adapter.
  const ObjectPathVector& devices =
      DBusThreadManager::Get()->GetNfcDeviceClient()->
          GetDevicesForAdapter(object_path_);
  bool device_found = false;
  for (ObjectPathVector::const_iterator iter = devices.begin();
       iter != devices.end(); ++iter) {
    if (*iter == object_path) {
      device_found = true;
      break;
    }
  }
  if (!device_found) {
    VLOG(1) << "Found peer device does not belong to the current adapter.";
    return;
  }

  // Create the peer object.
  NfcPeerChromeOS* peer_chromeos = new NfcPeerChromeOS(object_path);
  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();
  device::NfcPeer* peer = RemovePeer(object_path.value());
  if (!peer) {
    VLOG(1) << "Removed peer device does not belong to the current adapter.";
    return;
  }
  FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, PeerLost(this, peer));
  delete peer;
}

void NfcAdapterChromeOS::TagAdded(const dbus::ObjectPath& object_path) {
  if (!IsPresent())
    return;

  if (GetTag(object_path.value()))
    return;

  VLOG(1) << "NFC tag found: " << object_path.value();

  // 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::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) {
  DCHECK(!IsPresent());
  object_path_ = object_path;
  VLOG(1) << "Using NFC adapter: " << object_path.value();

  NfcAdapterClient::Properties* properties =
      DBusThreadManager::Get()->GetNfcAdapterClient()->
          GetProperties(object_path_);
  PresentChanged(true);
  if (properties->powered.value())
    PoweredChanged(true);
  if (properties->polling.value())
    PollingChanged(true);

  // Create peer objects for peers that were added before the adapter was set.
  const ObjectPathVector& devices =
      DBusThreadManager::Get()->GetNfcDeviceClient()->
          GetDevicesForAdapter(object_path_);
  for (ObjectPathVector::const_iterator iter = devices.begin();
       iter != devices.end(); ++iter) {
    const dbus::ObjectPath& object_path = *iter;
    if (GetPeer(object_path.value()))
      continue;
    NfcPeerChromeOS* peer_chromeos = new NfcPeerChromeOS(object_path);
    SetPeer(object_path.value(), peer_chromeos);
    FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
                      PeerFound(this, peer_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() {
  DCHECK(IsPresent());
  VLOG(1) << "NFC adapter removed: " << object_path_.value();

  NfcAdapterClient::Properties* properties =
      DBusThreadManager::Get()->GetNfcAdapterClient()->
          GetProperties(object_path_);
  if (properties->powered.value())
    PoweredChanged(false);
  if (properties->polling.value())
    PollingChanged(false);

  // Copy the tags and peers here and clear the original containers so that
  // GetPeers and GetTags return no values during the *Removed observer calls.
  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, peer));
    delete peer;
  }
  for (TagList::iterator iter = tags.begin();
       iter != tags.end(); ++iter) {
    device::NfcTag* tag = *iter;
    FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
                      TagLost(this, tag));
    delete tag;
  }

  object_path_ = dbus::ObjectPath("");
  PresentChanged(false);
}

void NfcAdapterChromeOS::PoweredChanged(bool powered) {
  FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
                    AdapterPoweredChanged(this, powered));
}

void NfcAdapterChromeOS::PollingChanged(bool polling) {
  FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
                    AdapterPollingChanged(this, polling));
}

void NfcAdapterChromeOS::PresentChanged(bool present) {
  FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
                    AdapterPresentChanged(this, present));
}

void NfcAdapterChromeOS::OnSetPowered(const base::Closure& callback,
                                      const ErrorCallback& error_callback,
                                      bool success) {
  VLOG(1) << "NfcAdapterChromeOS::OnSetPowered result: " << success;
  if (success) {
    // TODO(armansito): There is a bug in neard 0.13 that causes it not to emit
    // a signal when the "Powered" property changes. Sync the properties here,
    // but remove it in neard 0.14.
    if (IsPresent()) {
      DBusThreadManager::Get()->GetNfcAdapterClient()->
          GetProperties(object_path_)->GetAll();
    }
    callback.Run();
  } else {
    LOG(ERROR) << "Failed to power up the NFC antenna radio.";
    error_callback.Run();
  }
}

void NfcAdapterChromeOS::OnStartPolling(const base::Closure& callback) {
  callback.Run();
}

void NfcAdapterChromeOS::OnStartPollingError(
    const ErrorCallback& error_callback,
    const std::string& error_name,
    const std::string& error_message) {
  LOG(ERROR) << object_path_.value() << ": Failed to start polling: "
             << error_name << ": " << error_message;
  error_callback.Run();
}

void NfcAdapterChromeOS::OnStopPolling(const base::Closure& callback) {
  callback.Run();
}

void NfcAdapterChromeOS::OnStopPollingError(
    const ErrorCallback& error_callback,
    const std::string& error_name,
    const std::string& error_message) {
  LOG(ERROR) << object_path_.value() << ": Failed to stop polling: "
             << error_name << ": " << error_message;
  error_callback.Run();
}

}  // namespace chromeos