Commit 83d08a29 authored by isherman's avatar isherman Committed by Commit bot

[Easy Unlock] Port the BluetoothConnection class to native code.

BUG=413488
TEST=components_unittests
R=tengs@chromium.org

Review URL: https://codereview.chromium.org/585743004

Cr-Commit-Position: refs/heads/master@{#296872}
parent e9453ea6
......@@ -655,6 +655,7 @@
'copresence/rpc/http_post_unittest.cc',
'copresence/rpc/rpc_handler_unittest.cc',
'copresence/timed_map_unittest.cc',
'proximity_auth/bluetooth_connection_unittest.cc',
'proximity_auth/connection_unittest.cc',
'proximity_auth/proximity_auth_system_unittest.cc',
'proximity_auth/wire_message_unittest.cc',
......@@ -666,6 +667,7 @@
# Dependencies of proxmity_auth
'components.gyp:proximity_auth',
'../device/bluetooth/bluetooth.gyp:device_bluetooth_mocks',
],
}],
['chromeos==1', {
......
......@@ -16,6 +16,8 @@
'../net/net.gyp:net',
],
'sources': [
"proximity_auth/bluetooth_connection.cc",
"proximity_auth/bluetooth_connection.h",
"proximity_auth/bluetooth_util.cc",
"proximity_auth/bluetooth_util_chromeos.cc",
"proximity_auth/bluetooth_util.h",
......
......@@ -4,6 +4,8 @@
static_library("proximity_auth") {
sources = [
"bluetooth_connection.cc",
"bluetooth_connection.h",
"bluetooth_util.cc",
"bluetooth_util_chromeos.cc",
"bluetooth_util.h",
......@@ -27,6 +29,7 @@ static_library("proximity_auth") {
source_set("unit_tests") {
testonly = true
sources = [
"bluetooth_connection_unittest.cc",
"connection_unittest.cc",
"proximity_auth_system_unittest.cc",
"wire_message_unittest.cc",
......@@ -35,6 +38,7 @@ source_set("unit_tests") {
deps = [
":proximity_auth",
"//base/test:test_support",
"//device/bluetooth:mocks",
"//testing/gmock",
"//testing/gtest",
]
......
// 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.h"
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/numerics/safe_conversions.h"
#include "components/proximity_auth/bluetooth_util.h"
#include "components/proximity_auth/remote_device.h"
#include "components/proximity_auth/wire_message.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device.h"
#include "net/base/io_buffer.h"
namespace proximity_auth {
namespace {
const int kReceiveBufferSizeBytes = 1024;
}
BluetoothConnection::BluetoothConnection(const RemoteDevice& remote_device,
const device::BluetoothUUID& uuid)
: Connection(remote_device), uuid_(uuid), weak_ptr_factory_(this) {
}
BluetoothConnection::~BluetoothConnection() {
Disconnect();
}
void BluetoothConnection::Connect() {
if (status() != DISCONNECTED) {
VLOG(1)
<< "[BC] Ignoring attempt to connect a non-disconnected connection.";
return;
}
if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
VLOG(1)
<< "[BC] Connection failed: Bluetooth is unsupported on this platform.";
return;
}
SetStatus(IN_PROGRESS);
device::BluetoothAdapterFactory::GetAdapter(
base::Bind(&BluetoothConnection::OnAdapterInitialized,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothConnection::Disconnect() {
if (status() == DISCONNECTED) {
VLOG(1)
<< "[BC] Ignoring attempt to disconnect a non-connected connection.";
return;
}
// Set status as disconnected now, rather than after the socket closes, so
// this connection is not reused.
SetStatus(DISCONNECTED);
if (socket_.get()) {
socket_->Disconnect(base::Bind(&base::DoNothing));
socket_ = NULL;
}
if (adapter_.get()) {
adapter_->RemoveObserver(this);
adapter_ = NULL;
}
}
void BluetoothConnection::SendMessageImpl(scoped_ptr<WireMessage> message) {
DCHECK_EQ(status(), CONNECTED);
// Serialize the message.
std::string serialized_message = message->Serialize();
int message_length = base::checked_cast<int>(serialized_message.size());
scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(message_length);
memcpy(buffer->data(), serialized_message.c_str(), message_length);
// Send it.
pending_message_ = message.Pass();
base::WeakPtr<BluetoothConnection> weak_this = weak_ptr_factory_.GetWeakPtr();
socket_->Send(buffer,
message_length,
base::Bind(&BluetoothConnection::OnSend, weak_this),
base::Bind(&BluetoothConnection::OnSendError, weak_this));
}
void BluetoothConnection::DeviceRemoved(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) {
DCHECK_EQ(adapter, adapter_.get());
if (device->GetAddress() != remote_device().bluetooth_address)
return;
DCHECK_NE(status(), DISCONNECTED);
VLOG(1) << "[BC] Device disconnected...";
Disconnect();
}
void BluetoothConnection::ConnectToService(
device::BluetoothDevice* device,
const device::BluetoothUUID& uuid,
const device::BluetoothDevice::ConnectToServiceCallback& callback,
const device::BluetoothDevice::ConnectToServiceErrorCallback&
error_callback) {
bluetooth_util::ConnectToServiceInsecurely(
device, uuid, callback, error_callback);
}
void BluetoothConnection::StartReceive() {
base::WeakPtr<BluetoothConnection> weak_this = weak_ptr_factory_.GetWeakPtr();
socket_->Receive(kReceiveBufferSizeBytes,
base::Bind(&BluetoothConnection::OnReceive, weak_this),
base::Bind(&BluetoothConnection::OnReceiveError, weak_this));
}
void BluetoothConnection::OnAdapterInitialized(
scoped_refptr<device::BluetoothAdapter> adapter) {
const std::string address = remote_device().bluetooth_address;
device::BluetoothDevice* bluetooth_device = adapter->GetDevice(address);
if (!bluetooth_device) {
VLOG(1) << "[BC] Device with address " << address
<< " is not known to the system Bluetooth daemon.";
Disconnect();
return;
}
adapter_ = adapter;
adapter_->AddObserver(this);
base::WeakPtr<BluetoothConnection> weak_this = weak_ptr_factory_.GetWeakPtr();
ConnectToService(
bluetooth_device,
uuid_,
base::Bind(&BluetoothConnection::OnConnected, weak_this),
base::Bind(&BluetoothConnection::OnConnectionError, weak_this));
}
void BluetoothConnection::OnConnected(
scoped_refptr<device::BluetoothSocket> socket) {
if (status() != IN_PROGRESS) {
// This case is reachable if the client of |this| connection called
// |Disconnect()| while the backing Bluetooth connection was pending.
DCHECK_EQ(status(), DISCONNECTED);
VLOG(1) << "[BC] Ignoring successful backend Bluetooth connection to an "
<< "already disconnected logical connection.";
return;
}
VLOG(1) << "[BC] Connection established with "
<< remote_device().bluetooth_address;
socket_ = socket;
SetStatus(CONNECTED);
StartReceive();
}
void BluetoothConnection::OnConnectionError(const std::string& error_message) {
VLOG(1) << "[BC] Connection failed: " << error_message;
Disconnect();
}
void BluetoothConnection::OnSend(int bytes_sent) {
VLOG(1) << "[BC] Successfully sent " << bytes_sent << " bytes.";
OnDidSendMessage(*pending_message_, true);
pending_message_.reset();
}
void BluetoothConnection::OnSendError(const std::string& error_message) {
VLOG(1) << "[BC] Error when sending bytes: " << error_message;
OnDidSendMessage(*pending_message_, false);
pending_message_.reset();
Disconnect();
}
void BluetoothConnection::OnReceive(int bytes_received,
scoped_refptr<net::IOBuffer> buffer) {
VLOG(1) << "[BC] Received " << bytes_received << " bytes.";
OnBytesReceived(std::string(buffer->data(), bytes_received));
// Post a task to delay the read until the socket is available, as
// calling StartReceive at this point would error with ERR_IO_PENDING.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&BluetoothConnection::StartReceive,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothConnection::OnReceiveError(
device::BluetoothSocket::ErrorReason error_reason,
const std::string& error_message) {
VLOG(1) << "[BC] Error receiving bytes: " << error_message;
// Post a task to delay the read until the socket is available, as
// calling StartReceive at this point would error with ERR_IO_PENDING.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&BluetoothConnection::StartReceive,
weak_ptr_factory_.GetWeakPtr()));
}
} // namespace proximity_auth
// 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.
#ifndef COMPONENTS_PROXIMITY_AUTH_BLUETOOTH_CONNECTION_H
#define COMPONENTS_PROXIMITY_AUTH_BLUETOOTH_CONNECTION_H
#include <string>
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "components/proximity_auth/connection.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_socket.h"
#include "device/bluetooth/bluetooth_uuid.h"
namespace net {
class IOBuffer;
}
namespace proximity_auth {
struct RemoteDevice;
// Represents a Bluetooth connection with a remote device. The connection is a
// persistent bidirectional channel for sending and receiving wire messages.
class BluetoothConnection : public Connection,
public device::BluetoothAdapter::Observer {
public:
// Constructs a Bluetooth connection to the service with |uuid| on the
// |remote_device|. The |remote_device| must already be known to the system
// Bluetooth daemon.
BluetoothConnection(const RemoteDevice& remote_device,
const device::BluetoothUUID& uuid);
virtual ~BluetoothConnection();
protected:
// Connection:
virtual void Connect() OVERRIDE;
virtual void Disconnect() OVERRIDE;
virtual void SendMessageImpl(scoped_ptr<WireMessage> message) OVERRIDE;
// BluetoothAdapter::Observer:
virtual void DeviceRemoved(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) OVERRIDE;
// Exposed for testing.
virtual void ConnectToService(
device::BluetoothDevice* device,
const device::BluetoothUUID& uuid,
const device::BluetoothDevice::ConnectToServiceCallback& callback,
const device::BluetoothDevice::ConnectToServiceErrorCallback&
error_callback);
private:
// Registers receive callbacks with the backing |socket_|.
void StartReceive();
// Callbacks for asynchronous Bluetooth operations.
void OnAdapterInitialized(scoped_refptr<device::BluetoothAdapter> adapter);
void OnConnected(scoped_refptr<device::BluetoothSocket> socket);
void OnConnectionError(const std::string& error_message);
void OnSend(int bytes_sent);
void OnSendError(const std::string& error_message);
void OnReceive(int bytes_received, scoped_refptr<net::IOBuffer> buffer);
void OnReceiveError(device::BluetoothSocket::ErrorReason error_reason,
const std::string& error_message);
// The UUID (universally unique identifier) of the Bluetooth service on the
// remote device that |this| connection should connect to.
const device::BluetoothUUID uuid_;
// The Bluetooth adapter over which this connection is made. Non-null iff
// |this| connection is registered as an observer of the |adapter_|.
scoped_refptr<device::BluetoothAdapter> adapter_;
// The Bluetooth socket that backs this connection. NULL iff the connection is
// not in a connected state.
scoped_refptr<device::BluetoothSocket> socket_;
// The message that was sent over the backing |socket_|. NULL iff there is no
// send operation in progress.
scoped_ptr<WireMessage> pending_message_;
base::WeakPtrFactory<BluetoothConnection> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(BluetoothConnection);
};
} // namespace proximity_auth
#endif // COMPONENTS_PROXIMITY_AUTH_BLUETOOTH_CONNECTION_H
// 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.h"
#include "base/message_loop/message_loop.h"
#include "base/numerics/safe_conversions.h"
#include "base/run_loop.h"
#include "components/proximity_auth/remote_device.h"
#include "components/proximity_auth/wire_message.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_uuid.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
#include "device/bluetooth/test/mock_bluetooth_socket.h"
#include "net/base/io_buffer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::AnyNumber;
using testing::NiceMock;
using testing::Ref;
using testing::Return;
using testing::SaveArg;
using testing::StrictMock;
namespace proximity_auth {
namespace {
const char kDeviceName[] = "Device name";
const char kOtherDeviceName[] = "Other device name";
const char kBluetoothAddress[] = "11:22:33:44:55:66";
const char kOtherBluetoothAddress[] = "AA:BB:CC:DD:EE:FF";
const char kSerializedMessage[] = "Yarrr, this be a serialized message. Yarr!";
const int kSerializedMessageLength = strlen(kSerializedMessage);
const char kUuid[] = "DEADBEEF-CAFE-FEED-FOOD-D15EA5EBEEF";
const RemoteDevice kRemoteDevice = {kDeviceName, kBluetoothAddress};
const int kReceiveBufferSize = 6;
const char kReceiveBufferContents[] = "bytes";
// Create a buffer for testing received data.
scoped_refptr<net::IOBuffer> CreateReceiveBuffer() {
scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kReceiveBufferSize);
memcpy(buffer->data(), kReceiveBufferContents, kReceiveBufferSize);
return buffer;
}
class MockBluetoothConnection : public BluetoothConnection {
public:
MockBluetoothConnection()
: BluetoothConnection(kRemoteDevice, device::BluetoothUUID(kUuid)) {}
// Bluetooth dependencies.
typedef device::BluetoothDevice::ConnectToServiceCallback
ConnectToServiceCallback;
typedef device::BluetoothDevice::ConnectToServiceErrorCallback
ConnectToServiceErrorCallback;
MOCK_METHOD4(ConnectToService,
void(device::BluetoothDevice* device,
const device::BluetoothUUID& uuid,
const ConnectToServiceCallback& callback,
const ConnectToServiceErrorCallback& error_callback));
// Calls back into the parent Connection class.
MOCK_METHOD1(SetStatusProxy, void(Status status));
MOCK_METHOD1(OnBytesReceived, void(const std::string& bytes));
MOCK_METHOD2(OnDidSendMessage,
void(const WireMessage& message, bool success));
virtual void SetStatus(Status status) OVERRIDE {
SetStatusProxy(status);
BluetoothConnection::SetStatus(status);
}
using BluetoothConnection::status;
using BluetoothConnection::Connect;
using BluetoothConnection::DeviceRemoved;
using BluetoothConnection::Disconnect;
private:
DISALLOW_COPY_AND_ASSIGN(MockBluetoothConnection);
};
class TestWireMessage : public WireMessage {
public:
TestWireMessage() : WireMessage("permit id", "payload") {}
virtual ~TestWireMessage() {}
virtual std::string Serialize() const OVERRIDE { return kSerializedMessage; }
private:
DISALLOW_COPY_AND_ASSIGN(TestWireMessage);
};
} // namespace
class ProximityAuthBluetoothConnectionTest : public testing::Test {
public:
ProximityAuthBluetoothConnectionTest()
: adapter_(new device::MockBluetoothAdapter),
device_(adapter_.get(), 0, kDeviceName, kBluetoothAddress, true, true),
socket_(new StrictMock<device::MockBluetoothSocket>),
uuid_(kUuid) {
device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
// Suppress uninteresting Gmock call warnings.
EXPECT_CALL(*adapter_, GetDevice(_)).Times(AnyNumber());
}
// Transition the connection into an in-progress state.
void BeginConnecting(MockBluetoothConnection* connection) {
EXPECT_EQ(Connection::DISCONNECTED, connection->status());
ON_CALL(*adapter_, GetDevice(_)).WillByDefault(Return(&device_));
EXPECT_CALL(*connection, SetStatusProxy(Connection::IN_PROGRESS));
EXPECT_CALL(*adapter_, AddObserver(connection));
EXPECT_CALL(*connection, ConnectToService(&device_, uuid_, _, _));
connection->Connect();
EXPECT_EQ(Connection::IN_PROGRESS, connection->status());
}
// Transition the connection into a connected state.
// Saves the success and error callbacks passed into OnReceive(), which can be
// accessed via receive_callback() and receive_success_callback().
void Connect(MockBluetoothConnection* connection) {
EXPECT_EQ(Connection::DISCONNECTED, connection->status());
device::BluetoothDevice::ConnectToServiceCallback callback;
ON_CALL(*adapter_, GetDevice(_)).WillByDefault(Return(&device_));
EXPECT_CALL(*connection, SetStatusProxy(Connection::IN_PROGRESS));
EXPECT_CALL(*adapter_, AddObserver(connection));
EXPECT_CALL(*connection, ConnectToService(_, _, _, _))
.WillOnce(SaveArg<2>(&callback));
connection->Connect();
ASSERT_FALSE(callback.is_null());
EXPECT_CALL(*connection, SetStatusProxy(Connection::CONNECTED));
EXPECT_CALL(*socket_, Receive(_, _, _))
.WillOnce(DoAll(SaveArg<1>(&receive_callback_),
SaveArg<2>(&receive_error_callback_)));
callback.Run(socket_);
EXPECT_EQ(Connection::CONNECTED, connection->status());
}
device::BluetoothSocket::ReceiveCompletionCallback* receive_callback() {
return &receive_callback_;
}
device::BluetoothSocket::ReceiveErrorCompletionCallback*
receive_error_callback() {
return &receive_error_callback_;
}
protected:
// Mocks used for verifying interactions with the Bluetooth subsystem.
scoped_refptr<device::MockBluetoothAdapter> adapter_;
NiceMock<device::MockBluetoothDevice> device_;
scoped_refptr<StrictMock<device::MockBluetoothSocket>> socket_;
device::BluetoothUUID uuid_;
private:
base::MessageLoop message_loop_;
device::BluetoothSocket::ReceiveCompletionCallback receive_callback_;
device::BluetoothSocket::ReceiveErrorCompletionCallback
receive_error_callback_;
};
TEST_F(ProximityAuthBluetoothConnectionTest, Connect_ConnectionWasInProgress) {
// Create an in-progress connection.
StrictMock<MockBluetoothConnection> connection;
BeginConnecting(&connection);
// A second call to Connect() should be ignored.
EXPECT_CALL(connection, SetStatusProxy(_)).Times(0);
connection.Connect();
// The connection cleans up after itself upon destruction.
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
}
TEST_F(ProximityAuthBluetoothConnectionTest, Connect_ConnectionWasConnected) {
// Create a connected connection.
StrictMock<MockBluetoothConnection> connection;
Connect(&connection);
// A second call to Connect() should be ignored.
EXPECT_CALL(connection, SetStatusProxy(_)).Times(0);
connection.Connect();
// The connection disconnects and unregisters as an observer upon destruction.
EXPECT_CALL(*socket_, Disconnect(_));
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
}
TEST_F(ProximityAuthBluetoothConnectionTest, Connect_NoBluetoothAdapter) {
// Some platforms do not support Bluetooth. This test is only meaningful on
// those platforms.
adapter_ = NULL;
if (device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable())
return;
StrictMock<MockBluetoothConnection> connection;
EXPECT_CALL(connection, SetStatusProxy(_)).Times(0);
connection.Connect();
}
TEST_F(ProximityAuthBluetoothConnectionTest, Connect_DeviceMissing) {
StrictMock<MockBluetoothConnection> connection;
ON_CALL(*adapter_, GetDevice(_))
.WillByDefault(Return(static_cast<device::BluetoothDevice*>(NULL)));
EXPECT_CALL(connection, SetStatusProxy(Connection::IN_PROGRESS));
EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
connection.Connect();
}
TEST_F(ProximityAuthBluetoothConnectionTest,
Connect_DeviceRemovedWhileConnecting) {
// Create an in-progress connection.
StrictMock<MockBluetoothConnection> connection;
BeginConnecting(&connection);
// Remove the device while the connection is in-progress. This should cause
// the connection to disconnect.
EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
connection.DeviceRemoved(adapter_.get(), &device_);
}
TEST_F(ProximityAuthBluetoothConnectionTest,
Connect_OtherDeviceRemovedWhileConnecting) {
// Create an in-progress connection.
StrictMock<MockBluetoothConnection> connection;
BeginConnecting(&connection);
// Remove a device other than the one that is being connected to. This should
// not have any effect on the connection.
NiceMock<device::MockBluetoothDevice> other_device(
adapter_.get(), 0, kOtherDeviceName, kOtherBluetoothAddress, true, true);
EXPECT_CALL(connection, SetStatusProxy(_)).Times(0);
connection.DeviceRemoved(adapter_.get(), &other_device);
// The connection removes itself as an observer when it is destroyed.
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
}
TEST_F(ProximityAuthBluetoothConnectionTest, Connect_ConnectionFails) {
StrictMock<MockBluetoothConnection> connection;
device::BluetoothDevice::ConnectToServiceErrorCallback error_callback;
ON_CALL(*adapter_, GetDevice(_)).WillByDefault(Return(&device_));
EXPECT_CALL(connection, SetStatusProxy(Connection::IN_PROGRESS));
EXPECT_CALL(*adapter_, AddObserver(&connection));
EXPECT_CALL(connection, ConnectToService(&device_, uuid_, _, _))
.WillOnce(SaveArg<3>(&error_callback));
connection.Connect();
ASSERT_FALSE(error_callback.is_null());
EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
error_callback.Run("super descriptive error message");
}
TEST_F(ProximityAuthBluetoothConnectionTest, Connect_ConnectionSucceeds) {
StrictMock<MockBluetoothConnection> connection;
Connect(&connection);
// The connection disconnects and unregisters as an observer upon destruction.
EXPECT_CALL(*socket_, Disconnect(_));
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
}
TEST_F(ProximityAuthBluetoothConnectionTest,
Connect_ConnectionSucceeds_ThenDeviceRemoved) {
StrictMock<MockBluetoothConnection> connection;
Connect(&connection);
EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
EXPECT_CALL(*socket_, Disconnect(_));
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
connection.DeviceRemoved(adapter_.get(), &device_);
}
TEST_F(ProximityAuthBluetoothConnectionTest,
Connect_ConnectionSucceeds_ReceiveData) {
StrictMock<MockBluetoothConnection> connection;
Connect(&connection);
ASSERT_TRUE(receive_callback() && !receive_callback()->is_null());
// Receive some data. Once complete, the connection should re-register to be
// ready receive more data.
scoped_refptr<net::IOBuffer> buffer = CreateReceiveBuffer();
EXPECT_CALL(
connection,
OnBytesReceived(std::string(kReceiveBufferContents, kReceiveBufferSize)));
EXPECT_CALL(*socket_, Receive(_, _, _));
receive_callback()->Run(kReceiveBufferSize, buffer);
base::RunLoop run_loop;
run_loop.RunUntilIdle();
// The connection disconnects and unregisters as an observer upon destruction.
EXPECT_CALL(*socket_, Disconnect(_));
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
}
TEST_F(ProximityAuthBluetoothConnectionTest,
Connect_ConnectionSucceeds_ReceiveDataAfterReceiveError) {
StrictMock<MockBluetoothConnection> connection;
Connect(&connection);
ASSERT_TRUE(receive_error_callback() && !receive_error_callback()->is_null());
// Simulate an error while receiving data. The connection should re-register
// to be ready receive more data despite the error.
device::BluetoothSocket::ReceiveCompletionCallback receive_callback;
EXPECT_CALL(*socket_, Receive(_, _, _))
.WillOnce(SaveArg<1>(&receive_callback));
receive_error_callback()->Run(device::BluetoothSocket::kSystemError,
"The system is down. They're taking over!");
base::RunLoop run_loop;
run_loop.RunUntilIdle();
ASSERT_FALSE(receive_callback.is_null());
// Receive some data.
scoped_refptr<net::IOBuffer> buffer = CreateReceiveBuffer();
EXPECT_CALL(
connection,
OnBytesReceived(std::string(kReceiveBufferContents, kReceiveBufferSize)));
EXPECT_CALL(*socket_, Receive(_, _, _));
receive_callback.Run(kReceiveBufferSize, buffer);
base::RunLoop run_loop2;
run_loop2.RunUntilIdle();
// The connection disconnects and unregisters as an observer upon destruction.
EXPECT_CALL(*socket_, Disconnect(_));
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
}
TEST_F(ProximityAuthBluetoothConnectionTest,
Disconnect_ConnectionWasAlreadyDisconnected) {
StrictMock<MockBluetoothConnection> connection;
EXPECT_CALL(connection, SetStatusProxy(_)).Times(0);
connection.Disconnect();
}
TEST_F(ProximityAuthBluetoothConnectionTest,
Disconnect_ConnectionWasInProgress) {
// Create an in-progress connection.
StrictMock<MockBluetoothConnection> connection;
BeginConnecting(&connection);
EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
connection.Disconnect();
}
TEST_F(ProximityAuthBluetoothConnectionTest,
Disconnect_ConnectionWasConnected) {
// Create a connected connection.
StrictMock<MockBluetoothConnection> connection;
Connect(&connection);
EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
EXPECT_CALL(*socket_, Disconnect(_));
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
connection.Disconnect();
}
TEST_F(ProximityAuthBluetoothConnectionTest,
Connect_ThenDisconnectWhileInProgress_ThenBackingConnectionSucceeds) {
StrictMock<MockBluetoothConnection> connection;
device::BluetoothDevice::ConnectToServiceCallback callback;
ON_CALL(*adapter_, GetDevice(_)).WillByDefault(Return(&device_));
EXPECT_CALL(connection, SetStatusProxy(Connection::IN_PROGRESS));
EXPECT_CALL(*adapter_, AddObserver(&connection));
EXPECT_CALL(connection, ConnectToService(&device_, uuid_, _, _))
.WillOnce(SaveArg<2>(&callback));
connection.Connect();
ASSERT_FALSE(callback.is_null());
EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
connection.Disconnect();
EXPECT_CALL(connection, SetStatusProxy(_)).Times(0);
EXPECT_CALL(*socket_, Receive(_, _, _)).Times(0);
callback.Run(socket_);
}
TEST_F(ProximityAuthBluetoothConnectionTest,
SendMessage_SendsExpectedDataOverTheWire) {
// Create a connected connection.
StrictMock<MockBluetoothConnection> connection;
Connect(&connection);
scoped_refptr<net::IOBuffer> buffer;
scoped_ptr<TestWireMessage> wire_message(new TestWireMessage);
EXPECT_CALL(*socket_, Send(_, kSerializedMessageLength, _, _))
.WillOnce(SaveArg<0>(&buffer));
connection.SendMessage(wire_message.PassAs<WireMessage>());
ASSERT_TRUE(buffer.get());
EXPECT_EQ(kSerializedMessage,
std::string(buffer->data(), kSerializedMessageLength));
// The connection disconnects and unregisters as an observer upon destruction.
EXPECT_CALL(*socket_, Disconnect(_));
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
}
TEST_F(ProximityAuthBluetoothConnectionTest, SendMessage_Success) {
// Create a connected connection.
StrictMock<MockBluetoothConnection> connection;
Connect(&connection);
scoped_ptr<TestWireMessage> wire_message(new TestWireMessage);
// Ownership will be transfered below, so grab a reference here.
TestWireMessage* expected_wire_message = wire_message.get();
device::BluetoothSocket::SendCompletionCallback callback;
EXPECT_CALL(*socket_, Send(_, _, _, _)).WillOnce(SaveArg<2>(&callback));
connection.SendMessage(wire_message.PassAs<WireMessage>());
ASSERT_FALSE(callback.is_null());
EXPECT_CALL(connection, OnDidSendMessage(Ref(*expected_wire_message), true));
callback.Run(kSerializedMessageLength);
// The connection disconnects and unregisters as an observer upon destruction.
EXPECT_CALL(*socket_, Disconnect(_));
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
}
TEST_F(ProximityAuthBluetoothConnectionTest, SendMessage_Failure) {
// Create a connected connection.
StrictMock<MockBluetoothConnection> connection;
Connect(&connection);
scoped_ptr<TestWireMessage> wire_message(new TestWireMessage);
// Ownership will be transfered below, so grab a reference here.
TestWireMessage* expected_wire_message = wire_message.get();
device::BluetoothSocket::ErrorCompletionCallback error_callback;
EXPECT_CALL(*socket_, Send(_, _, _, _)).WillOnce(SaveArg<3>(&error_callback));
connection.SendMessage(wire_message.PassAs<WireMessage>());
ASSERT_FALSE(error_callback.is_null());
EXPECT_CALL(connection, OnDidSendMessage(Ref(*expected_wire_message), false));
EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
EXPECT_CALL(*socket_, Disconnect(_));
EXPECT_CALL(*adapter_, RemoveObserver(&connection));
error_callback.Run("The most helpful of error messages");
}
} // namespace proximity_auth
......@@ -28,7 +28,7 @@ class Connection {
// Constructs a connection to the given |remote_device|.
explicit Connection(const RemoteDevice& remote_device);
~Connection();
virtual ~Connection();
// Returns true iff the connection's status is CONNECTED.
bool IsConnected() const;
......@@ -45,10 +45,6 @@ class Connection {
// Abstract methods that subclasses should implement:
// Pauses or unpauses the handling of incoming messages. Pausing allows the
// user of the connection to add or remove observers without missing messages.
virtual void SetPaused(bool paused) = 0;
// Attempts to connect to the remote device if not already connected.
virtual void Connect() = 0;
......@@ -58,17 +54,20 @@ class Connection {
protected:
// Sets the connection's status to |status|. If this is different from the
// previous status, notifies observers of the change in status.
void SetStatus(Status status);
// Virtual for testing.
virtual void SetStatus(Status status);
Status status() const { return status_; }
// Called after attempting to send bytes over the connection, whether the
// message was successfully sent or not.
void OnDidSendMessage(const WireMessage& message, bool success);
// Virtual for testing.
virtual void OnDidSendMessage(const WireMessage& message, bool success);
// Called when bytes are read from the connection. There should not be a send
// in progress when this function is called.
void OnBytesReceived(const std::string& bytes);
// Virtual for testing.
virtual void OnBytesReceived(const std::string& bytes);
// Sends bytes over the connection. The implementing class should call
// OnSendCompleted() once the send succeeds or fails. At most one send will be
......
......@@ -5,6 +5,7 @@
#include "components/proximity_auth/connection.h"
#include "components/proximity_auth/connection_observer.h"
#include "components/proximity_auth/remote_device.h"
#include "components/proximity_auth/wire_message.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
......
......@@ -12,6 +12,7 @@ namespace proximity_auth {
struct RemoteDevice {
std::string name;
std::string bluetooth_address;
};
} // namespace proximity_auth
......
......@@ -111,6 +111,11 @@ scoped_ptr<WireMessage> WireMessage::Deserialize(
return scoped_ptr<WireMessage>(new WireMessage(permit_id, payload));
}
std::string WireMessage::Serialize() const {
// TODO(isherman): Implement.
return "This method is not yet implemented.";
}
WireMessage::WireMessage(const std::string& permit_id,
const std::string& payload)
: permit_id_(permit_id),
......
......@@ -24,6 +24,9 @@ class WireMessage {
const std::string& serialized_message,
bool* is_incomplete_message);
// Returns a serialized representation of |this| message.
virtual std::string Serialize() const;
const std::string& permit_id() const { return permit_id_; }
const std::string& payload() const { return payload_; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment