summaryrefslogtreecommitdiffstats
path: root/device
diff options
context:
space:
mode:
authorrpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-31 12:17:25 +0000
committerrpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-31 12:17:25 +0000
commitf1ef22779257ffc38e326c89b227016b837de8c2 (patch)
tree148b5f74cb2471203c47c1c9b8b4954bc1e8197a /device
parent9a391ca240057108941495db69c9c55d0613d167 (diff)
downloadchromium_src-f1ef22779257ffc38e326c89b227016b837de8c2.zip
chromium_src-f1ef22779257ffc38e326c89b227016b837de8c2.tar.gz
chromium_src-f1ef22779257ffc38e326c89b227016b837de8c2.tar.bz2
Improve processing of Bluetooth device discovery on Windows.
* Merge all events into DevicesPolled event, as this better reflects how the underlying discovery work in Windows. * Process the list of devices from DevicesPolled event with more granularity so we can detect adding, removing and updating single devices, and raise the appropriate events to the Chrome App. Add unit tests. * Don't emit "DevicesPolled" events when the worker thread encounters issues enumerating devices and services. Instead, log warnings and ignore the result of the enumeration. This is required to avoid a Chrome App being notified of inconsistent events if the worker thread can't successfully enumerate all devices/services. * Fix call to BluetoothFindDeviceOpen to use LUP_FLUSHCACHE when in "discovery" mode, ensuring services are discovered using a active SDP request. BUG=396337 Review URL: https://codereview.chromium.org/424093004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@286755 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'device')
-rw-r--r--device/bluetooth/bluetooth_adapter_win.cc73
-rw-r--r--device/bluetooth/bluetooth_adapter_win.h9
-rw-r--r--device/bluetooth/bluetooth_adapter_win_unittest.cc116
-rw-r--r--device/bluetooth/bluetooth_device_win.cc104
-rw-r--r--device/bluetooth/bluetooth_device_win.h17
-rw-r--r--device/bluetooth/bluetooth_device_win_unittest.cc40
-rw-r--r--device/bluetooth/bluetooth_low_energy_win.cc10
-rw-r--r--device/bluetooth/bluetooth_low_energy_win.h4
-rw-r--r--device/bluetooth/bluetooth_service_record_win.cc7
-rw-r--r--device/bluetooth/bluetooth_service_record_win.h2
-rw-r--r--device/bluetooth/bluetooth_task_manager_win.cc444
-rw-r--r--device/bluetooth/bluetooth_task_manager_win.h74
12 files changed, 640 insertions, 260 deletions
diff --git a/device/bluetooth/bluetooth_adapter_win.cc b/device/bluetooth/bluetooth_adapter_win.cc
index 2074986..cd673b0 100644
--- a/device/bluetooth/bluetooth_adapter_win.cc
+++ b/device/bluetooth/bluetooth_adapter_win.cc
@@ -209,33 +209,76 @@ void BluetoothAdapterWin::AdapterStateChanged(
}
}
-void BluetoothAdapterWin::DevicesDiscovered(
+void BluetoothAdapterWin::DevicesPolled(
const ScopedVector<BluetoothTaskManagerWin::DeviceState>& devices) {
DCHECK(thread_checker_.CalledOnValidThread());
+
+ // We are receiving a new list of all devices known to the system. Merge this
+ // new list with the list we know of (|devices_|) and raise corresponding
+ // DeviceAdded, DeviceRemoved and DeviceChanged events.
+
+ typedef std::set<std::string> DeviceAddressSet;
+ DeviceAddressSet known_devices;
+ for (DevicesMap::const_iterator iter = devices_.begin();
+ iter != devices_.end();
+ ++iter) {
+ known_devices.insert((*iter).first);
+ }
+
+ DeviceAddressSet new_devices;
for (ScopedVector<BluetoothTaskManagerWin::DeviceState>::const_iterator iter =
devices.begin();
iter != devices.end();
++iter) {
- if (discovered_devices_.find((*iter)->address) ==
- discovered_devices_.end()) {
- BluetoothDeviceWin device_win(
- **iter, ui_task_runner_, socket_thread_, NULL, net::NetLog::Source());
- FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
- DeviceAdded(this, &device_win));
- discovered_devices_.insert((*iter)->address);
- }
+ new_devices.insert((*iter)->address);
}
-}
-void BluetoothAdapterWin::DevicesUpdated(
- const ScopedVector<BluetoothTaskManagerWin::DeviceState>& devices) {
- STLDeleteValues(&devices_);
+ // Process device removal first
+ DeviceAddressSet removed_devices =
+ base::STLSetDifference<DeviceAddressSet>(known_devices, new_devices);
+ for (DeviceAddressSet::const_iterator iter = removed_devices.begin();
+ iter != removed_devices.end();
+ ++iter) {
+ BluetoothDevice* device_win = devices_[(*iter)];
+ devices_.erase(*iter);
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ DeviceRemoved(this, device_win));
+ delete device_win;
+ }
+
+ // Process added and (maybe) changed devices in one pass
+ DeviceAddressSet added_devices =
+ base::STLSetDifference<DeviceAddressSet>(new_devices, known_devices);
+ DeviceAddressSet changed_devices =
+ base::STLSetIntersection<DeviceAddressSet>(known_devices, new_devices);
for (ScopedVector<BluetoothTaskManagerWin::DeviceState>::const_iterator iter =
devices.begin();
iter != devices.end();
++iter) {
- devices_[(*iter)->address] = new BluetoothDeviceWin(
- **iter, ui_task_runner_, socket_thread_, NULL, net::NetLog::Source());
+ BluetoothTaskManagerWin::DeviceState* device_state = (*iter);
+ if (added_devices.find(device_state->address) != added_devices.end()) {
+ BluetoothDeviceWin* device_win =
+ new BluetoothDeviceWin(*device_state,
+ ui_task_runner_,
+ socket_thread_,
+ NULL,
+ net::NetLog::Source());
+ devices_[device_state->address] = device_win;
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ DeviceAdded(this, device_win));
+ } else if (changed_devices.find(device_state->address) !=
+ changed_devices.end()) {
+ BluetoothDeviceWin* device_win =
+ static_cast<BluetoothDeviceWin*>(devices_[device_state->address]);
+ if (!device_win->IsEqual(*device_state)) {
+ device_win->Update(*device_state);
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ DeviceChanged(this, device_win));
+ }
+ }
}
}
diff --git a/device/bluetooth/bluetooth_adapter_win.h b/device/bluetooth/bluetooth_adapter_win.h
index eb69003..a6b3609 100644
--- a/device/bluetooth/bluetooth_adapter_win.h
+++ b/device/bluetooth/bluetooth_adapter_win.h
@@ -71,13 +71,8 @@ class BluetoothAdapterWin : public BluetoothAdapter,
const BluetoothTaskManagerWin::AdapterState& state) OVERRIDE;
virtual void DiscoveryStarted(bool success) OVERRIDE;
virtual void DiscoveryStopped() OVERRIDE;
- virtual void DevicesDiscovered(
- const ScopedVector<BluetoothTaskManagerWin::DeviceState>& devices)
- OVERRIDE;
-
- virtual void DevicesUpdated(
- const ScopedVector<BluetoothTaskManagerWin::DeviceState>& devices)
- OVERRIDE;
+ virtual void DevicesPolled(const ScopedVector<
+ BluetoothTaskManagerWin::DeviceState>& devices) OVERRIDE;
const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner() const {
return ui_task_runner_;
diff --git a/device/bluetooth/bluetooth_adapter_win_unittest.cc b/device/bluetooth/bluetooth_adapter_win_unittest.cc
index c3dc910..ed3a304 100644
--- a/device/bluetooth/bluetooth_adapter_win_unittest.cc
+++ b/device/bluetooth/bluetooth_adapter_win_unittest.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/memory/ref_counted.h"
+#include "base/strings/string_number_conversions.h"
#include "base/test/test_simple_task_runner.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_win.h"
@@ -18,6 +19,13 @@ namespace {
const char kAdapterAddress[] = "A1:B2:C3:D4:E5:F6";
const char kAdapterName[] = "Bluetooth Adapter Name";
+const char kTestAudioSdpName[] = "Audio";
+const char kTestAudioSdpName2[] = "Audio2";
+const char kTestAudioSdpBytes[] =
+ "35510900000a00010001090001350319110a09000435103506190100090019350619001909"
+ "010209000535031910020900093508350619110d090102090100250c417564696f20536f75"
+ "726365090311090001";
+const device::BluetoothUUID kTestAudioSdpUuid("110a");
void MakeDeviceState(const std::string& name,
const std::string& address,
@@ -31,11 +39,15 @@ void MakeDeviceState(const std::string& name,
class AdapterObserver : public device::BluetoothAdapter::Observer {
public:
- AdapterObserver()
- : num_present_changed_(0),
- num_powered_changed_(0),
- num_discovering_changed_(0),
- num_device_added_(0) {
+ AdapterObserver() { ResetCounters(); }
+
+ void ResetCounters() {
+ num_present_changed_ = 0;
+ num_powered_changed_ = 0;
+ num_discovering_changed_ = 0;
+ num_device_added_ = 0;
+ num_device_removed_ = 0;
+ num_device_changed_ = 0;
}
virtual void AdapterPresentChanged(
@@ -59,27 +71,35 @@ class AdapterObserver : public device::BluetoothAdapter::Observer {
num_device_added_++;
}
- int num_present_changed() const {
- return num_present_changed_;
+ virtual void DeviceRemoved(device::BluetoothAdapter* adapter,
+ device::BluetoothDevice* device) OVERRIDE {
+ num_device_removed_++;
}
- int num_powered_changed() const {
- return num_powered_changed_;
+ virtual void DeviceChanged(device::BluetoothAdapter* adapter,
+ device::BluetoothDevice* device) OVERRIDE {
+ num_device_changed_++;
}
- int num_discovering_changed() const {
- return num_discovering_changed_;
- }
+ int num_present_changed() const { return num_present_changed_; }
- int num_device_added() const {
- return num_device_added_;
- }
+ int num_powered_changed() const { return num_powered_changed_; }
+
+ int num_discovering_changed() const { return num_discovering_changed_; }
+
+ int num_device_added() const { return num_device_added_; }
+
+ int num_device_removed() const { return num_device_removed_; }
+
+ int num_device_changed() const { return num_device_changed_; }
private:
int num_present_changed_;
int num_powered_changed_;
int num_discovering_changed_;
int num_device_added_;
+ int num_device_removed_;
+ int num_device_changed_;
};
} // namespace
@@ -487,23 +507,79 @@ TEST_F(BluetoothAdapterWinTest, StopDiscoveryBeforeDiscoveryStartedAndFailed) {
EXPECT_EQ(0, adapter_observer_.num_discovering_changed());
}
-TEST_F(BluetoothAdapterWinTest, DevicesDiscovered) {
+TEST_F(BluetoothAdapterWinTest, DevicesPolled) {
BluetoothTaskManagerWin::DeviceState* android_phone_state =
new BluetoothTaskManagerWin::DeviceState();
- MakeDeviceState("phone", "android phone address", android_phone_state);
+ MakeDeviceState("phone", "A1:B2:C3:D4:E5:E0", android_phone_state);
BluetoothTaskManagerWin::DeviceState* laptop_state =
new BluetoothTaskManagerWin::DeviceState();
- MakeDeviceState("laptop", "laptop address", laptop_state);
+ MakeDeviceState("laptop", "A1:B2:C3:D4:E5:E1", laptop_state);
BluetoothTaskManagerWin::DeviceState* iphone_state =
new BluetoothTaskManagerWin::DeviceState();
- MakeDeviceState("phone", "iphone address", iphone_state);
+ MakeDeviceState("phone", "A1:B2:C3:D4:E5:E2", iphone_state);
ScopedVector<BluetoothTaskManagerWin::DeviceState> devices;
devices.push_back(android_phone_state);
devices.push_back(laptop_state);
devices.push_back(iphone_state);
- adapter_win_->DevicesDiscovered(devices);
+ // Add 3 devices
+ adapter_observer_.ResetCounters();
+ adapter_win_->DevicesPolled(devices);
EXPECT_EQ(3, adapter_observer_.num_device_added());
+ EXPECT_EQ(0, adapter_observer_.num_device_removed());
+ EXPECT_EQ(0, adapter_observer_.num_device_changed());
+
+ // Change a device name
+ android_phone_state->name = "phone2";
+ adapter_observer_.ResetCounters();
+ adapter_win_->DevicesPolled(devices);
+ EXPECT_EQ(0, adapter_observer_.num_device_added());
+ EXPECT_EQ(0, adapter_observer_.num_device_removed());
+ EXPECT_EQ(1, adapter_observer_.num_device_changed());
+
+ // Change a device address
+ android_phone_state->address = "A1:B2:C3:D4:E5:E6";
+ adapter_observer_.ResetCounters();
+ adapter_win_->DevicesPolled(devices);
+ EXPECT_EQ(1, adapter_observer_.num_device_added());
+ EXPECT_EQ(1, adapter_observer_.num_device_removed());
+ EXPECT_EQ(0, adapter_observer_.num_device_changed());
+
+ // Remove a device
+ devices.erase(devices.begin());
+ adapter_observer_.ResetCounters();
+ adapter_win_->DevicesPolled(devices);
+ EXPECT_EQ(0, adapter_observer_.num_device_added());
+ EXPECT_EQ(1, adapter_observer_.num_device_removed());
+ EXPECT_EQ(0, adapter_observer_.num_device_changed());
+
+ // Add a service
+ BluetoothTaskManagerWin::ServiceRecordState* audio_state =
+ new BluetoothTaskManagerWin::ServiceRecordState();
+ audio_state->name = kTestAudioSdpName;
+ base::HexStringToBytes(kTestAudioSdpBytes, &audio_state->sdp_bytes);
+ laptop_state->service_record_states.push_back(audio_state);
+ adapter_observer_.ResetCounters();
+ adapter_win_->DevicesPolled(devices);
+ EXPECT_EQ(0, adapter_observer_.num_device_added());
+ EXPECT_EQ(0, adapter_observer_.num_device_removed());
+ EXPECT_EQ(1, adapter_observer_.num_device_changed());
+
+ // Change a service
+ audio_state->name = kTestAudioSdpName2;
+ adapter_observer_.ResetCounters();
+ adapter_win_->DevicesPolled(devices);
+ EXPECT_EQ(0, adapter_observer_.num_device_added());
+ EXPECT_EQ(0, adapter_observer_.num_device_removed());
+ EXPECT_EQ(1, adapter_observer_.num_device_changed());
+
+ // Remove a service
+ laptop_state->service_record_states.clear();
+ adapter_observer_.ResetCounters();
+ adapter_win_->DevicesPolled(devices);
+ EXPECT_EQ(0, adapter_observer_.num_device_added());
+ EXPECT_EQ(0, adapter_observer_.num_device_removed());
+ EXPECT_EQ(1, adapter_observer_.num_device_changed());
}
} // namespace device
diff --git a/device/bluetooth/bluetooth_device_win.cc b/device/bluetooth/bluetooth_device_win.cc
index db79c6a..67c2aec 100644
--- a/device/bluetooth/bluetooth_device_win.cc
+++ b/device/bluetooth/bluetooth_device_win.cc
@@ -7,6 +7,7 @@
#include <string>
#include "base/basictypes.h"
+#include "base/containers/scoped_ptr_hash_map.h"
#include "base/logging.h"
#include "base/memory/scoped_vector.h"
#include "base/sequenced_task_runner.h"
@@ -26,9 +27,9 @@ const int kSdpBytesBufferSize = 1024;
namespace device {
BluetoothDeviceWin::BluetoothDeviceWin(
- const BluetoothTaskManagerWin::DeviceState& state,
- scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
- scoped_refptr<BluetoothSocketThread> socket_thread,
+ const BluetoothTaskManagerWin::DeviceState& device_state,
+ const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
+ const scoped_refptr<BluetoothSocketThread>& socket_thread,
net::NetLog* net_log,
const net::NetLog::Source& net_log_source)
: BluetoothDevice(),
@@ -36,25 +37,100 @@ BluetoothDeviceWin::BluetoothDeviceWin(
socket_thread_(socket_thread),
net_log_(net_log),
net_log_source_(net_log_source) {
- name_ = state.name;
- address_ = CanonicalizeAddress(state.address);
- bluetooth_class_ = state.bluetooth_class;
- visible_ = state.visible;
- connected_ = state.connected;
- paired_ = state.authenticated;
+ Update(device_state);
+}
+
+BluetoothDeviceWin::~BluetoothDeviceWin() {
+}
+
+void BluetoothDeviceWin::Update(
+ const BluetoothTaskManagerWin::DeviceState& device_state) {
+ address_ = device_state.address;
+ // Note: Callers are responsible for providing a canonicalized address.
+ DCHECK_EQ(address_, BluetoothDevice::CanonicalizeAddress(address_));
+ name_ = device_state.name;
+ bluetooth_class_ = device_state.bluetooth_class;
+ visible_ = device_state.visible;
+ connected_ = device_state.connected;
+ paired_ = device_state.authenticated;
+ UpdateServices(device_state);
+}
+
+void BluetoothDeviceWin::UpdateServices(
+ const BluetoothTaskManagerWin::DeviceState& device_state) {
+ uuids_.clear();
+ service_record_list_.clear();
for (ScopedVector<BluetoothTaskManagerWin::ServiceRecordState>::const_iterator
- iter = state.service_record_states.begin();
- iter != state.service_record_states.end();
+ iter = device_state.service_record_states.begin();
+ iter != device_state.service_record_states.end();
++iter) {
- BluetoothServiceRecordWin* service_record = new BluetoothServiceRecordWin(
- state.address, (*iter)->name, (*iter)->sdp_bytes, (*iter)->gatt_uuid);
+ BluetoothServiceRecordWin* service_record =
+ new BluetoothServiceRecordWin(device_state.address,
+ (*iter)->name,
+ (*iter)->sdp_bytes,
+ (*iter)->gatt_uuid);
service_record_list_.push_back(service_record);
uuids_.push_back(service_record->uuid());
}
}
-BluetoothDeviceWin::~BluetoothDeviceWin() {
+bool BluetoothDeviceWin::IsEqual(
+ const BluetoothTaskManagerWin::DeviceState& device_state) {
+ if (address_ != device_state.address || name_ != device_state.name ||
+ bluetooth_class_ != device_state.bluetooth_class ||
+ visible_ != device_state.visible ||
+ connected_ != device_state.connected ||
+ paired_ != device_state.authenticated) {
+ return false;
+ }
+
+ // Checks service collection
+ typedef std::set<BluetoothUUID> UUIDSet;
+ typedef base::ScopedPtrHashMap<std::string, BluetoothServiceRecordWin>
+ ServiceRecordMap;
+
+ UUIDSet known_services;
+ for (UUIDList::const_iterator iter = uuids_.begin(); iter != uuids_.end();
+ ++iter) {
+ known_services.insert((*iter));
+ }
+
+ UUIDSet new_services;
+ ServiceRecordMap new_service_records;
+ for (ScopedVector<BluetoothTaskManagerWin::ServiceRecordState>::const_iterator
+ iter = device_state.service_record_states.begin();
+ iter != device_state.service_record_states.end();
+ ++iter) {
+ BluetoothServiceRecordWin* service_record = new BluetoothServiceRecordWin(
+ address_, (*iter)->name, (*iter)->sdp_bytes, (*iter)->gatt_uuid);
+ new_services.insert(service_record->uuid());
+ new_service_records.set(
+ service_record->uuid().canonical_value(),
+ scoped_ptr<BluetoothServiceRecordWin>(service_record));
+ }
+
+ UUIDSet removed_services =
+ base::STLSetDifference<UUIDSet>(known_services, new_services);
+ if (!removed_services.empty()) {
+ return false;
+ }
+ UUIDSet added_devices =
+ base::STLSetDifference<UUIDSet>(new_services, known_services);
+ if (!added_devices.empty()) {
+ return false;
+ }
+
+ for (ServiceRecordList::const_iterator iter = service_record_list_.begin();
+ iter != service_record_list_.end();
+ ++iter) {
+ BluetoothServiceRecordWin* service_record = (*iter);
+ BluetoothServiceRecordWin* new_service_record =
+ new_service_records.get((*iter)->uuid().canonical_value());
+ if (!service_record->IsEqual(*new_service_record))
+ return false;
+ }
+ return true;
}
void BluetoothDeviceWin::SetVisible(bool visible) {
diff --git a/device/bluetooth/bluetooth_device_win.h b/device/bluetooth/bluetooth_device_win.h
index aa262bd..8479fbe 100644
--- a/device/bluetooth/bluetooth_device_win.h
+++ b/device/bluetooth/bluetooth_device_win.h
@@ -22,9 +22,9 @@ class BluetoothSocketThread;
class BluetoothDeviceWin : public BluetoothDevice {
public:
explicit BluetoothDeviceWin(
- const BluetoothTaskManagerWin::DeviceState& state,
- scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
- scoped_refptr<BluetoothSocketThread> socket_thread,
+ const BluetoothTaskManagerWin::DeviceState& device_state,
+ const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
+ const scoped_refptr<BluetoothSocketThread>& socket_thread,
net::NetLog* net_log,
const net::NetLog::Source& net_log_source);
virtual ~BluetoothDeviceWin();
@@ -76,6 +76,14 @@ class BluetoothDeviceWin : public BluetoothDevice {
const BluetoothServiceRecordWin* GetServiceRecord(
const device::BluetoothUUID& uuid) const;
+ // Returns true if all fields and services of this instance are equal to the
+ // fields and services stored in |device_state|.
+ bool IsEqual(const BluetoothTaskManagerWin::DeviceState& device_state);
+
+ // Updates this instance with all fields and properties stored in
+ // |device_state|.
+ void Update(const BluetoothTaskManagerWin::DeviceState& device_state);
+
protected:
// BluetoothDevice override
virtual std::string GetDeviceName() const OVERRIDE;
@@ -87,6 +95,9 @@ class BluetoothDeviceWin : public BluetoothDevice {
// discovery.
void SetVisible(bool visible);
+ // Updates the services with services stored in |device_state|.
+ void UpdateServices(const BluetoothTaskManagerWin::DeviceState& device_state);
+
scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
scoped_refptr<BluetoothSocketThread> socket_thread_;
net::NetLog* net_log_;
diff --git a/device/bluetooth/bluetooth_device_win_unittest.cc b/device/bluetooth/bluetooth_device_win_unittest.cc
index ff77cd9..7fc8874 100644
--- a/device/bluetooth/bluetooth_device_win_unittest.cc
+++ b/device/bluetooth/bluetooth_device_win_unittest.cc
@@ -42,36 +42,39 @@ namespace device {
class BluetoothDeviceWinTest : public testing::Test {
public:
BluetoothDeviceWinTest() {
- BluetoothTaskManagerWin::DeviceState device_state;
- device_state.name = kDeviceName;
- device_state.address = kDeviceAddress;
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner(
+ new base::TestSimpleTaskRunner());
+ scoped_refptr<BluetoothSocketThread> socket_thread(
+ BluetoothSocketThread::Get());
// Add device with audio/video services.
+ device_state_.reset(new BluetoothTaskManagerWin::DeviceState());
+ device_state_->name = kDeviceName;
+ device_state_->address = kDeviceAddress;
+
BluetoothTaskManagerWin::ServiceRecordState* audio_state =
new BluetoothTaskManagerWin::ServiceRecordState();
audio_state->name = kTestAudioSdpName;
base::HexStringToBytes(kTestAudioSdpBytes, &audio_state->sdp_bytes);
- device_state.service_record_states.push_back(audio_state);
+ device_state_->service_record_states.push_back(audio_state);
BluetoothTaskManagerWin::ServiceRecordState* video_state =
new BluetoothTaskManagerWin::ServiceRecordState();
video_state->name = kTestVideoSdpName;
base::HexStringToBytes(kTestVideoSdpBytes, &video_state->sdp_bytes);
- device_state.service_record_states.push_back(video_state);
+ device_state_->service_record_states.push_back(video_state);
- scoped_refptr<base::SequencedTaskRunner> ui_task_runner(
- new base::TestSimpleTaskRunner());
- scoped_refptr<BluetoothSocketThread> socket_thread(
- BluetoothSocketThread::Get());
- device_.reset(new BluetoothDeviceWin(device_state,
+ device_.reset(new BluetoothDeviceWin(*device_state_,
ui_task_runner,
socket_thread,
NULL,
net::NetLog::Source()));
// Add empty device.
- device_state.service_record_states.clear();
- empty_device_.reset(new BluetoothDeviceWin(device_state,
+ empty_device_state_.reset(new BluetoothTaskManagerWin::DeviceState());
+ empty_device_state_->name = kDeviceName;
+ empty_device_state_->address = kDeviceAddress;
+ empty_device_.reset(new BluetoothDeviceWin(*empty_device_state_,
ui_task_runner,
socket_thread,
NULL,
@@ -79,8 +82,10 @@ class BluetoothDeviceWinTest : public testing::Test {
}
protected:
- scoped_ptr<BluetoothDevice> device_;
- scoped_ptr<BluetoothDevice> empty_device_;
+ scoped_ptr<BluetoothDeviceWin> device_;
+ scoped_ptr<BluetoothTaskManagerWin::DeviceState> device_state_;
+ scoped_ptr<BluetoothDeviceWin> empty_device_;
+ scoped_ptr<BluetoothTaskManagerWin::DeviceState> empty_device_state_;
};
TEST_F(BluetoothDeviceWinTest, GetUUIDs) {
@@ -94,4 +99,11 @@ TEST_F(BluetoothDeviceWinTest, GetUUIDs) {
EXPECT_EQ(0, uuids.size());
}
+TEST_F(BluetoothDeviceWinTest, IsEqual) {
+ EXPECT_TRUE(device_->IsEqual(*device_state_));
+ EXPECT_FALSE(device_->IsEqual(*empty_device_state_));
+ EXPECT_FALSE(empty_device_->IsEqual(*device_state_));
+ EXPECT_TRUE(empty_device_->IsEqual(*empty_device_state_));
+}
+
} // namespace device
diff --git a/device/bluetooth/bluetooth_low_energy_win.cc b/device/bluetooth/bluetooth_low_energy_win.cc
index b47bc9a..cf1ad4b 100644
--- a/device/bluetooth/bluetooth_low_energy_win.cc
+++ b/device/bluetooth/bluetooth_low_energy_win.cc
@@ -652,11 +652,15 @@ bool EnumerateKnownBluetoothLowEnergyDevices(
}
bool EnumerateKnownBluetoothLowEnergyServices(
- BluetoothLowEnergyDeviceInfo* device_info,
+ const base::FilePath& device_path,
ScopedVector<BluetoothLowEnergyServiceInfo>* services,
std::string* error) {
- return CollectBluetoothLowEnergyDeviceServices(
- device_info->path, services, error);
+ if (!IsBluetoothLowEnergySupported()) {
+ *error = kPlatformNotSupported;
+ return false;
+ }
+
+ return CollectBluetoothLowEnergyDeviceServices(device_path, services, error);
}
bool ExtractBluetoothAddressFromDeviceInstanceIdForTesting(
diff --git a/device/bluetooth/bluetooth_low_energy_win.h b/device/bluetooth/bluetooth_low_energy_win.h
index 46dd716..ffcb4de 100644
--- a/device/bluetooth/bluetooth_low_energy_win.h
+++ b/device/bluetooth/bluetooth_low_energy_win.h
@@ -102,12 +102,12 @@ bool EnumerateKnownBluetoothLowEnergyDevices(
std::string* error);
// Enumerates the list of known (i.e. cached) GATT services for a given
-// Bluetooth LE device |device_info| into |services|. In case of error, returns
+// Bluetooth LE device |device_path| into |services|. In case of error, returns
// false and sets |error| with an error message describing the problem. Note:
// This function returns an error if Bluetooth Low Energy is not supported on
// this Windows platform.
bool EnumerateKnownBluetoothLowEnergyServices(
- BluetoothLowEnergyDeviceInfo* device_info,
+ const base::FilePath& device_path,
ScopedVector<BluetoothLowEnergyServiceInfo>* services,
std::string* error);
diff --git a/device/bluetooth/bluetooth_service_record_win.cc b/device/bluetooth/bluetooth_service_record_win.cc
index 061316e..c4fccde 100644
--- a/device/bluetooth/bluetooth_service_record_win.cc
+++ b/device/bluetooth/bluetooth_service_record_win.cc
@@ -152,4 +152,11 @@ BluetoothServiceRecordWin::BluetoothServiceRecordWin(
}
}
+bool BluetoothServiceRecordWin::IsEqual(
+ const BluetoothServiceRecordWin& other) {
+ return device_address_ == other.device_address_ && name_ == other.name_ &&
+ uuid_ == other.uuid_ && supports_rfcomm_ == other.supports_rfcomm_ &&
+ rfcomm_channel_ == other.rfcomm_channel_;
+}
+
} // namespace device
diff --git a/device/bluetooth/bluetooth_service_record_win.h b/device/bluetooth/bluetooth_service_record_win.h
index ea1ea79..9612c9d 100644
--- a/device/bluetooth/bluetooth_service_record_win.h
+++ b/device/bluetooth/bluetooth_service_record_win.h
@@ -21,6 +21,8 @@ class BluetoothServiceRecordWin {
const std::vector<uint8>& sdp_bytes,
const BluetoothUUID& gatt_uuid);
+ bool IsEqual(const BluetoothServiceRecordWin& other);
+
// The BTH_ADDR address of the BluetoothDevice providing this service.
BTH_ADDR device_bth_addr() const { return device_bth_addr_; }
diff --git a/device/bluetooth/bluetooth_task_manager_win.cc b/device/bluetooth/bluetooth_task_manager_win.cc
index 7bfc32b..db2540b 100644
--- a/device/bluetooth/bluetooth_task_manager_win.cc
+++ b/device/bluetooth/bluetooth_task_manager_win.cc
@@ -17,6 +17,7 @@
#include "base/strings/sys_string_conversions.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/win/scoped_handle.h"
+#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_init_win.h"
#include "device/bluetooth/bluetooth_low_energy_win.h"
#include "device/bluetooth/bluetooth_service_record_win.h"
@@ -28,21 +29,31 @@ const int kNumThreadsInWorkerPool = 3;
const char kBluetoothThreadName[] = "BluetoothPollingThreadWin";
const int kMaxNumDeviceAddressChar = 127;
const int kServiceDiscoveryResultBufferSize = 5000;
-const int kMaxDeviceDiscoveryTimeout = 48;
-typedef device::BluetoothTaskManagerWin::ServiceRecordState ServiceRecordState;
+// See http://goo.gl/iNTRQe: cTimeoutMultiplier: A value that indicates the time
+// out for the inquiry, expressed in increments of 1.28 seconds. For example, an
+// inquiry of 12.8 seconds has a cTimeoutMultiplier value of 10. The maximum
+// value for this member is 48. When a value greater than 48 is used, the
+// calling function immediately fails and returns
+const int kMaxDeviceDiscoveryTimeoutMultiplier = 48;
-std::string BluetoothAddressToString(const BLUETOOTH_ADDRESS& btha) {
- return base::StringPrintf("%02X:%02X:%02X:%02X:%02X:%02X",
- btha.rgBytes[5],
- btha.rgBytes[4],
- btha.rgBytes[3],
- btha.rgBytes[2],
- btha.rgBytes[1],
- btha.rgBytes[0]);
-}
+typedef device::BluetoothTaskManagerWin::ServiceRecordState ServiceRecordState;
-device::BluetoothUUID BluetoothLowEnergyUuidToUBluetoothUuid(
+// Note: The string returned here must have the same format as
+// BluetoothDevice::CanonicalizeAddress.
+std::string BluetoothAddressToCanonicalString(const BLUETOOTH_ADDRESS& btha) {
+ std::string result = base::StringPrintf("%02X:%02X:%02X:%02X:%02X:%02X",
+ btha.rgBytes[5],
+ btha.rgBytes[4],
+ btha.rgBytes[3],
+ btha.rgBytes[2],
+ btha.rgBytes[1],
+ btha.rgBytes[0]);
+ DCHECK_EQ(result, device::BluetoothDevice::CanonicalizeAddress(result));
+ return result;
+}
+
+device::BluetoothUUID BluetoothLowEnergyUuidToBluetoothUuid(
const BTH_LE_UUID& bth_le_uuid) {
if (bth_le_uuid.IsShortUuid) {
std::string uuid_hex =
@@ -76,7 +87,7 @@ void GetAdapterState(HANDLE adapter_handle,
ERROR_SUCCESS == BluetoothGetRadioInfo(adapter_handle,
&adapter_info)) {
name = base::SysWideToUTF8(adapter_info.szName);
- address = BluetoothAddressToString(adapter_info.address);
+ address = BluetoothAddressToCanonicalString(adapter_info.address);
powered = !!BluetoothIsConnectable(adapter_handle);
}
state->name = name;
@@ -87,55 +98,13 @@ void GetAdapterState(HANDLE adapter_handle,
void GetDeviceState(const BLUETOOTH_DEVICE_INFO& device_info,
device::BluetoothTaskManagerWin::DeviceState* state) {
state->name = base::SysWideToUTF8(device_info.szName);
- state->address = BluetoothAddressToString(device_info.Address);
+ state->address = BluetoothAddressToCanonicalString(device_info.Address);
state->bluetooth_class = device_info.ulClassofDevice;
state->visible = true;
state->connected = !!device_info.fConnected;
state->authenticated = !!device_info.fAuthenticated;
}
-void DiscoverDeviceServices(
- const std::string& device_address,
- const GUID& protocol_uuid,
- ScopedVector<ServiceRecordState>* service_record_states) {
- // Bluetooth and WSAQUERYSET for Service Inquiry. See http://goo.gl/2v9pyt.
- WSAQUERYSET sdp_query;
- ZeroMemory(&sdp_query, sizeof(sdp_query));
- sdp_query.dwSize = sizeof(sdp_query);
- GUID protocol = protocol_uuid;
- sdp_query.lpServiceClassId = &protocol;
- sdp_query.dwNameSpace = NS_BTH;
- wchar_t device_address_context[kMaxNumDeviceAddressChar];
- std::size_t length = base::SysUTF8ToWide("(" + device_address + ")").copy(
- device_address_context, kMaxNumDeviceAddressChar);
- device_address_context[length] = NULL;
- sdp_query.lpszContext = device_address_context;
- HANDLE sdp_handle;
- if (ERROR_SUCCESS !=
- WSALookupServiceBegin(&sdp_query, LUP_RETURN_ALL, &sdp_handle)) {
- return;
- }
- char sdp_buffer[kServiceDiscoveryResultBufferSize];
- LPWSAQUERYSET sdp_result_data = reinterpret_cast<LPWSAQUERYSET>(sdp_buffer);
- while (true) {
- DWORD sdp_buffer_size = sizeof(sdp_buffer);
- if (ERROR_SUCCESS !=
- WSALookupServiceNext(
- sdp_handle, LUP_RETURN_ALL, &sdp_buffer_size, sdp_result_data)) {
- break;
- }
- ServiceRecordState* service_record_state = new ServiceRecordState();
- service_record_state->name =
- base::SysWideToUTF8(sdp_result_data->lpszServiceInstanceName);
- for (uint64 i = 0; i < sdp_result_data->lpBlob->cbSize; i++) {
- service_record_state->sdp_bytes.push_back(
- sdp_result_data->lpBlob->pBlobData[i]);
- }
- service_record_states->push_back(service_record_state);
- }
- WSALookupServiceEnd(sdp_handle);
-}
-
} // namespace
namespace device {
@@ -168,7 +137,8 @@ BluetoothTaskManagerWin::DeviceState::~DeviceState() {
BluetoothTaskManagerWin::BluetoothTaskManagerWin(
scoped_refptr<base::SequencedTaskRunner> ui_task_runner)
: ui_task_runner_(ui_task_runner),
- discovering_(false) {
+ discovering_(false),
+ current_logging_batch_count_(0) {
}
BluetoothTaskManagerWin::~BluetoothTaskManagerWin() {
@@ -257,6 +227,38 @@ void BluetoothTaskManagerWin::PostStopDiscoveryTask() {
base::Bind(&BluetoothTaskManagerWin::StopDiscovery, this));
}
+void BluetoothTaskManagerWin::LogPollingError(const char* message,
+ int win32_error) {
+ const int kLogPeriodInMilliseconds = 60 * 1000;
+ const int kMaxMessagesPerLogPeriod = 10;
+
+ // Check if we need to discard this message
+ if (!current_logging_batch_ticks_.is_null()) {
+ if (base::TimeTicks::Now() - current_logging_batch_ticks_ <=
+ base::TimeDelta::FromMilliseconds(kLogPeriodInMilliseconds)) {
+ if (current_logging_batch_count_ >= kMaxMessagesPerLogPeriod)
+ return;
+ } else {
+ // The batch expired, reset it to "null".
+ current_logging_batch_ticks_ = base::TimeTicks();
+ }
+ }
+
+ // Keep track of this batch of messages
+ if (current_logging_batch_ticks_.is_null()) {
+ current_logging_batch_ticks_ = base::TimeTicks::Now();
+ current_logging_batch_count_ = 0;
+ }
+ ++current_logging_batch_count_;
+
+ // Log the message
+ if (win32_error == 0)
+ LOG(WARNING) << message;
+ else
+ LOG(WARNING) << message << ": "
+ << logging::SystemErrorCodeToString(win32_error);
+}
+
void BluetoothTaskManagerWin::OnAdapterStateChanged(const AdapterState* state) {
DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
@@ -275,18 +277,11 @@ void BluetoothTaskManagerWin::OnDiscoveryStopped() {
DiscoveryStopped());
}
-void BluetoothTaskManagerWin::OnDevicesUpdated(
- const ScopedVector<DeviceState>* devices) {
- DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
- FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
- DevicesUpdated(*devices));
-}
-
-void BluetoothTaskManagerWin::OnDevicesDiscovered(
+void BluetoothTaskManagerWin::OnDevicesPolled(
const ScopedVector<DeviceState>* devices) {
DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
- FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
- DevicesDiscovered(*devices));
+ FOR_EACH_OBSERVER(
+ BluetoothTaskManagerWin::Observer, observers_, DevicesPolled(*devices));
}
void BluetoothTaskManagerWin::PollAdapter() {
@@ -372,7 +367,7 @@ void BluetoothTaskManagerWin::StopDiscovery() {
base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped, this));
}
-void BluetoothTaskManagerWin::DiscoverDevices(int timeout) {
+void BluetoothTaskManagerWin::DiscoverDevices(int timeout_multiplier) {
DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
if (!discovering_ || !adapter_handle_) {
ui_task_runner_->PostTask(
@@ -381,133 +376,252 @@ void BluetoothTaskManagerWin::DiscoverDevices(int timeout) {
return;
}
- ScopedVector<DeviceState>* device_list = new ScopedVector<DeviceState>();
- SearchDevices(timeout, false, device_list);
- if (device_list->empty()) {
- delete device_list;
- } else {
- DiscoverServices(device_list);
+ scoped_ptr<ScopedVector<DeviceState> > device_list(
+ new ScopedVector<DeviceState>());
+ if (SearchDevices(timeout_multiplier, false, device_list.get())) {
ui_task_runner_->PostTask(
FROM_HERE,
- base::Bind(&BluetoothTaskManagerWin::OnDevicesDiscovered,
+ base::Bind(&BluetoothTaskManagerWin::OnDevicesPolled,
this,
- base::Owned(device_list)));
+ base::Owned(device_list.release())));
}
- if (timeout < kMaxDeviceDiscoveryTimeout) {
- bluetooth_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&BluetoothTaskManagerWin::DiscoverDevices,
- this,
- timeout + 1));
- } else {
+ if (timeout_multiplier < kMaxDeviceDiscoveryTimeoutMultiplier)
+ ++timeout_multiplier;
+ bluetooth_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &BluetoothTaskManagerWin::DiscoverDevices, this, timeout_multiplier));
+}
+
+void BluetoothTaskManagerWin::GetKnownDevices() {
+ scoped_ptr<ScopedVector<DeviceState> > device_list(
+ new ScopedVector<DeviceState>());
+ if (SearchDevices(1, true, device_list.get())) {
ui_task_runner_->PostTask(
FROM_HERE,
- base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped, this));
- discovering_ = false;
+ base::Bind(&BluetoothTaskManagerWin::OnDevicesPolled,
+ this,
+ base::Owned(device_list.release())));
}
}
-void BluetoothTaskManagerWin::GetKnownDevices() {
- ScopedVector<DeviceState>* device_list = new ScopedVector<DeviceState>();
- SearchDevices(1, true, device_list);
-
- // Search for Bluetooth Low Energy devices
- if (win::IsBluetoothLowEnergySupported()) {
- ScopedVector<win::BluetoothLowEnergyDeviceInfo> btle_devices;
- std::string error;
- bool success =
- win::EnumerateKnownBluetoothLowEnergyDevices(&btle_devices, &error);
- if (success) {
- for (ScopedVector<win::BluetoothLowEnergyDeviceInfo>::iterator iter =
- btle_devices.begin();
- iter != btle_devices.end();
- ++iter) {
- win::BluetoothLowEnergyDeviceInfo* device_info = (*iter);
-
- DeviceState* device_state = new DeviceState();
- device_state->name = device_info->friendly_name;
- device_state->address = BluetoothAddressToString(device_info->address);
- device_state->visible = device_info->visible;
- device_state->authenticated = device_info->authenticated;
- device_state->connected = device_info->connected;
- device_state->path = device_info->path;
-
- ScopedVector<win::BluetoothLowEnergyServiceInfo> services;
- success = win::EnumerateKnownBluetoothLowEnergyServices(
- device_info, &services, &error);
- if (success) {
- for (ScopedVector<win::BluetoothLowEnergyServiceInfo>::iterator
- iter2 = services.begin();
- iter2 != services.end();
- ++iter2) {
- ServiceRecordState* service_state = new ServiceRecordState();
- service_state->gatt_uuid =
- BluetoothLowEnergyUuidToUBluetoothUuid((*iter2)->uuid);
- device_state->service_record_states.push_back(service_state);
- }
- }
- device_list->push_back(device_state);
+bool BluetoothTaskManagerWin::SearchDevices(
+ int timeout_multiplier,
+ bool search_cached_devices_only,
+ ScopedVector<DeviceState>* device_list) {
+ return SearchClassicDevices(
+ timeout_multiplier, search_cached_devices_only, device_list) &&
+ SearchLowEnergyDevices(device_list) &&
+ DiscoverServices(device_list, search_cached_devices_only);
+}
+
+bool BluetoothTaskManagerWin::SearchClassicDevices(
+ int timeout_multiplier,
+ bool search_cached_devices_only,
+ ScopedVector<DeviceState>* device_list) {
+ // Issues a device inquiry and waits for |timeout_multiplier| * 1.28 seconds.
+ BLUETOOTH_DEVICE_SEARCH_PARAMS device_search_params;
+ ZeroMemory(&device_search_params, sizeof(device_search_params));
+ device_search_params.dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS);
+ device_search_params.fReturnAuthenticated = 1;
+ device_search_params.fReturnRemembered = 1;
+ device_search_params.fReturnUnknown = (search_cached_devices_only ? 0 : 1);
+ device_search_params.fReturnConnected = 1;
+ device_search_params.fIssueInquiry = (search_cached_devices_only ? 0 : 1);
+ device_search_params.cTimeoutMultiplier = timeout_multiplier;
+
+ BLUETOOTH_DEVICE_INFO device_info;
+ ZeroMemory(&device_info, sizeof(device_info));
+ device_info.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
+ HBLUETOOTH_DEVICE_FIND handle =
+ BluetoothFindFirstDevice(&device_search_params, &device_info);
+ if (!handle) {
+ int last_error = GetLastError();
+ if (last_error == ERROR_NO_MORE_ITEMS) {
+ return true; // No devices is not an error.
+ }
+ LogPollingError("Error calling BluetoothFindFirstDevice", last_error);
+ return false;
+ }
+
+ while (true) {
+ DeviceState* device_state = new DeviceState();
+ GetDeviceState(device_info, device_state);
+ device_list->push_back(device_state);
+
+ // Reset device info before next call (as a safety precaution).
+ ZeroMemory(&device_info, sizeof(device_info));
+ device_info.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
+ if (!BluetoothFindNextDevice(handle, &device_info)) {
+ int last_error = GetLastError();
+ if (last_error == ERROR_NO_MORE_ITEMS) {
+ break; // No more items is expected error when done enumerating.
}
+ LogPollingError("Error calling BluetoothFindNextDevice", last_error);
+ BluetoothFindDeviceClose(handle);
+ return false;
}
}
- if (device_list->empty()) {
- delete device_list;
- return;
+ if (!BluetoothFindDeviceClose(handle)) {
+ LogPollingError("Error calling BluetoothFindDeviceClose", GetLastError());
+ return false;
}
- DiscoverServices(device_list);
- ui_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&BluetoothTaskManagerWin::OnDevicesUpdated,
- this,
- base::Owned(device_list)));
+ return true;
}
-void BluetoothTaskManagerWin::SearchDevices(
- int timeout,
- bool search_cached_devices_only,
+bool BluetoothTaskManagerWin::SearchLowEnergyDevices(
ScopedVector<DeviceState>* device_list) {
- BLUETOOTH_DEVICE_SEARCH_PARAMS device_search_params = {
- sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS),
- 1, // return authenticated devices
- 1, // return remembered devicess
- search_cached_devices_only ? 0 : 1, // return unknown devices
- 1, // return connected devices
- search_cached_devices_only ? 0 : 1, // issue a new inquiry
- timeout, // timeout for the inquiry in increments of 1.28 seconds
- adapter_handle_
- };
-
- BLUETOOTH_DEVICE_INFO device_info = { sizeof(BLUETOOTH_DEVICE_INFO), 0 };
- // Issues a device inquiry and waits for |timeout| * 1.28 seconds.
- HBLUETOOTH_DEVICE_FIND handle =
- BluetoothFindFirstDevice(&device_search_params, &device_info);
- if (handle) {
- do {
- DeviceState* device_state = new DeviceState();
- GetDeviceState(device_info, device_state);
- device_list->push_back(device_state);
- } while (BluetoothFindNextDevice(handle, &device_info));
-
- BluetoothFindDeviceClose(handle);
+ if (!win::IsBluetoothLowEnergySupported())
+ return true; // Bluetooth LE not supported is not an error.
+
+ ScopedVector<win::BluetoothLowEnergyDeviceInfo> btle_devices;
+ std::string error;
+ bool success =
+ win::EnumerateKnownBluetoothLowEnergyDevices(&btle_devices, &error);
+ if (!success) {
+ LogPollingError(error.c_str(), 0);
+ return false;
+ }
+
+ for (ScopedVector<win::BluetoothLowEnergyDeviceInfo>::iterator iter =
+ btle_devices.begin();
+ iter != btle_devices.end();
+ ++iter) {
+ win::BluetoothLowEnergyDeviceInfo* device_info = (*iter);
+ DeviceState* device_state = new DeviceState();
+ device_state->name = device_info->friendly_name;
+ device_state->address =
+ BluetoothAddressToCanonicalString(device_info->address);
+ device_state->visible = device_info->visible;
+ device_state->authenticated = device_info->authenticated;
+ device_state->connected = device_info->connected;
+ device_state->path = device_info->path;
+ device_list->push_back(device_state);
}
+ return true;
}
-void BluetoothTaskManagerWin::DiscoverServices(
- ScopedVector<DeviceState>* device_list) {
+bool BluetoothTaskManagerWin::DiscoverServices(
+ ScopedVector<DeviceState>* device_list,
+ bool search_cached_services_only) {
DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
net::EnsureWinsockInit();
for (ScopedVector<DeviceState>::iterator iter = device_list->begin();
iter != device_list->end();
++iter) {
- const std::string device_address = (*iter)->address;
+ DeviceState* device = (*iter);
ScopedVector<ServiceRecordState>* service_record_states =
&(*iter)->service_record_states;
- DiscoverDeviceServices(
- device_address, L2CAP_PROTOCOL_UUID, service_record_states);
+ if ((*iter)->is_bluetooth_classic()) {
+ if (!DiscoverClassicDeviceServices(device->address,
+ L2CAP_PROTOCOL_UUID,
+ search_cached_services_only,
+ service_record_states)) {
+ return false;
+ }
+ } else {
+ if (!DiscoverLowEnergyDeviceServices(device->path,
+ service_record_states)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool BluetoothTaskManagerWin::DiscoverClassicDeviceServices(
+ const std::string& device_address,
+ const GUID& protocol_uuid,
+ bool search_cached_services_only,
+ ScopedVector<ServiceRecordState>* service_record_states) {
+ // Bluetooth and WSAQUERYSET for Service Inquiry. See http://goo.gl/2v9pyt.
+ WSAQUERYSET sdp_query;
+ ZeroMemory(&sdp_query, sizeof(sdp_query));
+ sdp_query.dwSize = sizeof(sdp_query);
+ GUID protocol = protocol_uuid;
+ sdp_query.lpServiceClassId = &protocol;
+ sdp_query.dwNameSpace = NS_BTH;
+ wchar_t device_address_context[kMaxNumDeviceAddressChar];
+ std::size_t length = base::SysUTF8ToWide("(" + device_address + ")").copy(
+ device_address_context, kMaxNumDeviceAddressChar);
+ device_address_context[length] = NULL;
+ sdp_query.lpszContext = device_address_context;
+ DWORD control_flags = LUP_RETURN_ALL;
+ // See http://goo.gl/t1Hulo: "Applications should generally specify
+ // LUP_FLUSHCACHE. This flag instructs the system to ignore any cached
+ // information and establish an over-the-air SDP connection to the specified
+ // device to perform the SDP search. This non-cached operation may take
+ // several seconds (whereas a cached search returns quickly)."
+ // In summary, we need to specify LUP_FLUSHCACHE if we want to obtain the list
+ // of services for devices which have not been discovered before.
+ if (!search_cached_services_only)
+ control_flags |= LUP_FLUSHCACHE;
+ HANDLE sdp_handle;
+ if (ERROR_SUCCESS !=
+ WSALookupServiceBegin(&sdp_query, control_flags, &sdp_handle)) {
+ LogPollingError("Error calling WSALookupServiceBegin", WSAGetLastError());
+ return false;
+ }
+ char sdp_buffer[kServiceDiscoveryResultBufferSize];
+ LPWSAQUERYSET sdp_result_data = reinterpret_cast<LPWSAQUERYSET>(sdp_buffer);
+ while (true) {
+ DWORD sdp_buffer_size = sizeof(sdp_buffer);
+ if (ERROR_SUCCESS !=
+ WSALookupServiceNext(
+ sdp_handle, control_flags, &sdp_buffer_size, sdp_result_data)) {
+ int last_error = WSAGetLastError();
+ if (last_error == WSA_E_NO_MORE || last_error == WSAENOMORE) {
+ break;
+ }
+ LogPollingError("Error calling WSALookupServiceNext", last_error);
+ WSALookupServiceEnd(sdp_handle);
+ return false;
+ }
+ ServiceRecordState* service_record_state = new ServiceRecordState();
+ service_record_state->name =
+ base::SysWideToUTF8(sdp_result_data->lpszServiceInstanceName);
+ for (uint64 i = 0; i < sdp_result_data->lpBlob->cbSize; i++) {
+ service_record_state->sdp_bytes.push_back(
+ sdp_result_data->lpBlob->pBlobData[i]);
+ }
+ service_record_states->push_back(service_record_state);
+ }
+ if (ERROR_SUCCESS != WSALookupServiceEnd(sdp_handle)) {
+ LogPollingError("Error calling WSALookupServiceEnd", WSAGetLastError());
+ return false;
+ }
+
+ return true;
+}
+
+bool BluetoothTaskManagerWin::DiscoverLowEnergyDeviceServices(
+ const base::FilePath& device_path,
+ ScopedVector<ServiceRecordState>* service_record_states) {
+ if (!win::IsBluetoothLowEnergySupported())
+ return true; // Bluetooth LE not supported is not an error.
+
+ std::string error;
+ ScopedVector<win::BluetoothLowEnergyServiceInfo> services;
+ bool success = win::EnumerateKnownBluetoothLowEnergyServices(
+ device_path, &services, &error);
+ if (!success) {
+ LogPollingError(error.c_str(), 0);
+ return false;
+ }
+
+ for (ScopedVector<win::BluetoothLowEnergyServiceInfo>::iterator iter2 =
+ services.begin();
+ iter2 != services.end();
+ ++iter2) {
+ ServiceRecordState* service_state = new ServiceRecordState();
+ service_state->gatt_uuid =
+ BluetoothLowEnergyUuidToBluetoothUuid((*iter2)->uuid);
+ service_record_states->push_back(service_state);
}
+ return true;
}
} // namespace device
diff --git a/device/bluetooth/bluetooth_task_manager_win.h b/device/bluetooth/bluetooth_task_manager_win.h
index 7f6c988..be8b90c 100644
--- a/device/bluetooth/bluetooth_task_manager_win.h
+++ b/device/bluetooth/bluetooth_task_manager_win.h
@@ -46,9 +46,9 @@ class BluetoothTaskManagerWin
struct ServiceRecordState {
ServiceRecordState();
~ServiceRecordState();
- // Properties common to Bluetooth Radio and LE devices.
+ // Properties common to Bluetooth Classic and LE devices.
std::string name;
- // Properties specific to Bluetooth Radio devices.
+ // Properties specific to Bluetooth Classic devices.
std::vector<uint8> sdp_bytes;
// Properties specific to Bluetooth LE devices.
BluetoothUUID gatt_uuid;
@@ -57,14 +57,17 @@ class BluetoothTaskManagerWin
struct DeviceState {
DeviceState();
~DeviceState();
- // Properties common to Bluetooth Radio and LE devices.
- std::string name;
- std::string address;
+
+ bool is_bluetooth_classic() const { return path.empty(); }
+
+ // Properties common to Bluetooth Classic and LE devices.
+ std::string address; // This uniquely identifies the device.
+ std::string name; // Friendly name
bool visible;
bool connected;
bool authenticated;
ScopedVector<ServiceRecordState> service_record_states;
- // Properties specific to Bluetooth Radio devices.
+ // Properties specific to Bluetooth Classic devices.
uint32 bluetooth_class;
// Properties specific to Bluetooth LE devices.
base::FilePath path;
@@ -77,8 +80,14 @@ class BluetoothTaskManagerWin
virtual void AdapterStateChanged(const AdapterState& state) {}
virtual void DiscoveryStarted(bool success) {}
virtual void DiscoveryStopped() {}
- virtual void DevicesUpdated(const ScopedVector<DeviceState>& devices) {}
- virtual void DevicesDiscovered(const ScopedVector<DeviceState>& devices) {}
+ // Called when the adapter has just been polled for the list of *all* known
+ // devices. This includes devices previously paired, devices paired using
+ // the underlying Operating System UI, and devices discovered recently due
+ // to an active discovery session. Note that for a given device (address),
+ // the associated state can change over time. For example, during a
+ // discovery session, the "friendly" name may initially be "unknown" before
+ // the actual name is retrieved in subsequent poll events.
+ virtual void DevicesPolled(const ScopedVector<DeviceState>& devices) {}
};
explicit BluetoothTaskManagerWin(
@@ -107,13 +116,16 @@ class BluetoothTaskManagerWin
virtual ~BluetoothTaskManagerWin();
+ // Logs Win32 errors occuring during polling on the worker thread. The method
+ // may discards messages to avoid logging being too verbose.
+ void LogPollingError(const char* message, int win32_error);
+
// Notify all Observers of updated AdapterState. Should only be called on the
// UI thread.
void OnAdapterStateChanged(const AdapterState* state);
void OnDiscoveryStarted(bool success);
void OnDiscoveryStopped();
- void OnDevicesUpdated(const ScopedVector<DeviceState>* devices);
- void OnDevicesDiscovered(const ScopedVector<DeviceState>* devices);
+ void OnDevicesPolled(const ScopedVector<DeviceState>* devices);
// Called on BluetoothTaskRunner.
void StartPolling();
@@ -130,23 +142,47 @@ class BluetoothTaskManagerWin
void StartDiscovery();
void StopDiscovery();
- // Issues a device inquiry that runs for |timeout| * 1.28 seconds.
- // This posts itself again with |timeout| + 1 until |timeout| reaches the
- // maximum value or stop discovery call is received.
- void DiscoverDevices(int timeout);
+ // Issues a device inquiry that runs for |timeout_multiplier| * 1.28 seconds.
+ // This posts itself again with |timeout_multiplier| + 1 until
+ // |timeout_multiplier| reaches the maximum value or stop discovery call is
+ // received.
+ void DiscoverDevices(int timeout_multiplier);
// Fetch already known device information. Similar to |StartDiscovery|, except
// this function does not issue a discovery inquiry. Instead it gets the
// device info cached in the adapter.
void GetKnownDevices();
- // Sends a device search API call to the adapter.
- void SearchDevices(int timeout,
+ // Looks for Bluetooth Classic and Low Energy devices, as well as the services
+ // exposed by those devices.
+ bool SearchDevices(int timeout_multiplier,
bool search_cached_devices_only,
ScopedVector<DeviceState>* device_list);
+ // Sends a device search API call to the adapter to look for Bluetooth Classic
+ // devices.
+ bool SearchClassicDevices(int timeout_multiplier,
+ bool search_cached_devices_only,
+ ScopedVector<DeviceState>* device_list);
+
+ // Enumerate Bluetooth Low Energy devices.
+ bool SearchLowEnergyDevices(ScopedVector<DeviceState>* device_list);
+
// Discover services for the devices in |device_list|.
- void DiscoverServices(ScopedVector<DeviceState>* device_list);
+ bool DiscoverServices(ScopedVector<DeviceState>* device_list,
+ bool search_cached_services_only);
+
+ // Discover Bluetooth Classic services for the given |device_address|.
+ bool DiscoverClassicDeviceServices(
+ const std::string& device_address,
+ const GUID& protocol_uuid,
+ bool search_cached_services_only,
+ ScopedVector<ServiceRecordState>* service_record_states);
+
+ // Discover Bluetooth Low Energy services for the given |device_path|.
+ bool DiscoverLowEnergyDeviceServices(
+ const base::FilePath& device_path,
+ ScopedVector<ServiceRecordState>* service_record_states);
// UI task runner reference.
scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
@@ -163,6 +199,10 @@ class BluetoothTaskManagerWin
// indicates whether the adapter is in discovery mode or not.
bool discovering_;
+ // Use for discarding too many log messages.
+ base::TimeTicks current_logging_batch_ticks_;
+ int current_logging_batch_count_;
+
DISALLOW_COPY_AND_ASSIGN(BluetoothTaskManagerWin);
};