1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
// 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 <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/cryptauth/connection.h"
#include "components/proximity_auth/bluetooth_connection.h"
#include "components/proximity_auth/logging/logging.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
using device::BluetoothAdapter;
namespace proximity_auth {
BluetoothConnectionFinder::BluetoothConnectionFinder(
const cryptauth::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 cryptauth::ConnectionFinder::ConnectionCallback&
connection_callback) {
if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
PA_LOG(WARNING) << "Bluetooth is unsupported on this platform. Aborting.";
return;
}
DCHECK(start_time_.is_null());
start_time_ = base::TimeTicks::Now();
connection_callback_ = connection_callback;
device::BluetoothAdapterFactory::GetAdapter(
base::Bind(&BluetoothConnectionFinder::OnAdapterInitialized,
weak_ptr_factory_.GetWeakPtr()));
}
std::unique_ptr<cryptauth::Connection>
BluetoothConnectionFinder::CreateConnection() {
return std::unique_ptr<cryptauth::Connection>(
new BluetoothConnection(remote_device_, uuid_));
}
void BluetoothConnectionFinder::SeekDeviceByAddress(
const std::string& bluetooth_address,
const base::Closure& callback,
const bluetooth_util::ErrorCallback& error_callback) {
bluetooth_util::SeekDeviceByAddress(
bluetooth_address, callback, error_callback,
base::ThreadTaskRunnerHandle::Get().get());
}
bool BluetoothConnectionFinder::IsReadyToPoll() {
bool is_adapter_available =
adapter_.get() && adapter_->IsPresent() && adapter_->IsPowered();
PA_LOG(INFO) << "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_| is pending, wait for it to connect or fail prior to
// polling again.
if (connection_ &&
connection_->status() != cryptauth::Connection::DISCONNECTED)
return;
// This SeekDeviceByAddress operation is needed to connect to a device if
// it is not already known to the adapter.
if (!adapter_->GetDevice(remote_device_.bluetooth_address)) {
PA_LOG(INFO) << "Remote device [" << remote_device_.bluetooth_address
<< "] is not known. "
<< "Seeking device directly by address...";
SeekDeviceByAddress(
remote_device_.bluetooth_address,
base::Bind(&BluetoothConnectionFinder::OnSeekedDeviceByAddress,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothConnectionFinder::OnSeekedDeviceByAddressError,
weak_ptr_factory_.GetWeakPtr()));
} else {
PA_LOG(INFO) << "Remote device known, connecting...";
connection_ = CreateConnection();
connection_->AddObserver(this);
connection_->Connect();
}
}
void BluetoothConnectionFinder::PostDelayedPoll() {
if (has_delayed_poll_scheduled_) {
PA_LOG(WARNING) << "Delayed poll already scheduled, skipping.";
return;
}
PA_LOG(INFO) << "Posting delayed poll..";
has_delayed_poll_scheduled_ = true;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::Bind(&BluetoothConnectionFinder::OnDelayedPoll,
weak_ptr_factory_.GetWeakPtr()),
polling_interval_);
}
void BluetoothConnectionFinder::OnDelayedPoll() {
// Note that there is no longer a pending task, and therefore polling is
// permitted.
has_delayed_poll_scheduled_ = false;
PollIfReady();
}
void BluetoothConnectionFinder::OnSeekedDeviceByAddress() {
// Sanity check that the remote device is now known by the adapter.
if (adapter_->GetDevice(remote_device_.bluetooth_address))
PollIfReady();
else
PostDelayedPoll();
}
void BluetoothConnectionFinder::OnSeekedDeviceByAddressError(
const std::string& error_message) {
PA_LOG(ERROR) << "Failed to seek device: " << error_message;
PostDelayedPoll();
}
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(
cryptauth::Connection* connection,
cryptauth::Connection::Status old_status,
cryptauth::Connection::Status new_status) {
DCHECK_EQ(connection, connection_.get());
if (connection_->IsConnected()) {
base::TimeDelta elapsed = base::TimeTicks::Now() - start_time_;
PA_LOG(WARNING) << "Connection found! Elapsed Time: "
<< elapsed.InMilliseconds() << "ms.";
UnregisterAsObserver();
// If we invoke the callback now, the callback function may install its own
// observer to |connection_|. Because we are in the
// cryptauth::ConnectionObserver
// callstack, this new observer will receive this connection event.
// Therefore, we need to invoke the callback asynchronously.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&BluetoothConnectionFinder::InvokeCallbackAsync,
weak_ptr_factory_.GetWeakPtr()));
} else if (old_status == cryptauth::Connection::IN_PROGRESS) {
PA_LOG(WARNING)
<< "Connection failed! Scheduling another polling iteration.";
PostDelayedPoll();
}
}
void BluetoothConnectionFinder::InvokeCallbackAsync() {
connection_callback_.Run(std::move(connection_));
}
} // namespace proximity_auth
|