Commit 5c8589ac authored by bryeung@chromium.org's avatar bryeung@chromium.org

Add support for creating bluetooth RFCOMM sockets.

BUG=119473
TEST=added some unittests

Review URL: http://codereview.chromium.org/10007008

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@133485 0039d316-1c4b-4281-b951-d872f2087c98
parent 77e07b84
...@@ -4,17 +4,20 @@ ...@@ -4,17 +4,20 @@
#include "chrome/browser/chromeos/bluetooth/bluetooth_device.h" #include "chrome/browser/chromeos/bluetooth/bluetooth_device.h"
#include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/string16.h" #include "base/string16.h"
#include "base/string_util.h" #include "base/string_util.h"
#include "base/utf_string_conversions.h" #include "base/utf_string_conversions.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/chromeos/bluetooth/bluetooth_adapter.h" #include "chrome/browser/chromeos/bluetooth/bluetooth_adapter.h"
#include "chrome/browser/chromeos/bluetooth/bluetooth_service_record.h" #include "chrome/browser/chromeos/bluetooth/bluetooth_service_record.h"
#include "chrome/browser/chromeos/bluetooth/bluetooth_socket.h"
#include "chrome/browser/chromeos/dbus/introspect_util.h" #include "chrome/browser/chromeos/dbus/introspect_util.h"
#include "chromeos/dbus/bluetooth_adapter_client.h" #include "chromeos/dbus/bluetooth_adapter_client.h"
#include "chromeos/dbus/bluetooth_agent_service_provider.h" #include "chromeos/dbus/bluetooth_agent_service_provider.h"
...@@ -198,7 +201,7 @@ void BluetoothDevice::SearchServicesForNameCallback( ...@@ -198,7 +201,7 @@ void BluetoothDevice::SearchServicesForNameCallback(
for (BluetoothDeviceClient::ServiceMap::const_iterator i = for (BluetoothDeviceClient::ServiceMap::const_iterator i =
service_map.begin(); i != service_map.end(); ++i) { service_map.begin(); i != service_map.end(); ++i) {
BluetoothServiceRecord service_record(i->second); BluetoothServiceRecord service_record(address(), i->second);
if (service_record.name() == name) { if (service_record.name() == name) {
callback.Run(true); callback.Run(true);
return; return;
...@@ -451,6 +454,47 @@ void BluetoothDevice::Forget(ErrorCallback error_callback) { ...@@ -451,6 +454,47 @@ void BluetoothDevice::Forget(ErrorCallback error_callback) {
error_callback)); error_callback));
} }
void BluetoothDevice::ConnectToMatchingService(
const std::string& service_uuid,
SocketCallback callback,
const dbus::ObjectPath& object_path,
const BluetoothDeviceClient::ServiceMap& service_map,
bool success) {
if (success) {
// If multiple service records are found, use the first one that works.
for (BluetoothDeviceClient::ServiceMap::const_iterator i =
service_map.begin(); i != service_map.end(); ++i) {
BluetoothServiceRecord service_record(address(), i->second);
scoped_refptr<BluetoothSocket> socket(
BluetoothSocket::CreateBluetoothSocket(service_record));
if (socket.get() != NULL) {
callback.Run(socket);
break;
}
}
}
callback.Run(NULL);
}
void BluetoothDevice::ConnectToService(const std::string& service_uuid,
SocketCallback callback) {
// quick sanity check
if (!ProvidesServiceWithUUID(service_uuid)) {
callback.Run(NULL);
return;
}
DBusThreadManager::Get()->GetBluetoothDeviceClient()->
DiscoverServices(
object_path_,
service_uuid,
base::Bind(&BluetoothDevice::ConnectToMatchingService,
weak_ptr_factory_.GetWeakPtr(),
service_uuid,
callback));
}
void BluetoothDevice::ForgetCallback(ErrorCallback error_callback, void BluetoothDevice::ForgetCallback(ErrorCallback error_callback,
const dbus::ObjectPath& adapter_path, const dbus::ObjectPath& adapter_path,
bool success) { bool success) {
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/string16.h" #include "base/string16.h"
#include "chromeos/dbus/bluetooth_agent_service_provider.h" #include "chromeos/dbus/bluetooth_agent_service_provider.h"
#include "chromeos/dbus/bluetooth_device_client.h" #include "chromeos/dbus/bluetooth_device_client.h"
...@@ -20,6 +21,7 @@ ...@@ -20,6 +21,7 @@
namespace chromeos { namespace chromeos {
class BluetoothAdapter; class BluetoothAdapter;
class BluetoothSocket;
// The BluetoothDevice class represents a remote Bluetooth device, both // The BluetoothDevice class represents a remote Bluetooth device, both
// its properties and capabilities as discovered by a local adapter and // its properties and capabilities as discovered by a local adapter and
...@@ -249,6 +251,20 @@ class BluetoothDevice : private BluetoothDeviceClient::Observer, ...@@ -249,6 +251,20 @@ class BluetoothDevice : private BluetoothDeviceClient::Observer,
// have been deleted. If the request fails, |error_callback| will be called. // have been deleted. If the request fails, |error_callback| will be called.
void Forget(ErrorCallback error_callback); void Forget(ErrorCallback error_callback);
// SocketCallback is used by ConnectToService to return a BluetoothSocket
// to the caller, or NULL if there was an error. The socket will remain open
// until the last reference to the returned BluetoothSocket is released.
typedef base::Callback<void(scoped_refptr<BluetoothSocket>)> SocketCallback;
// Attempts to open a socket to a service matching |uuid| on this device. If
// the connection is successful, |callback| is called with a BluetoothSocket.
// Otherwise |callback| is called with NULL. The socket is closed as soon as
// all references to the BluetoothSocket are released. Note that the
// BluetoothSocket object can outlive both this BluetoothDevice and the
// BluetoothAdapter for this device.
void ConnectToService(const std::string& service_uuid,
SocketCallback callback);
private: private:
friend class BluetoothAdapter; friend class BluetoothAdapter;
...@@ -327,6 +343,17 @@ class BluetoothDevice : private BluetoothDeviceClient::Observer, ...@@ -327,6 +343,17 @@ class BluetoothDevice : private BluetoothDeviceClient::Observer,
const BluetoothDeviceClient::ServiceMap& service_map, const BluetoothDeviceClient::ServiceMap& service_map,
bool success); bool success);
// Called by BluetoothDeviceClient when a call to DiscoverServices() that was
// initated from ConnectToService completes. The |callback| is called with
// true iff a connection was successfully established. The rest of the
// parameters are as documented for a BluetoothDeviceClient::ServicesCallback.
void ConnectToMatchingService(
const std::string& service_uuid,
SocketCallback callback,
const dbus::ObjectPath& object_path,
const BluetoothDeviceClient::ServiceMap& service_map,
bool success);
// BluetoothDeviceClient::Observer override. // BluetoothDeviceClient::Observer override.
// //
// Called when the device with object path |object_path| is about // Called when the device with object path |object_path| is about
......
...@@ -5,18 +5,26 @@ ...@@ -5,18 +5,26 @@
#include "chrome/browser/chromeos/bluetooth/bluetooth_service_record.h" #include "chrome/browser/chromeos/bluetooth/bluetooth_service_record.h"
#include <string> #include <string>
#include <vector>
#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "chrome/common/libxml_utils.h" #include "chrome/common/libxml_utils.h"
namespace { namespace {
static const char* kAttributeNode = "attribute"; static const char* kAttributeNode = "attribute";
static const char* kIdAttribute = "id"; static const char* kIdAttribute = "id";
static const char* kProtocolDescriptorListId = "0x0004";
static const char* kRfcommUuid = "0x0003";
static const char* kSdpNameId = "0x0100"; static const char* kSdpNameId = "0x0100";
static const char* kSequenceNode = "sequence";
static const char* kTextNode = "text"; static const char* kTextNode = "text";
static const char* kUint8Node = "uint8";
static const char* kUuidNode = "uuid";
static const char* kValueAttribute = "value"; static const char* kValueAttribute = "value";
bool advanceToTag(XmlReader* reader, const char* node_type) { bool AdvanceToTag(XmlReader* reader, const char* node_type) {
do { do {
if (!reader->Read()) if (!reader->Read())
return false; return false;
...@@ -24,29 +32,70 @@ bool advanceToTag(XmlReader* reader, const char* node_type) { ...@@ -24,29 +32,70 @@ bool advanceToTag(XmlReader* reader, const char* node_type) {
return true; return true;
} }
bool ExtractTextValue(XmlReader* reader, std::string* value_out) {
if (AdvanceToTag(reader, kTextNode)) {
reader->NodeAttribute(kValueAttribute, value_out);
return true;
}
return false;
}
} // namespace } // namespace
namespace chromeos { namespace chromeos {
BluetoothServiceRecord::BluetoothServiceRecord(const std::string& xml_data) { BluetoothServiceRecord::BluetoothServiceRecord(
const std::string& address,
const std::string& xml_data)
: address_(address),
supports_rfcomm_(false) {
XmlReader reader; XmlReader reader;
if (!reader.Load(xml_data)) if (!reader.Load(xml_data))
return; return;
while (advanceToTag(&reader, kAttributeNode)) { while (AdvanceToTag(&reader, kAttributeNode)) {
std::string id; std::string id;
if (reader.NodeAttribute(kIdAttribute, &id)) { if (reader.NodeAttribute(kIdAttribute, &id)) {
if (id == kSdpNameId) { if (id == kSdpNameId) {
if (advanceToTag(&reader, kTextNode)) { ExtractTextValue(&reader, &name_);
std::string value; } else if (id == kProtocolDescriptorListId) {
if (reader.NodeAttribute(kValueAttribute, &value)) if (AdvanceToTag(&reader, kSequenceNode)) {
name_ = value; ExtractChannels(&reader);
} }
} }
} }
// We don't care about anything else here, so find the closing tag // We don't care about anything else here, so find the closing tag
advanceToTag(&reader, kAttributeNode); AdvanceToTag(&reader, kAttributeNode);
} }
} }
void BluetoothServiceRecord::ExtractChannels(XmlReader* reader) {
const int start_depth = reader->Depth();
do {
if (reader->NodeName() == kSequenceNode) {
if (AdvanceToTag(reader, kUuidNode)) {
std::string type;
if (reader->NodeAttribute(kValueAttribute, &type) &&
type == kRfcommUuid) {
if (AdvanceToTag(reader, kUint8Node)) {
std::string channel_string;
if (reader->NodeAttribute(kValueAttribute, &channel_string)) {
std::vector<uint8> channel_bytes;
if (base::HexStringToBytes(channel_string.substr(2),
&channel_bytes)) {
if (channel_bytes.size() == 1) {
rfcomm_channel_ = channel_bytes[0];
supports_rfcomm_ = true;
}
}
}
}
}
}
}
} while (AdvanceToTag(reader, kSequenceNode) &&
reader->Depth() != start_depth);
}
} // namespace chromeos } // namespace chromeos
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "base/basictypes.h" #include "base/basictypes.h"
class XmlReader;
namespace chromeos { namespace chromeos {
// The BluetoothServiceRecord represents an SDP service record. // The BluetoothServiceRecord represents an SDP service record.
...@@ -18,13 +20,32 @@ namespace chromeos { ...@@ -18,13 +20,32 @@ namespace chromeos {
// that have been necessary so far. // that have been necessary so far.
class BluetoothServiceRecord { class BluetoothServiceRecord {
public: public:
explicit BluetoothServiceRecord(const std::string& xml_data); BluetoothServiceRecord(
const std::string& address,
const std::string& xml_data);
// The human-readable name of this service.
const std::string& name() const { return name_; } const std::string& name() const { return name_; }
// The address of the BluetoothDevice providing this service.
const std::string& address() const { return address_; }
// Indicates if this service supports RFCOMM communication.
bool SupportsRfcomm() const { return supports_rfcomm_; }
// The RFCOMM channel to use, if this service supports RFCOMM communication.
// The return value is undefined if SupportsRfcomm() returns false.
uint8_t rfcomm_channel() const { return rfcomm_channel_; }
private: private:
void ExtractChannels(XmlReader* reader);
std::string address_;
std::string name_; std::string name_;
bool supports_rfcomm_;
uint8_t rfcomm_channel_;
DISALLOW_COPY_AND_ASSIGN(BluetoothServiceRecord); DISALLOW_COPY_AND_ASSIGN(BluetoothServiceRecord);
}; };
......
// Copyright (c) 2012 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 <string>
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "chrome/browser/chromeos/bluetooth/bluetooth_service_record.h"
#include "chrome/common/chrome_paths.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
static const char* kAddress = "01:02:03:04:05:06";
} // namespace
namespace chromeos {
class BluetoothServiceRecordTest : public testing::Test {
public:
FilePath GetTestDataFilePath(const char* file) {
FilePath path;
PathService::Get(chrome::DIR_TEST_DATA, &path);
path = path.AppendASCII("chromeos");
path = path.AppendASCII("bluetooth");
path = path.AppendASCII(file);
return path;
}
};
TEST_F(BluetoothServiceRecordTest, RfcommService) {
std::string xml_data;
file_util::ReadFileToString(GetTestDataFilePath("rfcomm.xml"), &xml_data);
BluetoothServiceRecord service_record(kAddress, xml_data);
EXPECT_EQ(kAddress, service_record.address());
EXPECT_EQ("Headset Audio Gateway", service_record.name());
EXPECT_TRUE(service_record.SupportsRfcomm());
EXPECT_EQ((uint8_t)12, service_record.rfcomm_channel());
}
} // namespace chromeos
// Copyright (c) 2012 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 "chrome/browser/chromeos/bluetooth/bluetooth_socket.h"
#include <vector>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "chrome/browser/chromeos/bluetooth/bluetooth_service_record.h"
#include "chrome/browser/chromeos/bluetooth/bluetooth_utils.h"
namespace chromeos {
BluetoothSocket::BluetoothSocket(const std::string& address, int fd)
: address_(address),
fd_(fd) {
}
BluetoothSocket::~BluetoothSocket() {
close(fd_);
}
// static
scoped_refptr<BluetoothSocket> BluetoothSocket::CreateBluetoothSocket(
const BluetoothServiceRecord& service_record) {
BluetoothSocket* bluetooth_socket = NULL;
if (service_record.SupportsRfcomm()) {
int socket_fd = socket(
AF_BLUETOOTH, SOCK_STREAM | SOCK_NONBLOCK, BTPROTO_RFCOMM);
struct sockaddr_rc socket_address = { 0 };
socket_address.rc_family = AF_BLUETOOTH;
socket_address.rc_channel = service_record.rfcomm_channel();
bdaddr_t bluetooth_address;
bluetooth_utils::str2ba(service_record.address(), &bluetooth_address);
int status = connect(socket_fd, (struct sockaddr *)&socket_address,
sizeof(socket_address));
if (status == 0 || errno == EINPROGRESS)
bluetooth_socket = new BluetoothSocket(service_record.address(),
socket_fd);
else
close(socket_fd);
} else {
// TODO(bryeung): add support for L2CAP sockets as well.
return NULL;
}
return scoped_refptr<BluetoothSocket>(bluetooth_socket);
}
} // namespace chromeos
// Copyright (c) 2012 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 CHROME_BROWSER_CHROMEOS_BLUETOOTH_BLUETOOTH_SOCKET_H_
#define CHROME_BROWSER_CHROMEOS_BLUETOOTH_BLUETOOTH_SOCKET_H_
#pragma once
#include <string>
#include "base/memory/weak_ptr.h"
#include "chrome/browser/chromeos/bluetooth/bluetooth_service_record.h"
namespace chromeos {
class BluetoothDevice;
// The BluetoothSocket class represents a socket to a specific service on
// a BluetoothDevice. BluetoothSocket objects are ref counted and may outlive
// both the BluetoothDevice and BluetoothAdapter that were involved in their
// creation.
class BluetoothSocket : public base::RefCounted<BluetoothSocket> {
public:
static scoped_refptr<BluetoothSocket> CreateBluetoothSocket(
const BluetoothServiceRecord& service_record);
int fd() const { return fd_; }
private:
friend class base::RefCounted<BluetoothSocket>;
BluetoothSocket(const std::string& address, int fd);
virtual ~BluetoothSocket();
const std::string address_;
const int fd_;
DISALLOW_COPY_AND_ASSIGN(BluetoothSocket);
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_BLUETOOTH_BLUETOOTH_SOCKET_H_
// Copyright (c) 2012 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 "chrome/browser/chromeos/bluetooth/bluetooth_utils.h"
#include <vector>
#include <bluetooth/bluetooth.h>
#include "base/logging.h"
#include "base/string_number_conversions.h"
namespace chromeos {
namespace bluetooth_utils {
bool str2ba(const std::string& in_address, bdaddr_t* out_address) {
if (!out_address)
return false;
memset(out_address, 0, sizeof(*out_address));
if (in_address.size() != 17)
return false;
std::string numbers_only;
for (int i = 0; i < 6; ++i) {
numbers_only += in_address.substr(i * 3, 2);
}
std::vector<uint8> address_bytes;
if (base::HexStringToBytes(numbers_only, &address_bytes)) {
if (address_bytes.size() == 6) {
for (int i = 0; i < 6; ++i) {
out_address->b[i] = address_bytes[i];
}
return true;
}
}
return false;
}
} // namespace bluetooth_utils
} // namespace chromeos
// Copyright (c) 2012 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 CHROME_BROWSER_CHROMEOS_BLUETOOTH_BLUETOOTH_UTILS_H_
#define CHROME_BROWSER_CHROMEOS_BLUETOOTH_BLUETOOTH_UTILS_H_
#pragma once
#include <string>
#include <bluetooth/bluetooth.h>
namespace chromeos {
namespace bluetooth_utils {
// Converts a bluetooth address in the format "B0:D0:9C:0F:3A:2D" into a
// bdaddr_t struct. Returns true on success, false on failure. The contents
// of |out_address| are zeroed on failure.
bool str2ba(const std::string& in_address, bdaddr_t* out_address);
} // namespace bluetooth_utils
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_BLUETOOTH_BLUETOOTH_UTILS_H_
// Copyright (c) 2012 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 <bluetooth/bluetooth.h>
#include "chrome/browser/chromeos/bluetooth/bluetooth_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
TEST(BluetoothUtilsTest, str2ba) {
bdaddr_t bluetooth_address;
EXPECT_TRUE(bluetooth_utils::str2ba("01:02:03:0A:10:A0", &bluetooth_address));
EXPECT_EQ(1, bluetooth_address.b[0]);
EXPECT_EQ(2, bluetooth_address.b[1]);
EXPECT_EQ(3, bluetooth_address.b[2]);
EXPECT_EQ(10, bluetooth_address.b[3]);
EXPECT_EQ(16, bluetooth_address.b[4]);
EXPECT_EQ(160, bluetooth_address.b[5]);
EXPECT_FALSE(bluetooth_utils::str2ba("obviously wrong", &bluetooth_address));
EXPECT_FALSE(bluetooth_utils::str2ba("00:00", &bluetooth_address));
EXPECT_FALSE(bluetooth_utils::str2ba("00:00:00:00:00:00:00",
&bluetooth_address));
EXPECT_FALSE(bluetooth_utils::str2ba("01:02:03:0A:10:A0", NULL));
}
} // namespace chromeos
...@@ -414,6 +414,10 @@ ...@@ -414,6 +414,10 @@
'browser/chromeos/bluetooth/bluetooth_device.h', 'browser/chromeos/bluetooth/bluetooth_device.h',
'browser/chromeos/bluetooth/bluetooth_service_record.cc', 'browser/chromeos/bluetooth/bluetooth_service_record.cc',
'browser/chromeos/bluetooth/bluetooth_service_record.h', 'browser/chromeos/bluetooth/bluetooth_service_record.h',
'browser/chromeos/bluetooth/bluetooth_socket.cc',
'browser/chromeos/bluetooth/bluetooth_socket.h',
'browser/chromeos/bluetooth/bluetooth_utils.cc',
'browser/chromeos/bluetooth/bluetooth_utils.h',
'browser/chromeos/boot_times_loader.cc', 'browser/chromeos/boot_times_loader.cc',
'browser/chromeos/boot_times_loader.h', 'browser/chromeos/boot_times_loader.h',
'browser/chromeos/choose_mobile_network_dialog.cc', 'browser/chromeos/choose_mobile_network_dialog.cc',
......
...@@ -1186,6 +1186,8 @@ ...@@ -1186,6 +1186,8 @@
'browser/chrome_browser_application_mac_unittest.mm', 'browser/chrome_browser_application_mac_unittest.mm',
'browser/chrome_browser_main_unittest.cc', 'browser/chrome_browser_main_unittest.cc',
'browser/chrome_page_zoom_unittest.cc', 'browser/chrome_page_zoom_unittest.cc',
'browser/chromeos/bluetooth/bluetooth_service_record_unittest.cc',
'browser/chromeos/bluetooth/bluetooth_utils_unittest.cc',
'browser/chromeos/cros/cros_network_functions_unittest.cc', 'browser/chromeos/cros/cros_network_functions_unittest.cc',
'browser/chromeos/cros/mock_chromeos_network.cc', 'browser/chromeos/cros/mock_chromeos_network.cc',
'browser/chromeos/cros/mock_chromeos_network.h', 'browser/chromeos/cros/mock_chromeos_network.h',
......
<?xml version="1.0" encoding="UTF-8" ?>
<record>
<attribute id="0x0000">
<uint32 value="0x00010003" />
</attribute>
<attribute id="0x0001">
<sequence>
<uuid value="0x1112" />
<uuid value="0x1203" />
</sequence>
</attribute>
<attribute id="0x0004">
<sequence>
<sequence>
<uuid value="0x0100" />
</sequence>
<sequence>
<uuid value="0x0003" />
<uint8 value="0x0c" />
</sequence>
</sequence>
</attribute>
<attribute id="0x0005">
<sequence>
<uuid value="0x1002" />
</sequence>
</attribute>
<attribute id="0x0009">
<sequence>
<sequence>
<uuid value="0x1108" />
<uint16 value="0x0102" />
</sequence>
</sequence>
</attribute>
<attribute id="0x0100">
<text value="Headset Audio Gateway" />
</attribute>
</record>
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