summaryrefslogtreecommitdiffstats
path: root/components/proximity_auth/bluetooth_connection_finder.cc
blob: 3ab43a4ad6ce34430fe72109cb8a1b39e09180ed (plain)
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
// 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/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.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(
    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::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
        FROM_HERE, base::Bind(&BluetoothConnectionFinder::DelayedPollIfReady,
                              weak_ptr_factory_.GetWeakPtr()),
        polling_interval_);
  }
}

}  // namespace proximity_auth