blob: 3fe6567c174a0e76dc338ba8c5da75c20d4e7808 [file] [log] [blame]
// 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 "components/proximity_auth/bluetooth_connection_finder.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
#include "components/proximity_auth/bluetooth_connection.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
using device::BluetoothAdapter;
namespace proximity_auth {
BluetoothConnectionFinder::BluetoothConnectionFinder(
const RemoteDevice& remote_device,
const device::BluetoothUUID& uuid,
const base::TimeDelta& polling_interval)
: remote_device_(remote_device),
uuid_(uuid),
polling_interval_(polling_interval),
has_delayed_poll_scheduled_(false),
weak_ptr_factory_(this) {
}
BluetoothConnectionFinder::~BluetoothConnectionFinder() {
UnregisterAsObserver();
}
void BluetoothConnectionFinder::Find(
const ConnectionCallback& connection_callback) {
if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
VLOG(1) << "[BCF] Bluetooth is unsupported on this platform. Aborting.";
return;
}
DCHECK(start_time_.is_null());
VLOG(1) << "[BCF] Finding Bluetooth connection...";
start_time_ = base::TimeTicks::Now();
connection_callback_ = connection_callback;
device::BluetoothAdapterFactory::GetAdapter(
base::Bind(&BluetoothConnectionFinder::OnAdapterInitialized,
weak_ptr_factory_.GetWeakPtr()));
}
scoped_ptr<Connection> BluetoothConnectionFinder::CreateConnection() {
return scoped_ptr<Connection>(new BluetoothConnection(remote_device_, uuid_));
}
bool BluetoothConnectionFinder::IsReadyToPoll() {
bool is_adapter_available =
adapter_.get() && adapter_->IsPresent() && adapter_->IsPowered();
VLOG(1) << "[BCF] Readiness: adapter="
<< (is_adapter_available ? "available" : "unavailable");
return is_adapter_available;
}
void BluetoothConnectionFinder::PollIfReady() {
if (!IsReadyToPoll())
return;
// If there is a pending task to poll at a later time, the time requisite
// timeout has not yet elapsed since the previous polling attempt. In that
// case, keep waiting until the delayed task comes in.
if (has_delayed_poll_scheduled_)
return;
// If the |connection_| exists, wait for it to connect or fail prior to
// polling again.
if (connection_)
return;
VLOG(1) << "[BCF] Polling for connection...";
connection_ = CreateConnection();
connection_->AddObserver(this);
connection_->Connect();
}
void BluetoothConnectionFinder::DelayedPollIfReady() {
// Note that there is no longer a pending task, and therefore polling is
// permitted.
has_delayed_poll_scheduled_ = false;
PollIfReady();
}
void BluetoothConnectionFinder::UnregisterAsObserver() {
if (connection_) {
connection_->RemoveObserver(this);
// The connection is about to be released or destroyed, so no need to clear
// it explicitly here.
}
if (adapter_.get()) {
adapter_->RemoveObserver(this);
adapter_ = NULL;
}
}
void BluetoothConnectionFinder::OnAdapterInitialized(
scoped_refptr<BluetoothAdapter> adapter) {
adapter_ = adapter;
adapter_->AddObserver(this);
PollIfReady();
}
void BluetoothConnectionFinder::AdapterPresentChanged(BluetoothAdapter* adapter,
bool present) {
PollIfReady();
}
void BluetoothConnectionFinder::AdapterPoweredChanged(BluetoothAdapter* adapter,
bool powered) {
PollIfReady();
}
void BluetoothConnectionFinder::OnConnectionStatusChanged(
const Connection& connection,
Connection::Status old_status,
Connection::Status new_status) {
DCHECK_EQ(&connection, connection_.get());
if (connection_->IsConnected()) {
base::TimeDelta elapsed = base::TimeTicks::Now() - start_time_;
VLOG(1) << "[BCF] Connection found! Elapsed Time: "
<< elapsed.InMilliseconds() << "ms.";
UnregisterAsObserver();
connection_callback_.Run(connection_.Pass());
} else if (old_status == Connection::IN_PROGRESS) {
VLOG(1) << "[BCF] Connection failed! Scheduling another polling iteration.";
connection_.reset();
has_delayed_poll_scheduled_ = true;
base::MessageLoopProxy::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&BluetoothConnectionFinder::DelayedPollIfReady,
weak_ptr_factory_.GetWeakPtr()),
polling_interval_);
}
}
} // namespace proximity_auth