// 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 BluetoothConnectionFinder::CreateConnection() { return scoped_ptr(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 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