summaryrefslogtreecommitdiff
path: root/device
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2013-05-13 16:52:09 +0100
committerTorne (Richard Coles) <torne@google.com>2013-05-13 16:52:09 +0100
commitb2df76ea8fec9e32f6f3718986dba0d95315b29c (patch)
tree0182623e854b794f7307829abf4be16435a0193a /device
parent121d6d4bf5931d9b1dbc0b9a262f6c609440f6c7 (diff)
downloadchromium_org-b2df76ea8fec9e32f6f3718986dba0d95315b29c.tar.gz
Merge from Chromium at DEPS revision r199464
This commit was generated by merge_to_master.py. Change-Id: Ic3d1f97a4fb4edd1e6ed66a70b5040da1622b5a9
Diffstat (limited to 'device')
-rw-r--r--device/bluetooth/bluetooth_adapter_experimental_chromeos.h2
-rw-r--r--device/bluetooth/bluetooth_device_experimental_chromeos.cc46
-rw-r--r--device/bluetooth/bluetooth_device_experimental_chromeos.h9
-rw-r--r--device/bluetooth/bluetooth_experimental_chromeos_unittest.cc64
-rw-r--r--device/bluetooth/bluetooth_profile.cc17
-rw-r--r--device/bluetooth/bluetooth_profile_chromeos_unittest.cc364
-rw-r--r--device/bluetooth/bluetooth_profile_experimental_chromeos.cc259
-rw-r--r--device/bluetooth/bluetooth_profile_experimental_chromeos.h132
-rw-r--r--device/bluetooth/bluetooth_socket_experimental_chromeos.cc170
-rw-r--r--device/bluetooth/bluetooth_socket_experimental_chromeos.h75
-rw-r--r--device/device.gyp5
11 files changed, 1126 insertions, 17 deletions
diff --git a/device/bluetooth/bluetooth_adapter_experimental_chromeos.h b/device/bluetooth/bluetooth_adapter_experimental_chromeos.h
index a3a0655dd1..80dce8407b 100644
--- a/device/bluetooth/bluetooth_adapter_experimental_chromeos.h
+++ b/device/bluetooth/bluetooth_adapter_experimental_chromeos.h
@@ -65,6 +65,8 @@ class BluetoothAdapterExperimentalChromeOS
friend class device::BluetoothAdapterFactory;
friend class BluetoothDeviceExperimentalChromeOS;
friend class BluetoothExperimentalChromeOSTest;
+ friend class BluetoothProfileExperimentalChromeOS;
+ friend class BluetoothProfileChromeOSTest;
BluetoothAdapterExperimentalChromeOS();
virtual ~BluetoothAdapterExperimentalChromeOS();
diff --git a/device/bluetooth/bluetooth_device_experimental_chromeos.cc b/device/bluetooth/bluetooth_device_experimental_chromeos.cc
index fb45ad0b75..c7296baf60 100644
--- a/device/bluetooth/bluetooth_device_experimental_chromeos.cc
+++ b/device/bluetooth/bluetooth_device_experimental_chromeos.cc
@@ -16,6 +16,7 @@
#include "chromeos/dbus/experimental_bluetooth_input_client.h"
#include "dbus/bus.h"
#include "device/bluetooth/bluetooth_adapter_experimental_chromeos.h"
+#include "device/bluetooth/bluetooth_profile_experimental_chromeos.h"
#include "device/bluetooth/bluetooth_socket.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
@@ -392,8 +393,24 @@ void BluetoothDeviceExperimentalChromeOS::ConnectToProfile(
device::BluetoothProfile* profile,
const base::Closure& callback,
const ErrorCallback& error_callback) {
- // TODO(keybuk): implement
- error_callback.Run();
+ BluetoothProfileExperimentalChromeOS* profile_chromeos =
+ static_cast<BluetoothProfileExperimentalChromeOS*>(profile);
+ VLOG(1) << object_path_.value() << ": Connecting profile: "
+ << profile_chromeos->uuid();
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ ConnectProfile(
+ object_path_,
+ profile_chromeos->uuid(),
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnConnectProfile,
+ weak_ptr_factory_.GetWeakPtr(),
+ profile,
+ callback),
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnConnectProfileError,
+ weak_ptr_factory_.GetWeakPtr(),
+ profile,
+ error_callback));
}
void BluetoothDeviceExperimentalChromeOS::SetOutOfBandPairingData(
@@ -682,6 +699,8 @@ void BluetoothDeviceExperimentalChromeOS::OnPairError(
ConnectErrorCode error_code = ERROR_UNKNOWN;
if (error_name == bluetooth_adapter::kErrorConnectionAttemptFailed) {
error_code = ERROR_FAILED;
+ } else if (error_name == bluetooth_adapter::kErrorFailed) {
+ error_code = ERROR_FAILED;
} else if (error_name == bluetooth_adapter::kErrorAuthenticationFailed) {
error_code = ERROR_AUTH_FAILED;
} else if (error_name == bluetooth_adapter::kErrorAuthenticationCanceled) {
@@ -804,4 +823,27 @@ bool BluetoothDeviceExperimentalChromeOS::RunPairingCallbacks(Status status) {
return callback_run;
}
+void BluetoothDeviceExperimentalChromeOS::OnConnectProfile(
+ device::BluetoothProfile* profile,
+ const base::Closure& callback) {
+ BluetoothProfileExperimentalChromeOS* profile_chromeos =
+ static_cast<BluetoothProfileExperimentalChromeOS*>(profile);
+ VLOG(1) << object_path_.value() << ": Profile connected: "
+ << profile_chromeos->uuid();
+ callback.Run();
+}
+
+void BluetoothDeviceExperimentalChromeOS::OnConnectProfileError(
+ device::BluetoothProfile* profile,
+ const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ BluetoothProfileExperimentalChromeOS* profile_chromeos =
+ static_cast<BluetoothProfileExperimentalChromeOS*>(profile);
+ VLOG(1) << object_path_.value() << ": Profile connection failed: "
+ << profile_chromeos->uuid() << ": "
+ << error_name << ": " << error_message;
+ error_callback.Run();
+}
+
} // namespace chromeos
diff --git a/device/bluetooth/bluetooth_device_experimental_chromeos.h b/device/bluetooth/bluetooth_device_experimental_chromeos.h
index fce7cd4feb..18b48e9ebd 100644
--- a/device/bluetooth/bluetooth_device_experimental_chromeos.h
+++ b/device/bluetooth/bluetooth_device_experimental_chromeos.h
@@ -173,6 +173,15 @@ class BluetoothDeviceExperimentalChromeOS
// pairing. Returns true if any callbacks were run, false if not.
bool RunPairingCallbacks(Status status);
+ // Called by dbus:: on completion of the D-Bus method call to
+ // connect a peofile.
+ void OnConnectProfile(device::BluetoothProfile* profile,
+ const base::Closure& callback);
+ void OnConnectProfileError(device::BluetoothProfile* profile,
+ const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
// Return the object path of the device; used by
// BluetoothAdapterExperimentalChromeOS
const dbus::ObjectPath& object_path() const { return object_path_; }
diff --git a/device/bluetooth/bluetooth_experimental_chromeos_unittest.cc b/device/bluetooth/bluetooth_experimental_chromeos_unittest.cc
index 1c6f5b629d..0dc3ad5e81 100644
--- a/device/bluetooth/bluetooth_experimental_chromeos_unittest.cc
+++ b/device/bluetooth/bluetooth_experimental_chromeos_unittest.cc
@@ -136,51 +136,50 @@ class TestPairingDelegate : public BluetoothDevice::PairingDelegate {
last_entered_(999U) {}
virtual ~TestPairingDelegate() {}
- void RequestPinCode(BluetoothDevice* device) OVERRIDE {
+ virtual void RequestPinCode(BluetoothDevice* device) OVERRIDE {
++call_count_;
++request_pincode_count_;
QuitMessageLoop();
}
- void RequestPasskey(BluetoothDevice* device) OVERRIDE {
+ virtual void RequestPasskey(BluetoothDevice* device) OVERRIDE {
++call_count_;
++request_passkey_count_;
QuitMessageLoop();
}
- void DisplayPinCode(BluetoothDevice* device,
- const std::string& pincode) OVERRIDE {
+ virtual void DisplayPinCode(BluetoothDevice* device,
+ const std::string& pincode) OVERRIDE {
++call_count_;
++display_pincode_count_;
last_pincode_ = pincode;
QuitMessageLoop();
}
- void DisplayPasskey(BluetoothDevice* device,
- uint32 passkey) OVERRIDE {
+ virtual void DisplayPasskey(BluetoothDevice* device,
+ uint32 passkey) OVERRIDE {
++call_count_;
++display_passkey_count_;
last_passkey_ = passkey;
QuitMessageLoop();
}
- void KeysEntered(BluetoothDevice* device,
- uint32 entered) OVERRIDE {
+ virtual void KeysEntered(BluetoothDevice* device, uint32 entered) OVERRIDE {
++call_count_;
++keys_entered_count_;
last_entered_ = entered;
QuitMessageLoop();
}
- void ConfirmPasskey(BluetoothDevice* device,
- uint32 passkey) OVERRIDE {
+ virtual void ConfirmPasskey(BluetoothDevice* device,
+ uint32 passkey) OVERRIDE {
++call_count_;
++confirm_passkey_count_;
last_passkey_ = passkey;
QuitMessageLoop();
}
- void DismissDisplayOrConfirm() OVERRIDE {
+ virtual void DismissDisplayOrConfirm() OVERRIDE {
++call_count_;
++dismiss_count_;
QuitMessageLoop();
@@ -1560,6 +1559,49 @@ TEST_F(BluetoothExperimentalChromeOSTest, PairWeirdDevice) {
EXPECT_TRUE(properties->trusted.value());
}
+TEST_F(BluetoothExperimentalChromeOSTest, PairUnpairableDeviceFails) {
+ base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevice(FakeBluetoothDeviceClient::kUnconnectableDeviceAddress);
+
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kUnpairableDeviceAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(0, pairing_delegate.call_count_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ // Run the loop to get the error..
+ message_loop.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+
+ EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_);
+
+ EXPECT_FALSE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+ EXPECT_FALSE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+}
+
TEST_F(BluetoothExperimentalChromeOSTest, PairingFails) {
base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
diff --git a/device/bluetooth/bluetooth_profile.cc b/device/bluetooth/bluetooth_profile.cc
index 2ef94250fd..6c59e5bcf0 100644
--- a/device/bluetooth/bluetooth_profile.cc
+++ b/device/bluetooth/bluetooth_profile.cc
@@ -4,7 +4,9 @@
#include "device/bluetooth/bluetooth_profile.h"
-#if defined(OS_MACOSX)
+#if defined(OS_CHROMEOS)
+#include "device/bluetooth/bluetooth_profile_experimental_chromeos.h"
+#elif defined(OS_MACOSX)
#include "base/mac/mac_util.h"
#include "device/bluetooth/bluetooth_profile_mac.h"
#elif defined(OS_WIN)
@@ -43,16 +45,23 @@ BluetoothProfile::~BluetoothProfile() {
void BluetoothProfile::Register(const std::string& uuid,
const Options& options,
const ProfileCallback& callback) {
+#if defined(OS_CHROMEOS)
+ chromeos::BluetoothProfileExperimentalChromeOS* profile = NULL;
+ profile = new chromeos::BluetoothProfileExperimentalChromeOS();
+ profile->Init(uuid, options, callback);
+#elif defined(OS_MACOSX)
BluetoothProfile* profile = NULL;
-#if defined(OS_MACOSX)
if (base::mac::IsOSLionOrLater())
profile = new BluetoothProfileMac(uuid, options.name);
+ callback.Run(profile);
#elif defined(OS_WIN)
+ BluetoothProfile* profile = NULL;
profile = new BluetoothProfileWin(uuid, options.name);
-#endif
-
callback.Run(profile);
+#else
+ callback.Run(NULL);
+#endif
}
} // namespace device
diff --git a/device/bluetooth/bluetooth_profile_chromeos_unittest.cc b/device/bluetooth/bluetooth_profile_chromeos_unittest.cc
new file mode 100644
index 0000000000..620116c26e
--- /dev/null
+++ b/device/bluetooth/bluetooth_profile_chromeos_unittest.cc
@@ -0,0 +1,364 @@
+// Copyright (c) 2013 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 "base/command_line.h"
+#include "base/message_loop.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/fake_bluetooth_device_client.h"
+#include "chromeos/dbus/fake_bluetooth_profile_manager_client.h"
+#include "chromeos/dbus/fake_bluetooth_profile_service_provider.h"
+#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_adapter_experimental_chromeos.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_device_experimental_chromeos.h"
+#include "device/bluetooth/bluetooth_profile.h"
+#include "device/bluetooth/bluetooth_profile_experimental_chromeos.h"
+#include "device/bluetooth/bluetooth_socket.h"
+#include "device/bluetooth/bluetooth_socket_experimental_chromeos.h"
+#include "net/base/io_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using device::BluetoothAdapter;
+using device::BluetoothDevice;
+using device::BluetoothProfile;
+using device::BluetoothSocket;
+
+namespace chromeos {
+
+class BluetoothProfileChromeOSTest : public testing::Test {
+ public:
+ BluetoothProfileChromeOSTest()
+ : message_loop_(MessageLoop::TYPE_IO),
+ callback_count_(0),
+ error_callback_count_(0),
+ profile_callback_count_(0),
+ connection_callback_count_(0),
+ last_profile_(NULL),
+ last_device_(NULL) {}
+
+ virtual void SetUp() {
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kEnableExperimentalBluetooth))
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ chromeos::switches::kEnableExperimentalBluetooth);
+
+ mock_dbus_thread_manager_ =
+ new MockDBusThreadManagerWithoutGMock();
+ DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager_);
+
+ fake_bluetooth_profile_manager_client_ =
+ mock_dbus_thread_manager_->fake_bluetooth_profile_manager_client();
+
+ device::BluetoothAdapterFactory::GetAdapter(
+ base::Bind(&BluetoothProfileChromeOSTest::AdapterCallback,
+ base::Unretained(this)));
+ ASSERT_TRUE(adapter_ != NULL);
+ ASSERT_TRUE(adapter_->IsInitialized());
+ ASSERT_TRUE(adapter_->IsPresent());
+
+ adapter_->SetPowered(
+ true,
+ base::Bind(&base::DoNothing),
+ base::Bind(&base::DoNothing));
+ ASSERT_TRUE(adapter_->IsPowered());
+ }
+
+ virtual void TearDown() {
+ adapter_ = NULL;
+ DBusThreadManager::Shutdown();
+ }
+
+ void AdapterCallback(scoped_refptr<BluetoothAdapter> adapter) {
+ adapter_ = adapter;
+ }
+
+ void Callback() {
+ ++callback_count_;
+ }
+
+ void ErrorCallback() {
+ ++error_callback_count_;
+
+ message_loop_.Quit();
+ }
+
+ void ProfileCallback(BluetoothProfile* profile) {
+ ++profile_callback_count_;
+ last_profile_ = profile;
+ }
+
+ void ConnectionCallback(const BluetoothDevice *device,
+ scoped_refptr<BluetoothSocket> socket) {
+ ++connection_callback_count_;
+ last_device_ = device;
+ last_socket_ = socket;
+
+ message_loop_.Quit();
+ }
+
+ protected:
+ base::MessageLoop message_loop_;
+
+ FakeBluetoothProfileManagerClient* fake_bluetooth_profile_manager_client_;
+ MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager_;
+ scoped_refptr<BluetoothAdapter> adapter_;
+
+ unsigned int callback_count_;
+ unsigned int error_callback_count_;
+ unsigned int profile_callback_count_;
+ unsigned int connection_callback_count_;
+ BluetoothProfile* last_profile_;
+ const BluetoothDevice* last_device_;
+ scoped_refptr<BluetoothSocket> last_socket_;
+};
+
+TEST_F(BluetoothProfileChromeOSTest, L2capEndToEnd) {
+ // Register the profile and expect the profile object to be passed to the
+ // callback.
+ BluetoothProfile::Options options;
+ BluetoothProfile::Register(
+ FakeBluetoothProfileManagerClient::kL2capUuid,
+ options,
+ base::Bind(&BluetoothProfileChromeOSTest::ProfileCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1U, profile_callback_count_);
+ EXPECT_TRUE(last_profile_ != NULL);
+ BluetoothProfile* profile = last_profile_;
+
+ // Make sure we have a profile service provider for it.
+ FakeBluetoothProfileServiceProvider* profile_service_provider =
+ fake_bluetooth_profile_manager_client_->GetProfileServiceProvider(
+ FakeBluetoothProfileManagerClient::kL2capUuid);
+ EXPECT_TRUE(profile_service_provider != NULL);
+
+ // Register the connection callback.
+ profile->SetConnectionCallback(
+ base::Bind(&BluetoothProfileChromeOSTest::ConnectionCallback,
+ base::Unretained(this)));
+
+ // Connect to the device, expect the success callback to be called and the
+ // connection callback to be called with the device we passed and a new
+ // socket instance.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kPairedDeviceAddress);
+ ASSERT_TRUE(device != NULL);
+
+ device->ConnectToProfile(
+ profile,
+ base::Bind(&BluetoothProfileChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothProfileChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ message_loop_.Run();
+
+ EXPECT_EQ(1U, callback_count_);
+ EXPECT_EQ(0U, error_callback_count_);
+
+ EXPECT_EQ(1U, connection_callback_count_);
+ EXPECT_EQ(device, last_device_);
+ EXPECT_TRUE(last_socket_.get() != NULL);
+
+ // Take the ownership of the socket for the remainder of the test and set
+ // up buffers for read/write tests.
+ scoped_refptr<BluetoothSocket> socket = last_socket_;
+ last_socket_ = NULL;
+
+ bool success;
+ scoped_refptr<net::GrowableIOBuffer> read_buffer;
+
+ scoped_refptr<net::StringIOBuffer> base_buffer(
+ new net::StringIOBuffer("test"));
+ scoped_refptr<net::DrainableIOBuffer> write_buffer;
+
+ // Read data from the socket; since no data should be waiting, this should
+ // return success but no data.
+ read_buffer = new net::GrowableIOBuffer;
+ success = socket->Receive(read_buffer);
+ EXPECT_TRUE(success);
+ EXPECT_EQ(0, read_buffer->capacity());
+ EXPECT_EQ(0, read_buffer->offset());
+ EXPECT_EQ("", socket->GetLastErrorMessage());
+
+ // Write data to the socket; the data should be consumed and no bytes should
+ // be remaining.
+ write_buffer = new net::DrainableIOBuffer(base_buffer, base_buffer->size());
+ success = socket->Send(write_buffer);
+ EXPECT_TRUE(success);
+ EXPECT_EQ(base_buffer->size(), write_buffer->BytesConsumed());
+ EXPECT_EQ(0, write_buffer->BytesRemaining());
+ EXPECT_EQ("", socket->GetLastErrorMessage());
+
+ // Read data from the socket; this should match the data we sent since the
+ // server just echoes us. We have to spin here until there is actually data
+ // to read.
+ read_buffer = new net::GrowableIOBuffer;
+ do {
+ success = socket->Receive(read_buffer);
+ } while (success && read_buffer->offset() == 0);
+ EXPECT_TRUE(success);
+ EXPECT_NE(0, read_buffer->capacity());
+ EXPECT_EQ(base_buffer->size(), read_buffer->offset());
+ EXPECT_EQ("", socket->GetLastErrorMessage());
+
+ std::string data = std::string(read_buffer->StartOfBuffer(),
+ read_buffer->offset());
+ EXPECT_EQ("test", data);
+
+ // Write data to the socket; since the socket is closed, this should return
+ // an error without writing the data and "Disconnected" as the message.
+ write_buffer = new net::DrainableIOBuffer(base_buffer, base_buffer->size());
+ success = socket->Send(write_buffer);
+ EXPECT_FALSE(success);
+ EXPECT_EQ(0, write_buffer->BytesConsumed());
+ EXPECT_EQ(base_buffer->size(), write_buffer->BytesRemaining());
+ EXPECT_EQ("Disconnected", socket->GetLastErrorMessage());
+
+ // Read data from the socket; since the socket is closed, this should return
+ // an error with "Disconnected" as the last message.
+ read_buffer = new net::GrowableIOBuffer;
+ success = socket->Receive(read_buffer);
+ EXPECT_FALSE(success);
+ EXPECT_EQ(0, read_buffer->capacity());
+ EXPECT_EQ(0, read_buffer->offset());
+ EXPECT_EQ("Disconnected", socket->GetLastErrorMessage());
+
+ // Close our end of the socket.
+ socket = NULL;
+
+ // Unregister the profile, make sure it's no longer registered.
+ last_profile_->Unregister();
+
+ profile_service_provider =
+ fake_bluetooth_profile_manager_client_->GetProfileServiceProvider(
+ FakeBluetoothProfileManagerClient::kL2capUuid);
+ EXPECT_TRUE(profile_service_provider == NULL);
+}
+
+TEST_F(BluetoothProfileChromeOSTest, RfcommEndToEnd) {
+ // Register the profile and expect the profile object to be passed to the
+ // callback.
+ BluetoothProfile::Options options;
+ BluetoothProfile::Register(
+ FakeBluetoothProfileManagerClient::kRfcommUuid,
+ options,
+ base::Bind(&BluetoothProfileChromeOSTest::ProfileCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1U, profile_callback_count_);
+ EXPECT_TRUE(last_profile_ != NULL);
+ BluetoothProfile* profile = last_profile_;
+
+ // Make sure we have a profile service provider for it.
+ FakeBluetoothProfileServiceProvider* profile_service_provider =
+ fake_bluetooth_profile_manager_client_->GetProfileServiceProvider(
+ FakeBluetoothProfileManagerClient::kRfcommUuid);
+ EXPECT_TRUE(profile_service_provider != NULL);
+
+ // Register the connection callback.
+ profile->SetConnectionCallback(
+ base::Bind(&BluetoothProfileChromeOSTest::ConnectionCallback,
+ base::Unretained(this)));
+
+ // Connect to the device, expect the success callback to be called and the
+ // connection callback to be called with the device we passed and a new
+ // socket instance.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kPairedDeviceAddress);
+ ASSERT_TRUE(device != NULL);
+
+ device->ConnectToProfile(
+ profile,
+ base::Bind(&BluetoothProfileChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothProfileChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ message_loop_.Run();
+
+ EXPECT_EQ(1U, callback_count_);
+ EXPECT_EQ(0U, error_callback_count_);
+
+ EXPECT_EQ(1U, connection_callback_count_);
+ EXPECT_EQ(device, last_device_);
+ EXPECT_TRUE(last_socket_.get() != NULL);
+
+ // Take the ownership of the socket for the remainder of the test and set
+ // up buffers for read/write tests.
+ scoped_refptr<BluetoothSocket> socket = last_socket_;
+ last_socket_ = NULL;
+
+ bool success;
+ scoped_refptr<net::GrowableIOBuffer> read_buffer;
+
+ scoped_refptr<net::StringIOBuffer> base_buffer(
+ new net::StringIOBuffer("test"));
+ scoped_refptr<net::DrainableIOBuffer> write_buffer;
+
+ // Read data from the socket; since no data should be waiting, this should
+ // return success but no data.
+ read_buffer = new net::GrowableIOBuffer;
+ success = socket->Receive(read_buffer);
+ EXPECT_TRUE(success);
+ EXPECT_EQ(0, read_buffer->offset());
+ EXPECT_EQ("", socket->GetLastErrorMessage());
+
+ // Write data to the socket; the data should be consumed and no bytes should
+ // be remaining.
+ write_buffer = new net::DrainableIOBuffer(base_buffer, base_buffer->size());
+ success = socket->Send(write_buffer);
+ EXPECT_TRUE(success);
+ EXPECT_EQ(base_buffer->size(), write_buffer->BytesConsumed());
+ EXPECT_EQ(0, write_buffer->BytesRemaining());
+ EXPECT_EQ("", socket->GetLastErrorMessage());
+
+ // Read data from the socket; this should match the data we sent since the
+ // server just echoes us. We have to spin here until there is actually data
+ // to read.
+ read_buffer = new net::GrowableIOBuffer;
+ do {
+ success = socket->Receive(read_buffer);
+ } while (success && read_buffer->offset() == 0);
+ EXPECT_TRUE(success);
+ EXPECT_NE(0, read_buffer->capacity());
+ EXPECT_EQ(base_buffer->size(), read_buffer->offset());
+ EXPECT_EQ("", socket->GetLastErrorMessage());
+
+ std::string data = std::string(read_buffer->StartOfBuffer(),
+ read_buffer->offset());
+ EXPECT_EQ("test", data);
+
+ // Write data to the socket; since the socket is closed, this should return
+ // an error without writing the data and "Disconnected" as the message.
+ write_buffer = new net::DrainableIOBuffer(base_buffer, base_buffer->size());
+ success = socket->Send(write_buffer);
+ EXPECT_FALSE(success);
+ EXPECT_EQ(0, write_buffer->BytesConsumed());
+ EXPECT_EQ(base_buffer->size(), write_buffer->BytesRemaining());
+ EXPECT_EQ("Disconnected", socket->GetLastErrorMessage());
+
+ // Read data from the socket; since the socket is closed, this should return
+ // an error with "Disconnected" as the last message.
+ read_buffer = new net::GrowableIOBuffer;
+ success = socket->Receive(read_buffer);
+ EXPECT_FALSE(success);
+ EXPECT_EQ(0, read_buffer->offset());
+ EXPECT_EQ("Disconnected", socket->GetLastErrorMessage());
+
+ // Close our end of the socket.
+ socket = NULL;
+
+ // Unregister the profile, make sure it's no longer registered.
+ last_profile_->Unregister();
+
+ profile_service_provider =
+ fake_bluetooth_profile_manager_client_->GetProfileServiceProvider(
+ FakeBluetoothProfileManagerClient::kRfcommUuid);
+ EXPECT_TRUE(profile_service_provider == NULL);
+}
+
+} // namespace chromeos
diff --git a/device/bluetooth/bluetooth_profile_experimental_chromeos.cc b/device/bluetooth/bluetooth_profile_experimental_chromeos.cc
new file mode 100644
index 0000000000..2ccea24543
--- /dev/null
+++ b/device/bluetooth/bluetooth_profile_experimental_chromeos.cc
@@ -0,0 +1,259 @@
+// Copyright (c) 2013 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 "device/bluetooth/bluetooth_profile_experimental_chromeos.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/threading/worker_pool.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/experimental_bluetooth_profile_manager_client.h"
+#include "chromeos/dbus/experimental_bluetooth_profile_service_provider.h"
+#include "dbus/bus.h"
+#include "dbus/file_descriptor.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_adapter_experimental_chromeos.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_device_experimental_chromeos.h"
+#include "device/bluetooth/bluetooth_profile.h"
+#include "device/bluetooth/bluetooth_socket.h"
+#include "device/bluetooth/bluetooth_socket_experimental_chromeos.h"
+
+using device::BluetoothAdapter;
+using device::BluetoothAdapterFactory;
+using device::BluetoothDevice;
+using device::BluetoothProfile;
+using device::BluetoothSocket;
+
+namespace {
+
+// Check the validity of a file descriptor received from D-Bus. Must be run
+// on a thread where i/o is permitted.
+scoped_ptr<dbus::FileDescriptor> CheckValidity(
+ scoped_ptr<dbus::FileDescriptor> fd) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ fd->CheckValidity();
+ return fd.Pass();
+}
+
+} // namespace
+
+
+namespace chromeos {
+
+BluetoothProfileExperimentalChromeOS::BluetoothProfileExperimentalChromeOS()
+ : weak_ptr_factory_(this) {
+}
+
+BluetoothProfileExperimentalChromeOS::~BluetoothProfileExperimentalChromeOS() {
+ DCHECK(object_path_.value().empty());
+ DCHECK(profile_.get() == NULL);
+}
+
+void BluetoothProfileExperimentalChromeOS::Init(
+ const std::string& uuid,
+ const device::BluetoothProfile::Options& options,
+ const ProfileCallback& callback) {
+ DCHECK(object_path_.value().empty());
+ DCHECK(profile_.get() == NULL);
+
+ if (!BluetoothDevice::IsUUIDValid(uuid)) {
+ callback.Run(NULL);
+ return;
+ }
+
+ uuid_ = uuid;
+
+ ExperimentalBluetoothProfileManagerClient::Options bluetooth_options;
+ bluetooth_options.name = options.name;
+ bluetooth_options.service = uuid;
+ bluetooth_options.channel = options.channel;
+ bluetooth_options.psm = options.psm;
+ bluetooth_options.require_authentication = options.require_authentication;
+ bluetooth_options.require_authorization = options.require_authorization;
+ bluetooth_options.auto_connect = options.auto_connect;
+ bluetooth_options.version = options.version;
+ bluetooth_options.features = options.features;
+
+ // The object path is relatively meaningless, but has to be unique, so we
+ // use the UUID of the profile.
+ std::string uuid_path;
+ ReplaceChars(uuid, ":-", "_", &uuid_path);
+
+ object_path_ = dbus::ObjectPath("/org/chromium/bluetooth_profile/" +
+ uuid_path);
+
+ dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
+ profile_.reset(ExperimentalBluetoothProfileServiceProvider::Create(
+ system_bus, object_path_, this));
+ DCHECK(profile_.get());
+
+ VLOG(1) << object_path_.value() << ": Register profile";
+ DBusThreadManager::Get()->GetExperimentalBluetoothProfileManagerClient()->
+ RegisterProfile(
+ object_path_,
+ uuid,
+ bluetooth_options,
+ base::Bind(
+ &BluetoothProfileExperimentalChromeOS::OnRegisterProfile,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(
+ &BluetoothProfileExperimentalChromeOS::OnRegisterProfileError,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback));
+}
+
+void BluetoothProfileExperimentalChromeOS::Unregister() {
+ DCHECK(!object_path_.value().empty());
+ DCHECK(profile_.get());
+
+ profile_.reset();
+
+ VLOG(1) << object_path_.value() << ": Unregister profile";
+ DBusThreadManager::Get()->GetExperimentalBluetoothProfileManagerClient()->
+ UnregisterProfile(
+ object_path_,
+ base::Bind(
+ &BluetoothProfileExperimentalChromeOS::OnUnregisterProfile,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(
+ &BluetoothProfileExperimentalChromeOS::OnUnregisterProfileError,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BluetoothProfileExperimentalChromeOS::SetConnectionCallback(
+ const ConnectionCallback& callback) {
+ connection_callback_ = callback;
+}
+
+void BluetoothProfileExperimentalChromeOS::Release() {
+ VLOG(1) << object_path_.value() << ": Release";
+}
+
+void BluetoothProfileExperimentalChromeOS::NewConnection(
+ const dbus::ObjectPath& device_path,
+ scoped_ptr<dbus::FileDescriptor> fd,
+ const ExperimentalBluetoothProfileServiceProvider::Delegate::Options&
+ options,
+ const ConfirmationCallback& callback) {
+ VLOG(1) << object_path_.value() << ": New connection from device: "
+ << device_path.value();;
+ if (connection_callback_.is_null()) {
+ callback.Run(REJECTED);
+ return;
+ }
+
+ // Punt descriptor validity check to a worker thread where i/o is permitted;
+ // on return we'll fetch the adapter and then call the connection callback.
+ //
+ // base::Passed is used to take ownership of the file descriptor during the
+ // CheckValidity() call and pass that ownership to the GetAdapter() call.
+ base::PostTaskAndReplyWithResult(
+ base::WorkerPool::GetTaskRunner(false),
+ FROM_HERE,
+ base::Bind(&CheckValidity, base::Passed(&fd)),
+ base::Bind(&BluetoothProfileExperimentalChromeOS::GetAdapter,
+ weak_ptr_factory_.GetWeakPtr(),
+ device_path,
+ options,
+ callback));
+}
+
+void BluetoothProfileExperimentalChromeOS::RequestDisconnection(
+ const dbus::ObjectPath& device_path,
+ const ConfirmationCallback& callback) {
+ VLOG(1) << object_path_.value() << ": Request disconnection";
+ callback.Run(SUCCESS);
+}
+
+void BluetoothProfileExperimentalChromeOS::Cancel() {
+ VLOG(1) << object_path_.value() << ": Cancel";
+}
+
+void BluetoothProfileExperimentalChromeOS::OnRegisterProfile(
+ const ProfileCallback& callback) {
+ VLOG(1) << object_path_.value() << ": Profile registered";
+ callback.Run(this);
+}
+
+void BluetoothProfileExperimentalChromeOS::OnRegisterProfileError(
+ const ProfileCallback& callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(WARNING) << object_path_.value() << ": Failed to register profile: "
+ << error_name << ": " << error_message;
+ callback.Run(NULL);
+
+ Unregister();
+}
+
+void BluetoothProfileExperimentalChromeOS::OnUnregisterProfile() {
+ VLOG(1) << object_path_.value() << ": Profile unregistered";
+ object_path_ = dbus::ObjectPath("");
+ delete this;
+}
+
+void BluetoothProfileExperimentalChromeOS::OnUnregisterProfileError(
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(WARNING) << object_path_.value() << ": Failed to unregister profile: "
+ << error_name << ": " << error_message;
+ object_path_ = dbus::ObjectPath("");
+ delete this;
+}
+
+void BluetoothProfileExperimentalChromeOS::GetAdapter(
+ const dbus::ObjectPath& device_path,
+ const ExperimentalBluetoothProfileServiceProvider::Delegate::Options&
+ options,
+ const ConfirmationCallback& callback,
+ scoped_ptr<dbus::FileDescriptor> fd) {
+ VLOG(1) << object_path_.value() << ": Validity check complete";
+ if (!fd->is_valid()) {
+ callback.Run(REJECTED);
+ return;
+ }
+
+ BluetoothAdapterFactory::GetAdapter(
+ base::Bind(&BluetoothProfileExperimentalChromeOS::OnGetAdapter,
+ weak_ptr_factory_.GetWeakPtr(),
+ device_path,
+ options,
+ callback,
+ base::Passed(&fd)));
+}
+
+void BluetoothProfileExperimentalChromeOS::OnGetAdapter(
+ const dbus::ObjectPath& device_path,
+ const ExperimentalBluetoothProfileServiceProvider::Delegate::Options&
+ options,
+ const ConfirmationCallback& callback,
+ scoped_ptr<dbus::FileDescriptor> fd,
+ scoped_refptr<BluetoothAdapter> adapter) {
+ VLOG(1) << object_path_.value() << ": Obtained adapter reference";
+ callback.Run(SUCCESS);
+
+ BluetoothDeviceExperimentalChromeOS* device =
+ static_cast<BluetoothAdapterExperimentalChromeOS*>(adapter.get())->
+ GetDeviceWithPath(device_path);
+ DCHECK(device);
+
+ scoped_refptr<BluetoothSocket> socket((
+ BluetoothSocketExperimentalChromeOS::Create(fd.get())));
+ connection_callback_.Run(device, socket);
+}
+
+} // namespace chromeos
diff --git a/device/bluetooth/bluetooth_profile_experimental_chromeos.h b/device/bluetooth/bluetooth_profile_experimental_chromeos.h
new file mode 100644
index 0000000000..61d4c44cbb
--- /dev/null
+++ b/device/bluetooth/bluetooth_profile_experimental_chromeos.h
@@ -0,0 +1,132 @@
+// Copyright (c) 2013 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 DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_EXPERIMENTAL_CHROMEOS_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_EXPERIMENTAL_CHROMEOS_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/experimental_bluetooth_profile_service_provider.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_profile.h"
+
+namespace dbus {
+
+class FileDescriptor;
+
+} // namespace dbus
+
+namespace device {
+
+class BluetoothAdapter;
+
+} // namespace device
+
+namespace chromeos {
+
+// The BluetoothProfileExperimentalChromeOS class implements BluetoothProfile
+// for the Chrome OS platform using the Bluetooth Smart capable backend (there
+// is no implementation for the older backend). It will be renamed to
+// BluetoothProfileChromeOS, once the backend is the sole implementation.
+class CHROMEOS_EXPORT BluetoothProfileExperimentalChromeOS
+ : public device::BluetoothProfile,
+ private ExperimentalBluetoothProfileServiceProvider::Delegate {
+ public:
+ // BluetoothProfile override.
+ virtual void Unregister() OVERRIDE;
+ virtual void SetConnectionCallback(
+ const ConnectionCallback& callback) OVERRIDE;
+
+ // Return the UUID of the profile.
+ const std::string& uuid() const { return uuid_; }
+
+ private:
+ friend class BluetoothProfile;
+
+ BluetoothProfileExperimentalChromeOS();
+ virtual ~BluetoothProfileExperimentalChromeOS();
+
+ // Called by BluetoothProfile::Register to initialize the profile object
+ // asynchronously. |uuid|, |options| and |callback| are the arguments to
+ // BluetoothProfile::Register.
+ void Init(const std::string& uuid,
+ const device::BluetoothProfile::Options& options,
+ const ProfileCallback& callback);
+
+ // ExperimentalBluetoothProfileServiceProvider::Delegate override.
+ virtual void Release() OVERRIDE;
+ virtual void NewConnection(
+ const dbus::ObjectPath& device_path,
+ scoped_ptr<dbus::FileDescriptor> fd,
+ const ExperimentalBluetoothProfileServiceProvider::Delegate::Options&
+ options,
+ const ConfirmationCallback& callback) OVERRIDE;
+ virtual void RequestDisconnection(
+ const dbus::ObjectPath& device_path,
+ const ConfirmationCallback& callback) OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+
+ // Called by dbus:: on completion of the D-Bus method call to register the
+ // profile object.
+ void OnRegisterProfile(const ProfileCallback& callback);
+ void OnRegisterProfileError(const ProfileCallback& callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Called by dbus:: on completion of the D-Bus method call to unregister
+ // the profile object.
+ void OnUnregisterProfile();
+ void OnUnregisterProfileError(const std::string& error_name,
+ const std::string& error_message);
+
+ // Method run once the file descriptor has been validated in order to get
+ // the default adapter, and method run once the default adapter has been
+ // obtained in order to get the device object to be passed to the connection
+ // callback.
+ //
+ // The |fd| argument is moved compared to the NewConnection() call since it
+ // becomes the result of a PostTaskAndReplyWithResult() call.
+ void GetAdapter(
+ const dbus::ObjectPath& device_path,
+ const ExperimentalBluetoothProfileServiceProvider::Delegate::Options&
+ options,
+ const ConfirmationCallback& callback,
+ scoped_ptr<dbus::FileDescriptor> fd);
+ void OnGetAdapter(
+ const dbus::ObjectPath& device_path,
+ const ExperimentalBluetoothProfileServiceProvider::Delegate::Options&
+ options,
+ const ConfirmationCallback& callback,
+ scoped_ptr<dbus::FileDescriptor> fd,
+ scoped_refptr<device::BluetoothAdapter>);
+
+ // UUID of the profile passed during initialization.
+ std::string uuid_;
+
+ // Object path of the local profile D-Bus object.
+ dbus::ObjectPath object_path_;
+
+ // Local profile D-Bus object used for receiving profile delegate methods
+ // from BlueZ.
+ scoped_ptr<ExperimentalBluetoothProfileServiceProvider> profile_;
+
+ // Callback used on both outgoing and incoming connections to pass the
+ // connected socket to profile object owner.
+ ConnectionCallback connection_callback_;
+
+ // Note: This should remain the last member so it'll be destroyed and
+ // invalidate its weak pointers before any other members are destroyed.
+ base::WeakPtrFactory<BluetoothProfileExperimentalChromeOS> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothProfileExperimentalChromeOS);
+};
+
+} // namespace chromeos
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_EXPERIMENTAL_CHROMEOS_H_
diff --git a/device/bluetooth/bluetooth_socket_experimental_chromeos.cc b/device/bluetooth/bluetooth_socket_experimental_chromeos.cc
new file mode 100644
index 0000000000..21d281a998
--- /dev/null
+++ b/device/bluetooth/bluetooth_socket_experimental_chromeos.cc
@@ -0,0 +1,170 @@
+// Copyright (c) 2013 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 "device/bluetooth/bluetooth_socket_experimental_chromeos.h"
+
+#include <errno.h>
+#include <poll.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/safe_strerror_posix.h"
+#include "base/threading/thread_restrictions.h"
+#include "dbus/file_descriptor.h"
+#include "device/bluetooth/bluetooth_socket.h"
+#include "net/base/io_buffer.h"
+
+namespace chromeos {
+
+BluetoothSocketExperimentalChromeOS::BluetoothSocketExperimentalChromeOS(
+ int fd)
+ : fd_(fd) {
+ // Fetch the socket type so we read from it correctly.
+ int optval;
+ socklen_t opt_len = sizeof optval;
+ if (getsockopt(fd_, SOL_SOCKET, SO_TYPE, &optval, &opt_len) < 0) {
+ // Sequenced packet is the safest assumption since it won't result in
+ // truncated packets.
+ LOG(WARNING) << "Unable to get socket type: " << safe_strerror(errno);
+ optval = SOCK_SEQPACKET;
+ }
+
+ if (optval == SOCK_DGRAM || optval == SOCK_SEQPACKET) {
+ socket_type_ = L2CAP;
+ } else {
+ socket_type_ = RFCOMM;
+ }
+}
+
+BluetoothSocketExperimentalChromeOS::~BluetoothSocketExperimentalChromeOS() {
+ HANDLE_EINTR(close(fd_));
+}
+
+bool BluetoothSocketExperimentalChromeOS::Receive(
+ net::GrowableIOBuffer *buffer) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (socket_type_ == L2CAP) {
+ int count;
+ if (ioctl(fd_, FIONREAD, &count) < 0) {
+ error_message_ = safe_strerror(errno);
+ LOG(WARNING) << "Unable to get waiting data size: " << error_message_;
+ return true;
+ }
+
+ // No bytes waiting can mean either nothing to read, or the other end has
+ // been closed, and reading zero bytes always returns zero.
+ //
+ // We can't do a short read for fear of a race where data arrives between
+ // calls and we trunctate it. So use poll() to check for the POLLHUP flag.
+ if (count == 0) {
+ struct pollfd pollfd;
+
+ pollfd.fd = fd_;
+ pollfd.events = 0;
+ pollfd.revents = 0;
+
+ // Timeout parameter set to 0 so this call will not block.
+ if (HANDLE_EINTR(poll(&pollfd, 1, 0)) < 0) {
+ error_message_ = safe_strerror(errno);
+ LOG(WARNING) << "Unable to check whether socket is closed: "
+ << error_message_;
+ return false;
+ }
+
+ if (pollfd.revents & POLLHUP) {
+ // TODO(keybuk, youngki): Agree a common way to flag disconnected.
+ error_message_ = "Disconnected";
+ return false;
+ }
+ }
+
+ buffer->SetCapacity(count);
+ } else {
+ buffer->SetCapacity(1024);
+ }
+
+ ssize_t bytes_read;
+ do {
+ if (buffer->RemainingCapacity() == 0)
+ buffer->SetCapacity(buffer->capacity() * 2);
+ bytes_read =
+ HANDLE_EINTR(read(fd_, buffer->data(), buffer->RemainingCapacity()));
+ if (bytes_read > 0)
+ buffer->set_offset(buffer->offset() + bytes_read);
+ } while (socket_type_ == RFCOMM && bytes_read > 0);
+
+ // Ignore an error if at least one read() call succeeded; it'll be returned
+ // the next read() call.
+ if (buffer->offset() > 0)
+ return true;
+
+ if (bytes_read < 0) {
+ if (errno == ECONNRESET || errno == ENOTCONN) {
+ // TODO(keybuk, youngki): Agree a common way to flag disconnected.
+ error_message_ = "Disconnected";
+ return false;
+ } else if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ error_message_ = safe_strerror(errno);
+ return false;
+ }
+ }
+
+ if (bytes_read == 0 && socket_type_ == RFCOMM) {
+ // TODO(keybuk, youngki): Agree a common way to flag disconnected.
+ error_message_ = "Disconnected";
+ return false;
+ }
+
+ return true;
+}
+
+bool BluetoothSocketExperimentalChromeOS::Send(
+ net::DrainableIOBuffer *buffer) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ ssize_t bytes_written;
+ do {
+ bytes_written =
+ HANDLE_EINTR(write(fd_, buffer->data(), buffer->BytesRemaining()));
+ if (bytes_written > 0)
+ buffer->DidConsume(bytes_written);
+ } while (buffer->BytesRemaining() > 0 && bytes_written > 0);
+
+ if (bytes_written < 0) {
+ if (errno == EPIPE || errno == ECONNRESET || errno == ENOTCONN) {
+ // TODO(keybuk, youngki): Agree a common way to flag disconnected.
+ error_message_ = "Disconnected";
+ return false;
+ } else if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ error_message_ = safe_strerror(errno);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+std::string BluetoothSocketExperimentalChromeOS::GetLastErrorMessage() const {
+ return error_message_;
+}
+
+// static
+scoped_refptr<device::BluetoothSocket>
+BluetoothSocketExperimentalChromeOS::Create(dbus::FileDescriptor* fd) {
+ DCHECK(fd->is_valid());
+
+ BluetoothSocketExperimentalChromeOS* bluetooth_socket =
+ new BluetoothSocketExperimentalChromeOS(fd->TakeValue());;
+ return scoped_refptr<BluetoothSocketExperimentalChromeOS>(bluetooth_socket);
+}
+
+} // namespace chromeos
diff --git a/device/bluetooth/bluetooth_socket_experimental_chromeos.h b/device/bluetooth/bluetooth_socket_experimental_chromeos.h
new file mode 100644
index 0000000000..c0e1af0b06
--- /dev/null
+++ b/device/bluetooth/bluetooth_socket_experimental_chromeos.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2013 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 DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_EXPERIMENTAL_CHROMEOS_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_EXPERIMENTAL_CHROMEOS_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "chromeos/chromeos_export.h"
+#include "device/bluetooth/bluetooth_socket.h"
+
+namespace dbus {
+
+class FileDescriptor;
+
+} // namespace dbus
+
+namespace net {
+
+class DrainableIOBuffer;
+class GrowableIOBuffer;
+
+} // namespace net
+
+namespace chromeos {
+
+// The BluetoothSocketExperimentalChromeOS class is an alternate implementation
+// of BluetoothSocket for the Chrome OS platform using the Bluetooth Smart
+// capable backend. It will become the sole implementation for Chrome OS, and
+// be renamed to BluetoothSocketChromeOS, once the backend is switched.
+class CHROMEOS_EXPORT BluetoothSocketExperimentalChromeOS
+ : public device::BluetoothSocket {
+ public:
+ // BluetoothSocket override.
+ virtual bool Receive(net::GrowableIOBuffer* buffer) OVERRIDE;
+ virtual bool Send(net::DrainableIOBuffer* buffer) OVERRIDE;
+ virtual std::string GetLastErrorMessage() const OVERRIDE;
+
+ // Create an instance of a BluetoothSocket from the passed file descriptor
+ // received over D-Bus in |fd|, the descriptor will be taken from that object
+ // and ownership passed to the returned object.
+ static scoped_refptr<device::BluetoothSocket> Create(
+ dbus::FileDescriptor* fd);
+
+ protected:
+ virtual ~BluetoothSocketExperimentalChromeOS();
+
+ private:
+ BluetoothSocketExperimentalChromeOS(int fd);
+
+ // The different socket types have different reading patterns; l2cap sockets
+ // have to be read with boundaries between datagrams preserved while rfcomm
+ // sockets do not.
+ enum SocketType {
+ L2CAP,
+ RFCOMM
+ };
+
+ // File descriptor and socket type of the socket.
+ const int fd_;
+ SocketType socket_type_;
+
+ // Last error message, set during Receive() and Send() and retrieved using
+ // GetLastErrorMessage().
+ std::string error_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothSocketExperimentalChromeOS);
+};
+
+} // namespace chromeos
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_EXPERIMENTAL_CHROMEOS_H_
diff --git a/device/device.gyp b/device/device.gyp
index 12de0c438f..68cb89d6a5 100644
--- a/device/device.gyp
+++ b/device/device.gyp
@@ -46,6 +46,8 @@
'bluetooth/bluetooth_out_of_band_pairing_data.h',
'bluetooth/bluetooth_profile.cc',
'bluetooth/bluetooth_profile.h',
+ 'bluetooth/bluetooth_profile_experimental_chromeos.cc',
+ 'bluetooth/bluetooth_profile_experimental_chromeos.h',
'bluetooth/bluetooth_profile_mac.h',
'bluetooth/bluetooth_profile_mac.mm',
'bluetooth/bluetooth_profile_win.cc',
@@ -61,6 +63,8 @@
'bluetooth/bluetooth_socket.h',
'bluetooth/bluetooth_socket_chromeos.cc',
'bluetooth/bluetooth_socket_chromeos.h',
+ 'bluetooth/bluetooth_socket_experimental_chromeos.cc',
+ 'bluetooth/bluetooth_socket_experimental_chromeos.h',
'bluetooth/bluetooth_socket_mac.h',
'bluetooth/bluetooth_socket_mac.mm',
'bluetooth/bluetooth_socket_win.cc',
@@ -173,6 +177,7 @@
'bluetooth/bluetooth_adapter_win_unittest.cc',
'bluetooth/bluetooth_device_win_unittest.cc',
'bluetooth/bluetooth_experimental_chromeos_unittest.cc',
+ 'bluetooth/bluetooth_profile_chromeos_unittest.cc',
'bluetooth/bluetooth_service_record_chromeos_unittest.cc',
'bluetooth/bluetooth_service_record_mac_unittest.mm',
'bluetooth/bluetooth_service_record_win_unittest.cc',