summaryrefslogtreecommitdiffstats
path: root/chromium/device
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/device
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/device')
-rw-r--r--chromium/device/DEPS1
-rw-r--r--chromium/device/OWNERS2
-rw-r--r--chromium/device/bluetooth/DEPS1
-rw-r--r--chromium/device/bluetooth/OWNERS7
-rw-r--r--chromium/device/bluetooth/bluetooth.gyp71
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter.cc99
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter.h307
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_chromeos.cc631
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_chromeos.h181
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_factory.cc88
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_factory.h19
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_mac.h108
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_mac.mm377
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_unittest.cc192
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_win.cc119
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_win.h60
-rw-r--r--chromium/device/bluetooth/bluetooth_adapter_win_unittest.cc74
-rw-r--r--chromium/device/bluetooth/bluetooth_channel_mac.h64
-rw-r--r--chromium/device/bluetooth/bluetooth_channel_mac.mm29
-rw-r--r--chromium/device/bluetooth/bluetooth_chromeos_unittest.cc1719
-rw-r--r--chromium/device/bluetooth/bluetooth_device.cc96
-rw-r--r--chromium/device/bluetooth/bluetooth_device.h225
-rw-r--r--chromium/device/bluetooth/bluetooth_device_chromeos.cc659
-rw-r--r--chromium/device/bluetooth/bluetooth_device_chromeos.h158
-rw-r--r--chromium/device/bluetooth/bluetooth_device_mac.h56
-rw-r--r--chromium/device/bluetooth/bluetooth_device_mac.mm235
-rw-r--r--chromium/device/bluetooth/bluetooth_device_unittest.cc59
-rw-r--r--chromium/device/bluetooth/bluetooth_device_win.cc130
-rw-r--r--chromium/device/bluetooth/bluetooth_device_win.h62
-rw-r--r--chromium/device/bluetooth/bluetooth_device_win_unittest.cc110
-rw-r--r--chromium/device/bluetooth/bluetooth_discovery_manager_mac.h66
-rw-r--r--chromium/device/bluetooth/bluetooth_discovery_manager_mac.mm222
-rw-r--r--chromium/device/bluetooth/bluetooth_discovery_session.cc65
-rw-r--r--chromium/device/bluetooth/bluetooth_discovery_session.h90
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_characteristic.cc27
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_characteristic.h210
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc1346
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_connection.cc15
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_connection.h50
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_connection_chromeos.cc107
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_connection_chromeos.h66
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_descriptor.cc91
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_descriptor.h188
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_notify_session.cc15
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_notify_session.h44
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_notify_session_chromeos.cc132
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_notify_session_chromeos.h78
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_service.cc26
-rw-r--r--chromium/device/bluetooth/bluetooth_gatt_service.h307
-rw-r--r--chromium/device/bluetooth/bluetooth_l2cap_channel_mac.h69
-rw-r--r--chromium/device/bluetooth/bluetooth_l2cap_channel_mac.mm173
-rw-r--r--chromium/device/bluetooth/bluetooth_out_of_band_pairing_data.h27
-rw-r--r--chromium/device/bluetooth/bluetooth_pairing_chromeos.cc273
-rw-r--r--chromium/device/bluetooth/bluetooth_pairing_chromeos.h146
-rw-r--r--chromium/device/bluetooth/bluetooth_profile.cc67
-rw-r--r--chromium/device/bluetooth/bluetooth_profile.h109
-rw-r--r--chromium/device/bluetooth/bluetooth_profile_chromeos.cc253
-rw-r--r--chromium/device/bluetooth/bluetooth_profile_chromeos.h127
-rw-r--r--chromium/device/bluetooth/bluetooth_profile_chromeos_unittest.cc369
-rw-r--r--chromium/device/bluetooth/bluetooth_profile_mac.h47
-rw-r--r--chromium/device/bluetooth/bluetooth_profile_mac.mm84
-rw-r--r--chromium/device/bluetooth/bluetooth_profile_win.cc47
-rw-r--r--chromium/device/bluetooth/bluetooth_profile_win.h38
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc479
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h169
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.cc137
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.h89
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service_chromeos.cc286
-rw-r--r--chromium/device/bluetooth/bluetooth_remote_gatt_service_chromeos.h158
-rw-r--r--chromium/device/bluetooth/bluetooth_rfcomm_channel_mac.h69
-rw-r--r--chromium/device/bluetooth/bluetooth_rfcomm_channel_mac.mm168
-rw-r--r--chromium/device/bluetooth/bluetooth_service_record.cc15
-rw-r--r--chromium/device/bluetooth/bluetooth_service_record.h70
-rw-r--r--chromium/device/bluetooth/bluetooth_service_record_mac.h37
-rw-r--r--chromium/device/bluetooth/bluetooth_service_record_mac.mm77
-rw-r--r--chromium/device/bluetooth/bluetooth_service_record_mac_unittest.mm152
-rw-r--r--chromium/device/bluetooth/bluetooth_service_record_win.cc19
-rw-r--r--chromium/device/bluetooth/bluetooth_service_record_win.h31
-rw-r--r--chromium/device/bluetooth/bluetooth_service_record_win_unittest.cc11
-rw-r--r--chromium/device/bluetooth/bluetooth_socket.cc11
-rw-r--r--chromium/device/bluetooth/bluetooth_socket.h73
-rw-r--r--chromium/device/bluetooth/bluetooth_socket_chromeos.cc662
-rw-r--r--chromium/device/bluetooth/bluetooth_socket_chromeos.h211
-rw-r--r--chromium/device/bluetooth/bluetooth_socket_chromeos_unittest.cc513
-rw-r--r--chromium/device/bluetooth/bluetooth_socket_mac.h206
-rw-r--r--chromium/device/bluetooth/bluetooth_socket_mac.mm968
-rw-r--r--chromium/device/bluetooth/bluetooth_socket_net.cc312
-rw-r--r--chromium/device/bluetooth/bluetooth_socket_net.h131
-rw-r--r--chromium/device/bluetooth/bluetooth_socket_thread.cc78
-rw-r--r--chromium/device/bluetooth/bluetooth_socket_thread.h50
-rw-r--r--chromium/device/bluetooth/bluetooth_socket_win.cc422
-rw-r--r--chromium/device/bluetooth/bluetooth_socket_win.h96
-rw-r--r--chromium/device/bluetooth/bluetooth_strings.grd22
-rw-r--r--chromium/device/bluetooth/bluetooth_task_manager_win.cc105
-rw-r--r--chromium/device/bluetooth/bluetooth_task_manager_win.h6
-rw-r--r--chromium/device/bluetooth/bluetooth_utils.cc59
-rw-r--r--chromium/device/bluetooth/bluetooth_utils.h31
-rw-r--r--chromium/device/bluetooth/bluetooth_utils_unittest.cc53
-rw-r--r--chromium/device/bluetooth/bluetooth_uuid.cc95
-rw-r--r--chromium/device/bluetooth/bluetooth_uuid.h97
-rw-r--r--chromium/device/bluetooth/bluetooth_uuid_unittest.cc107
-rw-r--r--chromium/device/device_tests.gyp25
-rw-r--r--chromium/device/hid/DEPS3
-rw-r--r--chromium/device/hid/device_monitor_linux.cc150
-rw-r--r--chromium/device/hid/device_monitor_linux.h75
-rw-r--r--chromium/device/hid/hid.gyp67
-rw-r--r--chromium/device/hid/hid_connection.cc22
-rw-r--r--chromium/device/hid/hid_connection.h65
-rw-r--r--chromium/device/hid/hid_connection_linux.cc210
-rw-r--r--chromium/device/hid/hid_connection_linux.h59
-rw-r--r--chromium/device/hid/hid_connection_mac.cc176
-rw-r--r--chromium/device/hid/hid_connection_mac.h81
-rw-r--r--chromium/device/hid/hid_connection_unittest.cc128
-rw-r--r--chromium/device/hid/hid_connection_win.cc303
-rw-r--r--chromium/device/hid/hid_connection_win.h57
-rw-r--r--chromium/device/hid/hid_device_info.cc25
-rw-r--r--chromium/device/hid/hid_device_info.h55
-rw-r--r--chromium/device/hid/hid_report_descriptor.cc61
-rw-r--r--chromium/device/hid/hid_report_descriptor.h38
-rw-r--r--chromium/device/hid/hid_report_descriptor_item.cc112
-rw-r--r--chromium/device/hid/hid_report_descriptor_item.h183
-rw-r--r--chromium/device/hid/hid_report_descriptor_unittest.cc613
-rw-r--r--chromium/device/hid/hid_service.cc99
-rw-r--r--chromium/device/hid/hid_service.h64
-rw-r--r--chromium/device/hid/hid_service_linux.cc202
-rw-r--r--chromium/device/hid/hid_service_linux.h42
-rw-r--r--chromium/device/hid/hid_service_mac.cc192
-rw-r--r--chromium/device/hid/hid_service_mac.h64
-rw-r--r--chromium/device/hid/hid_service_unittest.cc27
-rw-r--r--chromium/device/hid/hid_service_win.cc250
-rw-r--r--chromium/device/hid/hid_service_win.h38
-rw-r--r--chromium/device/hid/hid_usage_and_page.cc13
-rw-r--r--chromium/device/hid/hid_usage_and_page.h134
-rw-r--r--chromium/device/hid/hid_utils_mac.cc45
-rw-r--r--chromium/device/hid/hid_utils_mac.h30
-rw-r--r--chromium/device/hid/input_service_linux.cc240
-rw-r--r--chromium/device/hid/input_service_linux.h98
-rw-r--r--chromium/device/hid/input_service_linux_unittest.cc24
-rw-r--r--chromium/device/media_transfer_protocol/media_transfer_protocol_daemon_client.cc5
-rw-r--r--chromium/device/nfc/OWNERS2
-rw-r--r--chromium/device/nfc/nfc.gyp11
-rw-r--r--chromium/device/nfc/nfc_adapter.cc46
-rw-r--r--chromium/device/nfc/nfc_adapter.h19
-rw-r--r--chromium/device/nfc/nfc_adapter_chromeos.cc178
-rw-r--r--chromium/device/nfc/nfc_adapter_chromeos.h4
-rw-r--r--chromium/device/nfc/nfc_chromeos_unittest.cc675
-rw-r--r--chromium/device/nfc/nfc_ndef_record.cc55
-rw-r--r--chromium/device/nfc/nfc_ndef_record.h8
-rw-r--r--chromium/device/nfc/nfc_ndef_record_unittest.cc39
-rw-r--r--chromium/device/nfc/nfc_ndef_record_utils_chromeos.cc238
-rw-r--r--chromium/device/nfc/nfc_ndef_record_utils_chromeos.h36
-rw-r--r--chromium/device/nfc/nfc_peer.h12
-rw-r--r--chromium/device/nfc/nfc_peer_chromeos.cc194
-rw-r--r--chromium/device/nfc/nfc_peer_chromeos.h81
-rw-r--r--chromium/device/nfc/nfc_tag.h35
-rw-r--r--chromium/device/nfc/nfc_tag_chromeos.cc164
-rw-r--r--chromium/device/nfc/nfc_tag_chromeos.h70
-rw-r--r--chromium/device/nfc/nfc_tag_technology.cc7
-rw-r--r--chromium/device/nfc/nfc_tag_technology.h47
-rw-r--r--chromium/device/nfc/nfc_tag_technology_chromeos.cc186
-rw-r--r--chromium/device/nfc/nfc_tag_technology_chromeos.h87
-rw-r--r--chromium/device/serial/BUILD.gn37
-rw-r--r--chromium/device/serial/serial.gyp43
-rw-r--r--chromium/device/serial/serial.mojom16
-rw-r--r--chromium/device/serial/serial_device_enumerator.cc13
-rw-r--r--chromium/device/serial/serial_device_enumerator.h27
-rw-r--r--chromium/device/serial/serial_device_enumerator_linux.cc107
-rw-r--r--chromium/device/serial/serial_device_enumerator_linux.h36
-rw-r--r--chromium/device/serial/serial_device_enumerator_mac.cc60
-rw-r--r--chromium/device/serial/serial_device_enumerator_mac.h27
-rw-r--r--chromium/device/serial/serial_device_enumerator_win.cc41
-rw-r--r--chromium/device/serial/serial_device_enumerator_win.h27
-rw-r--r--chromium/device/udev_linux/BUILD.gn22
-rw-r--r--chromium/device/udev_linux/udev.cc27
-rw-r--r--chromium/device/udev_linux/udev.gyp30
-rw-r--r--chromium/device/udev_linux/udev.h38
-rw-r--r--chromium/device/usb/BUILD.gn31
-rw-r--r--chromium/device/usb/tools/usb_ids.py110
-rw-r--r--chromium/device/usb/usb.gyp4
179 files changed, 20194 insertions, 3959 deletions
diff --git a/chromium/device/DEPS b/chromium/device/DEPS
index 79b8e8ba13b..80961ee9052 100644
--- a/chromium/device/DEPS
+++ b/chromium/device/DEPS
@@ -1,3 +1,4 @@
include_rules = [
"+chromeos",
+ "+mojo/public",
]
diff --git a/chromium/device/OWNERS b/chromium/device/OWNERS
index a83889f764b..f6716f4c5a0 100644
--- a/chromium/device/OWNERS
+++ b/chromium/device/OWNERS
@@ -1,3 +1,5 @@
keybuk@chromium.org
gdk@chromium.org
miket@chromium.org
+rockot@chromium.org
+rpaquay@chromium.org
diff --git a/chromium/device/bluetooth/DEPS b/chromium/device/bluetooth/DEPS
index 7f93e426e9f..93c11ef1e66 100644
--- a/chromium/device/bluetooth/DEPS
+++ b/chromium/device/bluetooth/DEPS
@@ -3,6 +3,7 @@ include_rules = [
"+dbus",
"+grit",
"+net/base",
+ "+net/socket",
"+ui/base/l10n",
"+third_party/cros_system_api/dbus",
"+third_party/libxml/chromium",
diff --git a/chromium/device/bluetooth/OWNERS b/chromium/device/bluetooth/OWNERS
index 5bdf96a9ede..9226f87bcd7 100644
--- a/chromium/device/bluetooth/OWNERS
+++ b/chromium/device/bluetooth/OWNERS
@@ -1,2 +1,7 @@
+set noparent
+
keybuk@chromium.org
-youngki@chromium.org
+armansito@chromium.org
+
+per-file *_mac*=rpaquay@chromium.org
+per-file *_win*=rpaquay@chromium.org
diff --git a/chromium/device/bluetooth/bluetooth.gyp b/chromium/device/bluetooth/bluetooth.gyp
index c5d8cadec1e..4d206ecef3d 100644
--- a/chromium/device/bluetooth/bluetooth.gyp
+++ b/chromium/device/bluetooth/bluetooth.gyp
@@ -15,8 +15,9 @@
'../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
'../../net/net.gyp:net',
'../../third_party/libxml/libxml.gyp:libxml',
+ '../../ui/base/ui_base.gyp:ui_base',
'../../ui/gfx/gfx.gyp:gfx',
- '../../ui/ui.gyp:ui',
+ '../../ui/gfx/gfx.gyp:gfx_geometry',
'bluetooth_strings.gyp:device_bluetooth_strings',
],
'sources': [
@@ -30,6 +31,8 @@
'bluetooth_adapter_mac.mm',
'bluetooth_adapter_win.cc',
'bluetooth_adapter_win.h',
+ 'bluetooth_channel_mac.mm',
+ 'bluetooth_channel_mac.h',
'bluetooth_device.cc',
'bluetooth_device.h',
'bluetooth_device_chromeos.cc',
@@ -38,34 +41,56 @@
'bluetooth_device_mac.mm',
'bluetooth_device_win.cc',
'bluetooth_device_win.h',
+ 'bluetooth_discovery_manager_mac.mm',
+ 'bluetooth_discovery_manager_mac.h',
+ 'bluetooth_discovery_session.cc',
+ 'bluetooth_discovery_session.h',
+ 'bluetooth_gatt_characteristic.cc',
+ 'bluetooth_gatt_characteristic.h',
+ 'bluetooth_gatt_connection.cc',
+ 'bluetooth_gatt_connection.h',
+ 'bluetooth_gatt_connection_chromeos.cc',
+ 'bluetooth_gatt_connection_chromeos.h',
+ 'bluetooth_gatt_descriptor.cc',
+ 'bluetooth_gatt_descriptor.h',
+ 'bluetooth_gatt_notify_session.cc',
+ 'bluetooth_gatt_notify_session.h',
+ 'bluetooth_gatt_notify_session_chromeos.cc',
+ 'bluetooth_gatt_notify_session_chromeos.h',
+ 'bluetooth_gatt_service.cc',
+ 'bluetooth_gatt_service.h',
'bluetooth_init_win.cc',
'bluetooth_init_win.h',
- 'bluetooth_out_of_band_pairing_data.h',
- 'bluetooth_profile.cc',
- 'bluetooth_profile.h',
- 'bluetooth_profile_chromeos.cc',
- 'bluetooth_profile_chromeos.h',
- 'bluetooth_profile_mac.h',
- 'bluetooth_profile_mac.mm',
- 'bluetooth_profile_win.cc',
- 'bluetooth_profile_win.h',
- 'bluetooth_service_record.cc',
- 'bluetooth_service_record.h',
- 'bluetooth_service_record_mac.h',
- 'bluetooth_service_record_mac.mm',
+ 'bluetooth_l2cap_channel_mac.mm',
+ 'bluetooth_l2cap_channel_mac.h',
+ 'bluetooth_pairing_chromeos.cc',
+ 'bluetooth_pairing_chromeos.h',
+ 'bluetooth_remote_gatt_characteristic_chromeos.cc',
+ 'bluetooth_remote_gatt_characteristic_chromeos.h',
+ 'bluetooth_remote_gatt_descriptor_chromeos.cc',
+ 'bluetooth_remote_gatt_descriptor_chromeos.h',
+ 'bluetooth_remote_gatt_service_chromeos.cc',
+ 'bluetooth_remote_gatt_service_chromeos.h',
+ 'bluetooth_rfcomm_channel_mac.mm',
+ 'bluetooth_rfcomm_channel_mac.h',
'bluetooth_service_record_win.cc',
'bluetooth_service_record_win.h',
+ 'bluetooth_socket.cc',
'bluetooth_socket.h',
'bluetooth_socket_chromeos.cc',
'bluetooth_socket_chromeos.h',
'bluetooth_socket_mac.h',
'bluetooth_socket_mac.mm',
+ 'bluetooth_socket_net.cc',
+ 'bluetooth_socket_net.h',
+ 'bluetooth_socket_thread.cc',
+ 'bluetooth_socket_thread.h',
'bluetooth_socket_win.cc',
'bluetooth_socket_win.h',
'bluetooth_task_manager_win.cc',
'bluetooth_task_manager_win.h',
- 'bluetooth_utils.cc',
- 'bluetooth_utils.h',
+ 'bluetooth_uuid.cc',
+ 'bluetooth_uuid.h',
],
'conditions': [
['chromeos==1', {
@@ -112,8 +137,18 @@
'test/mock_bluetooth_adapter.h',
'test/mock_bluetooth_device.cc',
'test/mock_bluetooth_device.h',
- 'test/mock_bluetooth_profile.cc',
- 'test/mock_bluetooth_profile.h',
+ 'test/mock_bluetooth_discovery_session.cc',
+ 'test/mock_bluetooth_discovery_session.h',
+ 'test/mock_bluetooth_gatt_characteristic.cc',
+ 'test/mock_bluetooth_gatt_characteristic.h',
+ 'test/mock_bluetooth_gatt_connection.cc',
+ 'test/mock_bluetooth_gatt_connection.h',
+ 'test/mock_bluetooth_gatt_descriptor.cc',
+ 'test/mock_bluetooth_gatt_descriptor.h',
+ 'test/mock_bluetooth_gatt_notify_session.cc',
+ 'test/mock_bluetooth_gatt_notify_session.h',
+ 'test/mock_bluetooth_gatt_service.cc',
+ 'test/mock_bluetooth_gatt_service.h',
'test/mock_bluetooth_socket.cc',
'test/mock_bluetooth_socket.h',
],
diff --git a/chromium/device/bluetooth/bluetooth_adapter.cc b/chromium/device/bluetooth/bluetooth_adapter.cc
index 8cce6ece2c1..1f5922292d0 100644
--- a/chromium/device/bluetooth/bluetooth_adapter.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter.cc
@@ -4,18 +4,46 @@
#include "device/bluetooth/bluetooth_adapter.h"
+#include "base/bind.h"
#include "base/stl_util.h"
#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_discovery_session.h"
namespace device {
-BluetoothAdapter::BluetoothAdapter() {
+#if !defined(OS_CHROMEOS) && !defined(OS_WIN) && !defined(OS_MACOSX)
+//static
+base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
+ const InitCallback& init_callback) {
+ return base::WeakPtr<BluetoothAdapter>();
+}
+#endif // !defined(OS_CHROMEOS) && !defined(OS_WIN) && !defined(OS_MACOSX)
+
+const int BluetoothAdapter::kChannelAuto = 0;
+const int BluetoothAdapter::kPsmAuto = 0;
+
+BluetoothAdapter::BluetoothAdapter()
+ : weak_ptr_factory_(this) {
}
BluetoothAdapter::~BluetoothAdapter() {
STLDeleteValues(&devices_);
}
+base::WeakPtr<BluetoothAdapter> BluetoothAdapter::GetWeakPtrForTesting() {
+ return weak_ptr_factory_.GetWeakPtr();
+}
+
+void BluetoothAdapter::StartDiscoverySession(
+ const DiscoverySessionCallback& callback,
+ const ErrorCallback& error_callback) {
+ AddDiscoverySession(
+ base::Bind(&BluetoothAdapter::OnStartDiscoverySession,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ error_callback);
+}
+
BluetoothAdapter::DeviceList BluetoothAdapter::GetDevices() {
ConstDeviceList const_devices =
const_cast<const BluetoothAdapter *>(this)->GetDevices();
@@ -45,11 +73,78 @@ BluetoothDevice* BluetoothAdapter::GetDevice(const std::string& address) {
const BluetoothDevice* BluetoothAdapter::GetDevice(
const std::string& address) const {
- DevicesMap::const_iterator iter = devices_.find(address);
+ std::string canonicalized_address =
+ BluetoothDevice::CanonicalizeAddress(address);
+ if (canonicalized_address.empty())
+ return NULL;
+
+ DevicesMap::const_iterator iter = devices_.find(canonicalized_address);
if (iter != devices_.end())
return iter->second;
return NULL;
}
+void BluetoothAdapter::AddPairingDelegate(
+ BluetoothDevice::PairingDelegate* pairing_delegate,
+ PairingDelegatePriority priority) {
+ // Remove the delegate, if it already exists, before inserting to allow a
+ // change of priority.
+ RemovePairingDelegate(pairing_delegate);
+
+ // Find the first point with a lower priority, or the end of the list.
+ std::list<PairingDelegatePair>::iterator iter = pairing_delegates_.begin();
+ while (iter != pairing_delegates_.end() && iter->second >= priority)
+ ++iter;
+
+ pairing_delegates_.insert(iter, std::make_pair(pairing_delegate, priority));
+}
+
+void BluetoothAdapter::RemovePairingDelegate(
+ BluetoothDevice::PairingDelegate* pairing_delegate) {
+ for (std::list<PairingDelegatePair>::iterator iter =
+ pairing_delegates_.begin(); iter != pairing_delegates_.end(); ++iter) {
+ if (iter->first == pairing_delegate) {
+ RemovePairingDelegateInternal(pairing_delegate);
+ pairing_delegates_.erase(iter);
+ return;
+ }
+ }
+}
+
+BluetoothDevice::PairingDelegate* BluetoothAdapter::DefaultPairingDelegate() {
+ if (pairing_delegates_.empty())
+ return NULL;
+
+ return pairing_delegates_.front().first;
+}
+
+void BluetoothAdapter::OnStartDiscoverySession(
+ const DiscoverySessionCallback& callback) {
+ VLOG(1) << "Discovery session started!";
+ scoped_ptr<BluetoothDiscoverySession> discovery_session(
+ new BluetoothDiscoverySession(scoped_refptr<BluetoothAdapter>(this)));
+ discovery_sessions_.insert(discovery_session.get());
+ callback.Run(discovery_session.Pass());
+}
+
+void BluetoothAdapter::MarkDiscoverySessionsAsInactive() {
+ // As sessions are marked as inactive they will notify the adapter that they
+ // have become inactive, upon which the adapter will remove them from
+ // |discovery_sessions_|. To avoid invalidating the iterator, make a copy
+ // here.
+ std::set<BluetoothDiscoverySession*> temp(discovery_sessions_);
+ for (std::set<BluetoothDiscoverySession*>::iterator
+ iter = temp.begin();
+ iter != temp.end(); ++iter) {
+ (*iter)->MarkAsInactive();
+ }
+}
+
+void BluetoothAdapter::DiscoverySessionBecameInactive(
+ BluetoothDiscoverySession* discovery_session) {
+ DCHECK(!discovery_session->IsActive());
+ discovery_sessions_.erase(discovery_session);
+}
+
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_adapter.h b/chromium/device/bluetooth/bluetooth_adapter.h
index ed615860120..cec051fffba 100644
--- a/chromium/device/bluetooth/bluetooth_adapter.h
+++ b/chromium/device/bluetooth/bluetooth_adapter.h
@@ -5,22 +5,26 @@
#ifndef DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_H_
+#include <list>
#include <map>
+#include <set>
#include <string>
-#include <vector>
+#include <utility>
#include "base/callback.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "device/bluetooth/bluetooth_device.h"
namespace device {
-class BluetoothDevice;
-
-struct BluetoothOutOfBandPairingData;
+class BluetoothDiscoverySession;
+class BluetoothSocket;
+class BluetoothUUID;
// BluetoothAdapter represents a local Bluetooth adapter which may be used to
// interact with remote Bluetooth devices. As well as providing support for
-// determining whether an adapter is present, and whether the radio is powered,
+// determining whether an adapter is present and whether the radio is powered,
// this class also provides support for obtaining the list of remote devices
// known to the adapter, discovering new devices, and providing notification of
// updates to device information.
@@ -31,138 +35,299 @@ class BluetoothAdapter : public base::RefCounted<BluetoothAdapter> {
public:
virtual ~Observer() {}
- // Called when the presence of the adapter |adapter| changes, when
- // |present| is true the adapter is now present, false means the adapter
- // has been removed from the system.
+ // Called when the presence of the adapter |adapter| changes. When |present|
+ // is true the adapter is now present, false means the adapter has been
+ // removed from the system.
virtual void AdapterPresentChanged(BluetoothAdapter* adapter,
bool present) {}
- // Called when the radio power state of the adapter |adapter| changes,
- // when |powered| is true the adapter radio is powered, false means the
- // adapter radio is off.
+ // Called when the radio power state of the adapter |adapter| changes. When
+ // |powered| is true the adapter radio is powered, false means the adapter
+ // radio is off.
virtual void AdapterPoweredChanged(BluetoothAdapter* adapter,
bool powered) {}
- // Called when the discovering state of the adapter |adapter| changes,
- // when |discovering| is true the adapter is seeking new devices, false
- // means it is not.
+ // Called when the discoverability state of the adapter |adapter| changes.
+ // When |discoverable| is true the adapter is discoverable by other devices,
+ // false means the adapter is not discoverable.
+ virtual void AdapterDiscoverableChanged(BluetoothAdapter* adapter,
+ bool discoverable) {}
+
+ // Called when the discovering state of the adapter |adapter| changes. When
+ // |discovering| is true the adapter is seeking new devices, false means it
+ // is not.
virtual void AdapterDiscoveringChanged(BluetoothAdapter* adapter,
bool discovering) {}
// Called when a new device |device| is added to the adapter |adapter|,
// either because it has been discovered or a connection made. |device|
- // should not be cached, instead copy its address.
+ // should not be cached. Instead, copy its Bluetooth address.
virtual void DeviceAdded(BluetoothAdapter* adapter,
BluetoothDevice* device) {}
// Called when properties of the device |device| known to the adapter
- // |adapter| change. |device| should not be cached, instead copy its
- // address.
+ // |adapter| change. |device| should not be cached. Instead, copy its
+ // Bluetooth address.
virtual void DeviceChanged(BluetoothAdapter* adapter,
BluetoothDevice* device) {}
// Called when the device |device| is removed from the adapter |adapter|,
// either as a result of a discovered device being lost between discovering
- // phases or pairing information deleted. |device| should not be cached.
+ // phases or pairing information deleted. |device| should not be
+ // cached. Instead, copy its Bluetooth address.
virtual void DeviceRemoved(BluetoothAdapter* adapter,
BluetoothDevice* device) {}
};
- // The ErrorCallback is used for methods that can fail in which case it
- // is called, in the success case the callback is simply not called.
- typedef base::Callback<void()> ErrorCallback;
-
- // The BluetoothOutOfBandPairingDataCallback is used to return
- // BluetoothOutOfBandPairingData to the caller.
- typedef base::Callback<void(const BluetoothOutOfBandPairingData& data)>
- BluetoothOutOfBandPairingDataCallback;
-
- // Adds and removes observers for events on this bluetooth adapter,
- // if monitoring multiple adapters check the |adapter| parameter of
- // observer methods to determine which adapter is issuing the event.
+ // The ErrorCallback is used for methods that can fail in which case it is
+ // called, in the success case the callback is simply not called.
+ typedef base::Closure ErrorCallback;
+
+ // The InitCallback is used to trigger a callback after asynchronous
+ // initialization, if initialization is asynchronous on the platform.
+ typedef base::Callback<void()> InitCallback;
+
+ // Returns a weak pointer to a new adapter. For platforms with asynchronous
+ // initialization, the returned adapter will run the |init_callback| once
+ // asynchronous initialization is complete.
+ // Caution: The returned pointer also transfers ownership of the adapter. The
+ // caller is expected to call |AddRef()| on the returned pointer, typically by
+ // storing it into a |scoped_refptr|.
+ static base::WeakPtr<BluetoothAdapter> CreateAdapter(
+ const InitCallback& init_callback);
+
+ // Returns a weak pointer to an existing adapter for testing purposes only.
+ base::WeakPtr<BluetoothAdapter> GetWeakPtrForTesting();
+
+ // Adds and removes observers for events on this bluetooth adapter. If
+ // monitoring multiple adapters, check the |adapter| parameter of observer
+ // methods to determine which adapter is issuing the event.
virtual void AddObserver(BluetoothAdapter::Observer* observer) = 0;
virtual void RemoveObserver(
BluetoothAdapter::Observer* observer) = 0;
- // The address of this adapter. The address format is "XX:XX:XX:XX:XX:XX",
+ // The address of this adapter. The address format is "XX:XX:XX:XX:XX:XX",
// where each XX is a hexadecimal number.
virtual std::string GetAddress() const = 0;
// The name of the adapter.
virtual std::string GetName() const = 0;
+ // Set the human-readable name of the adapter to |name|. On success,
+ // |callback| will be called. On failure, |error_callback| will be called.
+ virtual void SetName(const std::string& name,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+
// Indicates whether the adapter is initialized and ready to use.
virtual bool IsInitialized() const = 0;
- // Indicates whether the adapter is actually present on the system, for
- // the default adapter this indicates whether any adapter is present. An
- // adapter is only considered present if the address has been obtained.
+ // Indicates whether the adapter is actually present on the system. For the
+ // default adapter, this indicates whether any adapter is present. An adapter
+ // is only considered present if the address has been obtained.
virtual bool IsPresent() const = 0;
// Indicates whether the adapter radio is powered.
virtual bool IsPowered() const = 0;
- // Requests a change to the adapter radio power, setting |powered| to true
- // will turn on the radio and false will turn it off. On success, callback
- // will be called. On failure, |error_callback| will be called.
+ // Requests a change to the adapter radio power. Setting |powered| to true
+ // will turn on the radio and false will turn it off. On success, |callback|
+ // will be called. On failure, |error_callback| will be called.
virtual void SetPowered(bool powered,
const base::Closure& callback,
const ErrorCallback& error_callback) = 0;
+ // Indicates whether the adapter radio is discoverable.
+ virtual bool IsDiscoverable() const = 0;
+
+ // Requests that the adapter change its discoverability state. If
+ // |discoverable| is true, then it will be discoverable by other Bluetooth
+ // devices. On successly changing the adapter's discoverability, |callback|
+ // will be called. On failure, |error_callback| will be called.
+ virtual void SetDiscoverable(bool discoverable,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+
// Indicates whether the adapter is currently discovering new devices.
virtual bool IsDiscovering() const = 0;
- // Requests that the adapter begin discovering new devices, code must
- // always call this method if they require the adapter be in discovery
- // and should not make it conditional on the value of IsDiscovering()
- // as other adapter users may be making the same request. Code must also
- // call StopDiscovering() when done. On success |callback| will be called,
- // on failure |error_callback| will be called instead.
+ // Requests the adapter to start a new discovery session. On success, a new
+ // instance of BluetoothDiscoverySession will be returned to the caller via
+ // |callback| and the adapter will be discovering nearby Bluetooth devices.
+ // The returned BluetoothDiscoverySession is owned by the caller and it's the
+ // owner's responsibility to properly clean it up and stop the session when
+ // device discovery is no longer needed.
//
- // Since discovery may already be in progress when this method is called,
- // callers should retrieve the current set of discovered devices by calling
- // GetDevices() and checking for those with IsPaired() as false.
- virtual void StartDiscovering(const base::Closure& callback,
- const ErrorCallback& error_callback) = 0;
-
- // Requests that an earlier call to StartDiscovering() be cancelled; the
- // adapter may not actually cease discovering devices if other callers
- // have called StartDiscovering() and not yet called this method. On
- // success |callback| will be called, on failure |error_callback| will be
- // called instead.
- virtual void StopDiscovering(const base::Closure& callback,
- const ErrorCallback& error_callback) = 0;
-
- // Requests the list of devices from the adapter, all are returned
- // including those currently connected and those paired. Use the
- // returned device pointers to determine which they are.
+ // If clients desire device discovery to run, they should always call this
+ // method and never make it conditional on the value of IsDiscovering(), as
+ // another client might cause discovery to stop unexpectedly. Hence, clients
+ // should always obtain a BluetoothDiscoverySession and call
+ // BluetoothDiscoverySession::Stop when done. When this method gets called,
+ // device discovery may actually be in progress. Clients can call GetDevices()
+ // and check for those with IsPaired() as false to obtain the list of devices
+ // that have been discovered so far. Otherwise, clients can be notified of all
+ // new and lost devices by implementing the Observer methods "DeviceAdded" and
+ // "DeviceRemoved".
+ typedef base::Callback<void(scoped_ptr<BluetoothDiscoverySession>)>
+ DiscoverySessionCallback;
+ virtual void StartDiscoverySession(const DiscoverySessionCallback& callback,
+ const ErrorCallback& error_callback);
+
+ // Requests the list of devices from the adapter. All devices are returned,
+ // including those currently connected and those paired. Use the returned
+ // device pointers to determine which they are.
typedef std::vector<BluetoothDevice*> DeviceList;
virtual DeviceList GetDevices();
typedef std::vector<const BluetoothDevice*> ConstDeviceList;
virtual ConstDeviceList GetDevices() const;
- // Returns a pointer to the device with the given address |address| or
- // NULL if no such device is known.
+ // Returns a pointer to the device with the given address |address| or NULL if
+ // no such device is known.
virtual BluetoothDevice* GetDevice(const std::string& address);
- virtual const BluetoothDevice* GetDevice(
- const std::string& address) const;
+ virtual const BluetoothDevice* GetDevice(const std::string& address) const;
- // Requests the local Out Of Band pairing data.
- virtual void ReadLocalOutOfBandPairingData(
- const BluetoothOutOfBandPairingDataCallback& callback,
- const ErrorCallback& error_callback) = 0;
+ // Possible priorities for AddPairingDelegate(), low is intended for
+ // permanent UI and high is intended for interactive UI or applications.
+ enum PairingDelegatePriority {
+ PAIRING_DELEGATE_PRIORITY_LOW,
+ PAIRING_DELEGATE_PRIORITY_HIGH
+ };
+
+ // Adds a default pairing delegate with priority |priority|. Method calls
+ // will be made on |pairing_delegate| for incoming pairing requests if the
+ // priority is higher than any other registered; or for those of the same
+ // priority, the first registered.
+ //
+ // |pairing_delegate| must not be freed without first calling
+ // RemovePairingDelegate().
+ virtual void AddPairingDelegate(
+ BluetoothDevice::PairingDelegate* pairing_delegate,
+ PairingDelegatePriority priority);
+
+ // Removes a previously added pairing delegate.
+ virtual void RemovePairingDelegate(
+ BluetoothDevice::PairingDelegate* pairing_delegate);
+
+ // Returns the first registered pairing delegate with the highest priority,
+ // or NULL if no delegate is registered. Used to select the delegate for
+ // incoming pairing requests.
+ virtual BluetoothDevice::PairingDelegate* DefaultPairingDelegate();
+
+ // Creates an RFCOMM service on this adapter advertised with UUID |uuid|,
+ // listening on channel |channel|, which may be the constant |kChannelAuto|
+ // to automatically allocate one. |callback| will be called on success with a
+ // BluetoothSocket instance that is to be owned by the received.
+ // |error_callback| will be called on failure with a message indicating the
+ // cause.
+ typedef base::Callback<void(scoped_refptr<BluetoothSocket>)>
+ CreateServiceCallback;
+ typedef base::Callback<void(const std::string& message)>
+ CreateServiceErrorCallback;
+ static const int kChannelAuto;
+ virtual void CreateRfcommService(
+ const BluetoothUUID& uuid,
+ int channel,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) = 0;
+
+ // Creates an L2CAP service on this adapter advertised with UUID |uuid|,
+ // listening on PSM |psm|, which may be the constant |kPsmAuto| to
+ // automatically allocate one. |callback| will be called on success with a
+ // BluetoothSocket instance that is to be owned by the received.
+ // |error_callback| will be called on failure with a message indicating the
+ // cause.
+ static const int kPsmAuto;
+ virtual void CreateL2capService(
+ const BluetoothUUID& uuid,
+ int psm,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) = 0;
protected:
friend class base::RefCounted<BluetoothAdapter>;
+ friend class BluetoothDiscoverySession;
BluetoothAdapter();
virtual ~BluetoothAdapter();
+ // Internal methods for initiating and terminating device discovery sessions.
+ // An implementation of BluetoothAdapter keeps an internal reference count to
+ // make sure that the underlying controller is constantly searching for nearby
+ // devices and retrieving information from them as long as there are clients
+ // who have requested discovery. These methods behave in the following way:
+ //
+ // On a call to AddDiscoverySession:
+ // - If there is a pending request to the subsystem, queue this request to
+ // execute once the pending requests are done.
+ // - If the count is 0, issue a request to the subsystem to start
+ // device discovery. On success, increment the count to 1.
+ // - If the count is greater than 0, increment the count and return
+ // success.
+ // As long as the count is non-zero, the underlying controller will be
+ // discovering for devices. This means that Chrome will restart device
+ // scan and inquiry sessions if they ever end, unless these sessions
+ // terminate due to an unexpected reason.
+ //
+ // On a call to RemoveDiscoverySession:
+ // - If there is a pending request to the subsystem, queue this request to
+ // execute once the pending requests are done.
+ // - If the count is 0, return failure, as there is no active discovery
+ // session.
+ // - If the count is 1, issue a request to the subsystem to stop device
+ // discovery and decrement the count to 0 on success.
+ // - If the count is greater than 1, decrement the count and return
+ // success.
+ //
+ // These methods invoke |callback| for success and |error_callback| for
+ // failures.
+ virtual void AddDiscoverySession(const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+ virtual void RemoveDiscoverySession(const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // Called by RemovePairingDelegate() in order to perform any class-specific
+ // internal functionality necessary to remove the pairing delegate, such as
+ // cleaning up ongoing pairings using it.
+ virtual void RemovePairingDelegateInternal(
+ BluetoothDevice::PairingDelegate* pairing_delegate) = 0;
+
+ // Success callback passed to AddDiscoverySession by StartDiscoverySession.
+ void OnStartDiscoverySession(const DiscoverySessionCallback& callback);
+
+ // Marks all known DiscoverySession instances as inactive. Called by
+ // BluetoothAdapter in the event that the adapter unexpectedly stops
+ // discovering. This should be called by all platform implementations.
+ void MarkDiscoverySessionsAsInactive();
+
+ // Removes |discovery_session| from |discovery_sessions_|, if its in there.
+ // Called by DiscoverySession when an instance is destroyed or becomes
+ // inactive.
+ void DiscoverySessionBecameInactive(
+ BluetoothDiscoverySession* discovery_session);
+
// Devices paired with, connected to, discovered by, or visible to the
- // adapter. The key is the Bluetooth address of the device and the value
- // is the BluetoothDevice object whose lifetime is managed by the
- // adapter instance.
+ // adapter. The key is the Bluetooth address of the device and the value is
+ // the BluetoothDevice object whose lifetime is managed by the adapter
+ // instance.
typedef std::map<const std::string, BluetoothDevice*> DevicesMap;
DevicesMap devices_;
+
+ // Default pairing delegates registered with the adapter.
+ typedef std::pair<BluetoothDevice::PairingDelegate*,
+ PairingDelegatePriority> PairingDelegatePair;
+ std::list<PairingDelegatePair> pairing_delegates_;
+
+ private:
+ // List of active DiscoverySession objects. This is used to notify sessions to
+ // become inactive in case of an unexpected change to the adapter discovery
+ // state. We keep raw pointers, with the invariant that a DiscoverySession
+ // will remove itself from this list when it gets destroyed or becomes
+ // inactive by calling DiscoverySessionBecameInactive(), hence no pointers to
+ // deallocated sessions are kept.
+ std::set<BluetoothDiscoverySession*> discovery_sessions_;
+
+ // 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<BluetoothAdapter> weak_ptr_factory_;
};
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_adapter_chromeos.cc b/chromium/device/bluetooth/bluetooth_adapter_chromeos.cc
index 319a6e99a63..c392af98fb3 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_chromeos.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_chromeos.cc
@@ -9,25 +9,82 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
+#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
#include "base/sys_info.h"
+#include "base/thread_task_runner_handle.h"
#include "chromeos/dbus/bluetooth_adapter_client.h"
+#include "chromeos/dbus/bluetooth_agent_manager_client.h"
+#include "chromeos/dbus/bluetooth_agent_service_provider.h"
#include "chromeos/dbus/bluetooth_device_client.h"
#include "chromeos/dbus/bluetooth_input_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_device_chromeos.h"
+#include "device/bluetooth/bluetooth_pairing_chromeos.h"
+#include "device/bluetooth/bluetooth_socket_chromeos.h"
+#include "device/bluetooth/bluetooth_socket_thread.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
using device::BluetoothAdapter;
using device::BluetoothDevice;
+using device::BluetoothSocket;
+using device::BluetoothUUID;
+
+namespace {
+
+// The agent path is relatively meaningless since BlueZ only permits one to
+// exist per D-Bus connection, it just has to be unique within Chromium.
+const char kAgentPath[] = "/org/chromium/bluetooth_agent";
+
+void OnUnregisterAgentError(const std::string& error_name,
+ const std::string& error_message) {
+ // It's okay if the agent didn't exist, it means we never saw an adapter.
+ if (error_name == bluetooth_agent_manager::kErrorDoesNotExist)
+ return;
+
+ LOG(WARNING) << "Failed to unregister pairing agent: "
+ << error_name << ": " << error_message;
+}
+
+} // namespace
+
+namespace device {
+
+// static
+base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
+ const InitCallback& init_callback) {
+ return chromeos::BluetoothAdapterChromeOS::CreateAdapter();
+}
+
+}
namespace chromeos {
+// static
+base::WeakPtr<BluetoothAdapter> BluetoothAdapterChromeOS::CreateAdapter() {
+ BluetoothAdapterChromeOS* adapter = new BluetoothAdapterChromeOS();
+ return adapter->weak_ptr_factory_.GetWeakPtr();
+}
+
BluetoothAdapterChromeOS::BluetoothAdapterChromeOS()
- : weak_ptr_factory_(this) {
+ : num_discovery_sessions_(0),
+ discovery_request_pending_(false),
+ weak_ptr_factory_(this) {
+ ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+ socket_thread_ = device::BluetoothSocketThread::Get();
+
DBusThreadManager::Get()->GetBluetoothAdapterClient()->AddObserver(this);
DBusThreadManager::Get()->GetBluetoothDeviceClient()->AddObserver(this);
DBusThreadManager::Get()->GetBluetoothInputClient()->AddObserver(this);
+ // Register the pairing agent.
+ dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
+ agent_.reset(BluetoothAgentServiceProvider::Create(
+ system_bus, dbus::ObjectPath(kAgentPath), this));
+ DCHECK(agent_.get());
+
std::vector<dbus::ObjectPath> object_paths =
DBusThreadManager::Get()->GetBluetoothAdapterClient()->GetAdapters();
@@ -41,6 +98,13 @@ BluetoothAdapterChromeOS::~BluetoothAdapterChromeOS() {
DBusThreadManager::Get()->GetBluetoothAdapterClient()->RemoveObserver(this);
DBusThreadManager::Get()->GetBluetoothDeviceClient()->RemoveObserver(this);
DBusThreadManager::Get()->GetBluetoothInputClient()->RemoveObserver(this);
+
+ VLOG(1) << "Unregistering pairing agent";
+ DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
+ UnregisterAgent(
+ dbus::ObjectPath(kAgentPath),
+ base::Bind(&base::DoNothing),
+ base::Bind(&OnUnregisterAgentError));
}
void BluetoothAdapterChromeOS::AddObserver(
@@ -64,7 +128,7 @@ std::string BluetoothAdapterChromeOS::GetAddress() const {
GetProperties(object_path_);
DCHECK(properties);
- return properties->address.value();
+ return BluetoothDevice::CanonicalizeAddress(properties->address.value());
}
std::string BluetoothAdapterChromeOS::GetName() const {
@@ -79,6 +143,21 @@ std::string BluetoothAdapterChromeOS::GetName() const {
return properties->alias.value();
}
+void BluetoothAdapterChromeOS::SetName(const std::string& name,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (!IsPresent())
+ error_callback.Run();
+
+ DBusThreadManager::Get()->GetBluetoothAdapterClient()->
+ GetProperties(object_path_)->alias.Set(
+ name,
+ base::Bind(&BluetoothAdapterChromeOS::OnPropertyChangeCompleted,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ error_callback));
+}
+
bool BluetoothAdapterChromeOS::IsInitialized() const {
return true;
}
@@ -102,16 +181,19 @@ void BluetoothAdapterChromeOS::SetPowered(
bool powered,
const base::Closure& callback,
const ErrorCallback& error_callback) {
+ if (!IsPresent())
+ error_callback.Run();
+
DBusThreadManager::Get()->GetBluetoothAdapterClient()->
GetProperties(object_path_)->powered.Set(
powered,
- base::Bind(&BluetoothAdapterChromeOS::OnSetPowered,
+ base::Bind(&BluetoothAdapterChromeOS::OnPropertyChangeCompleted,
weak_ptr_factory_.GetWeakPtr(),
callback,
error_callback));
}
-bool BluetoothAdapterChromeOS::IsDiscovering() const {
+bool BluetoothAdapterChromeOS::IsDiscoverable() const {
if (!IsPresent())
return false;
@@ -119,45 +201,92 @@ bool BluetoothAdapterChromeOS::IsDiscovering() const {
DBusThreadManager::Get()->GetBluetoothAdapterClient()->
GetProperties(object_path_);
- return properties->discovering.value();
+ return properties->discoverable.value();
}
-void BluetoothAdapterChromeOS::StartDiscovering(
+void BluetoothAdapterChromeOS::SetDiscoverable(
+ bool discoverable,
const base::Closure& callback,
const ErrorCallback& error_callback) {
- // BlueZ counts discovery sessions, and permits multiple sessions for a
- // single connection, so issue a StartDiscovery() call for every use
- // within Chromium for the right behavior.
+ if (!IsPresent())
+ error_callback.Run();
+
DBusThreadManager::Get()->GetBluetoothAdapterClient()->
- StartDiscovery(
- object_path_,
- base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery,
- weak_ptr_factory_.GetWeakPtr(),
- callback),
- base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError,
+ GetProperties(object_path_)->discoverable.Set(
+ discoverable,
+ base::Bind(&BluetoothAdapterChromeOS::OnSetDiscoverable,
weak_ptr_factory_.GetWeakPtr(),
+ callback,
error_callback));
}
-void BluetoothAdapterChromeOS::StopDiscovering(
- const base::Closure& callback,
- const ErrorCallback& error_callback) {
- // Inform BlueZ to stop one of our open discovery sessions.
- DBusThreadManager::Get()->GetBluetoothAdapterClient()->
- StopDiscovery(
- object_path_,
- base::Bind(&BluetoothAdapterChromeOS::OnStopDiscovery,
- weak_ptr_factory_.GetWeakPtr(),
- callback),
- base::Bind(&BluetoothAdapterChromeOS::OnStopDiscoveryError,
- weak_ptr_factory_.GetWeakPtr(),
- error_callback));
+bool BluetoothAdapterChromeOS::IsDiscovering() const {
+ if (!IsPresent())
+ return false;
+
+ BluetoothAdapterClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothAdapterClient()->
+ GetProperties(object_path_);
+
+ return properties->discovering.value();
}
-void BluetoothAdapterChromeOS::ReadLocalOutOfBandPairingData(
- const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback,
- const ErrorCallback& error_callback) {
- error_callback.Run();
+void BluetoothAdapterChromeOS::CreateRfcommService(
+ const BluetoothUUID& uuid,
+ int channel,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) {
+ VLOG(1) << object_path_.value() << ": Creating RFCOMM service: "
+ << uuid.canonical_value();
+ scoped_refptr<BluetoothSocketChromeOS> socket =
+ BluetoothSocketChromeOS::CreateBluetoothSocket(
+ ui_task_runner_,
+ socket_thread_,
+ NULL,
+ net::NetLog::Source());
+ socket->Listen(this,
+ BluetoothSocketChromeOS::kRfcomm,
+ uuid,
+ channel,
+ base::Bind(callback, socket),
+ error_callback);
+}
+
+void BluetoothAdapterChromeOS::CreateL2capService(
+ const BluetoothUUID& uuid,
+ int psm,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) {
+ VLOG(1) << object_path_.value() << ": Creating L2CAP service: "
+ << uuid.canonical_value();
+ scoped_refptr<BluetoothSocketChromeOS> socket =
+ BluetoothSocketChromeOS::CreateBluetoothSocket(
+ ui_task_runner_,
+ socket_thread_,
+ NULL,
+ net::NetLog::Source());
+ socket->Listen(this,
+ BluetoothSocketChromeOS::kL2cap,
+ uuid,
+ psm,
+ base::Bind(callback, socket),
+ error_callback);
+}
+
+void BluetoothAdapterChromeOS::RemovePairingDelegateInternal(
+ BluetoothDevice::PairingDelegate* pairing_delegate) {
+ // Before removing a pairing delegate make sure that there aren't any devices
+ // currently using it; if there are, clear the pairing context which will
+ // make any responses no-ops.
+ for (DevicesMap::iterator iter = devices_.begin();
+ iter != devices_.end(); ++iter) {
+ BluetoothDeviceChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceChromeOS*>(iter->second);
+
+ BluetoothPairingChromeOS* pairing = device_chromeos->GetPairing();
+ if (pairing && pairing->GetPairingDelegate() == pairing_delegate)
+ device_chromeos->EndPairing();
+ }
}
void BluetoothAdapterChromeOS::AdapterAdded(
@@ -185,6 +314,8 @@ void BluetoothAdapterChromeOS::AdapterPropertyChanged(
if (property_name == properties->powered.name())
PoweredChanged(properties->powered.value());
+ else if (property_name == properties->discoverable.name())
+ DiscoverableChanged(properties->discoverable.value());
else if (property_name == properties->discovering.name())
DiscoveringChanged(properties->discovering.value());
}
@@ -198,7 +329,10 @@ void BluetoothAdapterChromeOS::DeviceAdded(
return;
BluetoothDeviceChromeOS* device_chromeos =
- new BluetoothDeviceChromeOS(this, object_path);
+ new BluetoothDeviceChromeOS(this,
+ object_path,
+ ui_task_runner_,
+ socket_thread_);
DCHECK(devices_.find(device_chromeos->GetAddress()) == devices_.end());
devices_[device_chromeos->GetAddress()] = device_chromeos;
@@ -241,11 +375,27 @@ void BluetoothAdapterChromeOS::DevicePropertyChanged(
property_name == properties->paired.name() ||
property_name == properties->trusted.name() ||
property_name == properties->connected.name() ||
- property_name == properties->uuids.name())
+ property_name == properties->uuids.name() ||
+ property_name == properties->rssi.name() ||
+ property_name == properties->connection_rssi.name() ||
+ property_name == properties->connection_tx_power.name())
NotifyDeviceChanged(device_chromeos);
+ // When a device becomes paired, mark it as trusted so that the user does
+ // not need to approve every incoming connection
+ if (property_name == properties->paired.name() &&
+ properties->paired.value() && !properties->trusted.value())
+ device_chromeos->SetTrusted();
+
// UMA connection counting
if (property_name == properties->connected.name()) {
+ // PlayStation joystick tries to reconnect after disconnection from USB.
+ // If it is still not trusted, set it, so it becomes available on the
+ // list of known devices.
+ if (properties->connected.value() && device_chromeos->IsTrustable() &&
+ !properties->trusted.value())
+ device_chromeos->SetTrusted();
+
int count = 0;
for (DevicesMap::iterator iter = devices_.begin();
@@ -277,6 +427,175 @@ void BluetoothAdapterChromeOS::InputPropertyChanged(
NotifyDeviceChanged(device_chromeos);
}
+void BluetoothAdapterChromeOS::Released() {
+ DCHECK(agent_.get());
+ VLOG(1) << "Release";
+
+ // Called after we unregister the pairing agent, e.g. when changing I/O
+ // capabilities. Nothing much to be done right now.
+}
+
+void BluetoothAdapterChromeOS::RequestPinCode(
+ const dbus::ObjectPath& device_path,
+ const PinCodeCallback& callback) {
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": RequestPinCode";
+
+ BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+ if (!pairing) {
+ callback.Run(REJECTED, "");
+ return;
+ }
+
+ pairing->RequestPinCode(callback);
+}
+
+void BluetoothAdapterChromeOS::DisplayPinCode(
+ const dbus::ObjectPath& device_path,
+ const std::string& pincode) {
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": DisplayPinCode: " << pincode;
+
+ BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+ if (!pairing)
+ return;
+
+ pairing->DisplayPinCode(pincode);
+}
+
+void BluetoothAdapterChromeOS::RequestPasskey(
+ const dbus::ObjectPath& device_path,
+ const PasskeyCallback& callback) {
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": RequestPasskey";
+
+ BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+ if (!pairing) {
+ callback.Run(REJECTED, 0);
+ return;
+ }
+
+ pairing->RequestPasskey(callback);
+}
+
+void BluetoothAdapterChromeOS::DisplayPasskey(
+ const dbus::ObjectPath& device_path,
+ uint32 passkey,
+ uint16 entered) {
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": DisplayPasskey: " << passkey
+ << " (" << entered << " entered)";
+
+ BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+ if (!pairing)
+ return;
+
+ if (entered == 0)
+ pairing->DisplayPasskey(passkey);
+
+ pairing->KeysEntered(entered);
+}
+
+void BluetoothAdapterChromeOS::RequestConfirmation(
+ const dbus::ObjectPath& device_path,
+ uint32 passkey,
+ const ConfirmationCallback& callback) {
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": RequestConfirmation: " << passkey;
+
+ BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+ if (!pairing) {
+ callback.Run(REJECTED);
+ return;
+ }
+
+ pairing->RequestConfirmation(passkey, callback);
+}
+
+void BluetoothAdapterChromeOS::RequestAuthorization(
+ const dbus::ObjectPath& device_path,
+ const ConfirmationCallback& callback) {
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": RequestAuthorization";
+
+ BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+ if (!pairing) {
+ callback.Run(REJECTED);
+ return;
+ }
+
+ pairing->RequestAuthorization(callback);
+}
+
+void BluetoothAdapterChromeOS::AuthorizeService(
+ const dbus::ObjectPath& device_path,
+ const std::string& uuid,
+ const ConfirmationCallback& callback) {
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": AuthorizeService: " << uuid;
+
+ BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(device_path);
+ if (!device_chromeos) {
+ callback.Run(CANCELLED);
+ return;
+ }
+
+ // We always set paired devices to Trusted, so the only reason that this
+ // method call would ever be called is in the case of a race condition where
+ // our "Set('Trusted', true)" method call is still pending in the Bluetooth
+ // daemon because it's busy handling the incoming connection.
+ if (device_chromeos->IsPaired()) {
+ callback.Run(SUCCESS);
+ return;
+ }
+
+ // TODO(keybuk): reject service authorizations when not paired, determine
+ // whether this is acceptable long-term.
+ LOG(WARNING) << "Rejecting service connection from unpaired device "
+ << device_chromeos->GetAddress() << " for UUID " << uuid;
+ callback.Run(REJECTED);
+}
+
+void BluetoothAdapterChromeOS::Cancel() {
+ DCHECK(agent_.get());
+ VLOG(1) << "Cancel";
+}
+
+void BluetoothAdapterChromeOS::OnRegisterAgent() {
+ VLOG(1) << "Pairing agent registered, requesting to be made default";
+
+ DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
+ RequestDefaultAgent(
+ dbus::ObjectPath(kAgentPath),
+ base::Bind(&BluetoothAdapterChromeOS::OnRequestDefaultAgent,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&BluetoothAdapterChromeOS::OnRequestDefaultAgentError,
+ weak_ptr_factory_.GetWeakPtr()));
+
+}
+
+void BluetoothAdapterChromeOS::OnRegisterAgentError(
+ const std::string& error_name,
+ const std::string& error_message) {
+ // Our agent being already registered isn't an error.
+ if (error_name == bluetooth_agent_manager::kErrorAlreadyExists)
+ return;
+
+ LOG(WARNING) << ": Failed to register pairing agent: "
+ << error_name << ": " << error_message;
+}
+
+void BluetoothAdapterChromeOS::OnRequestDefaultAgent() {
+ VLOG(1) << "Pairing agent now default";
+}
+
+void BluetoothAdapterChromeOS::OnRequestDefaultAgentError(
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(WARNING) << ": Failed to make pairing agent default: "
+ << error_name << ": " << error_message;
+}
+
BluetoothDeviceChromeOS*
BluetoothAdapterChromeOS::GetDeviceWithPath(
const dbus::ObjectPath& object_path) {
@@ -291,13 +610,46 @@ BluetoothAdapterChromeOS::GetDeviceWithPath(
return NULL;
}
+BluetoothPairingChromeOS* BluetoothAdapterChromeOS::GetPairing(
+ const dbus::ObjectPath& object_path)
+{
+ BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path);
+ if (!device_chromeos) {
+ LOG(WARNING) << "Pairing Agent request for unknown device: "
+ << object_path.value();
+ return NULL;
+ }
+
+ BluetoothPairingChromeOS* pairing = device_chromeos->GetPairing();
+ if (pairing)
+ return pairing;
+
+ // The device doesn't have its own pairing context, so this is an incoming
+ // pairing request that should use our best default delegate (if we have one).
+ BluetoothDevice::PairingDelegate* pairing_delegate = DefaultPairingDelegate();
+ if (!pairing_delegate)
+ return NULL;
+
+ return device_chromeos->BeginPairing(pairing_delegate);
+}
+
void BluetoothAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) {
DCHECK(!IsPresent());
object_path_ = object_path;
VLOG(1) << object_path_.value() << ": using adapter.";
- SetAdapterName();
+ VLOG(1) << "Registering pairing agent";
+ DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
+ RegisterAgent(
+ dbus::ObjectPath(kAgentPath),
+ bluetooth_agent_manager::kKeyboardDisplayCapability,
+ base::Bind(&BluetoothAdapterChromeOS::OnRegisterAgent,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&BluetoothAdapterChromeOS::OnRegisterAgentError,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ SetDefaultAdapterName();
BluetoothAdapterClient::Properties* properties =
DBusThreadManager::Get()->GetBluetoothAdapterClient()->
@@ -307,6 +659,8 @@ void BluetoothAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) {
if (properties->powered.value())
PoweredChanged(true);
+ if (properties->discoverable.value())
+ DiscoverableChanged(true);
if (properties->discovering.value())
DiscoveringChanged(true);
@@ -316,17 +670,11 @@ void BluetoothAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) {
for (std::vector<dbus::ObjectPath>::iterator iter = device_paths.begin();
iter != device_paths.end(); ++iter) {
- BluetoothDeviceChromeOS* device_chromeos =
- new BluetoothDeviceChromeOS(this, *iter);
-
- devices_[device_chromeos->GetAddress()] = device_chromeos;
-
- FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
- DeviceAdded(this, device_chromeos));
+ DeviceAdded(*iter);
}
}
-void BluetoothAdapterChromeOS::SetAdapterName() {
+void BluetoothAdapterChromeOS::SetDefaultAdapterName() {
std::string board = base::SysInfo::GetLsbReleaseBoard();
std::string alias;
if (board.substr(0, 6) == "stumpy") {
@@ -337,16 +685,7 @@ void BluetoothAdapterChromeOS::SetAdapterName() {
alias = "Chromebook";
}
- DBusThreadManager::Get()->GetBluetoothAdapterClient()->
- GetProperties(object_path_)->alias.Set(
- alias,
- base::Bind(&BluetoothAdapterChromeOS::OnSetAlias,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void BluetoothAdapterChromeOS::OnSetAlias(bool success) {
- LOG_IF(WARNING, !success) << object_path_.value()
- << ": Failed to set adapter alias";
+ SetName(alias, base::Bind(&base::DoNothing), base::Bind(&base::DoNothing));
}
void BluetoothAdapterChromeOS::RemoveAdapter() {
@@ -361,6 +700,8 @@ void BluetoothAdapterChromeOS::RemoveAdapter() {
if (properties->powered.value())
PoweredChanged(false);
+ if (properties->discoverable.value())
+ DiscoverableChanged(false);
if (properties->discovering.value())
DiscoveringChanged(false);
@@ -384,8 +725,22 @@ void BluetoothAdapterChromeOS::PoweredChanged(bool powered) {
AdapterPoweredChanged(this, powered));
}
+void BluetoothAdapterChromeOS::DiscoverableChanged(bool discoverable) {
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ AdapterDiscoverableChanged(this, discoverable));
+}
+
void BluetoothAdapterChromeOS::DiscoveringChanged(
bool discovering) {
+ // If the adapter stopped discovery due to a reason other than a request by
+ // us, reset the count to 0.
+ VLOG(1) << "Discovering changed: " << discovering;
+ if (!discovering && !discovery_request_pending_
+ && num_discovery_sessions_ > 0) {
+ VLOG(1) << "Marking sessions as inactive.";
+ num_discovery_sessions_ = 0;
+ MarkDiscoverySessionsAsInactive();
+ }
FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
AdapterDiscoveringChanged(this, discovering));
}
@@ -403,30 +758,169 @@ void BluetoothAdapterChromeOS::NotifyDeviceChanged(
DeviceChanged(this, device));
}
-void BluetoothAdapterChromeOS::OnSetPowered(const base::Closure& callback,
- const ErrorCallback& error_callback,
- bool success) {
+void BluetoothAdapterChromeOS::OnSetDiscoverable(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback,
+ bool success) {
+ // Set the discoverable_timeout property to zero so the adapter remains
+ // discoverable forever.
+ DBusThreadManager::Get()->GetBluetoothAdapterClient()->
+ GetProperties(object_path_)->discoverable_timeout.Set(
+ 0,
+ base::Bind(&BluetoothAdapterChromeOS::OnPropertyChangeCompleted,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ error_callback));
+}
+
+void BluetoothAdapterChromeOS::OnPropertyChangeCompleted(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback,
+ bool success) {
if (success)
callback.Run();
else
error_callback.Run();
}
+void BluetoothAdapterChromeOS::AddDiscoverySession(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ VLOG(1) << __func__;
+ if (discovery_request_pending_) {
+ // The pending request is either to stop a previous session or to start a
+ // new one. Either way, queue this one.
+ DCHECK(num_discovery_sessions_ == 1 || num_discovery_sessions_ == 0);
+ VLOG(1) << "Pending request to start/stop device discovery. Queueing "
+ << "request to start a new discovery session.";
+ discovery_request_queue_.push(std::make_pair(callback, error_callback));
+ return;
+ }
+
+ // The adapter is already discovering.
+ if (num_discovery_sessions_ > 0) {
+ DCHECK(IsDiscovering());
+ DCHECK(!discovery_request_pending_);
+ num_discovery_sessions_++;
+ callback.Run();
+ return;
+ }
+
+ // There are no active discovery sessions.
+ DCHECK(num_discovery_sessions_ == 0);
+
+ // This is the first request to start device discovery.
+ discovery_request_pending_ = true;
+ DBusThreadManager::Get()->GetBluetoothAdapterClient()->
+ StartDiscovery(
+ object_path_,
+ base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ error_callback));
+}
+
+void BluetoothAdapterChromeOS::RemoveDiscoverySession(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ VLOG(1) << __func__;
+ // There are active sessions other than the one currently being removed.
+ if (num_discovery_sessions_ > 1) {
+ DCHECK(IsDiscovering());
+ DCHECK(!discovery_request_pending_);
+ num_discovery_sessions_--;
+ callback.Run();
+ return;
+ }
+
+ // If there is a pending request to BlueZ, then queue this request.
+ if (discovery_request_pending_) {
+ VLOG(1) << "Pending request to start/stop device discovery. Queueing "
+ << "request to stop discovery session.";
+ error_callback.Run();
+ return;
+ }
+
+ // There are no active sessions. Return error.
+ if (num_discovery_sessions_ == 0) {
+ // TODO(armansito): This should never happen once we have the
+ // DiscoverySession API. Replace this case with an assert once it's
+ // the deprecated methods have been removed. (See crbug.com/3445008).
+ VLOG(1) << "No active discovery sessions. Returning error.";
+ error_callback.Run();
+ return;
+ }
+
+ // There is exactly one active discovery session. Request BlueZ to stop
+ // discovery.
+ DCHECK(num_discovery_sessions_ == 1);
+ discovery_request_pending_ = true;
+ DBusThreadManager::Get()->GetBluetoothAdapterClient()->
+ StopDiscovery(
+ object_path_,
+ base::Bind(&BluetoothAdapterChromeOS::OnStopDiscovery,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&BluetoothAdapterChromeOS::OnStopDiscoveryError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+}
+
void BluetoothAdapterChromeOS::OnStartDiscovery(const base::Closure& callback) {
+ // Report success on the original request and increment the count.
+ VLOG(1) << __func__;
+ DCHECK(discovery_request_pending_);
+ DCHECK(num_discovery_sessions_ == 0);
+ discovery_request_pending_ = false;
+ num_discovery_sessions_++;
callback.Run();
+
+ // Try to add a new discovery session for each queued request.
+ ProcessQueuedDiscoveryRequests();
}
void BluetoothAdapterChromeOS::OnStartDiscoveryError(
+ const base::Closure& callback,
const ErrorCallback& error_callback,
const std::string& error_name,
const std::string& error_message) {
LOG(WARNING) << object_path_.value() << ": Failed to start discovery: "
<< error_name << ": " << error_message;
- error_callback.Run();
+
+ // Failed to start discovery. This can only happen if the count is at 0.
+ DCHECK(num_discovery_sessions_ == 0);
+ DCHECK(discovery_request_pending_);
+ discovery_request_pending_ = false;
+
+ // Discovery request may fail if discovery was previously initiated by Chrome,
+ // but the session were invalidated due to the discovery state unexpectedly
+ // changing to false and then back to true. In this case, report success.
+ if (error_name == bluetooth_device::kErrorInProgress && IsDiscovering()) {
+ VLOG(1) << "Discovery previously initiated. Reporting success.";
+ num_discovery_sessions_++;
+ callback.Run();
+ } else {
+ error_callback.Run();
+ }
+
+ // Try to add a new discovery session for each queued request.
+ ProcessQueuedDiscoveryRequests();
}
void BluetoothAdapterChromeOS::OnStopDiscovery(const base::Closure& callback) {
+ // Report success on the original request and decrement the count.
+ VLOG(1) << __func__;
+ DCHECK(discovery_request_pending_);
+ DCHECK(num_discovery_sessions_ == 1);
+ discovery_request_pending_ = false;
+ num_discovery_sessions_--;
callback.Run();
+
+ // Try to add a new discovery session for each queued request.
+ ProcessQueuedDiscoveryRequests();
}
void BluetoothAdapterChromeOS::OnStopDiscoveryError(
@@ -435,7 +929,30 @@ void BluetoothAdapterChromeOS::OnStopDiscoveryError(
const std::string& error_message) {
LOG(WARNING) << object_path_.value() << ": Failed to stop discovery: "
<< error_name << ": " << error_message;
+
+ // Failed to stop discovery. This can only happen if the count is at 1.
+ DCHECK(discovery_request_pending_);
+ DCHECK(num_discovery_sessions_ == 1);
+ discovery_request_pending_ = false;
error_callback.Run();
+
+ // Try to add a new discovery session for each queued request.
+ ProcessQueuedDiscoveryRequests();
+}
+
+void BluetoothAdapterChromeOS::ProcessQueuedDiscoveryRequests() {
+ while (!discovery_request_queue_.empty()) {
+ VLOG(1) << "Process queued discovery request.";
+ DiscoveryCallbackPair callbacks = discovery_request_queue_.front();
+ discovery_request_queue_.pop();
+ AddDiscoverySession(callbacks.first, callbacks.second);
+
+ // If the queued request resulted in a pending call, then let it
+ // asynchonously process the remaining queued requests once the pending
+ // call returns.
+ if (discovery_request_pending_)
+ return;
+ }
}
} // namespace chromeos
diff --git a/chromium/device/bluetooth/bluetooth_adapter_chromeos.h b/chromium/device/bluetooth/bluetooth_adapter_chromeos.h
index 74beb3521db..4225744e029 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_chromeos.h
+++ b/chromium/device/bluetooth/bluetooth_adapter_chromeos.h
@@ -5,41 +5,50 @@
#ifndef DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_CHROMEOS_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_CHROMEOS_H_
+#include <queue>
#include <string>
#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
#include "chromeos/dbus/bluetooth_adapter_client.h"
+#include "chromeos/dbus/bluetooth_agent_service_provider.h"
#include "chromeos/dbus/bluetooth_device_client.h"
#include "chromeos/dbus/bluetooth_input_client.h"
#include "dbus/object_path.h"
#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_device.h"
namespace device {
-
-class BluetoothAdapterFactory;
-
+class BluetoothSocketThread;
} // namespace device
namespace chromeos {
class BluetoothChromeOSTest;
class BluetoothDeviceChromeOS;
+class BluetoothPairingChromeOS;
// The BluetoothAdapterChromeOS class implements BluetoothAdapter for the
// Chrome OS platform.
class BluetoothAdapterChromeOS
: public device::BluetoothAdapter,
- private chromeos::BluetoothAdapterClient::Observer,
- private chromeos::BluetoothDeviceClient::Observer,
- private chromeos::BluetoothInputClient::Observer {
+ public chromeos::BluetoothAdapterClient::Observer,
+ public chromeos::BluetoothDeviceClient::Observer,
+ public chromeos::BluetoothInputClient::Observer,
+ public chromeos::BluetoothAgentServiceProvider::Delegate {
public:
- // BluetoothAdapter override
+ static base::WeakPtr<BluetoothAdapter> CreateAdapter();
+
+ // BluetoothAdapter:
virtual void AddObserver(
device::BluetoothAdapter::Observer* observer) OVERRIDE;
virtual void RemoveObserver(
device::BluetoothAdapter::Observer* observer) OVERRIDE;
virtual std::string GetAddress() const OVERRIDE;
virtual std::string GetName() const OVERRIDE;
+ virtual void SetName(const std::string& name,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
virtual bool IsInitialized() const OVERRIDE;
virtual bool IsPresent() const OVERRIDE;
virtual bool IsPowered() const OVERRIDE;
@@ -47,24 +56,48 @@ class BluetoothAdapterChromeOS
bool powered,
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
- virtual bool IsDiscovering() const OVERRIDE;
- virtual void StartDiscovering(
- const base::Closure& callback,
- const ErrorCallback& error_callback) OVERRIDE;
- virtual void StopDiscovering(
+ virtual bool IsDiscoverable() const OVERRIDE;
+ virtual void SetDiscoverable(
+ bool discoverable,
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
- virtual void ReadLocalOutOfBandPairingData(
- const device::BluetoothAdapter::BluetoothOutOfBandPairingDataCallback&
- callback,
- const ErrorCallback& error_callback) OVERRIDE;
+ virtual bool IsDiscovering() const OVERRIDE;
+ virtual void CreateRfcommService(
+ const device::BluetoothUUID& uuid,
+ int channel,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) OVERRIDE;
+ virtual void CreateL2capService(
+ const device::BluetoothUUID& uuid,
+ int psm,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) OVERRIDE;
+
+ // Locates the device object by object path (the devices map and
+ // BluetoothDevice methods are by address).
+ BluetoothDeviceChromeOS* GetDeviceWithPath(
+ const dbus::ObjectPath& object_path);
+
+ // Announce to observers a change in device state that is not reflected by
+ // its D-Bus properties.
+ void NotifyDeviceChanged(BluetoothDeviceChromeOS* device);
+
+ // Returns the object path of the adapter.
+ const dbus::ObjectPath& object_path() const { return object_path_; }
+
+ protected:
+ // BluetoothAdapter:
+ virtual void RemovePairingDelegateInternal(
+ device::BluetoothDevice::PairingDelegate* pairing_delegate) OVERRIDE;
private:
- friend class device::BluetoothAdapterFactory;
friend class BluetoothChromeOSTest;
- friend class BluetoothDeviceChromeOS;
- friend class BluetoothProfileChromeOS;
- friend class BluetoothProfileChromeOSTest;
+
+ // typedef for callback parameters that are passed to AddDiscoverySession
+ // and RemoveDiscoverySession. This is used to queue incoming requests while
+ // a call to BlueZ is pending.
+ typedef std::pair<base::Closure, ErrorCallback> DiscoveryCallbackPair;
+ typedef std::queue<DiscoveryCallbackPair> DiscoveryCallbackQueue;
BluetoothAdapterChromeOS();
virtual ~BluetoothAdapterChromeOS();
@@ -86,19 +119,53 @@ class BluetoothAdapterChromeOS
virtual void InputPropertyChanged(const dbus::ObjectPath& object_path,
const std::string& property_name) OVERRIDE;
- // Internal method used to locate the device object by object path
- // (the devices map and BluetoothDevice methods are by address)
- BluetoothDeviceChromeOS* GetDeviceWithPath(
- const dbus::ObjectPath& object_path);
+ // BluetoothAgentServiceProvider::Delegate override.
+ virtual void Released() OVERRIDE;
+ virtual void RequestPinCode(const dbus::ObjectPath& device_path,
+ const PinCodeCallback& callback) OVERRIDE;
+ virtual void DisplayPinCode(const dbus::ObjectPath& device_path,
+ const std::string& pincode) OVERRIDE;
+ virtual void RequestPasskey(const dbus::ObjectPath& device_path,
+ const PasskeyCallback& callback) OVERRIDE;
+ virtual void DisplayPasskey(const dbus::ObjectPath& device_path,
+ uint32 passkey, uint16 entered) OVERRIDE;
+ virtual void RequestConfirmation(const dbus::ObjectPath& device_path,
+ uint32 passkey,
+ const ConfirmationCallback& callback)
+ OVERRIDE;
+ virtual void RequestAuthorization(const dbus::ObjectPath& device_path,
+ const ConfirmationCallback& callback)
+ OVERRIDE;
+ virtual void AuthorizeService(const dbus::ObjectPath& device_path,
+ const std::string& uuid,
+ const ConfirmationCallback& callback) OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+
+ // Called by dbus:: on completion of the D-Bus method call to register the
+ // pairing agent.
+ void OnRegisterAgent();
+ void OnRegisterAgentError(const std::string& error_name,
+ const std::string& error_message);
+
+ // Called by dbus:: on completion of the D-Bus method call to request that
+ // the pairing agent be made the default.
+ void OnRequestDefaultAgent();
+ void OnRequestDefaultAgentError(const std::string& error_name,
+ const std::string& error_message);
+
+ // Internal method to obtain a BluetoothPairingChromeOS object for the device
+ // with path |object_path|. Returns the existing pairing object if the device
+ // already has one (usually an outgoing connection in progress) or a new
+ // pairing object with the default pairing delegate if not. If no default
+ // pairing object exists, NULL will be returned.
+ BluetoothPairingChromeOS* GetPairing(const dbus::ObjectPath& object_path);
// Set the tracked adapter to the one in |object_path|, this object will
// subsequently operate on that adapter until it is removed.
void SetAdapter(const dbus::ObjectPath& object_path);
- // Set the adapter name to one chosen from the system information, and method
- // called by dbus:: on completion of the alias property change.
- void SetAdapterName();
- void OnSetAlias(bool success);
+ // Set the adapter name to one chosen from the system information.
+ void SetDefaultAdapterName();
// Remove the currently tracked adapter. IsPresent() will return false after
// this is called.
@@ -106,21 +173,32 @@ class BluetoothAdapterChromeOS
// Announce to observers a change in the adapter state.
void PoweredChanged(bool powered);
+ void DiscoverableChanged(bool discoverable);
void DiscoveringChanged(bool discovering);
void PresentChanged(bool present);
- // Announce to observers a change in device state that is not reflected by
- // its D-Bus properties.
- void NotifyDeviceChanged(BluetoothDeviceChromeOS* device);
+ // Called by dbus:: on completion of the discoverable property change.
+ void OnSetDiscoverable(const base::Closure& callback,
+ const ErrorCallback& error_callback,
+ bool success);
- // Called by dbus:: on completion of the powered property change.
- void OnSetPowered(const base::Closure& callback,
- const ErrorCallback& error_callback,
- bool success);
+ // Called by dbus:: on completion of an adapter property change.
+ void OnPropertyChangeCompleted(const base::Closure& callback,
+ const ErrorCallback& error_callback,
+ bool success);
+
+ // BluetoothAdapter:
+ virtual void AddDiscoverySession(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual void RemoveDiscoverySession(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
// Called by dbus:: on completion of the D-Bus method call to start discovery.
void OnStartDiscovery(const base::Closure& callback);
- void OnStartDiscoveryError(const ErrorCallback& error_callback,
+ void OnStartDiscoveryError(const base::Closure& callback,
+ const ErrorCallback& error_callback,
const std::string& error_name,
const std::string& error_message);
@@ -130,12 +208,43 @@ class BluetoothAdapterChromeOS
const std::string& error_name,
const std::string& error_message);
+ // Processes the queued discovery requests. For each DiscoveryCallbackPair in
+ // the queue, this method will try to add a new discovery session. This method
+ // is called whenever a pending D-Bus call to start or stop discovery has
+ // ended (with either success or failure).
+ void ProcessQueuedDiscoveryRequests();
+
+ // Number of discovery sessions that have been added.
+ int num_discovery_sessions_;
+
+ // True, if there is a pending request to start or stop discovery.
+ bool discovery_request_pending_;
+
+ // List of queued requests to add new discovery sessions. While there is a
+ // pending request to BlueZ to start or stop discovery, many requests from
+ // within Chrome to start or stop discovery sessions may occur. We only
+ // queue requests to add new sessions to be processed later. All requests to
+ // remove a session while a call is pending immediately return failure. Note
+ // that since BlueZ keeps its own reference count of applications that have
+ // requested discovery, dropping our count to 0 won't necessarily result in
+ // the controller actually stopping discovery if, for example, an application
+ // other than Chrome, such as bt_console, was also used to start discovery.
+ DiscoveryCallbackQueue discovery_request_queue_;
+
// Object path of the adapter we track.
dbus::ObjectPath object_path_;
// List of observers interested in event notifications from us.
ObserverList<device::BluetoothAdapter::Observer> observers_;
+ // Instance of the D-Bus agent object used for pairing, initialized with
+ // our own class as its delegate.
+ scoped_ptr<BluetoothAgentServiceProvider> agent_;
+
+ // UI thread task runner and socket thread object used to create sockets.
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
+ scoped_refptr<device::BluetoothSocketThread> socket_thread_;
+
// 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<BluetoothAdapterChromeOS> weak_ptr_factory_;
diff --git a/chromium/device/bluetooth/bluetooth_adapter_factory.cc b/chromium/device/bluetooth/bluetooth_adapter_factory.cc
index c15d26a514b..33950531312 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_factory.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_factory.cc
@@ -12,27 +12,22 @@
#include "base/memory/weak_ptr.h"
#include "device/bluetooth/bluetooth_adapter.h"
-#if defined(OS_CHROMEOS)
-#include "device/bluetooth/bluetooth_adapter_chromeos.h"
-#elif defined(OS_WIN)
-#include "device/bluetooth/bluetooth_adapter_win.h"
-#elif defined(OS_MACOSX)
+#if defined(OS_MACOSX)
#include "base/mac/mac_util.h"
-#include "device/bluetooth/bluetooth_adapter_mac.h"
#endif
-namespace {
+namespace device {
-using device::BluetoothAdapter;
-using device::BluetoothAdapterFactory;
+namespace {
-// Shared default adapter instance, we don't want to keep this class around
-// if nobody is using it so use a WeakPtr and create the object when needed;
-// since Google C++ Style (and clang's static analyzer) forbids us having
-// exit-time destructors we use a leaky lazy instance for it.
-base::LazyInstance<base::WeakPtr<device::BluetoothAdapter> >::Leaky
- default_adapter = LAZY_INSTANCE_INITIALIZER;
+// Shared default adapter instance. We don't want to keep this class around
+// if nobody is using it, so use a WeakPtr and create the object when needed.
+// Since Google C++ Style (and clang's static analyzer) forbids us having
+// exit-time destructors, we use a leaky lazy instance for it.
+base::LazyInstance<base::WeakPtr<BluetoothAdapter> >::Leaky default_adapter =
+ LAZY_INSTANCE_INITIALIZER;
+#if defined(OS_WIN)
typedef std::vector<BluetoothAdapterFactory::AdapterCallback>
AdapterCallbackList;
@@ -42,9 +37,8 @@ typedef std::vector<BluetoothAdapterFactory::AdapterCallback>
base::LazyInstance<AdapterCallbackList> adapter_callbacks =
LAZY_INSTANCE_INITIALIZER;
-#if defined(OS_WIN)
void RunAdapterCallbacks() {
- CHECK(default_adapter.Get().get());
+ DCHECK(default_adapter.Get());
scoped_refptr<BluetoothAdapter> adapter(default_adapter.Get().get());
for (std::vector<BluetoothAdapterFactory::AdapterCallback>::const_iterator
iter = adapter_callbacks.Get().begin();
@@ -58,49 +52,57 @@ void RunAdapterCallbacks() {
} // namespace
-namespace device {
-
// static
bool BluetoothAdapterFactory::IsBluetoothAdapterAvailable() {
-#if defined(OS_CHROMEOS)
- return true;
-#elif defined(OS_WIN)
+ // SetAdapterForTesting() may be used to provide a test or mock adapter
+ // instance even on platforms that would otherwise not support it.
+ if (default_adapter.Get())
+ return true;
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
return true;
#elif defined(OS_MACOSX)
return base::mac::IsOSLionOrLater();
-#endif
+#else
return false;
+#endif
}
// static
void BluetoothAdapterFactory::GetAdapter(const AdapterCallback& callback) {
- if (!default_adapter.Get().get()) {
-#if defined(OS_CHROMEOS)
- chromeos::BluetoothAdapterChromeOS* new_adapter =
- new chromeos::BluetoothAdapterChromeOS();
- default_adapter.Get() = new_adapter->weak_ptr_factory_.GetWeakPtr();
-#elif defined(OS_WIN)
- BluetoothAdapterWin* new_adapter = new BluetoothAdapterWin(
- base::Bind(&RunAdapterCallbacks));
- new_adapter->Init();
- default_adapter.Get() = new_adapter->weak_ptr_factory_.GetWeakPtr();
-#elif defined(OS_MACOSX)
- BluetoothAdapterMac* new_adapter = new BluetoothAdapterMac();
- new_adapter->Init();
- default_adapter.Get() = new_adapter->weak_ptr_factory_.GetWeakPtr();
-#endif
+ DCHECK(IsBluetoothAdapterAvailable());
+
+#if defined(OS_WIN)
+ if (!default_adapter.Get()) {
+ default_adapter.Get() =
+ BluetoothAdapter::CreateAdapter(base::Bind(&RunAdapterCallbacks));
+ DCHECK(!default_adapter.Get()->IsInitialized());
}
- if (default_adapter.Get()->IsInitialized()) {
- callback.Run(scoped_refptr<BluetoothAdapter>(default_adapter.Get().get()));
- } else {
+ if (!default_adapter.Get()->IsInitialized())
adapter_callbacks.Get().push_back(callback);
+#else // !defined(OS_WIN)
+ if (!default_adapter.Get()) {
+ default_adapter.Get() =
+ BluetoothAdapter::CreateAdapter(BluetoothAdapter::InitCallback());
}
+
+ DCHECK(default_adapter.Get()->IsInitialized());
+#endif // defined(OS_WIN)
+
+ if (default_adapter.Get()->IsInitialized())
+ callback.Run(scoped_refptr<BluetoothAdapter>(default_adapter.Get().get()));
+
+}
+
+// static
+void BluetoothAdapterFactory::SetAdapterForTesting(
+ scoped_refptr<BluetoothAdapter> adapter) {
+ default_adapter.Get() = adapter->GetWeakPtrForTesting();
}
// static
-scoped_refptr<BluetoothAdapter> BluetoothAdapterFactory::MaybeGetAdapter() {
- return scoped_refptr<BluetoothAdapter>(default_adapter.Get().get());
+bool BluetoothAdapterFactory::HasSharedInstanceForTesting() {
+ return default_adapter.Get();
}
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_adapter_factory.h b/chromium/device/bluetooth/bluetooth_adapter_factory.h
index 3b6a2c27ea5..0e61a2c4231 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_factory.h
+++ b/chromium/device/bluetooth/bluetooth_adapter_factory.h
@@ -5,17 +5,14 @@
#ifndef DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_FACTORY_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_FACTORY_H_
-#include <string>
-
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "device/bluetooth/bluetooth_adapter.h"
namespace device {
-// BluetoothAdapterFactory is a class that contains static methods, which
-// instantiate either a specific Bluetooth adapter, or the generic "default
-// adapter" which may change depending on availability.
+// A factory class for building a Bluetooth adapter on platforms where Bluetooth
+// is available.
class BluetoothAdapterFactory {
public:
typedef base::Callback<void(scoped_refptr<BluetoothAdapter> adapter)>
@@ -31,10 +28,14 @@ class BluetoothAdapterFactory {
// use.
static void GetAdapter(const AdapterCallback& callback);
- // Returns the shared instance of the adapter that has already been created,
- // but may or may not have been initialized.
- // It returns NULL if no adapter has been created at the time.
- static scoped_refptr<BluetoothAdapter> MaybeGetAdapter();
+ // Sets the shared instance of the default adapter for testing purposes only,
+ // no reference is retained after completion of the call, removing the last
+ // reference will reset the factory.
+ static void SetAdapterForTesting(scoped_refptr<BluetoothAdapter> adapter);
+
+ // Returns true iff the implementation has a (non-NULL) shared instance of the
+ // adapter. Exposed for testing.
+ static bool HasSharedInstanceForTesting();
};
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_adapter_mac.h b/chromium/device/bluetooth/bluetooth_adapter_mac.h
index 7054acf5d0e..7040932db28 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_mac.h
+++ b/chromium/device/bluetooth/bluetooth_adapter_mac.h
@@ -11,24 +11,16 @@
#include <vector>
#include "base/containers/hash_tables.h"
+#include "base/mac/scoped_nsobject.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_discovery_manager_mac.h"
-#ifdef __OBJC__
-@class BluetoothAdapterMacDelegate;
@class IOBluetoothDevice;
-@class IOBluetoothDeviceInquiry;
@class NSArray;
@class NSDate;
-#else
-class BluetoothAdapterMacDelegate;
-class IOBluetoothDevice;
-class IOBluetoothDeviceInquiry;
-class NSArray;
-class NSDate;
-#endif
namespace base {
@@ -40,13 +32,19 @@ namespace device {
class BluetoothAdapterMacTest;
-class BluetoothAdapterMac : public BluetoothAdapter {
+class BluetoothAdapterMac : public BluetoothAdapter,
+ public BluetoothDiscoveryManagerMac::Observer {
public:
- // BluetoothAdapter override
+ static base::WeakPtr<BluetoothAdapter> CreateAdapter();
+
+ // BluetoothAdapter:
virtual void AddObserver(BluetoothAdapter::Observer* observer) OVERRIDE;
virtual void RemoveObserver(BluetoothAdapter::Observer* observer) OVERRIDE;
virtual std::string GetAddress() const OVERRIDE;
virtual std::string GetName() const OVERRIDE;
+ virtual void SetName(const std::string& name,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
virtual bool IsInitialized() const OVERRIDE;
virtual bool IsPresent() const OVERRIDE;
virtual bool IsPowered() const OVERRIDE;
@@ -54,66 +52,68 @@ class BluetoothAdapterMac : public BluetoothAdapter {
bool powered,
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
- virtual bool IsDiscovering() const OVERRIDE;
-
- virtual void StartDiscovering(
- const base::Closure& callback,
- const ErrorCallback& error_callback) OVERRIDE;
- virtual void StopDiscovering(
+ virtual bool IsDiscoverable() const OVERRIDE;
+ virtual void SetDiscoverable(
+ bool discoverable,
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
- virtual void ReadLocalOutOfBandPairingData(
- const BluetoothOutOfBandPairingDataCallback& callback,
- const ErrorCallback& error_callback) OVERRIDE;
-
- // called by BluetoothAdapterMacDelegate.
- void DeviceInquiryStarted(IOBluetoothDeviceInquiry* inquiry);
- void DeviceFound(IOBluetoothDeviceInquiry* inquiry,
- IOBluetoothDevice* device);
- void DeviceInquiryComplete(IOBluetoothDeviceInquiry* inquiry,
- IOReturn error,
- bool aborted);
+ virtual bool IsDiscovering() const OVERRIDE;
+ virtual void CreateRfcommService(
+ const BluetoothUUID& uuid,
+ int channel,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) OVERRIDE;
+ virtual void CreateL2capService(
+ const BluetoothUUID& uuid,
+ int psm,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) OVERRIDE;
+
+ // BluetoothDiscoveryManagerMac::Observer overrides
+ virtual void DeviceFound(BluetoothDiscoveryManagerMac* manager,
+ IOBluetoothDevice* device) OVERRIDE;
+ virtual void DiscoveryStopped(BluetoothDiscoveryManagerMac* manager,
+ bool unexpected) OVERRIDE;
+
+ // Registers that a new |device| has connected to the local host.
+ void DeviceConnected(IOBluetoothDevice* device);
+
+ protected:
+ // BluetoothAdapter:
+ virtual void RemovePairingDelegateInternal(
+ device::BluetoothDevice::PairingDelegate* pairing_delegate) OVERRIDE;
private:
- friend class BluetoothAdapterFactory;
friend class BluetoothAdapterMacTest;
- enum DiscoveryStatus {
- NOT_DISCOVERING,
- DISCOVERY_STARTING,
- DISCOVERING,
- DISCOVERY_STOPPING
- };
-
BluetoothAdapterMac();
virtual ~BluetoothAdapterMac();
+ // BluetoothAdapter:
+ virtual void AddDiscoverySession(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual void RemoveDiscoverySession(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+
void Init();
void InitForTest(scoped_refptr<base::SequencedTaskRunner> ui_task_runner);
void PollAdapter();
- // Updates |devices_| to be consistent with |devices|.
- void UpdateDevices(NSArray* devices);
-
- void MaybeStartDeviceInquiry();
- void MaybeStopDeviceInquiry();
-
- typedef std::vector<std::pair<base::Closure, ErrorCallback> >
- DiscoveryCallbackList;
- void RunCallbacks(const DiscoveryCallbackList& callback_list,
- bool success) const;
+ // Updates |devices_| to include the currently paired devices, as well as any
+ // connected, but unpaired, devices. Notifies observers if any previously
+ // paired or connected devices are no longer present.
+ void UpdateDevices();
std::string address_;
std::string name_;
bool powered_;
- DiscoveryStatus discovery_status_;
- DiscoveryCallbackList on_start_discovery_callbacks_;
- DiscoveryCallbackList on_stop_discovery_callbacks_;
- size_t num_discovery_listeners_;
+ int num_discovery_sessions_;
- BluetoothAdapterMacDelegate* adapter_delegate_;
- IOBluetoothDeviceInquiry* device_inquiry_;
+ // Discovery manager for Bluetooth Classic.
+ scoped_ptr<BluetoothDiscoveryManagerMac> classic_discovery_manager_;
// A list of discovered device addresses.
// This list is used to check if the same device is discovered twice during
@@ -122,7 +122,7 @@ class BluetoothAdapterMac : public BluetoothAdapter {
// Timestamp for the recently accessed device.
// Used to determine if |devices_| needs an update.
- NSDate* recently_accessed_device_timestamp_;
+ base::scoped_nsobject<NSDate> recently_accessed_device_timestamp_;
scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
diff --git a/chromium/device/bluetooth/bluetooth_adapter_mac.mm b/chromium/device/bluetooth/bluetooth_adapter_mac.mm
index e15b894a9cc..d44a0f37773 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/chromium/device/bluetooth/bluetooth_adapter_mac.mm
@@ -5,7 +5,6 @@
#include "device/bluetooth/bluetooth_adapter_mac.h"
#import <IOBluetooth/objc/IOBluetoothDevice.h>
-#import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h>
#import <IOBluetooth/objc/IOBluetoothHostController.h>
#include <string>
@@ -14,6 +13,7 @@
#include "base/compiler_specific.h"
#include "base/containers/hash_tables.h"
#include "base/location.h"
+#include "base/mac/sdk_forward_declarations.h"
#include "base/memory/scoped_ptr.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
@@ -21,66 +21,8 @@
#include "base/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "device/bluetooth/bluetooth_device_mac.h"
-
-// Replicate specific 10.7 SDK declarations for building with prior SDKs.
-#if !defined(MAC_OS_X_VERSION_10_7) || \
-MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
-
-@interface IOBluetoothHostController (LionSDKDeclarations)
-- (NSString*)nameAsString;
-- (BluetoothHCIPowerState)powerState;
-@end
-
-@interface IOBluetoothDevice (LionSDKDeclarations)
-- (NSString*)addressString;
-@end
-
-@protocol IOBluetoothDeviceInquiryDelegate
-- (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender;
-- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
- device:(IOBluetoothDevice*)device;
-- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
- error:(IOReturn)error
- aborted:(BOOL)aborted;
-@end
-
-#endif // MAC_OS_X_VERSION_10_7
-
-@interface BluetoothAdapterMacDelegate
- : NSObject <IOBluetoothDeviceInquiryDelegate> {
- @private
- device::BluetoothAdapterMac* adapter_; // weak
-}
-
-- (id)initWithAdapter:(device::BluetoothAdapterMac*)adapter;
-
-@end
-
-@implementation BluetoothAdapterMacDelegate
-
-- (id)initWithAdapter:(device::BluetoothAdapterMac*)adapter {
- if ((self = [super init]))
- adapter_ = adapter;
-
- return self;
-}
-
-- (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender {
- adapter_->DeviceInquiryStarted(sender);
-}
-
-- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
- device:(IOBluetoothDevice*)device {
- adapter_->DeviceFound(sender, device);
-}
-
-- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
- error:(IOReturn)error
- aborted:(BOOL)aborted {
- adapter_->DeviceInquiryComplete(sender, error, aborted);
-}
-
-@end
+#include "device/bluetooth/bluetooth_socket_mac.h"
+#include "device/bluetooth/bluetooth_uuid.h"
namespace {
@@ -90,23 +32,30 @@ const int kPollIntervalMs = 500;
namespace device {
+// static
+base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
+ const InitCallback& init_callback) {
+ return BluetoothAdapterMac::CreateAdapter();
+}
+
+// static
+base::WeakPtr<BluetoothAdapter> BluetoothAdapterMac::CreateAdapter() {
+ BluetoothAdapterMac* adapter = new BluetoothAdapterMac();
+ adapter->Init();
+ return adapter->weak_ptr_factory_.GetWeakPtr();
+}
+
BluetoothAdapterMac::BluetoothAdapterMac()
: BluetoothAdapter(),
powered_(false),
- discovery_status_(NOT_DISCOVERING),
- adapter_delegate_(
- [[BluetoothAdapterMacDelegate alloc] initWithAdapter:this]),
- device_inquiry_(
- [[IOBluetoothDeviceInquiry
- inquiryWithDelegate:adapter_delegate_] retain]),
- recently_accessed_device_timestamp_(nil),
+ num_discovery_sessions_(0),
+ classic_discovery_manager_(
+ BluetoothDiscoveryManagerMac::CreateClassic(this)),
weak_ptr_factory_(this) {
+ DCHECK(classic_discovery_manager_.get());
}
BluetoothAdapterMac::~BluetoothAdapterMac() {
- [device_inquiry_ release];
- [adapter_delegate_ release];
- [recently_accessed_device_timestamp_ release];
}
void BluetoothAdapterMac::AddObserver(BluetoothAdapter::Observer* observer) {
@@ -127,6 +76,12 @@ std::string BluetoothAdapterMac::GetName() const {
return name_;
}
+void BluetoothAdapterMac::SetName(const std::string& name,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ NOTIMPLEMENTED();
+}
+
bool BluetoothAdapterMac::IsInitialized() const {
return true;
}
@@ -142,40 +97,152 @@ bool BluetoothAdapterMac::IsPowered() const {
void BluetoothAdapterMac::SetPowered(bool powered,
const base::Closure& callback,
const ErrorCallback& error_callback) {
+ NOTIMPLEMENTED();
+}
+
+bool BluetoothAdapterMac::IsDiscoverable() const {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void BluetoothAdapterMac::SetDiscoverable(
+ bool discoverable,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ NOTIMPLEMENTED();
}
bool BluetoothAdapterMac::IsDiscovering() const {
- return discovery_status_ == DISCOVERING ||
- discovery_status_ == DISCOVERY_STOPPING;
+ return classic_discovery_manager_->IsDiscovering();
+}
+
+void BluetoothAdapterMac::CreateRfcommService(
+ const BluetoothUUID& uuid,
+ int channel,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) {
+ scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
+ socket->ListenUsingRfcomm(
+ this, uuid, channel, base::Bind(callback, socket), error_callback);
+}
+
+void BluetoothAdapterMac::CreateL2capService(
+ const BluetoothUUID& uuid,
+ int psm,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) {
+ scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
+ socket->ListenUsingL2cap(
+ this, uuid, psm, base::Bind(callback, socket), error_callback);
+}
+
+void BluetoothAdapterMac::DeviceFound(BluetoothDiscoveryManagerMac* manager,
+ IOBluetoothDevice* device) {
+ // TODO(isherman): The list of discovered devices is never reset. This should
+ // probably key off of |devices_| instead. Currently, if a device is paired,
+ // then unpaired, then paired again, the app would never hear about the second
+ // pairing.
+ std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
+ if (discovered_devices_.find(device_address) == discovered_devices_.end()) {
+ BluetoothDeviceMac device_mac(device);
+ FOR_EACH_OBSERVER(
+ BluetoothAdapter::Observer, observers_, DeviceAdded(this, &device_mac));
+ discovered_devices_.insert(device_address);
+ }
+}
+
+void BluetoothAdapterMac::DiscoveryStopped(
+ BluetoothDiscoveryManagerMac* manager,
+ bool unexpected) {
+ DCHECK_EQ(manager, classic_discovery_manager_.get());
+ if (unexpected) {
+ DVLOG(1) << "Discovery stopped unexpectedly";
+ num_discovery_sessions_ = 0;
+ MarkDiscoverySessionsAsInactive();
+ }
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ AdapterDiscoveringChanged(this, false));
+}
+
+void BluetoothAdapterMac::DeviceConnected(IOBluetoothDevice* device) {
+ // TODO(isherman): Call -registerForDisconnectNotification:selector:, and
+ // investigate whether this method can be replaced with a call to
+ // +registerForConnectNotifications:selector:.
+ std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
+ DVLOG(1) << "Adapter registered a new connection from device with address: "
+ << device_address;
+
+ // Only notify once per device.
+ if (devices_.count(device_address))
+ return;
+
+ scoped_ptr<BluetoothDeviceMac> device_mac(new BluetoothDeviceMac(device));
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ DeviceAdded(this, device_mac.get()));
+ devices_[device_address] = device_mac.release();
}
-void BluetoothAdapterMac::StartDiscovering(
+void BluetoothAdapterMac::AddDiscoverySession(
const base::Closure& callback,
const ErrorCallback& error_callback) {
- if (discovery_status_ == DISCOVERING) {
- num_discovery_listeners_++;
+ DVLOG(1) << __func__;
+ if (num_discovery_sessions_ > 0) {
+ DCHECK(IsDiscovering());
+ num_discovery_sessions_++;
callback.Run();
return;
}
- on_start_discovery_callbacks_.push_back(
- std::make_pair(callback, error_callback));
- MaybeStartDeviceInquiry();
-}
-void BluetoothAdapterMac::StopDiscovering(const base::Closure& callback,
- const ErrorCallback& error_callback) {
- if (discovery_status_ == NOT_DISCOVERING) {
+ DCHECK_EQ(0, num_discovery_sessions_);
+
+ if (!classic_discovery_manager_->StartDiscovery()) {
+ DVLOG(1) << "Failed to add a discovery session";
error_callback.Run();
return;
}
- on_stop_discovery_callbacks_.push_back(
- std::make_pair(callback, error_callback));
- MaybeStopDeviceInquiry();
+
+ DVLOG(1) << "Added a discovery session";
+ num_discovery_sessions_++;
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ AdapterDiscoveringChanged(this, true));
+ callback.Run();
}
-void BluetoothAdapterMac::ReadLocalOutOfBandPairingData(
- const BluetoothOutOfBandPairingDataCallback& callback,
+void BluetoothAdapterMac::RemoveDiscoverySession(
+ const base::Closure& callback,
const ErrorCallback& error_callback) {
+ DVLOG(1) << __func__;
+
+ if (num_discovery_sessions_ > 1) {
+ // There are active sessions other than the one currently being removed.
+ DCHECK(IsDiscovering());
+ num_discovery_sessions_--;
+ callback.Run();
+ return;
+ }
+
+ if (num_discovery_sessions_ == 0) {
+ DVLOG(1) << "No active discovery sessions. Returning error.";
+ error_callback.Run();
+ return;
+ }
+
+ if (!classic_discovery_manager_->StopDiscovery()) {
+ DVLOG(1) << "Failed to stop discovery";
+ error_callback.Run();
+ return;
+ }
+
+ DVLOG(1) << "Discovery stopped";
+ num_discovery_sessions_--;
+ callback.Run();
+}
+
+void BluetoothAdapterMac::RemovePairingDelegateInternal(
+ BluetoothDevice::PairingDelegate* pairing_delegate) {
}
void BluetoothAdapterMac::Init() {
@@ -191,15 +258,16 @@ void BluetoothAdapterMac::InitForTest(
void BluetoothAdapterMac::PollAdapter() {
bool was_present = IsPresent();
- std::string name = "";
- std::string address = "";
+ std::string name;
+ std::string address;
bool powered = false;
IOBluetoothHostController* controller =
[IOBluetoothHostController defaultController];
if (controller != nil) {
name = base::SysNSStringToUTF8([controller nameAsString]);
- address = base::SysNSStringToUTF8([controller addressAsString]);
+ address = BluetoothDevice::CanonicalizeAddress(
+ base::SysNSStringToUTF8([controller addressAsString]));
powered = ([controller powerState] == kBluetoothHCIPowerStateON);
}
@@ -217,6 +285,7 @@ void BluetoothAdapterMac::PollAdapter() {
AdapterPoweredChanged(this, powered_));
}
+ // TODO(isherman): This doesn't detect when a device is unpaired.
IOBluetoothDevice* recent_device =
[[IOBluetoothDevice recentDevices:1] lastObject];
NSDate* access_timestamp = [recent_device recentAccessDate];
@@ -224,9 +293,8 @@ void BluetoothAdapterMac::PollAdapter() {
access_timestamp == nil ||
[recently_accessed_device_timestamp_ compare:access_timestamp] ==
NSOrderedAscending) {
- UpdateDevices([IOBluetoothDevice pairedDevices]);
- [recently_accessed_device_timestamp_ release];
- recently_accessed_device_timestamp_ = [access_timestamp copy];
+ UpdateDevices();
+ recently_accessed_device_timestamp_.reset([access_timestamp copy]);
}
ui_task_runner_->PostDelayedTask(
@@ -236,104 +304,39 @@ void BluetoothAdapterMac::PollAdapter() {
base::TimeDelta::FromMilliseconds(kPollIntervalMs));
}
-void BluetoothAdapterMac::UpdateDevices(NSArray* devices) {
- STLDeleteValues(&devices_);
- for (IOBluetoothDevice* device in devices) {
- std::string device_address =
- base::SysNSStringToUTF8([device addressString]);
- devices_[device_address] = new BluetoothDeviceMac(device);
+void BluetoothAdapterMac::UpdateDevices() {
+ // Snapshot the devices observers were previously notified of.
+ // Note that the code below is careful to take ownership of any values that
+ // are erased from the map, since the map owns the memory for all its mapped
+ // devices.
+ DevicesMap old_devices = devices_;
+
+ // Add all the paired devices.
+ devices_.clear();
+ for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) {
+ std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
+ scoped_ptr<BluetoothDevice> device_mac(old_devices[device_address]);
+ if (!device_mac)
+ device_mac.reset(new BluetoothDeviceMac(device));
+ devices_[device_address] = device_mac.release();
+ old_devices.erase(device_address);
}
-}
-
-void BluetoothAdapterMac::DeviceInquiryStarted(
- IOBluetoothDeviceInquiry* inquiry) {
- DCHECK(device_inquiry_ == inquiry);
- if (discovery_status_ == DISCOVERING)
- return;
- discovery_status_ = DISCOVERING;
- RunCallbacks(on_start_discovery_callbacks_, true);
- num_discovery_listeners_ = on_start_discovery_callbacks_.size();
- on_start_discovery_callbacks_.clear();
-
- FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
- AdapterDiscoveringChanged(this, true));
- MaybeStopDeviceInquiry();
-}
+ // Add any unpaired connected devices.
+ for (const auto& old_device : old_devices) {
+ if (!old_device.second->IsConnected())
+ continue;
-void BluetoothAdapterMac::DeviceFound(IOBluetoothDeviceInquiry* inquiry,
- IOBluetoothDevice* device) {
- DCHECK(device_inquiry_ == inquiry);
- std::string device_address = base::SysNSStringToUTF8([device addressString]);
- if (discovered_devices_.find(device_address) == discovered_devices_.end()) {
- scoped_ptr<BluetoothDeviceMac> device_mac(new BluetoothDeviceMac(device));
- FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
- DeviceAdded(this, device_mac.get()));
- discovered_devices_.insert(device_address);
- }
-}
-
-void BluetoothAdapterMac::DeviceInquiryComplete(
- IOBluetoothDeviceInquiry* inquiry,
- IOReturn error,
- bool aborted) {
- DCHECK(device_inquiry_ == inquiry);
- if (discovery_status_ == DISCOVERING &&
- [device_inquiry_ start] == kIOReturnSuccess) {
- return;
+ const std::string& device_address = old_device.first;
+ DCHECK(!devices_.count(device_address));
+ devices_[device_address] = old_device.second;
+ old_devices.erase(device_address);
}
- // Device discovery is done.
- discovered_devices_.clear();
- discovery_status_ = NOT_DISCOVERING;
- RunCallbacks(on_stop_discovery_callbacks_, error == kIOReturnSuccess);
- num_discovery_listeners_ = 0;
- on_stop_discovery_callbacks_.clear();
- FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
- AdapterDiscoveringChanged(this, false));
- MaybeStartDeviceInquiry();
-}
-
-void BluetoothAdapterMac::MaybeStartDeviceInquiry() {
- if (discovery_status_ == NOT_DISCOVERING &&
- !on_start_discovery_callbacks_.empty()) {
- discovery_status_ = DISCOVERY_STARTING;
- if ([device_inquiry_ start] != kIOReturnSuccess) {
- discovery_status_ = NOT_DISCOVERING;
- RunCallbacks(on_start_discovery_callbacks_, false);
- on_start_discovery_callbacks_.clear();
- }
- }
-}
-
-void BluetoothAdapterMac::MaybeStopDeviceInquiry() {
- if (discovery_status_ != DISCOVERING)
- return;
-
- if (on_stop_discovery_callbacks_.size() < num_discovery_listeners_) {
- RunCallbacks(on_stop_discovery_callbacks_, true);
- num_discovery_listeners_ -= on_stop_discovery_callbacks_.size();
- on_stop_discovery_callbacks_.clear();
- return;
- }
-
- discovery_status_ = DISCOVERY_STOPPING;
- if ([device_inquiry_ stop] != kIOReturnSuccess) {
- RunCallbacks(on_stop_discovery_callbacks_, false);
- on_stop_discovery_callbacks_.clear();
- }
-}
-
-void BluetoothAdapterMac::RunCallbacks(
- const DiscoveryCallbackList& callback_list, bool success) const {
- for (DiscoveryCallbackList::const_iterator iter = callback_list.begin();
- iter != callback_list.end();
- ++iter) {
- if (success)
- ui_task_runner_->PostTask(FROM_HERE, iter->first);
- else
- ui_task_runner_->PostTask(FROM_HERE, iter->second);
- }
+ // TODO(isherman): Notify observers of any devices that are no longer in
+ // range. Note that it's possible for a device to be neither paired nor
+ // connected, but to still be in range.
+ STLDeleteValues(&old_devices);
}
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_adapter_unittest.cc b/chromium/device/bluetooth/bluetooth_adapter_unittest.cc
new file mode 100644
index 00000000000..19ff6033847
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_adapter_unittest.cc
@@ -0,0 +1,192 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/ref_counted.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using device::BluetoothAdapter;
+using device::BluetoothDevice;
+
+namespace device {
+
+class TestBluetoothAdapter : public BluetoothAdapter {
+ public:
+ TestBluetoothAdapter() {
+ }
+
+ virtual void AddObserver(BluetoothAdapter::Observer* observer) OVERRIDE {
+ }
+
+ virtual void RemoveObserver(BluetoothAdapter::Observer* observer) OVERRIDE {
+
+ }
+
+ virtual std::string GetAddress() const OVERRIDE {
+ return "";
+ }
+
+ virtual std::string GetName() const OVERRIDE {
+ return "";
+ }
+
+ virtual void SetName(const std::string& name,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ }
+
+ virtual bool IsInitialized() const OVERRIDE {
+ return false;
+ }
+
+ virtual bool IsPresent() const OVERRIDE {
+ return false;
+ }
+
+ virtual bool IsPowered() const OVERRIDE {
+ return false;
+ }
+
+ virtual void SetPowered(
+ bool powered,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ }
+
+ virtual bool IsDiscoverable() const OVERRIDE {
+ return false;
+ }
+
+ virtual void SetDiscoverable(
+ bool discoverable,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ }
+
+ virtual bool IsDiscovering() const OVERRIDE {
+ return false;
+ }
+
+ virtual void StartDiscoverySession(
+ const DiscoverySessionCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ }
+
+ virtual void CreateRfcommService(
+ const BluetoothUUID& uuid,
+ int channel,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) OVERRIDE {
+ }
+
+ virtual void CreateL2capService(
+ const BluetoothUUID& uuid,
+ int psm,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) OVERRIDE {
+ }
+
+ protected:
+ virtual ~TestBluetoothAdapter() {}
+
+ virtual void AddDiscoverySession(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ }
+
+ virtual void RemoveDiscoverySession(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ }
+
+ virtual void RemovePairingDelegateInternal(
+ BluetoothDevice::PairingDelegate* pairing_delegate) OVERRIDE {
+ }
+};
+
+class TestPairingDelegate : public BluetoothDevice::PairingDelegate {
+ public:
+ virtual void RequestPinCode(BluetoothDevice* device) OVERRIDE {}
+ virtual void RequestPasskey(BluetoothDevice* device) OVERRIDE {}
+ virtual void DisplayPinCode(BluetoothDevice* device,
+ const std::string& pincode) OVERRIDE {}
+ virtual void DisplayPasskey(BluetoothDevice* device,
+ uint32 passkey) OVERRIDE {}
+ virtual void KeysEntered(BluetoothDevice* device,
+ uint32 entered) OVERRIDE {}
+ virtual void ConfirmPasskey(BluetoothDevice* device,
+ uint32 passkey) OVERRIDE {}
+ virtual void AuthorizePairing(BluetoothDevice* device) OVERRIDE {}
+};
+
+
+TEST(BluetoothAdapterTest, NoDefaultPairingDelegate) {
+ scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();
+
+ // Verify that when there is no registered pairing delegate, NULL is returned.
+ EXPECT_TRUE(adapter->DefaultPairingDelegate() == NULL);
+}
+
+TEST(BluetoothAdapterTest, OneDefaultPairingDelegate) {
+ scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();
+
+ // Verify that when there is one registered pairing delegate, it is returned.
+ TestPairingDelegate delegate;
+
+ adapter->AddPairingDelegate(&delegate,
+ BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
+
+ EXPECT_EQ(&delegate, adapter->DefaultPairingDelegate());
+}
+
+TEST(BluetoothAdapterTest, SamePriorityDelegates) {
+ scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();
+
+ // Verify that when there are two registered pairing delegates of the same
+ // priority, the first one registered is returned.
+ TestPairingDelegate delegate1, delegate2;
+
+ adapter->AddPairingDelegate(&delegate1,
+ BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
+ adapter->AddPairingDelegate(&delegate2,
+ BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
+
+ EXPECT_EQ(&delegate1, adapter->DefaultPairingDelegate());
+
+ // After unregistering the first, the second can be returned.
+ adapter->RemovePairingDelegate(&delegate1);
+
+ EXPECT_EQ(&delegate2, adapter->DefaultPairingDelegate());
+}
+
+TEST(BluetoothAdapterTest, HighestPriorityDelegate) {
+ scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();
+
+ // Verify that when there are two registered pairing delegates, the one with
+ // the highest priority is returned.
+ TestPairingDelegate delegate1, delegate2;
+
+ adapter->AddPairingDelegate(&delegate1,
+ BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
+ adapter->AddPairingDelegate(&delegate2,
+ BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+
+ EXPECT_EQ(&delegate2, adapter->DefaultPairingDelegate());
+}
+
+TEST(BluetoothAdapterTest, UnregisterDelegate) {
+ scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();
+
+ // Verify that after unregistering a delegate, NULL is returned.
+ TestPairingDelegate delegate;
+
+ adapter->AddPairingDelegate(&delegate,
+ BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
+ adapter->RemovePairingDelegate(&delegate);
+
+ EXPECT_TRUE(adapter->DefaultPairingDelegate() == NULL);
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_adapter_win.cc b/chromium/device/bluetooth/bluetooth_adapter_win.cc
index f6ca1d65ebe..8ef54705f18 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_win.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_win.cc
@@ -14,10 +14,27 @@
#include "base/stl_util.h"
#include "base/thread_task_runner_handle.h"
#include "device/bluetooth/bluetooth_device_win.h"
+#include "device/bluetooth/bluetooth_socket_thread.h"
+#include "device/bluetooth/bluetooth_socket_win.h"
#include "device/bluetooth/bluetooth_task_manager_win.h"
+#include "device/bluetooth/bluetooth_uuid.h"
namespace device {
+// static
+base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
+ const InitCallback& init_callback) {
+ return BluetoothAdapterWin::CreateAdapter(init_callback);
+}
+
+// static
+base::WeakPtr<BluetoothAdapter> BluetoothAdapterWin::CreateAdapter(
+ const InitCallback& init_callback) {
+ BluetoothAdapterWin* adapter = new BluetoothAdapterWin(init_callback);
+ adapter->Init();
+ return adapter->weak_ptr_factory_.GetWeakPtr();
+}
+
BluetoothAdapterWin::BluetoothAdapterWin(const InitCallback& init_callback)
: BluetoothAdapter(),
init_callback_(init_callback),
@@ -53,6 +70,12 @@ std::string BluetoothAdapterWin::GetName() const {
return name_;
}
+void BluetoothAdapterWin::SetName(const std::string& name,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ NOTIMPLEMENTED();
+}
+
// TODO(youngki): Return true when |task_manager_| initializes the adapter
// state.
bool BluetoothAdapterWin::IsInitialized() const {
@@ -74,35 +97,21 @@ void BluetoothAdapterWin::SetPowered(
task_manager_->PostSetPoweredBluetoothTask(powered, callback, error_callback);
}
-bool BluetoothAdapterWin::IsDiscovering() const {
- return discovery_status_ == DISCOVERING ||
- discovery_status_ == DISCOVERY_STOPPING;
+bool BluetoothAdapterWin::IsDiscoverable() const {
+ NOTIMPLEMENTED();
+ return false;
}
-// If the method is called when |discovery_status_| is DISCOVERY_STOPPING,
-// starting again is handled by BluetoothAdapterWin::DiscoveryStopped().
-void BluetoothAdapterWin::StartDiscovering(
+void BluetoothAdapterWin::SetDiscoverable(
+ bool discoverable,
const base::Closure& callback,
const ErrorCallback& error_callback) {
- if (discovery_status_ == DISCOVERING) {
- num_discovery_listeners_++;
- callback.Run();
- return;
- }
- on_start_discovery_callbacks_.push_back(
- std::make_pair(callback, error_callback));
- MaybePostStartDiscoveryTask();
+ NOTIMPLEMENTED();
}
-void BluetoothAdapterWin::StopDiscovering(
- const base::Closure& callback,
- const ErrorCallback& error_callback) {
- if (discovery_status_ == NOT_DISCOVERING) {
- error_callback.Run();
- return;
- }
- on_stop_discovery_callbacks_.push_back(callback);
- MaybePostStopDiscoveryTask();
+bool BluetoothAdapterWin::IsDiscovering() const {
+ return discovery_status_ == DISCOVERING ||
+ discovery_status_ == DISCOVERY_STOPPING;
}
void BluetoothAdapterWin::DiscoveryStarted(bool success) {
@@ -152,19 +161,42 @@ void BluetoothAdapterWin::DiscoveryStopped() {
MaybePostStartDiscoveryTask();
}
-void BluetoothAdapterWin::ReadLocalOutOfBandPairingData(
- const BluetoothOutOfBandPairingDataCallback& callback,
- const ErrorCallback& error_callback) {
+void BluetoothAdapterWin::CreateRfcommService(
+ const BluetoothUUID& uuid,
+ int channel,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) {
+ scoped_refptr<BluetoothSocketWin> socket =
+ BluetoothSocketWin::CreateBluetoothSocket(
+ ui_task_runner_,
+ socket_thread_,
+ NULL,
+ net::NetLog::Source());
+ socket->Listen(this, uuid, channel,
+ base::Bind(callback, socket),
+ error_callback);
+}
+
+void BluetoothAdapterWin::CreateL2capService(
+ const BluetoothUUID& uuid,
+ int psm,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) {
+ // TODO(keybuk): implement.
NOTIMPLEMENTED();
}
+void BluetoothAdapterWin::RemovePairingDelegateInternal(
+ BluetoothDevice::PairingDelegate* pairing_delegate) {
+}
+
void BluetoothAdapterWin::AdapterStateChanged(
const BluetoothTaskManagerWin::AdapterState& state) {
DCHECK(thread_checker_.CalledOnValidThread());
name_ = state.name;
bool was_present = IsPresent();
bool is_present = !state.address.empty();
- address_ = state.address;
+ address_ = BluetoothDevice::CanonicalizeAddress(state.address);
if (was_present != is_present) {
FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
AdapterPresentChanged(this, is_present));
@@ -189,7 +221,8 @@ void BluetoothAdapterWin::DevicesDiscovered(
++iter) {
if (discovered_devices_.find((*iter)->address) ==
discovered_devices_.end()) {
- BluetoothDeviceWin device_win(**iter);
+ 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);
@@ -204,12 +237,40 @@ void BluetoothAdapterWin::DevicesUpdated(
devices.begin();
iter != devices.end();
++iter) {
- devices_[(*iter)->address] = new BluetoothDeviceWin(**iter);
+ devices_[(*iter)->address] = new BluetoothDeviceWin(
+ **iter, ui_task_runner_, socket_thread_, NULL, net::NetLog::Source());
}
}
+// If the method is called when |discovery_status_| is DISCOVERY_STOPPING,
+// starting again is handled by BluetoothAdapterWin::DiscoveryStopped().
+void BluetoothAdapterWin::AddDiscoverySession(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (discovery_status_ == DISCOVERING) {
+ num_discovery_listeners_++;
+ callback.Run();
+ return;
+ }
+ on_start_discovery_callbacks_.push_back(
+ std::make_pair(callback, error_callback));
+ MaybePostStartDiscoveryTask();
+}
+
+void BluetoothAdapterWin::RemoveDiscoverySession(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (discovery_status_ == NOT_DISCOVERING) {
+ error_callback.Run();
+ return;
+ }
+ on_stop_discovery_callbacks_.push_back(callback);
+ MaybePostStopDiscoveryTask();
+}
+
void BluetoothAdapterWin::Init() {
ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+ socket_thread_ = BluetoothSocketThread::Get();
task_manager_ =
new BluetoothTaskManagerWin(ui_task_runner_);
task_manager_->AddObserver(this);
diff --git a/chromium/device/bluetooth/bluetooth_adapter_win.h b/chromium/device/bluetooth/bluetooth_adapter_win.h
index a017e29831d..80e185989bf 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_win.h
+++ b/chromium/device/bluetooth/bluetooth_adapter_win.h
@@ -18,45 +18,53 @@
#include "device/bluetooth/bluetooth_task_manager_win.h"
namespace base {
-
class SequencedTaskRunner;
-
+class Thread;
} // namespace base
namespace device {
-class BluetoothAdapterFactory;
class BluetoothAdapterWinTest;
class BluetoothDevice;
+class BluetoothSocketThread;
class BluetoothAdapterWin : public BluetoothAdapter,
public BluetoothTaskManagerWin::Observer {
public:
- typedef base::Callback<void()> InitCallback;
+ static base::WeakPtr<BluetoothAdapter> CreateAdapter(
+ const InitCallback& init_callback);
- // BluetoothAdapter override
+ // BluetoothAdapter:
virtual void AddObserver(BluetoothAdapter::Observer* observer) OVERRIDE;
virtual void RemoveObserver(BluetoothAdapter::Observer* observer) OVERRIDE;
virtual std::string GetAddress() const OVERRIDE;
virtual std::string GetName() const OVERRIDE;
+ virtual void SetName(const std::string& name,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
virtual bool IsInitialized() const OVERRIDE;
virtual bool IsPresent() const OVERRIDE;
virtual bool IsPowered() const OVERRIDE;
virtual void SetPowered(
- bool powered,
- const base::Closure& callback,
- const ErrorCallback& error_callback) OVERRIDE;
- virtual bool IsDiscovering() const OVERRIDE;
-
- virtual void StartDiscovering(
+ bool discoverable,
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
- virtual void StopDiscovering(
+ virtual bool IsDiscoverable() const OVERRIDE;
+ virtual void SetDiscoverable(
+ bool discoverable,
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
- virtual void ReadLocalOutOfBandPairingData(
- const BluetoothOutOfBandPairingDataCallback& callback,
- const ErrorCallback& error_callback) OVERRIDE;
+ virtual bool IsDiscovering() const OVERRIDE;
+ virtual void CreateRfcommService(
+ const BluetoothUUID& uuid,
+ int channel,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) OVERRIDE;
+ virtual void CreateL2capService(
+ const BluetoothUUID& uuid,
+ int psm,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) OVERRIDE;
// BluetoothTaskManagerWin::Observer override
virtual void AdapterStateChanged(
@@ -71,8 +79,19 @@ class BluetoothAdapterWin : public BluetoothAdapter,
const ScopedVector<BluetoothTaskManagerWin::DeviceState>& devices)
OVERRIDE;
+ const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner() const {
+ return ui_task_runner_;
+ }
+ const scoped_refptr<BluetoothSocketThread>& socket_thread() const {
+ return socket_thread_;
+ }
+
+ protected:
+ // BluetoothAdapter:
+ virtual void RemovePairingDelegateInternal(
+ device::BluetoothDevice::PairingDelegate* pairing_delegate) OVERRIDE;
+
private:
- friend class BluetoothAdapterFactory;
friend class BluetoothAdapterWinTest;
enum DiscoveryStatus {
@@ -85,6 +104,14 @@ class BluetoothAdapterWin : public BluetoothAdapter,
explicit BluetoothAdapterWin(const InitCallback& init_callback);
virtual ~BluetoothAdapterWin();
+ // BluetoothAdapter:
+ virtual void AddDiscoverySession(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual void RemoveDiscoverySession(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+
void Init();
void InitForTest(
scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
@@ -107,6 +134,7 @@ class BluetoothAdapterWin : public BluetoothAdapter,
size_t num_discovery_listeners_;
scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
+ scoped_refptr<BluetoothSocketThread> socket_thread_;
scoped_refptr<BluetoothTaskManagerWin> task_manager_;
base::ThreadChecker thread_checker_;
diff --git a/chromium/device/bluetooth/bluetooth_adapter_win_unittest.cc b/chromium/device/bluetooth/bluetooth_adapter_win_unittest.cc
index 4ea7a1e6f3d..c3dc9101ec4 100644
--- a/chromium/device/bluetooth/bluetooth_adapter_win_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_adapter_win_unittest.cc
@@ -15,7 +15,7 @@
namespace {
-const char kAdapterAddress[] = "Bluetooth Adapter Address";
+const char kAdapterAddress[] = "A1:B2:C3:D4:E5:F6";
const char kAdapterName[] = "Bluetooth Adapter Name";
@@ -131,6 +131,18 @@ class BluetoothAdapterWinTest : public testing::Test {
num_stop_discovery_error_callbacks_++;
}
+ void CallAddDiscoverySession(
+ const base::Closure& callback,
+ const BluetoothAdapter::ErrorCallback& error_callback) {
+ adapter_win_->AddDiscoverySession(callback, error_callback);
+ }
+
+ void CallRemoveDiscoverySession(
+ const base::Closure& callback,
+ const BluetoothAdapter::ErrorCallback& error_callback) {
+ adapter_win_->RemoveDiscoverySession(callback, error_callback);
+ }
+
protected:
scoped_refptr<base::TestSimpleTaskRunner> ui_task_runner_;
scoped_refptr<base::TestSimpleTaskRunner> bluetooth_task_runner_;
@@ -194,7 +206,7 @@ TEST_F(BluetoothAdapterWinTest, AdapterInitialized) {
TEST_F(BluetoothAdapterWinTest, SingleStartDiscovery) {
bluetooth_task_runner_->ClearPendingTasks();
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
base::Unretained(this)),
BluetoothAdapter::ErrorCallback());
@@ -210,7 +222,7 @@ TEST_F(BluetoothAdapterWinTest, SingleStartDiscovery) {
}
TEST_F(BluetoothAdapterWinTest, SingleStartDiscoveryFailure) {
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Closure(),
base::Bind(
&BluetoothAdapterWinTest::IncrementNumStartDiscoveryErrorCallbacks,
@@ -226,7 +238,7 @@ TEST_F(BluetoothAdapterWinTest, MultipleStartDiscoveries) {
bluetooth_task_runner_->ClearPendingTasks();
int num_discoveries = 5;
for (int i = 0; i < num_discoveries; i++) {
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Bind(
&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
base::Unretained(this)),
@@ -246,7 +258,7 @@ TEST_F(BluetoothAdapterWinTest, MultipleStartDiscoveries) {
TEST_F(BluetoothAdapterWinTest, MultipleStartDiscoveriesFailure) {
int num_discoveries = 5;
for (int i = 0; i < num_discoveries; i++) {
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Closure(),
base::Bind(
&BluetoothAdapterWinTest::IncrementNumStartDiscoveryErrorCallbacks,
@@ -260,7 +272,7 @@ TEST_F(BluetoothAdapterWinTest, MultipleStartDiscoveriesFailure) {
}
TEST_F(BluetoothAdapterWinTest, MultipleStartDiscoveriesAfterDiscovering) {
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
base::Unretained(this)),
BluetoothAdapter::ErrorCallback());
@@ -272,7 +284,7 @@ TEST_F(BluetoothAdapterWinTest, MultipleStartDiscoveriesAfterDiscovering) {
bluetooth_task_runner_->ClearPendingTasks();
for (int i = 0; i < 5; i++) {
int num_start_discovery_callbacks = num_start_discovery_callbacks_;
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Bind(
&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
base::Unretained(this)),
@@ -287,7 +299,7 @@ TEST_F(BluetoothAdapterWinTest, MultipleStartDiscoveriesAfterDiscovering) {
}
TEST_F(BluetoothAdapterWinTest, StartDiscoveryAfterDiscoveringFailure) {
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Closure(),
base::Bind(
&BluetoothAdapterWinTest::IncrementNumStartDiscoveryErrorCallbacks,
@@ -297,7 +309,7 @@ TEST_F(BluetoothAdapterWinTest, StartDiscoveryAfterDiscoveringFailure) {
EXPECT_FALSE(adapter_->IsDiscovering());
EXPECT_EQ(1, num_start_discovery_error_callbacks_);
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
base::Unretained(this)),
BluetoothAdapter::ErrorCallback());
@@ -308,11 +320,11 @@ TEST_F(BluetoothAdapterWinTest, StartDiscoveryAfterDiscoveringFailure) {
}
TEST_F(BluetoothAdapterWinTest, SingleStopDiscovery) {
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Closure(), BluetoothAdapter::ErrorCallback());
adapter_win_->DiscoveryStarted(true);
ui_task_runner_->ClearPendingTasks();
- adapter_win_->StopDiscovering(
+ CallRemoveDiscoverySession(
base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
base::Unretained(this)),
BluetoothAdapter::ErrorCallback());
@@ -330,14 +342,14 @@ TEST_F(BluetoothAdapterWinTest, SingleStopDiscovery) {
TEST_F(BluetoothAdapterWinTest, MultipleStopDiscoveries) {
int num_discoveries = 5;
for (int i = 0; i < num_discoveries; i++) {
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Closure(), BluetoothAdapter::ErrorCallback());
}
adapter_win_->DiscoveryStarted(true);
ui_task_runner_->ClearPendingTasks();
bluetooth_task_runner_->ClearPendingTasks();
for (int i = 0; i < num_discoveries - 1; i++) {
- adapter_win_->StopDiscovering(
+ CallRemoveDiscoverySession(
base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
base::Unretained(this)),
BluetoothAdapter::ErrorCallback());
@@ -345,7 +357,7 @@ TEST_F(BluetoothAdapterWinTest, MultipleStopDiscoveries) {
ui_task_runner_->RunPendingTasks();
EXPECT_EQ(i + 1, num_stop_discovery_callbacks_);
}
- adapter_win_->StopDiscovering(
+ CallRemoveDiscoverySession(
base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
base::Unretained(this)),
BluetoothAdapter::ErrorCallback());
@@ -360,23 +372,23 @@ TEST_F(BluetoothAdapterWinTest, MultipleStopDiscoveries) {
TEST_F(BluetoothAdapterWinTest,
StartDiscoveryAndStartDiscoveryAndStopDiscoveries) {
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
base::Unretained(this)),
BluetoothAdapter::ErrorCallback());
adapter_win_->DiscoveryStarted(true);
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
base::Unretained(this)),
BluetoothAdapter::ErrorCallback());
ui_task_runner_->ClearPendingTasks();
bluetooth_task_runner_->ClearPendingTasks();
- adapter_win_->StopDiscovering(
+ CallRemoveDiscoverySession(
base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
base::Unretained(this)),
BluetoothAdapter::ErrorCallback());
EXPECT_TRUE(bluetooth_task_runner_->GetPendingTasks().empty());
- adapter_win_->StopDiscovering(
+ CallRemoveDiscoverySession(
base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
base::Unretained(this)),
BluetoothAdapter::ErrorCallback());
@@ -385,27 +397,27 @@ TEST_F(BluetoothAdapterWinTest,
TEST_F(BluetoothAdapterWinTest,
StartDiscoveryAndStopDiscoveryAndStartDiscovery) {
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Closure(), BluetoothAdapter::ErrorCallback());
adapter_win_->DiscoveryStarted(true);
EXPECT_TRUE(adapter_->IsDiscovering());
- adapter_win_->StopDiscovering(
+ CallRemoveDiscoverySession(
base::Closure(), BluetoothAdapter::ErrorCallback());
adapter_win_->DiscoveryStopped();
EXPECT_FALSE(adapter_->IsDiscovering());
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Closure(), BluetoothAdapter::ErrorCallback());
adapter_win_->DiscoveryStarted(true);
EXPECT_TRUE(adapter_->IsDiscovering());
}
TEST_F(BluetoothAdapterWinTest, StartDiscoveryBeforeDiscoveryStopped) {
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Closure(), BluetoothAdapter::ErrorCallback());
adapter_win_->DiscoveryStarted(true);
- adapter_win_->StopDiscovering(
+ CallRemoveDiscoverySession(
base::Closure(), BluetoothAdapter::ErrorCallback());
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Closure(), BluetoothAdapter::ErrorCallback());
bluetooth_task_runner_->ClearPendingTasks();
adapter_win_->DiscoveryStopped();
@@ -413,7 +425,7 @@ TEST_F(BluetoothAdapterWinTest, StartDiscoveryBeforeDiscoveryStopped) {
}
TEST_F(BluetoothAdapterWinTest, StopDiscoveryWithoutStartDiscovery) {
- adapter_win_->StopDiscovering(
+ CallRemoveDiscoverySession(
base::Closure(),
base::Bind(
&BluetoothAdapterWinTest::IncrementNumStopDiscoveryErrorCallbacks,
@@ -422,9 +434,9 @@ TEST_F(BluetoothAdapterWinTest, StopDiscoveryWithoutStartDiscovery) {
}
TEST_F(BluetoothAdapterWinTest, StopDiscoveryBeforeDiscoveryStarted) {
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Closure(), BluetoothAdapter::ErrorCallback());
- adapter_win_->StopDiscovering(
+ CallRemoveDiscoverySession(
base::Closure(), BluetoothAdapter::ErrorCallback());
bluetooth_task_runner_->ClearPendingTasks();
adapter_win_->DiscoveryStarted(true);
@@ -435,14 +447,14 @@ TEST_F(BluetoothAdapterWinTest, StartAndStopBeforeDiscoveryStarted) {
int num_expected_start_discoveries = 3;
int num_expected_stop_discoveries = 2;
for (int i = 0; i < num_expected_start_discoveries; i++) {
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Bind(
&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
base::Unretained(this)),
BluetoothAdapter::ErrorCallback());
}
for (int i = 0; i < num_expected_stop_discoveries; i++) {
- adapter_win_->StopDiscovering(
+ CallRemoveDiscoverySession(
base::Bind(
&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
base::Unretained(this)),
@@ -457,12 +469,12 @@ TEST_F(BluetoothAdapterWinTest, StartAndStopBeforeDiscoveryStarted) {
}
TEST_F(BluetoothAdapterWinTest, StopDiscoveryBeforeDiscoveryStartedAndFailed) {
- adapter_win_->StartDiscovering(
+ CallAddDiscoverySession(
base::Closure(),
base::Bind(
&BluetoothAdapterWinTest::IncrementNumStartDiscoveryErrorCallbacks,
base::Unretained(this)));
- adapter_win_->StopDiscovering(
+ CallRemoveDiscoverySession(
base::Bind(
&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
base::Unretained(this)),
diff --git a/chromium/device/bluetooth/bluetooth_channel_mac.h b/chromium/device/bluetooth/bluetooth_channel_mac.h
new file mode 100644
index 00000000000..588a45c5558
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_channel_mac.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_CHANNEL_MAC_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_CHANNEL_MAC_H_
+
+#import <IOKit/IOReturn.h>
+
+#include <string>
+
+#include "base/macros.h"
+
+@class IOBluetoothDevice;
+
+namespace device {
+
+class BluetoothSocketMac;
+
+// Wraps a native RFCOMM or L2CAP channel.
+class BluetoothChannelMac {
+ public:
+ BluetoothChannelMac();
+ virtual ~BluetoothChannelMac();
+
+ // Sets the channel's owning socket to |socket|. Should only be called if the
+ // socket was previously unset. Note: This can synchronously call back into
+ // socket->OnChannelOpenComplete().
+ virtual void SetSocket(BluetoothSocketMac* socket);
+
+ // Returns the Bluetooth address for the device associated with |this|
+ // channel.
+ std::string GetDeviceAddress();
+
+ // Returns the Bluetooth device associated with |this| channel.
+ virtual IOBluetoothDevice* GetDevice() = 0;
+
+ // Returns the outgoing MTU (maximum transmission unit) for the channel.
+ virtual uint16_t GetOutgoingMTU() = 0;
+
+ // Writes |data| of length |length| bytes into the channel. The |refcon| is a
+ // user-supplied value that gets passed to the write callback.
+ // Returns kIOReturnSuccess if the data was buffered successfully.
+ // If the return value is an error condition none of the data was sent.
+ // The number of bytes to be sent must not exceed the channel MTU.
+ //
+ // Once the data has been successfully passed to the hardware to be
+ // transmitted, the socket's method OnChannelWriteComplete() will be called
+ // with the |refcon| that was passed to this method.
+ virtual IOReturn WriteAsync(void* data, uint16_t length, void* refcon) = 0;
+
+ protected:
+ BluetoothSocketMac* socket() { return socket_; }
+
+ private:
+ // The socket that owns |this|.
+ BluetoothSocketMac* socket_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothChannelMac);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_CHANNEL_MAC_H_
diff --git a/chromium/device/bluetooth/bluetooth_channel_mac.mm b/chromium/device/bluetooth/bluetooth_channel_mac.mm
new file mode 100644
index 00000000000..50f31b991fc
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_channel_mac.mm
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_channel_mac.h"
+
+#import <IOBluetooth/IOBluetooth.h>
+
+#include "base/logging.h"
+#include "device/bluetooth/bluetooth_device_mac.h"
+
+namespace device {
+
+BluetoothChannelMac::BluetoothChannelMac() : socket_(NULL) {
+}
+
+BluetoothChannelMac::~BluetoothChannelMac() {
+}
+
+void BluetoothChannelMac::SetSocket(BluetoothSocketMac* socket) {
+ DCHECK(!socket_);
+ socket_ = socket;
+}
+
+std::string BluetoothChannelMac::GetDeviceAddress() {
+ return BluetoothDeviceMac::GetDeviceAddress(GetDevice());
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_chromeos_unittest.cc b/chromium/device/bluetooth/bluetooth_chromeos_unittest.cc
index dea13221e6a..379d1dfd25f 100644
--- a/chromium/device/bluetooth/bluetooth_chromeos_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_chromeos_unittest.cc
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/dbus/fake_bluetooth_adapter_client.h"
#include "chromeos/dbus/fake_bluetooth_agent_manager_client.h"
#include "chromeos/dbus/fake_bluetooth_device_client.h"
+#include "chromeos/dbus/fake_bluetooth_gatt_service_client.h"
#include "chromeos/dbus/fake_bluetooth_input_client.h"
#include "chromeos/dbus/fake_dbus_thread_manager.h"
#include "dbus/object_path.h"
@@ -15,19 +17,27 @@
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_device_chromeos.h"
+#include "device/bluetooth/bluetooth_discovery_session.h"
+#include "device/bluetooth/bluetooth_pairing_chromeos.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
using device::BluetoothAdapter;
using device::BluetoothAdapterFactory;
using device::BluetoothDevice;
+using device::BluetoothDiscoverySession;
+using device::BluetoothUUID;
namespace chromeos {
+namespace {
+
class TestObserver : public BluetoothAdapter::Observer {
public:
TestObserver(scoped_refptr<BluetoothAdapter> adapter)
: present_changed_count_(0),
powered_changed_count_(0),
+ discoverable_changed_count_(0),
discovering_changed_count_(0),
last_present_(false),
last_powered_(false),
@@ -37,8 +47,12 @@ class TestObserver : public BluetoothAdapter::Observer {
device_removed_count_(0),
last_device_(NULL),
adapter_(adapter) {
+ adapter_->AddObserver(this);
+ }
+
+ virtual ~TestObserver() {
+ adapter_->RemoveObserver(this);
}
- virtual ~TestObserver() {}
virtual void AdapterPresentChanged(BluetoothAdapter* adapter,
bool present) OVERRIDE {
@@ -56,6 +70,13 @@ class TestObserver : public BluetoothAdapter::Observer {
last_powered_ = powered;
}
+ virtual void AdapterDiscoverableChanged(BluetoothAdapter* adapter,
+ bool discoverable) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+
+ ++discoverable_changed_count_;
+ }
+
virtual void AdapterDiscoveringChanged(BluetoothAdapter* adapter,
bool discovering) OVERRIDE {
EXPECT_EQ(adapter_, adapter);
@@ -99,6 +120,7 @@ class TestObserver : public BluetoothAdapter::Observer {
int present_changed_count_;
int powered_changed_count_;
+ int discoverable_changed_count_;
int discovering_changed_count_;
bool last_present_;
bool last_powered_;
@@ -121,6 +143,8 @@ class TestObserver : public BluetoothAdapter::Observer {
scoped_refptr<BluetoothAdapter> adapter_;
};
+} // namespace
+
class TestPairingDelegate : public BluetoothDevice::PairingDelegate {
public:
TestPairingDelegate()
@@ -131,7 +155,7 @@ class TestPairingDelegate : public BluetoothDevice::PairingDelegate {
display_passkey_count_(0),
keys_entered_count_(0),
confirm_passkey_count_(0),
- dismiss_count_(0),
+ authorize_pairing_count_(0),
last_passkey_(9999999U),
last_entered_(999U) {}
virtual ~TestPairingDelegate() {}
@@ -179,9 +203,9 @@ class TestPairingDelegate : public BluetoothDevice::PairingDelegate {
QuitMessageLoop();
}
- virtual void DismissDisplayOrConfirm() OVERRIDE {
+ virtual void AuthorizePairing(BluetoothDevice* device) OVERRIDE {
++call_count_;
- ++dismiss_count_;
+ ++authorize_pairing_count_;
QuitMessageLoop();
}
@@ -192,7 +216,7 @@ class TestPairingDelegate : public BluetoothDevice::PairingDelegate {
int display_passkey_count_;
int keys_entered_count_;
int confirm_passkey_count_;
- int dismiss_count_;
+ int authorize_pairing_count_;
uint32 last_passkey_;
uint32 last_entered_;
std::string last_pincode_;
@@ -222,14 +246,37 @@ class BluetoothChromeOSTest : public testing::Test {
fake_dbus_thread_manager->SetBluetoothAgentManagerClient(
scoped_ptr<BluetoothAgentManagerClient>(
new FakeBluetoothAgentManagerClient));
+ fake_dbus_thread_manager->SetBluetoothGattServiceClient(
+ scoped_ptr<BluetoothGattServiceClient>(
+ new FakeBluetoothGattServiceClient));
DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager);
+ fake_bluetooth_adapter_client_->SetSimulationIntervalMs(10);
+
callback_count_ = 0;
error_callback_count_ = 0;
last_connect_error_ = BluetoothDevice::ERROR_UNKNOWN;
+ last_client_error_ = "";
}
virtual void TearDown() {
+ for (ScopedVector<BluetoothDiscoverySession>::iterator iter =
+ discovery_sessions_.begin();
+ iter != discovery_sessions_.end();
+ ++iter) {
+ BluetoothDiscoverySession* session = *iter;
+ if (!session->IsActive())
+ continue;
+ callback_count_ = 0;
+ session->Stop(
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+ ASSERT_EQ(1, callback_count_);
+ }
+ discovery_sessions_.clear();
adapter_ = NULL;
DBusThreadManager::Shutdown();
}
@@ -237,13 +284,29 @@ class BluetoothChromeOSTest : public testing::Test {
// Generic callbacks
void Callback() {
++callback_count_;
+ QuitMessageLoop();
+ }
+
+ void DiscoverySessionCallback(
+ scoped_ptr<BluetoothDiscoverySession> discovery_session) {
+ ++callback_count_;
+ discovery_sessions_.push_back(discovery_session.release());
+ QuitMessageLoop();
}
void ErrorCallback() {
++error_callback_count_;
+ QuitMessageLoop();
+ }
+
+ void DBusErrorCallback(const std::string& error_name,
+ const std::string& error_message) {
+ ++error_callback_count_;
+ last_client_error_ = error_name;
+ QuitMessageLoop();
}
- void ConnectErrorCallback(enum BluetoothDevice::ConnectErrorCode error) {
+ void ConnectErrorCallback(BluetoothDevice::ConnectErrorCode error) {
++error_callback_count_;
last_connect_error_ = error;
}
@@ -262,17 +325,10 @@ class BluetoothChromeOSTest : public testing::Test {
// without using this function.
void DiscoverDevice(const std::string& address) {
ASSERT_TRUE(adapter_.get() != NULL);
-
- if (base::MessageLoop::current() == NULL) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
- DiscoverDevices();
- return;
- }
-
+ ASSERT_TRUE(base::MessageLoop::current() != NULL);
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
adapter_->SetPowered(
true,
@@ -280,13 +336,16 @@ class BluetoothChromeOSTest : public testing::Test {
base::Unretained(this)),
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
base::Unretained(this)));
- adapter_->StartDiscovering(
- base::Bind(&BluetoothChromeOSTest::Callback,
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
base::Unretained(this)));
+ base::MessageLoop::current()->Run();
ASSERT_EQ(2, callback_count_);
ASSERT_EQ(0, error_callback_count_);
+ ASSERT_EQ((size_t)1, discovery_sessions_.size());
+ ASSERT_TRUE(discovery_sessions_[0]->IsActive());
callback_count_ = 0;
ASSERT_TRUE(adapter_->IsPowered());
@@ -296,18 +355,17 @@ class BluetoothChromeOSTest : public testing::Test {
observer.last_device_address_ != address)
base::MessageLoop::current()->Run();
- adapter_->StopDiscovering(
+ discovery_sessions_[0]->Stop(
base::Bind(&BluetoothChromeOSTest::Callback,
base::Unretained(this)),
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
base::Unretained(this)));
+ base::MessageLoop::current()->Run();
ASSERT_EQ(1, callback_count_);
ASSERT_EQ(0, error_callback_count_);
callback_count_ = 0;
ASSERT_FALSE(adapter_->IsDiscovering());
-
- adapter_->RemoveObserver(&observer);
}
// Run a discovery phase so we have devices that can be paired with.
@@ -318,6 +376,7 @@ class BluetoothChromeOSTest : public testing::Test {
}
protected:
+ base::MessageLoop message_loop_;
FakeBluetoothAdapterClient* fake_bluetooth_adapter_client_;
FakeBluetoothDeviceClient* fake_bluetooth_device_client_;
scoped_refptr<BluetoothAdapter> adapter_;
@@ -325,6 +384,17 @@ class BluetoothChromeOSTest : public testing::Test {
int callback_count_;
int error_callback_count_;
enum BluetoothDevice::ConnectErrorCode last_connect_error_;
+ std::string last_client_error_;
+ ScopedVector<BluetoothDiscoverySession> discovery_sessions_;
+
+ private:
+ // Some tests use a message loop since background processing is simulated;
+ // break out of those loops.
+ void QuitMessageLoop() {
+ if (base::MessageLoop::current() &&
+ base::MessageLoop::current()->is_running())
+ base::MessageLoop::current()->Quit();
+ }
};
TEST_F(BluetoothChromeOSTest, AlreadyPresent) {
@@ -353,7 +423,6 @@ TEST_F(BluetoothChromeOSTest, BecomePresent) {
// Install an observer; expect the AdapterPresentChanged to be called
// with true, and IsPresent() to return true.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
fake_bluetooth_adapter_client_->SetVisible(true);
@@ -381,7 +450,6 @@ TEST_F(BluetoothChromeOSTest, BecomeNotPresent) {
// Install an observer; expect the AdapterPresentChanged to be called
// with false, and IsPresent() to return false.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
fake_bluetooth_adapter_client_->SetVisible(false);
@@ -409,7 +477,6 @@ TEST_F(BluetoothChromeOSTest, SecondAdapter) {
// Install an observer, then add a second adapter. Nothing should change,
// we ignore the second adapter.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
fake_bluetooth_adapter_client_->SetSecondVisible(true);
@@ -456,7 +523,6 @@ TEST_F(BluetoothChromeOSTest, BecomePowered) {
// Install an observer; expect the AdapterPoweredChanged to be called
// with true, and IsPowered() to return true.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
adapter_->SetPowered(
true,
@@ -490,7 +556,6 @@ TEST_F(BluetoothChromeOSTest, BecomeNotPowered) {
// Install an observer; expect the AdapterPoweredChanged to be called
// with false, and IsPowered() to return false.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
adapter_->SetPowered(
false,
@@ -507,36 +572,33 @@ TEST_F(BluetoothChromeOSTest, BecomeNotPowered) {
EXPECT_FALSE(adapter_->IsPowered());
}
-TEST_F(BluetoothChromeOSTest, StopDiscovery) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
-
+TEST_F(BluetoothChromeOSTest, ChangeAdapterName) {
GetAdapter();
- adapter_->SetPowered(
- true,
- base::Bind(&BluetoothChromeOSTest::Callback,
- base::Unretained(this)),
- base::Bind(&BluetoothChromeOSTest::ErrorCallback,
- base::Unretained(this)));
- adapter_->StartDiscovering(
+ static const std::string new_name(".__.");
+
+ adapter_->SetName(
+ new_name,
base::Bind(&BluetoothChromeOSTest::Callback,
base::Unretained(this)),
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
base::Unretained(this)));
- EXPECT_EQ(2, callback_count_);
+ EXPECT_EQ(1, callback_count_);
EXPECT_EQ(0, error_callback_count_);
- callback_count_ = 0;
- ASSERT_TRUE(adapter_->IsPowered());
- ASSERT_TRUE(adapter_->IsDiscovering());
+ EXPECT_EQ(new_name, adapter_->GetName());
+}
- // Install an observer; aside from the callback, expect the
- // AdapterDiscoveringChanged method to be called and no longer to be
- // discovering,
+TEST_F(BluetoothChromeOSTest, BecomeDiscoverable) {
+ GetAdapter();
+ ASSERT_FALSE(adapter_->IsDiscoverable());
+
+ // Install an observer; expect the AdapterDiscoverableChanged to be called
+ // with true, and IsDiscoverable() to return true.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
- adapter_->StopDiscovering(
+ adapter_->SetDiscoverable(
+ true,
base::Bind(&BluetoothChromeOSTest::Callback,
base::Unretained(this)),
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
@@ -544,74 +606,78 @@ TEST_F(BluetoothChromeOSTest, StopDiscovery) {
EXPECT_EQ(1, callback_count_);
EXPECT_EQ(0, error_callback_count_);
- EXPECT_EQ(1, observer.discovering_changed_count_);
- EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_EQ(1, observer.discoverable_changed_count_);
- EXPECT_FALSE(adapter_->IsDiscovering());
+ EXPECT_TRUE(adapter_->IsDiscoverable());
}
-TEST_F(BluetoothChromeOSTest, StopDiscoveryAfterTwoStarts) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
-
+TEST_F(BluetoothChromeOSTest, BecomeNotDiscoverable) {
GetAdapter();
-
- adapter_->SetPowered(
+ adapter_->SetDiscoverable(
true,
base::Bind(&BluetoothChromeOSTest::Callback,
base::Unretained(this)),
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
base::Unretained(this)));
- adapter_->StartDiscovering(
- base::Bind(&BluetoothChromeOSTest::Callback,
- base::Unretained(this)),
- base::Bind(&BluetoothChromeOSTest::ErrorCallback,
- base::Unretained(this)));
- EXPECT_EQ(2, callback_count_);
+ EXPECT_EQ(1, callback_count_);
EXPECT_EQ(0, error_callback_count_);
callback_count_ = 0;
- ASSERT_TRUE(adapter_->IsPowered());
- ASSERT_TRUE(adapter_->IsDiscovering());
+ ASSERT_TRUE(adapter_->IsDiscoverable());
- // Install an observer and start discovering again; only the callback
- // should be called since we were already discovering to begin with.
+ // Install an observer; expect the AdapterDiscoverableChanged to be called
+ // with false, and IsDiscoverable() to return false.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
- adapter_->StartDiscovering(
+ adapter_->SetDiscoverable(
+ false,
base::Bind(&BluetoothChromeOSTest::Callback,
base::Unretained(this)),
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
base::Unretained(this)));
EXPECT_EQ(1, callback_count_);
EXPECT_EQ(0, error_callback_count_);
- callback_count_ = 0;
- EXPECT_EQ(0, observer.discovering_changed_count_);
+ EXPECT_EQ(1, observer.discoverable_changed_count_);
+
+ EXPECT_FALSE(adapter_->IsDiscoverable());
+}
+
+TEST_F(BluetoothChromeOSTest, StopDiscovery) {
+ GetAdapter();
- // Stop discovering; only the callback should be called since we're still
- // discovering. The adapter should be still discovering.
- adapter_->StopDiscovering(
+ adapter_->SetPowered(
+ true,
base::Bind(&BluetoothChromeOSTest::Callback,
base::Unretained(this)),
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
base::Unretained(this)));
- EXPECT_EQ(1, callback_count_);
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+ EXPECT_EQ(2, callback_count_);
EXPECT_EQ(0, error_callback_count_);
callback_count_ = 0;
- EXPECT_EQ(0, observer.discovering_changed_count_);
-
- EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_TRUE(adapter_->IsPowered());
+ ASSERT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)1, discovery_sessions_.size());
+ ASSERT_TRUE(discovery_sessions_[0]->IsActive());
- // Stop discovering one more time; aside from the callback, expect the
+ // Install an observer; aside from the callback, expect the
// AdapterDiscoveringChanged method to be called and no longer to be
// discovering,
- adapter_->StopDiscovering(
+ TestObserver observer(adapter_);
+
+ discovery_sessions_[0]->Stop(
base::Bind(&BluetoothChromeOSTest::Callback,
base::Unretained(this)),
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
base::Unretained(this)));
+ message_loop_.Run();
EXPECT_EQ(1, callback_count_);
EXPECT_EQ(0, error_callback_count_);
@@ -623,13 +689,10 @@ TEST_F(BluetoothChromeOSTest, StopDiscoveryAfterTwoStarts) {
TEST_F(BluetoothChromeOSTest, Discovery) {
// Test a simulated discovery session.
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
-
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
adapter_->SetPowered(
true,
@@ -637,32 +700,35 @@ TEST_F(BluetoothChromeOSTest, Discovery) {
base::Unretained(this)),
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
base::Unretained(this)));
- adapter_->StartDiscovering(
- base::Bind(&BluetoothChromeOSTest::Callback,
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
base::Unretained(this)));
+ message_loop_.Run();
EXPECT_EQ(2, callback_count_);
EXPECT_EQ(0, error_callback_count_);
callback_count_ = 0;
ASSERT_TRUE(adapter_->IsPowered());
ASSERT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)1, discovery_sessions_.size());
+ ASSERT_TRUE(discovery_sessions_[0]->IsActive());
- // First device to appear should be an Apple Mouse.
- message_loop.Run();
+ // First two devices to appear.
+ message_loop_.Run();
- EXPECT_EQ(1, observer.device_added_count_);
- EXPECT_EQ(FakeBluetoothDeviceClient::kAppleMouseAddress,
+ EXPECT_EQ(2, observer.device_added_count_);
+ EXPECT_EQ(FakeBluetoothDeviceClient::kLowEnergyAddress,
observer.last_device_address_);
// Next we should get another two devices...
- message_loop.Run();
- EXPECT_EQ(3, observer.device_added_count_);
+ message_loop_.Run();
+ EXPECT_EQ(4, observer.device_added_count_);
// Okay, let's run forward until a device is actually removed...
while (!observer.device_removed_count_)
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(1, observer.device_removed_count_);
EXPECT_EQ(FakeBluetoothDeviceClient::kVanishingDeviceAddress,
@@ -670,8 +736,6 @@ TEST_F(BluetoothChromeOSTest, Discovery) {
}
TEST_F(BluetoothChromeOSTest, PoweredAndDiscovering) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
-
GetAdapter();
adapter_->SetPowered(
true,
@@ -679,14 +743,17 @@ TEST_F(BluetoothChromeOSTest, PoweredAndDiscovering) {
base::Unretained(this)),
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
base::Unretained(this)));
- adapter_->StartDiscovering(
- base::Bind(&BluetoothChromeOSTest::Callback,
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
base::Unretained(this)),
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
base::Unretained(this)));
+ message_loop_.Run();
EXPECT_EQ(2, callback_count_);
EXPECT_EQ(0, error_callback_count_);
callback_count_ = 0;
+ ASSERT_EQ((size_t)1, discovery_sessions_.size());
+ ASSERT_TRUE(discovery_sessions_[0]->IsActive());
// Stop the timers that the simulation uses
fake_bluetooth_device_client_->EndDiscoverySimulation(
@@ -697,13 +764,13 @@ TEST_F(BluetoothChromeOSTest, PoweredAndDiscovering) {
fake_bluetooth_adapter_client_->SetVisible(false);
ASSERT_FALSE(adapter_->IsPresent());
+ ASSERT_FALSE(discovery_sessions_[0]->IsActive());
// Install an observer; expect the AdapterPresentChanged,
// AdapterPoweredChanged and AdapterDiscoveringChanged methods to be called
// with true, and IsPresent(), IsPowered() and IsDiscovering() to all
// return true.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
fake_bluetooth_adapter_client_->SetVisible(true);
@@ -740,6 +807,635 @@ TEST_F(BluetoothChromeOSTest, PoweredAndDiscovering) {
EXPECT_FALSE(adapter_->IsDiscovering());
}
+// This unit test asserts that the basic reference counting logic works
+// correctly for discovery requests done via the BluetoothAdapter.
+TEST_F(BluetoothChromeOSTest, MultipleDiscoverySessions) {
+ GetAdapter();
+ adapter_->SetPowered(
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(adapter_->IsPowered());
+ callback_count_ = 0;
+
+ TestObserver observer(adapter_);
+
+ EXPECT_EQ(0, observer.discovering_changed_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+
+ // Request device discovery 3 times.
+ for (int i = 0; i < 3; i++) {
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ }
+ // Run only once, as there should have been one D-Bus call.
+ message_loop_.Run();
+
+ // The observer should have received the discovering changed event exactly
+ // once, the success callback should have been called 3 times and the adapter
+ // should be discovering.
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_EQ(3, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)3, discovery_sessions_.size());
+
+ // Request to stop discovery twice.
+ for (int i = 0; i < 2; i++) {
+ discovery_sessions_[i]->Stop(
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ }
+
+ // The observer should have received no additional discovering changed events,
+ // the success callback should have been called 2 times and the adapter should
+ // still be discovering.
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_EQ(5, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ EXPECT_FALSE(discovery_sessions_[0]->IsActive());
+ EXPECT_FALSE(discovery_sessions_[1]->IsActive());
+ EXPECT_TRUE(discovery_sessions_[2]->IsActive());
+
+ // Request device discovery 3 times.
+ for (int i = 0; i < 3; i++) {
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ }
+
+ // The observer should have received no additional discovering changed events,
+ // the success callback should have been called 3 times and the adapter should
+ // still be discovering.
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_EQ(8, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)6, discovery_sessions_.size());
+
+ // Request to stop discovery 4 times.
+ for (int i = 2; i < 6; i++) {
+ discovery_sessions_[i]->Stop(
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ }
+ // Run only once, as there should have been one D-Bus call.
+ message_loop_.Run();
+
+ // The observer should have received the discovering changed event exactly
+ // once, the success callback should have been called 4 times and the adapter
+ // should no longer be discovering.
+ EXPECT_EQ(2, observer.discovering_changed_count_);
+ EXPECT_EQ(12, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+
+ // All discovery sessions should be inactive.
+ for (int i = 0; i < 6; i++)
+ EXPECT_FALSE(discovery_sessions_[i]->IsActive());
+
+ // Request to stop discovery on of the inactive sessions.
+ discovery_sessions_[0]->Stop(
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ // The call should have failed.
+ EXPECT_EQ(2, observer.discovering_changed_count_);
+ EXPECT_EQ(12, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+}
+
+// This unit test asserts that the reference counting logic works correctly in
+// the cases when the adapter gets reset and D-Bus calls are made outside of
+// the BluetoothAdapter.
+TEST_F(BluetoothChromeOSTest,
+ UnexpectedChangesDuringMultipleDiscoverySessions) {
+ GetAdapter();
+ adapter_->SetPowered(
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(adapter_->IsPowered());
+ callback_count_ = 0;
+
+ TestObserver observer(adapter_);
+
+ EXPECT_EQ(0, observer.discovering_changed_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+
+ // Request device discovery 3 times.
+ for (int i = 0; i < 3; i++) {
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ }
+ // Run only once, as there should have been one D-Bus call.
+ message_loop_.Run();
+
+ // The observer should have received the discovering changed event exactly
+ // once, the success callback should have been called 3 times and the adapter
+ // should be discovering.
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_EQ(3, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)3, discovery_sessions_.size());
+
+ for (int i = 0; i < 3; i++)
+ EXPECT_TRUE(discovery_sessions_[i]->IsActive());
+
+ // Stop the timers that the simulation uses
+ fake_bluetooth_device_client_->EndDiscoverySimulation(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));
+
+ ASSERT_TRUE(adapter_->IsPowered());
+ ASSERT_TRUE(adapter_->IsDiscovering());
+
+ // Stop device discovery behind the adapter. The adapter and the observer
+ // should be notified of the change and the reference count should be reset.
+ // Even though FakeBluetoothAdapterClient does its own reference counting and
+ // we called 3 BluetoothAdapter::StartDiscoverySession 3 times, the
+ // FakeBluetoothAdapterClient's count should be only 1 and a single call to
+ // FakeBluetoothAdapterClient::StopDiscovery should work.
+ fake_bluetooth_adapter_client_->StopDiscovery(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+ EXPECT_EQ(2, observer.discovering_changed_count_);
+ EXPECT_EQ(4, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+
+ // All discovery session instances should have been updated.
+ for (int i = 0; i < 3; i++)
+ EXPECT_FALSE(discovery_sessions_[i]->IsActive());
+ discovery_sessions_.clear();
+
+ // It should be possible to successfully start discovery.
+ for (int i = 0; i < 2; i++) {
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ }
+ // Run only once, as there should have been one D-Bus call.
+ message_loop_.Run();
+ EXPECT_EQ(3, observer.discovering_changed_count_);
+ EXPECT_EQ(6, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)2, discovery_sessions_.size());
+
+ for (int i = 0; i < 2; i++)
+ EXPECT_TRUE(discovery_sessions_[i]->IsActive());
+
+ fake_bluetooth_device_client_->EndDiscoverySimulation(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));
+
+ // Make the adapter disappear and appear. This will make it come back as
+ // discovering. When this happens, the reference count should become and
+ // remain 0 as no new request was made through the BluetoothAdapter.
+ fake_bluetooth_adapter_client_->SetVisible(false);
+ ASSERT_FALSE(adapter_->IsPresent());
+ EXPECT_EQ(4, observer.discovering_changed_count_);
+ EXPECT_EQ(6, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+
+ for (int i = 0; i < 2; i++)
+ EXPECT_FALSE(discovery_sessions_[i]->IsActive());
+ discovery_sessions_.clear();
+
+ fake_bluetooth_adapter_client_->SetVisible(true);
+ ASSERT_TRUE(adapter_->IsPresent());
+ EXPECT_EQ(5, observer.discovering_changed_count_);
+ EXPECT_EQ(6, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+
+ // Start and stop discovery. At this point, FakeBluetoothAdapterClient has
+ // a reference count that is equal to 1. Pretend that this was done by an
+ // application other than us. Starting and stopping discovery will succeed
+ // but it won't cause the discovery state to change.
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run(); // Run the loop, as there should have been a D-Bus call.
+ EXPECT_EQ(5, observer.discovering_changed_count_);
+ EXPECT_EQ(7, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)1, discovery_sessions_.size());
+ EXPECT_TRUE(discovery_sessions_[0]->IsActive());
+
+ discovery_sessions_[0]->Stop(
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run(); // Run the loop, as there should have been a D-Bus call.
+ EXPECT_EQ(5, observer.discovering_changed_count_);
+ EXPECT_EQ(8, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ EXPECT_FALSE(discovery_sessions_[0]->IsActive());
+ discovery_sessions_.clear();
+
+ // Start discovery again.
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run(); // Run the loop, as there should have been a D-Bus call.
+ EXPECT_EQ(5, observer.discovering_changed_count_);
+ EXPECT_EQ(9, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)1, discovery_sessions_.size());
+ EXPECT_TRUE(discovery_sessions_[0]->IsActive());
+
+ // Stop discovery via D-Bus. The fake client's reference count will drop but
+ // the discovery state won't change since our BluetoothAdapter also just
+ // requested it via D-Bus.
+ fake_bluetooth_adapter_client_->StopDiscovery(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+ EXPECT_EQ(5, observer.discovering_changed_count_);
+ EXPECT_EQ(10, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+
+ // Now end the discovery session. This should change the adapter's discovery
+ // state.
+ discovery_sessions_[0]->Stop(
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+ EXPECT_EQ(6, observer.discovering_changed_count_);
+ EXPECT_EQ(11, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+ EXPECT_FALSE(discovery_sessions_[0]->IsActive());
+}
+
+TEST_F(BluetoothChromeOSTest, InvalidatedDiscoverySessions) {
+ GetAdapter();
+ adapter_->SetPowered(
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(adapter_->IsPowered());
+ callback_count_ = 0;
+
+ TestObserver observer(adapter_);
+
+ EXPECT_EQ(0, observer.discovering_changed_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+
+ // Request device discovery 3 times.
+ for (int i = 0; i < 3; i++) {
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ }
+ // Run only once, as there should have been one D-Bus call.
+ message_loop_.Run();
+
+ // The observer should have received the discovering changed event exactly
+ // once, the success callback should have been called 3 times and the adapter
+ // should be discovering.
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_EQ(3, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)3, discovery_sessions_.size());
+
+ for (int i = 0; i < 3; i++)
+ EXPECT_TRUE(discovery_sessions_[i]->IsActive());
+
+ // Stop the timers that the simulation uses
+ fake_bluetooth_device_client_->EndDiscoverySimulation(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));
+
+ ASSERT_TRUE(adapter_->IsPowered());
+ ASSERT_TRUE(adapter_->IsDiscovering());
+
+ // Delete all but one discovery session.
+ discovery_sessions_.pop_back();
+ discovery_sessions_.pop_back();
+ ASSERT_EQ((size_t)1, discovery_sessions_.size());
+ EXPECT_TRUE(discovery_sessions_[0]->IsActive());
+ EXPECT_TRUE(adapter_->IsDiscovering());
+
+ // Stop device discovery behind the adapter. The one active discovery session
+ // should become inactive, but more importantly, we shouldn't run into any
+ // memory errors as the sessions that we explicitly deleted should get
+ // cleaned up.
+ fake_bluetooth_adapter_client_->StopDiscovery(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+ EXPECT_EQ(2, observer.discovering_changed_count_);
+ EXPECT_EQ(4, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+ EXPECT_FALSE(discovery_sessions_[0]->IsActive());
+}
+
+TEST_F(BluetoothChromeOSTest, QueuedDiscoveryRequests) {
+ GetAdapter();
+
+ adapter_->SetPowered(
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(adapter_->IsPowered());
+ callback_count_ = 0;
+
+ TestObserver observer(adapter_);
+
+ EXPECT_EQ(0, observer.discovering_changed_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+
+ // Request to start discovery. The call should be pending.
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(0, callback_count_);
+
+ fake_bluetooth_device_client_->EndDiscoverySimulation(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));
+
+ // The underlying adapter has started discovery, but our call hasn't returned
+ // yet.
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ EXPECT_TRUE(discovery_sessions_.empty());
+
+ // Request to start discovery twice. These should get queued and there should
+ // be no change in state.
+ for (int i = 0; i < 2; i++) {
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ }
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ EXPECT_TRUE(discovery_sessions_.empty());
+
+ // Process the pending call. The queued calls should execute and the discovery
+ // session reference count should increase.
+ message_loop_.Run();
+ EXPECT_EQ(3, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)3, discovery_sessions_.size());
+
+ // Verify the reference count by removing sessions 3 times. The last request
+ // should remain pending.
+ for (int i = 0; i < 3; i++) {
+ discovery_sessions_[i]->Stop(
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ }
+ EXPECT_EQ(5, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(2, observer.discovering_changed_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+ EXPECT_FALSE(discovery_sessions_[0]->IsActive());
+ EXPECT_FALSE(discovery_sessions_[1]->IsActive());
+ EXPECT_TRUE(discovery_sessions_[2]->IsActive());
+
+ // Request to stop the session whose call is pending should fail.
+ discovery_sessions_[2]->Stop(
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(5, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(2, observer.discovering_changed_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+ EXPECT_TRUE(discovery_sessions_[2]->IsActive());
+
+ // Request to start should get queued.
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(5, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(2, observer.discovering_changed_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)3, discovery_sessions_.size());
+
+ // Run the pending request.
+ message_loop_.Run();
+ EXPECT_EQ(6, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(3, observer.discovering_changed_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)3, discovery_sessions_.size());
+ EXPECT_FALSE(discovery_sessions_[2]->IsActive());
+
+ // The queued request to start discovery should have been issued but is still
+ // pending. Run the loop and verify.
+ message_loop_.Run();
+ EXPECT_EQ(7, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(3, observer.discovering_changed_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)4, discovery_sessions_.size());
+ EXPECT_TRUE(discovery_sessions_[3]->IsActive());
+}
+
+TEST_F(BluetoothChromeOSTest, StartDiscoverySession) {
+ GetAdapter();
+
+ adapter_->SetPowered(
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(adapter_->IsPowered());
+ callback_count_ = 0;
+
+ TestObserver observer(adapter_);
+
+ EXPECT_EQ(0, observer.discovering_changed_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+ EXPECT_TRUE(discovery_sessions_.empty());
+
+ // Request a new discovery session.
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)1, discovery_sessions_.size());
+ EXPECT_TRUE(discovery_sessions_[0]->IsActive());
+
+ // Start another session. A new one should be returned in the callback, which
+ // in turn will destroy the previous session. Adapter should still be
+ // discovering and the reference count should be 1.
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_EQ(2, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)2, discovery_sessions_.size());
+ EXPECT_TRUE(discovery_sessions_[0]->IsActive());
+
+ // Request a new session.
+ adapter_->StartDiscoverySession(
+ base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_EQ(3, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)3, discovery_sessions_.size());
+ EXPECT_TRUE(discovery_sessions_[1]->IsActive());
+ EXPECT_NE(discovery_sessions_[0], discovery_sessions_[1]);
+
+ // Stop the previous discovery session. The session should end but discovery
+ // should continue.
+ discovery_sessions_[0]->Stop(
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_EQ(4, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+ ASSERT_EQ((size_t)3, discovery_sessions_.size());
+ EXPECT_FALSE(discovery_sessions_[0]->IsActive());
+ EXPECT_TRUE(discovery_sessions_[1]->IsActive());
+
+ // Delete the current active session. Discovery should eventually stop.
+ discovery_sessions_.clear();
+ while (observer.last_discovering_)
+ message_loop_.RunUntilIdle();
+
+ EXPECT_EQ(2, observer.discovering_changed_count_);
+ EXPECT_EQ(4, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+}
+
TEST_F(BluetoothChromeOSTest, DeviceProperties) {
GetAdapter();
@@ -749,7 +1445,7 @@ TEST_F(BluetoothChromeOSTest, DeviceProperties) {
devices[0]->GetAddress());
// Verify the other device properties.
- EXPECT_EQ(UTF8ToUTF16(FakeBluetoothDeviceClient::kPairedDeviceName),
+ EXPECT_EQ(base::UTF8ToUTF16(FakeBluetoothDeviceClient::kPairedDeviceName),
devices[0]->GetName());
EXPECT_EQ(BluetoothDevice::DEVICE_COMPUTER, devices[0]->GetDeviceType());
EXPECT_TRUE(devices[0]->IsPaired());
@@ -759,11 +1455,12 @@ TEST_F(BluetoothChromeOSTest, DeviceProperties) {
// Non HID devices are always connectable.
EXPECT_TRUE(devices[0]->IsConnectable());
- BluetoothDevice::ServiceList uuids = devices[0]->GetServices();
+ BluetoothDevice::UUIDList uuids = devices[0]->GetUUIDs();
ASSERT_EQ(2U, uuids.size());
- EXPECT_EQ(uuids[0], "00001800-0000-1000-8000-00805f9b34fb");
- EXPECT_EQ(uuids[1], "00001801-0000-1000-8000-00805f9b34fb");
+ EXPECT_EQ(uuids[0], BluetoothUUID("1800"));
+ EXPECT_EQ(uuids[1], BluetoothUUID("1801"));
+ EXPECT_EQ(BluetoothDevice::VENDOR_ID_USB, devices[0]->GetVendorIDSource());
EXPECT_EQ(0x05ac, devices[0]->GetVendorID());
EXPECT_EQ(0x030d, devices[0]->GetProductID());
EXPECT_EQ(0x0306, devices[0]->GetDeviceID());
@@ -783,7 +1480,6 @@ TEST_F(BluetoothChromeOSTest, DeviceClassChanged) {
// Install an observer; expect the DeviceChanged method to be called when
// we change the class of the device.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
@@ -805,13 +1501,12 @@ TEST_F(BluetoothChromeOSTest, DeviceNameChanged) {
ASSERT_EQ(1U, devices.size());
ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
devices[0]->GetAddress());
- ASSERT_EQ(UTF8ToUTF16(FakeBluetoothDeviceClient::kPairedDeviceName),
+ ASSERT_EQ(base::UTF8ToUTF16(FakeBluetoothDeviceClient::kPairedDeviceName),
devices[0]->GetName());
// Install an observer; expect the DeviceChanged method to be called when
// we change the alias of the device.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
@@ -823,7 +1518,7 @@ TEST_F(BluetoothChromeOSTest, DeviceNameChanged) {
EXPECT_EQ(1, observer.device_changed_count_);
EXPECT_EQ(devices[0], observer.last_device_);
- EXPECT_EQ(UTF8ToUTF16(new_name), devices[0]->GetName());
+ EXPECT_EQ(base::UTF8ToUTF16(new_name), devices[0]->GetName());
}
TEST_F(BluetoothChromeOSTest, DeviceUuidsChanged) {
@@ -835,37 +1530,39 @@ TEST_F(BluetoothChromeOSTest, DeviceUuidsChanged) {
ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
devices[0]->GetAddress());
- BluetoothDevice::ServiceList uuids = devices[0]->GetServices();
+ BluetoothDevice::UUIDList uuids = devices[0]->GetUUIDs();
ASSERT_EQ(2U, uuids.size());
- ASSERT_EQ(uuids[0], "00001800-0000-1000-8000-00805f9b34fb");
- ASSERT_EQ(uuids[1], "00001801-0000-1000-8000-00805f9b34fb");
+ ASSERT_EQ(uuids[0], BluetoothUUID("1800"));
+ ASSERT_EQ(uuids[1], BluetoothUUID("1801"));
// Install an observer; expect the DeviceChanged method to be called when
// we change the class of the device.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath));
- uuids.push_back("0000110c-0000-1000-8000-00805f9b34fb");
- uuids.push_back("0000110e-0000-1000-8000-00805f9b34fb");
- uuids.push_back("0000110a-0000-1000-8000-00805f9b34fb");
+ std::vector<std::string> new_uuids;
+ new_uuids.push_back(uuids[0].canonical_value());
+ new_uuids.push_back(uuids[1].canonical_value());
+ new_uuids.push_back("0000110c-0000-1000-8000-00805f9b34fb");
+ new_uuids.push_back("0000110e-0000-1000-8000-00805f9b34fb");
+ new_uuids.push_back("0000110a-0000-1000-8000-00805f9b34fb");
- properties->uuids.ReplaceValue(uuids);
+ properties->uuids.ReplaceValue(new_uuids);
EXPECT_EQ(1, observer.device_changed_count_);
EXPECT_EQ(devices[0], observer.last_device_);
// Fetching the value should give the new one.
- uuids = devices[0]->GetServices();
+ uuids = devices[0]->GetUUIDs();
ASSERT_EQ(5U, uuids.size());
- EXPECT_EQ(uuids[0], "00001800-0000-1000-8000-00805f9b34fb");
- EXPECT_EQ(uuids[1], "00001801-0000-1000-8000-00805f9b34fb");
- EXPECT_EQ(uuids[2], "0000110c-0000-1000-8000-00805f9b34fb");
- EXPECT_EQ(uuids[3], "0000110e-0000-1000-8000-00805f9b34fb");
- EXPECT_EQ(uuids[4], "0000110a-0000-1000-8000-00805f9b34fb");
+ EXPECT_EQ(uuids[0], BluetoothUUID("1800"));
+ EXPECT_EQ(uuids[1], BluetoothUUID("1801"));
+ EXPECT_EQ(uuids[2], BluetoothUUID("110c"));
+ EXPECT_EQ(uuids[3], BluetoothUUID("110e"));
+ EXPECT_EQ(uuids[4], BluetoothUUID("110a"));
}
TEST_F(BluetoothChromeOSTest, ForgetDevice) {
@@ -881,7 +1578,6 @@ TEST_F(BluetoothChromeOSTest, ForgetDevice) {
// Install an observer; expect the DeviceRemoved method to be called
// with the device we remove.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
devices[0]->Forget(
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
@@ -901,7 +1597,7 @@ TEST_F(BluetoothChromeOSTest, ForgetUnpairedDevice) {
DiscoverDevices();
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kMicrosoftMouseAddress);
+ FakeBluetoothDeviceClient::kConnectUnpairableAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
@@ -923,13 +1619,12 @@ TEST_F(BluetoothChromeOSTest, ForgetUnpairedDevice) {
// Make sure the trusted property has been set to true.
FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
- dbus::ObjectPath(FakeBluetoothDeviceClient::kMicrosoftMousePath));
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kConnectUnpairablePath));
ASSERT_TRUE(properties->trusted.value());
// Install an observer; expect the DeviceRemoved method to be called
// with the device we remove.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
device->Forget(
base::Bind(&BluetoothChromeOSTest::ErrorCallback,
@@ -937,12 +1632,12 @@ TEST_F(BluetoothChromeOSTest, ForgetUnpairedDevice) {
EXPECT_EQ(0, error_callback_count_);
EXPECT_EQ(1, observer.device_removed_count_);
- EXPECT_EQ(FakeBluetoothDeviceClient::kMicrosoftMouseAddress,
+ EXPECT_EQ(FakeBluetoothDeviceClient::kConnectUnpairableAddress,
observer.last_device_address_);
// GetDevices shouldn't return the device either.
device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kMicrosoftMouseAddress);
+ FakeBluetoothDeviceClient::kConnectUnpairableAddress);
EXPECT_FALSE(device != NULL);
}
@@ -955,7 +1650,6 @@ TEST_F(BluetoothChromeOSTest, ConnectPairedDevice) {
ASSERT_TRUE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
// Connect without a pairing delegate; since the device is already Paired
// this should succeed and the device should become connected.
@@ -983,12 +1677,11 @@ TEST_F(BluetoothChromeOSTest, ConnectUnpairableDevice) {
DiscoverDevices();
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kMicrosoftMouseAddress);
+ FakeBluetoothDeviceClient::kConnectUnpairableAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
// Connect without a pairing delegate; since the device does not require
// pairing, this should succeed and the device should become connected.
@@ -1013,13 +1706,13 @@ TEST_F(BluetoothChromeOSTest, ConnectUnpairableDevice) {
// Make sure the trusted property has been set to true.
FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
- dbus::ObjectPath(FakeBluetoothDeviceClient::kMicrosoftMousePath));
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kConnectUnpairablePath));
EXPECT_TRUE(properties->trusted.value());
// Verify is a HID device and is not connectable.
- BluetoothDevice::ServiceList uuids = device->GetServices();
+ BluetoothDevice::UUIDList uuids = device->GetUUIDs();
ASSERT_EQ(1U, uuids.size());
- EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb");
+ EXPECT_EQ(uuids[0], BluetoothUUID("1124"));
EXPECT_FALSE(device->IsConnectable());
}
@@ -1047,7 +1740,6 @@ TEST_F(BluetoothChromeOSTest, ConnectConnectedDevice) {
// Connect again; since the device is already Connected, this shouldn't do
// anything to initiate the connection.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
device->Connect(
NULL,
@@ -1072,12 +1764,11 @@ TEST_F(BluetoothChromeOSTest, ConnectDeviceFails) {
DiscoverDevices();
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kAppleMouseAddress);
+ FakeBluetoothDeviceClient::kLegacyAutopairAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
// Connect without a pairing delegate; since the device requires pairing,
// this should fail with an error.
@@ -1123,7 +1814,6 @@ TEST_F(BluetoothChromeOSTest, DisconnectDevice) {
// Disconnect the device, we should see the observer method fire and the
// device get dropped.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
device->Disconnect(
base::Bind(&BluetoothChromeOSTest::Callback,
@@ -1152,7 +1842,6 @@ TEST_F(BluetoothChromeOSTest, DisconnectUnconnectedDevice) {
// Disconnect the device, we should see the observer method fire and the
// device get dropped.
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
device->Disconnect(
base::Bind(&BluetoothChromeOSTest::Callback,
@@ -1168,22 +1857,20 @@ TEST_F(BluetoothChromeOSTest, DisconnectUnconnectedDevice) {
EXPECT_FALSE(device->IsConnected());
}
-TEST_F(BluetoothChromeOSTest, PairAppleMouse) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+TEST_F(BluetoothChromeOSTest, PairLegacyAutopair) {
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
DiscoverDevices();
- // The Apple Mouse requires no PIN or Passkey to pair; this is equivalent
- // to Simple Secure Pairing or a device with a fixed 0000 PIN.
+ // The Legacy Autopair device requires no PIN or Passkey to pair because
+ // the daemon provides 0000 to the device for us.
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kAppleMouseAddress);
+ FakeBluetoothDeviceClient::kLegacyAutopairAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1196,7 +1883,7 @@ TEST_F(BluetoothChromeOSTest, PairAppleMouse) {
EXPECT_EQ(0, pairing_delegate.call_count_);
EXPECT_TRUE(device->IsConnecting());
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(1, callback_count_);
EXPECT_EQ(0, error_callback_count_);
@@ -1213,38 +1900,31 @@ TEST_F(BluetoothChromeOSTest, PairAppleMouse) {
EXPECT_TRUE(device->IsPaired());
// Verify is a HID device and is connectable.
- BluetoothDevice::ServiceList uuids = device->GetServices();
+ BluetoothDevice::UUIDList uuids = device->GetUUIDs();
ASSERT_EQ(1U, uuids.size());
- EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb");
+ EXPECT_EQ(uuids[0], BluetoothUUID("1124"));
EXPECT_TRUE(device->IsConnectable());
- // Pairing dialog should be dismissed
- EXPECT_EQ(1, pairing_delegate.call_count_);
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
-
// Make sure the trusted property has been set to true.
FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
- dbus::ObjectPath(FakeBluetoothDeviceClient::kAppleMousePath));
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLegacyAutopairPath));
EXPECT_TRUE(properties->trusted.value());
}
-TEST_F(BluetoothChromeOSTest, PairAppleKeyboard) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+TEST_F(BluetoothChromeOSTest, PairDisplayPinCode) {
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
DiscoverDevices();
- // The Apple Keyboard requires that we display a randomly generated
- // PIN on the screen.
+ // Requires that we display a randomly generated PIN on the screen.
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kAppleKeyboardAddress);
+ FakeBluetoothDeviceClient::kDisplayPinCodeAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1259,7 +1939,7 @@ TEST_F(BluetoothChromeOSTest, PairAppleKeyboard) {
EXPECT_EQ("123456", pairing_delegate.last_pincode_);
EXPECT_TRUE(device->IsConnecting());
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(1, callback_count_);
EXPECT_EQ(0, error_callback_count_);
@@ -1276,37 +1956,32 @@ TEST_F(BluetoothChromeOSTest, PairAppleKeyboard) {
EXPECT_TRUE(device->IsPaired());
// Verify is a HID device and is connectable.
- BluetoothDevice::ServiceList uuids = device->GetServices();
+ BluetoothDevice::UUIDList uuids = device->GetUUIDs();
ASSERT_EQ(1U, uuids.size());
- EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb");
+ EXPECT_EQ(uuids[0], BluetoothUUID("1124"));
EXPECT_TRUE(device->IsConnectable());
- // Pairing dialog should be dismissed
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
-
// Make sure the trusted property has been set to true.
FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
- dbus::ObjectPath(FakeBluetoothDeviceClient::kAppleKeyboardPath));
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kDisplayPinCodePath));
EXPECT_TRUE(properties->trusted.value());
}
-TEST_F(BluetoothChromeOSTest, PairMotorolaKeyboard) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+TEST_F(BluetoothChromeOSTest, PairDisplayPasskey) {
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
DiscoverDevices();
- // The Motorola Keyboard requires that we display a randomly generated
- // Passkey on the screen, and notifies us as it's typed in.
+ // Requires that we display a randomly generated Passkey on the screen,
+ // and notifies us as it's typed in.
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kMotorolaKeyboardAddress);
+ FakeBluetoothDeviceClient::kDisplayPasskeyAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1327,18 +2002,18 @@ TEST_F(BluetoothChromeOSTest, PairMotorolaKeyboard) {
// One call to KeysEntered() for each key, including [enter].
for(int i = 1; i <= 7; ++i) {
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(2 + i, pairing_delegate.call_count_);
EXPECT_EQ(1 + i, pairing_delegate.keys_entered_count_);
EXPECT_EQ(static_cast<uint32_t>(i), pairing_delegate.last_entered_);
}
- message_loop.Run();
+ message_loop_.Run();
- // 8 KeysEntered notifications (0 to 7, inclusive). Two aditional calls for
- // DisplayPasskey() and DismissDisplayOrConfirm().
- EXPECT_EQ(10, pairing_delegate.call_count_);
+ // 8 KeysEntered notifications (0 to 7, inclusive) and one aditional call for
+ // DisplayPasskey().
+ EXPECT_EQ(9, pairing_delegate.call_count_);
EXPECT_EQ(8, pairing_delegate.keys_entered_count_);
EXPECT_EQ(7U, pairing_delegate.last_entered_);
@@ -1357,38 +2032,33 @@ TEST_F(BluetoothChromeOSTest, PairMotorolaKeyboard) {
EXPECT_TRUE(device->IsPaired());
// Verify is a HID device.
- BluetoothDevice::ServiceList uuids = device->GetServices();
+ BluetoothDevice::UUIDList uuids = device->GetUUIDs();
ASSERT_EQ(1U, uuids.size());
- EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb");
+ EXPECT_EQ(uuids[0], BluetoothUUID("1124"));
- // Fake MotorolaKeyboard is not connectable.
+ // And usually not connectable.
EXPECT_FALSE(device->IsConnectable());
- // Pairing dialog should be dismissed
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
-
// Make sure the trusted property has been set to true.
FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
- dbus::ObjectPath(FakeBluetoothDeviceClient::kMotorolaKeyboardPath));
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kDisplayPasskeyPath));
EXPECT_TRUE(properties->trusted.value());
}
-TEST_F(BluetoothChromeOSTest, PairSonyHeadphones) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+TEST_F(BluetoothChromeOSTest, PairRequestPinCode) {
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
DiscoverDevices();
- // The Sony Headphones fake requires that the user enters a PIN for them.
+ // Requires that the user enters a PIN for them.
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kSonyHeadphonesAddress);
+ FakeBluetoothDeviceClient::kRequestPinCodeAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1404,7 +2074,7 @@ TEST_F(BluetoothChromeOSTest, PairSonyHeadphones) {
// Set the PIN.
device->SetPinCode("1234");
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(1, callback_count_);
EXPECT_EQ(0, error_callback_count_);
@@ -1420,38 +2090,32 @@ TEST_F(BluetoothChromeOSTest, PairSonyHeadphones) {
EXPECT_TRUE(device->IsPaired());
// Verify is not a HID device.
- BluetoothDevice::ServiceList uuids = device->GetServices();
+ BluetoothDevice::UUIDList uuids = device->GetUUIDs();
ASSERT_EQ(0U, uuids.size());
// Non HID devices are always connectable.
EXPECT_TRUE(device->IsConnectable());
- // Pairing dialog should be dismissed
- EXPECT_EQ(2, pairing_delegate.call_count_);
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
-
// Make sure the trusted property has been set to true.
FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
- dbus::ObjectPath(FakeBluetoothDeviceClient::kSonyHeadphonesPath));
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath));
EXPECT_TRUE(properties->trusted.value());
}
-TEST_F(BluetoothChromeOSTest, PairPhone) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+TEST_F(BluetoothChromeOSTest, PairConfirmPasskey) {
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
DiscoverDevices();
- // The fake phone requests that we confirm a displayed passkey.
+ // Requests that we confirm a displayed passkey.
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kPhoneAddress);
+ FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1468,7 +2132,7 @@ TEST_F(BluetoothChromeOSTest, PairPhone) {
// Confirm the passkey.
device->ConfirmPairing();
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(1, callback_count_);
EXPECT_EQ(0, error_callback_count_);
@@ -1486,34 +2150,27 @@ TEST_F(BluetoothChromeOSTest, PairPhone) {
// Non HID devices are always connectable.
EXPECT_TRUE(device->IsConnectable());
- // Pairing dialog should be dismissed
- EXPECT_EQ(2, pairing_delegate.call_count_);
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
-
// Make sure the trusted property has been set to true.
FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
- dbus::ObjectPath(FakeBluetoothDeviceClient::kPhonePath));
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath));
EXPECT_TRUE(properties->trusted.value());
}
-TEST_F(BluetoothChromeOSTest, PairWeirdDevice) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+TEST_F(BluetoothChromeOSTest, PairRequestPasskey) {
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
DiscoverDevices();
- // Use the "weird device" fake that requires that the user enters a Passkey,
- // this would be some kind of device that has a display, but doesn't use
- // "just works" - maybe a car?
+ // Requires that the user enters a Passkey, this would be some kind of
+ // device that has a display, but doesn't use "just works" - maybe a car?
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kWeirdDeviceAddress);
+ FakeBluetoothDeviceClient::kRequestPasskeyAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1529,7 +2186,7 @@ TEST_F(BluetoothChromeOSTest, PairWeirdDevice) {
// Set the Passkey.
device->SetPasskey(1234);
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(1, callback_count_);
EXPECT_EQ(0, error_callback_count_);
@@ -1547,19 +2204,64 @@ TEST_F(BluetoothChromeOSTest, PairWeirdDevice) {
// Non HID devices are always connectable.
EXPECT_TRUE(device->IsConnectable());
- // Pairing dialog should be dismissed
- EXPECT_EQ(2, pairing_delegate.call_count_);
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+ // Make sure the trusted property has been set to true.
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
+ EXPECT_TRUE(properties->trusted.value());
+}
+
+TEST_F(BluetoothChromeOSTest, PairJustWorks) {
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // Uses just-works pairing, since this is an outgoing pairing, no delegate
+ // interaction is required.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kJustWorksAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(0, pairing_delegate.call_count_);
+
+ message_loop_.Run();
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ // Two changes for connecting, one change for connected, one for paired and
+ // two for trusted (after pairing and connection).
+ EXPECT_EQ(6, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_TRUE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+
+ EXPECT_TRUE(device->IsPaired());
+
+ // Non HID devices are always connectable.
+ EXPECT_TRUE(device->IsConnectable());
// Make sure the trusted property has been set to true.
FakeBluetoothDeviceClient::Properties* properties =
fake_bluetooth_device_client_->GetProperties(
- dbus::ObjectPath(FakeBluetoothDeviceClient::kWeirdDevicePath));
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath));
EXPECT_TRUE(properties->trusted.value());
}
TEST_F(BluetoothChromeOSTest, PairUnpairableDeviceFails) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1571,7 +2273,6 @@ TEST_F(BluetoothChromeOSTest, PairUnpairableDeviceFails) {
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1585,7 +2286,7 @@ TEST_F(BluetoothChromeOSTest, PairUnpairableDeviceFails) {
EXPECT_TRUE(device->IsConnecting());
// Run the loop to get the error..
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(0, callback_count_);
EXPECT_EQ(1, error_callback_count_);
@@ -1595,14 +2296,9 @@ TEST_F(BluetoothChromeOSTest, PairUnpairableDeviceFails) {
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(BluetoothChromeOSTest, PairingFails) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1615,7 +2311,6 @@ TEST_F(BluetoothChromeOSTest, PairingFails) {
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1629,7 +2324,7 @@ TEST_F(BluetoothChromeOSTest, PairingFails) {
EXPECT_TRUE(device->IsConnecting());
// Run the loop to get the error..
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(0, callback_count_);
EXPECT_EQ(1, error_callback_count_);
@@ -1639,14 +2334,9 @@ TEST_F(BluetoothChromeOSTest, PairingFails) {
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(BluetoothChromeOSTest, PairingFailsAtConnection) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1660,7 +2350,6 @@ TEST_F(BluetoothChromeOSTest, PairingFailsAtConnection) {
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1673,7 +2362,7 @@ TEST_F(BluetoothChromeOSTest, PairingFailsAtConnection) {
EXPECT_EQ(0, pairing_delegate.call_count_);
EXPECT_TRUE(device->IsConnecting());
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(0, callback_count_);
EXPECT_EQ(1, error_callback_count_);
@@ -1689,10 +2378,6 @@ TEST_F(BluetoothChromeOSTest, PairingFailsAtConnection) {
EXPECT_TRUE(device->IsPaired());
- // Pairing dialog should be dismissed
- EXPECT_EQ(1, pairing_delegate.call_count_);
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
-
// Make sure the trusted property has been set to true still (since pairing
// worked).
FakeBluetoothDeviceClient::Properties* properties =
@@ -1703,7 +2388,6 @@ TEST_F(BluetoothChromeOSTest, PairingFailsAtConnection) {
}
TEST_F(BluetoothChromeOSTest, PairingRejectedAtPinCode) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1711,12 +2395,11 @@ TEST_F(BluetoothChromeOSTest, PairingRejectedAtPinCode) {
// Reject the pairing after we receive a request for the PIN code.
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kSonyHeadphonesAddress);
+ FakeBluetoothDeviceClient::kRequestPinCodeAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1732,7 +2415,7 @@ TEST_F(BluetoothChromeOSTest, PairingRejectedAtPinCode) {
// Reject the pairing.
device->RejectPairing();
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(0, callback_count_);
EXPECT_EQ(1, error_callback_count_);
@@ -1743,14 +2426,9 @@ TEST_F(BluetoothChromeOSTest, PairingRejectedAtPinCode) {
EXPECT_FALSE(device->IsConnected());
EXPECT_FALSE(device->IsConnecting());
EXPECT_FALSE(device->IsPaired());
-
- // Pairing dialog should be dismissed
- EXPECT_EQ(2, pairing_delegate.call_count_);
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
}
TEST_F(BluetoothChromeOSTest, PairingCancelledAtPinCode) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1758,12 +2436,11 @@ TEST_F(BluetoothChromeOSTest, PairingCancelledAtPinCode) {
// Cancel the pairing after we receive a request for the PIN code.
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kSonyHeadphonesAddress);
+ FakeBluetoothDeviceClient::kRequestPinCodeAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1779,7 +2456,7 @@ TEST_F(BluetoothChromeOSTest, PairingCancelledAtPinCode) {
// Cancel the pairing.
device->CancelPairing();
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(0, callback_count_);
EXPECT_EQ(1, error_callback_count_);
@@ -1790,14 +2467,9 @@ TEST_F(BluetoothChromeOSTest, PairingCancelledAtPinCode) {
EXPECT_FALSE(device->IsConnected());
EXPECT_FALSE(device->IsConnecting());
EXPECT_FALSE(device->IsPaired());
-
- // Pairing dialog should be dismissed
- EXPECT_EQ(2, pairing_delegate.call_count_);
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
}
TEST_F(BluetoothChromeOSTest, PairingRejectedAtPasskey) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1805,12 +2477,11 @@ TEST_F(BluetoothChromeOSTest, PairingRejectedAtPasskey) {
// Reject the pairing after we receive a request for the passkey.
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kWeirdDeviceAddress);
+ FakeBluetoothDeviceClient::kRequestPasskeyAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1826,7 +2497,7 @@ TEST_F(BluetoothChromeOSTest, PairingRejectedAtPasskey) {
// Reject the pairing.
device->RejectPairing();
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(0, callback_count_);
EXPECT_EQ(1, error_callback_count_);
@@ -1837,14 +2508,9 @@ TEST_F(BluetoothChromeOSTest, PairingRejectedAtPasskey) {
EXPECT_FALSE(device->IsConnected());
EXPECT_FALSE(device->IsConnecting());
EXPECT_FALSE(device->IsPaired());
-
- // Pairing dialog should be dismissed
- EXPECT_EQ(2, pairing_delegate.call_count_);
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
}
TEST_F(BluetoothChromeOSTest, PairingCancelledAtPasskey) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1852,12 +2518,11 @@ TEST_F(BluetoothChromeOSTest, PairingCancelledAtPasskey) {
// Cancel the pairing after we receive a request for the passkey.
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kWeirdDeviceAddress);
+ FakeBluetoothDeviceClient::kRequestPasskeyAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1873,7 +2538,7 @@ TEST_F(BluetoothChromeOSTest, PairingCancelledAtPasskey) {
// Cancel the pairing.
device->CancelPairing();
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(0, callback_count_);
EXPECT_EQ(1, error_callback_count_);
@@ -1884,14 +2549,9 @@ TEST_F(BluetoothChromeOSTest, PairingCancelledAtPasskey) {
EXPECT_FALSE(device->IsConnected());
EXPECT_FALSE(device->IsConnecting());
EXPECT_FALSE(device->IsPaired());
-
- // Pairing dialog should be dismissed
- EXPECT_EQ(2, pairing_delegate.call_count_);
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
}
TEST_F(BluetoothChromeOSTest, PairingRejectedAtConfirmation) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1899,12 +2559,11 @@ TEST_F(BluetoothChromeOSTest, PairingRejectedAtConfirmation) {
// Reject the pairing after we receive a request for passkey confirmation.
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kPhoneAddress);
+ FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1920,7 +2579,7 @@ TEST_F(BluetoothChromeOSTest, PairingRejectedAtConfirmation) {
// Reject the pairing.
device->RejectPairing();
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(0, callback_count_);
EXPECT_EQ(1, error_callback_count_);
@@ -1931,14 +2590,9 @@ TEST_F(BluetoothChromeOSTest, PairingRejectedAtConfirmation) {
EXPECT_FALSE(device->IsConnected());
EXPECT_FALSE(device->IsConnecting());
EXPECT_FALSE(device->IsPaired());
-
- // Pairing dialog should be dismissed
- EXPECT_EQ(2, pairing_delegate.call_count_);
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
}
TEST_F(BluetoothChromeOSTest, PairingCancelledAtConfirmation) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1946,12 +2600,11 @@ TEST_F(BluetoothChromeOSTest, PairingCancelledAtConfirmation) {
// Cancel the pairing after we receive a request for the passkey.
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kPhoneAddress);
+ FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -1967,7 +2620,7 @@ TEST_F(BluetoothChromeOSTest, PairingCancelledAtConfirmation) {
// Cancel the pairing.
device->CancelPairing();
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(0, callback_count_);
EXPECT_EQ(1, error_callback_count_);
@@ -1978,14 +2631,9 @@ TEST_F(BluetoothChromeOSTest, PairingCancelledAtConfirmation) {
EXPECT_FALSE(device->IsConnected());
EXPECT_FALSE(device->IsConnecting());
EXPECT_FALSE(device->IsPaired());
-
- // Pairing dialog should be dismissed
- EXPECT_EQ(2, pairing_delegate.call_count_);
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
}
TEST_F(BluetoothChromeOSTest, PairingCancelledInFlight) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1993,12 +2641,11 @@ TEST_F(BluetoothChromeOSTest, PairingCancelledInFlight) {
// Cancel the pairing while we're waiting for the remote host.
BluetoothDevice* device = adapter_->GetDevice(
- FakeBluetoothDeviceClient::kAppleMouseAddress);
+ FakeBluetoothDeviceClient::kLegacyAutopairAddress);
ASSERT_TRUE(device != NULL);
ASSERT_FALSE(device->IsPaired());
TestObserver observer(adapter_);
- adapter_->AddObserver(&observer);
TestPairingDelegate pairing_delegate;
device->Connect(
@@ -2013,7 +2660,7 @@ TEST_F(BluetoothChromeOSTest, PairingCancelledInFlight) {
// Cancel the pairing.
device->CancelPairing();
- message_loop.Run();
+ message_loop_.Run();
EXPECT_EQ(0, callback_count_);
EXPECT_EQ(1, error_callback_count_);
@@ -2024,10 +2671,512 @@ TEST_F(BluetoothChromeOSTest, PairingCancelledInFlight) {
EXPECT_FALSE(device->IsConnected());
EXPECT_FALSE(device->IsConnecting());
EXPECT_FALSE(device->IsPaired());
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairRequestPinCode) {
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+
+ TestPairingDelegate pairing_delegate;
+ adapter_->AddPairingDelegate(
+ &pairing_delegate,
+ BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+
+ // Requires that we provide a PIN code.
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kRequestPinCodeAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+
+ fake_bluetooth_device_client_->SimulatePairing(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath),
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
+
+ // Set the PIN.
+ device->SetPinCode("1234");
+ message_loop_.Run();
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ // One change for paired, and one for trusted.
+ EXPECT_EQ(2, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_TRUE(device->IsPaired());
+
+ // Make sure the trusted property has been set to true.
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath));
+ ASSERT_TRUE(properties->trusted.value());
+
+ // No pairing context should remain on the device.
+ BluetoothDeviceChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceChromeOS*>(device);
+ EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairConfirmPasskey) {
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+
+ TestPairingDelegate pairing_delegate;
+ adapter_->AddPairingDelegate(
+ &pairing_delegate,
+ BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+
+ // Requests that we confirm a displayed passkey.
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+
+ fake_bluetooth_device_client_->SimulatePairing(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath),
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_);
+ EXPECT_EQ(123456U, pairing_delegate.last_passkey_);
+
+ // Confirm the passkey.
+ device->ConfirmPairing();
+ message_loop_.Run();
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ // One change for paired, and one for trusted.
+ EXPECT_EQ(2, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_TRUE(device->IsPaired());
+
+ // Make sure the trusted property has been set to true.
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath));
+ ASSERT_TRUE(properties->trusted.value());
+
+ // No pairing context should remain on the device.
+ BluetoothDeviceChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceChromeOS*>(device);
+ EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairRequestPasskey) {
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+
+ TestPairingDelegate pairing_delegate;
+ adapter_->AddPairingDelegate(
+ &pairing_delegate,
+ BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+
+ // Requests that we provide a Passkey.
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kRequestPasskeyAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+
+ fake_bluetooth_device_client_->SimulatePairing(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath),
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
+
+ // Set the Passkey.
+ device->SetPasskey(1234);
+ message_loop_.Run();
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ // One change for paired, and one for trusted.
+ EXPECT_EQ(2, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_TRUE(device->IsPaired());
+
+ // Make sure the trusted property has been set to true.
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
+ ASSERT_TRUE(properties->trusted.value());
+
+ // No pairing context should remain on the device.
+ BluetoothDeviceChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceChromeOS*>(device);
+ EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairJustWorks) {
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+
+ TestPairingDelegate pairing_delegate;
+ adapter_->AddPairingDelegate(
+ &pairing_delegate,
+ BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+
+ // Uses just-works pairing so, sinec this an incoming pairing, require
+ // authorization from the user.
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kJustWorksAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+
+ fake_bluetooth_device_client_->SimulatePairing(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath),
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.authorize_pairing_count_);
+
+ // Confirm the pairing.
+ device->ConfirmPairing();
+ message_loop_.Run();
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ // One change for paired, and one for trusted.
+ EXPECT_EQ(2, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_TRUE(device->IsPaired());
+
+ // Make sure the trusted property has been set to true.
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath));
+ ASSERT_TRUE(properties->trusted.value());
+
+ // No pairing context should remain on the device.
+ BluetoothDeviceChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceChromeOS*>(device);
+ EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairRequestPinCodeWithoutDelegate) {
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+
+ // Requires that we provide a PIN Code, without a pairing delegate,
+ // that will be rejected.
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kRequestPinCodeAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+
+ fake_bluetooth_device_client_->SimulatePairing(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath),
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+
+ message_loop_.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_);
+
+ // No changes should be observer.
+ EXPECT_EQ(0, observer.device_changed_count_);
+
+ EXPECT_FALSE(device->IsPaired());
+
+ // No pairing context should remain on the device.
+ BluetoothDeviceChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceChromeOS*>(device);
+ EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairConfirmPasskeyWithoutDelegate) {
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+
+ // Requests that we confirm a displayed passkey, without a pairing delegate,
+ // that will be rejected.
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+
+ fake_bluetooth_device_client_->SimulatePairing(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath),
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+
+ message_loop_.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_);
+
+ // No changes should be observer.
+ EXPECT_EQ(0, observer.device_changed_count_);
+
+ EXPECT_FALSE(device->IsPaired());
+
+ // No pairing context should remain on the device.
+ BluetoothDeviceChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceChromeOS*>(device);
+ EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairRequestPasskeyWithoutDelegate) {
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+
+ // Requests that we provide a displayed passkey, without a pairing delegate,
+ // that will be rejected.
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kRequestPasskeyAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+
+ fake_bluetooth_device_client_->SimulatePairing(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath),
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+
+ message_loop_.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_);
+
+ // No changes should be observer.
+ EXPECT_EQ(0, observer.device_changed_count_);
+
+ EXPECT_FALSE(device->IsPaired());
+
+ // No pairing context should remain on the device.
+ BluetoothDeviceChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceChromeOS*>(device);
+ EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairJustWorksWithoutDelegate) {
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+
+ // Uses just-works pairing and thus requires authorization for incoming
+ // pairings, without a pairing delegate, that will be rejected.
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kJustWorksAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+
+ fake_bluetooth_device_client_->SimulatePairing(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath),
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+
+ message_loop_.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_);
+
+ // No changes should be observer.
+ EXPECT_EQ(0, observer.device_changed_count_);
+
+ EXPECT_FALSE(device->IsPaired());
+
+ // No pairing context should remain on the device.
+ BluetoothDeviceChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceChromeOS*>(device);
+ EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, RemovePairingDelegateDuringPairing) {
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+
+ TestPairingDelegate pairing_delegate;
+ adapter_->AddPairingDelegate(
+ &pairing_delegate,
+ BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+
+ // Requests that we provide a Passkey.
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kRequestPasskeyAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+
+ fake_bluetooth_device_client_->SimulatePairing(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath),
+ true,
+ base::Bind(&BluetoothChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
- // Pairing dialog should be dismissed
EXPECT_EQ(1, pairing_delegate.call_count_);
- EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+ EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
+
+ // A pairing context should now be set on the device.
+ BluetoothDeviceChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceChromeOS*>(device);
+ ASSERT_TRUE(device_chromeos->GetPairing() != NULL);
+
+ // Removing the pairing delegate should remove that pairing context.
+ adapter_->RemovePairingDelegate(&pairing_delegate);
+
+ EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+
+ // Set the Passkey, this should now have no effect since the pairing has
+ // been, in-effect, cancelled
+ device->SetPasskey(1234);
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(0, observer.device_changed_count_);
+
+ EXPECT_FALSE(device->IsPaired());
+}
+
+TEST_F(BluetoothChromeOSTest, DeviceId) {
+ GetAdapter();
+
+ // Use the built-in paired device for this test, grab its Properties
+ // structure so we can adjust the underlying modalias property.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kPairedDeviceAddress);
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath));
+
+ ASSERT_TRUE(device != NULL);
+ ASSERT_TRUE(properties != NULL);
+
+ // Valid USB IF-assigned identifier.
+ ASSERT_EQ("usb:v05ACp030Dd0306", properties->modalias.value());
+
+ EXPECT_EQ(BluetoothDevice::VENDOR_ID_USB, device->GetVendorIDSource());
+ EXPECT_EQ(0x05ac, device->GetVendorID());
+ EXPECT_EQ(0x030d, device->GetProductID());
+ EXPECT_EQ(0x0306, device->GetDeviceID());
+
+ // Valid Bluetooth SIG-assigned identifier.
+ properties->modalias.ReplaceValue("bluetooth:v00E0p2400d0400");
+
+ EXPECT_EQ(BluetoothDevice::VENDOR_ID_BLUETOOTH, device->GetVendorIDSource());
+ EXPECT_EQ(0x00e0, device->GetVendorID());
+ EXPECT_EQ(0x2400, device->GetProductID());
+ EXPECT_EQ(0x0400, device->GetDeviceID());
+
+ // Invalid USB IF-assigned identifier.
+ properties->modalias.ReplaceValue("usb:x00E0p2400d0400");
+
+ EXPECT_EQ(BluetoothDevice::VENDOR_ID_UNKNOWN, device->GetVendorIDSource());
+ EXPECT_EQ(0, device->GetVendorID());
+ EXPECT_EQ(0, device->GetProductID());
+ EXPECT_EQ(0, device->GetDeviceID());
+
+ // Invalid Bluetooth SIG-assigned identifier.
+ properties->modalias.ReplaceValue("bluetooth:x00E0p2400d0400");
+
+ EXPECT_EQ(BluetoothDevice::VENDOR_ID_UNKNOWN, device->GetVendorIDSource());
+ EXPECT_EQ(0, device->GetVendorID());
+ EXPECT_EQ(0, device->GetProductID());
+ EXPECT_EQ(0, device->GetDeviceID());
+
+ // Unknown vendor specification identifier.
+ properties->modalias.ReplaceValue("chrome:v00E0p2400d0400");
+
+ EXPECT_EQ(BluetoothDevice::VENDOR_ID_UNKNOWN, device->GetVendorIDSource());
+ EXPECT_EQ(0, device->GetVendorID());
+ EXPECT_EQ(0, device->GetProductID());
+ EXPECT_EQ(0, device->GetDeviceID());
}
} // namespace chromeos
diff --git a/chromium/device/bluetooth/bluetooth_device.cc b/chromium/device/bluetooth/bluetooth_device.cc
index 7b6ee0d927e..87c11901496 100644
--- a/chromium/device/bluetooth/bluetooth_device.cc
+++ b/chromium/device/bluetooth/bluetooth_device.cc
@@ -6,35 +6,32 @@
#include <string>
+#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
-#include "device/bluetooth/bluetooth_utils.h"
+#include "device/bluetooth/bluetooth_gatt_service.h"
#include "grit/device_bluetooth_strings.h"
#include "ui/base/l10n/l10n_util.h"
namespace device {
-// static
-bool BluetoothDevice::IsUUIDValid(const std::string& uuid) {
- return !bluetooth_utils::CanonicalUuid(uuid).empty();
-}
-
BluetoothDevice::BluetoothDevice() {
}
BluetoothDevice::~BluetoothDevice() {
+ STLDeleteValues(&gatt_services_);
}
-string16 BluetoothDevice::GetName() const {
+base::string16 BluetoothDevice::GetName() const {
std::string name = GetDeviceName();
if (!name.empty()) {
- return UTF8ToUTF16(name);
+ return base::UTF8ToUTF16(name);
} else {
return GetAddressWithLocalizedDeviceTypeName();
}
}
-string16 BluetoothDevice::GetAddressWithLocalizedDeviceTypeName() const {
- string16 address_utf16 = UTF8ToUTF16(GetAddress());
+base::string16 BluetoothDevice::GetAddressWithLocalizedDeviceTypeName() const {
+ base::string16 address_utf16 = base::UTF8ToUTF16(GetAddress());
BluetoothDevice::DeviceType device_type = GetDeviceType();
switch (device_type) {
case DEVICE_COMPUTER:
@@ -165,26 +162,79 @@ bool BluetoothDevice::IsPairable() const {
std::string vendor = GetAddress().substr(0, 8);
// Verbatim "Bluetooth Mouse", model 96674
- if ((type == DEVICE_MOUSE && vendor == "00:12:A1") ||
+ if (type == DEVICE_MOUSE && vendor == "00:12:A1")
+ return false;
// Microsoft "Microsoft Bluetooth Notebook Mouse 5000", model X807028-001
- (type == DEVICE_MOUSE && vendor == "7C:ED:8D"))
- return false;
+ if (type == DEVICE_MOUSE && vendor == "7C:ED:8D")
+ return false;
+ // Sony PlayStation Dualshock3
+ if (IsTrustable())
+ return false;
+
// TODO: Move this database into a config file.
return true;
}
-bool BluetoothDevice::ProvidesServiceWithUUID(
- const std::string& uuid) const {
- std::string canonical_uuid = bluetooth_utils::CanonicalUuid(uuid);
- BluetoothDevice::ServiceList services = GetServices();
- for (BluetoothDevice::ServiceList::const_iterator iter = services.begin();
- iter != services.end();
- ++iter) {
- if (bluetooth_utils::CanonicalUuid(*iter) == canonical_uuid)
- return true;
- }
+bool BluetoothDevice::IsTrustable() const {
+ // Sony PlayStation Dualshock3
+ if ((GetVendorID() == 0x054c && GetProductID() == 0x0268 &&
+ GetDeviceName() == "PLAYSTATION(R)3 Controller"))
+ return true;
+
return false;
}
+std::vector<BluetoothGattService*>
+ BluetoothDevice::GetGattServices() const {
+ std::vector<BluetoothGattService*> services;
+ for (GattServiceMap::const_iterator iter = gatt_services_.begin();
+ iter != gatt_services_.end(); ++iter)
+ services.push_back(iter->second);
+ return services;
+}
+
+BluetoothGattService* BluetoothDevice::GetGattService(
+ const std::string& identifier) const {
+ GattServiceMap::const_iterator iter = gatt_services_.find(identifier);
+ if (iter != gatt_services_.end())
+ return iter->second;
+ return NULL;
+}
+
+// static
+std::string BluetoothDevice::CanonicalizeAddress(const std::string& address) {
+ std::string canonicalized = address;
+ if (address.size() == 12) {
+ // Might be an address in the format "1A2B3C4D5E6F". Add separators.
+ for (size_t i = 2; i < canonicalized.size(); i += 3) {
+ canonicalized.insert(i, ":");
+ }
+ }
+
+ // Verify that the length matches the canonical format "1A:2B:3C:4D:5E:6F".
+ const size_t kCanonicalAddressLength = 17;
+ if (canonicalized.size() != kCanonicalAddressLength)
+ return std::string();
+
+ const char separator = canonicalized[2];
+ for (size_t i = 0; i < canonicalized.size(); ++i) {
+ bool is_separator = (i + 1) % 3 == 0;
+ if (is_separator) {
+ // All separators in the input |address| should be consistent.
+ if (canonicalized[i] != separator)
+ return std::string();
+
+ canonicalized[i] = ':';
+ } else {
+ if (!IsHexDigit(canonicalized[i]))
+ return std::string();
+
+ canonicalized[i] = base::ToUpperASCII(canonicalized[i]);
+ }
+ }
+
+ return canonicalized;
+}
+
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_device.h b/chromium/device/bluetooth/bluetooth_device.h
index aaf4e93c7f2..582caf34fd2 100644
--- a/chromium/device/bluetooth/bluetooth_device.h
+++ b/chromium/device/bluetooth/bluetooth_device.h
@@ -5,20 +5,23 @@
#ifndef DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_H_
+#include <map>
#include <string>
+#include <vector>
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_vector.h"
#include "base/strings/string16.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+#include "net/base/net_log.h"
namespace device {
-class BluetoothProfile;
-class BluetoothServiceRecord;
+class BluetoothGattConnection;
+class BluetoothGattService;
class BluetoothSocket;
-
-struct BluetoothOutOfBandPairingData;
+class BluetoothUUID;
// BluetoothDevice represents a remote Bluetooth device, both its properties and
// capabilities as discovered by a local adapter and actions that may be
@@ -33,6 +36,15 @@ struct BluetoothOutOfBandPairingData;
// for devices coming and going, as well as properties being updated.
class BluetoothDevice {
public:
+ // Possible values that may be returned by GetVendorIDSource(),
+ // indicating different organisations that allocate the identifiers returned
+ // by GetVendorID().
+ enum VendorIDSource {
+ VENDOR_ID_UNKNOWN,
+ VENDOR_ID_BLUETOOTH,
+ VENDOR_ID_USB
+ };
+
// Possible values that may be returned by GetDeviceType(), representing
// different types of bluetooth device that we support or are aware of
// decoded from the bluetooth class information.
@@ -53,6 +65,9 @@ class BluetoothDevice {
DEVICE_KEYBOARD_MOUSE_COMBO
};
+ // The value returned if the RSSI or transmit power cannot be read.
+ static const int kUnknownPower = 127;
+
// Possible errors passed back to an error callback function in case of a
// failed call to Connect().
enum ConnectErrorCode {
@@ -71,6 +86,18 @@ class BluetoothDevice {
public:
virtual ~Observer() {}
+ // Called when a new GATT service |service| is added to the device |device|,
+ // as the service is received from the device. Don't cache |service|. Store
+ // its identifier instead (i.e. BluetoothGattService::GetIdentifier).
+ virtual void GattServiceAdded(BluetoothDevice* device,
+ BluetoothGattService* service) {}
+
+ // Called when the GATT service |service| is removed from the device
+ // |device|. This can happen if the attribute database of the remote device
+ // changes or when |device| gets removed.
+ virtual void GattServiceRemoved(BluetoothDevice* device,
+ BluetoothGattService* service) {}
+
// TODO(keybuk): add observers for pairing and connection.
};
@@ -102,9 +129,7 @@ class BluetoothDevice {
// This method will be called when the Bluetooth daemon requires that the
// user enter the PIN code |pincode| into the device |device| so that it
- // may be authenticated. The DismissDisplayOrConfirm() method
- // will be called to dismiss the display once pairing is complete or
- // cancelled.
+ // may be authenticated.
//
// This is used for Bluetooth 2.0 and earlier keyboard devices, the
// |pincode| will always be a six-digit numeric in the range 000000-999999
@@ -114,8 +139,7 @@ class BluetoothDevice {
// This method will be called when the Bluetooth daemon requires that the
// user enter the Passkey |passkey| into the device |device| so that it
- // may be authenticated. The DismissDisplayOrConfirm() method will be
- // called to dismiss the display once pairing is complete or cancelled.
+ // may be authenticated.
//
// This is used for Bluetooth 2.1 and later devices that support input
// but not display, such as keyboards. The Passkey is a numeric in the
@@ -129,8 +153,7 @@ class BluetoothDevice {
// using a PIN code or a Passkey.
//
// This method will be called only after DisplayPinCode() or
- // DisplayPasskey() is called and before the corresponding
- // DismissDisplayOrConfirm() is called, but is not warranted to be called
+ // DisplayPasskey() method is called, but is not warranted to be called
// on every pairing process that requires a PIN code or a Passkey because
// some device may not support this feature.
//
@@ -155,18 +178,24 @@ class BluetoothDevice {
virtual void ConfirmPasskey(BluetoothDevice* device,
uint32 passkey) = 0;
- // This method will be called when any previous DisplayPinCode(),
- // DisplayPasskey() or ConfirmPasskey() request should be concluded
- // and removed from the user.
- virtual void DismissDisplayOrConfirm() = 0;
+ // This method will be called when the Bluetooth daemon requires that a
+ // pairing request, usually only incoming, using the just-works model is
+ // authorized. The delegate should decide whether the user should confirm
+ // or not, then call ConfirmPairing() on the device to confirm the pairing
+ // (whether by user action or by default), RejectPairing() on the device to
+ // reject or CancelPairing() on the device to cancel authorization for
+ // any other reason.
+ virtual void AuthorizePairing(BluetoothDevice* device) = 0;
};
- // Returns true if uuid is in a a valid canonical format
- // (see utils::CanonicalUuid).
- static bool IsUUIDValid(const std::string& uuid);
-
virtual ~BluetoothDevice();
+ // Adds and removes observers for events on this Bluetooth device. If
+ // monitoring multiple devices, check the |device| parameter of the observer
+ // methods to determine which device is issuing the event.
+ virtual void AddObserver(Observer* observer) = 0;
+ virtual void RemoveObserver(Observer* observer) = 0;
+
// Returns the Bluetooth class of the device, used by GetDeviceType()
// and metrics logging,
virtual uint32 GetBluetoothClass() const = 0;
@@ -175,6 +204,10 @@ class BluetoothDevice {
// a unique key to identify the device and copied where needed.
virtual std::string GetAddress() const = 0;
+ // Returns the allocation source of the identifier returned by GetVendorID(),
+ // where available, or VENDOR_ID_UNKNOWN where not.
+ virtual VendorIDSource GetVendorIDSource() const = 0;
+
// Returns the Vendor ID of the device, where available.
virtual uint16 GetVendorID() const = 0;
@@ -188,7 +221,7 @@ class BluetoothDevice {
// Returns the name of the device suitable for displaying, this may
// be a synthesized string containing the address and localized type name
// if the device has no obtained name.
- virtual string16 GetName() const;
+ virtual base::string16 GetName() const;
// Returns the type of the device, limited to those we support or are
// aware of, by decoding the bluetooth class information. The returned
@@ -196,6 +229,28 @@ class BluetoothDevice {
// DEVICE_PERIPHERAL.
DeviceType GetDeviceType() const;
+ // Gets the "received signal strength indication" (RSSI) of the current
+ // connection to the device. The RSSI indicates the power present in the
+ // received radio signal, measured in dBm, to a resolution of 1dBm. Larger
+ // (typically, less negative) values indicate a stronger signal.
+ // If the device is not currently connected, then returns the RSSI read from
+ // the last inquiry that returned the device, where available. In case of an
+ // error, returns |kUnknownPower|. Otherwise, returns the connection's RSSI.
+ virtual int GetRSSI() const = 0;
+
+ // These two methods are used to read the current or maximum transmit power
+ // ("Tx power") of the current connection to the device. The transmit power
+ // indicates the strength of the signal broadcast from the host's Bluetooth
+ // antenna when communicating with the device, measured in dBm, to a
+ // resolution of 1dBm. Larger (typically, less negative) values
+ // indicate a stronger signal.
+ // It is only meaningful to call this method when there is a connection
+ // established to the device. If there is no connection, or in case of an
+ // error, returns |kUnknownPower|. Otherwise, returns the connection's
+ // transmit power.
+ virtual int GetCurrentHostTransmitPower() const = 0;
+ virtual int GetMaximumHostTransmitPower() const = 0;
+
// Indicates whether the device is known to support pairing based on its
// device class and address.
bool IsPairable() const;
@@ -220,10 +275,16 @@ class BluetoothDevice {
// were called after the corresponding call to Connect().
virtual bool IsConnecting() const = 0;
- // Returns the services (as UUID strings) that this device provides.
- // TODO(youngki): Rename this to GetProfiles().
- typedef std::vector<std::string> ServiceList;
- virtual ServiceList GetServices() const = 0;
+ // Indicates whether the device can be trusted, based on device properties,
+ // such as vendor and product id.
+ bool IsTrustable() const;
+
+ // Returns the set of UUIDs that this device supports. For classic Bluetooth
+ // devices this data is collected from both the EIR data and SDP tables,
+ // for Low Energy devices this data is collected from AD and GATT primary
+ // services, for dual mode devices this may be collected from both./
+ typedef std::vector<BluetoothUUID> UUIDList;
+ virtual UUIDList GetUUIDs() const = 0;
// The ErrorCallback is used for methods that can fail in which case it
// is called, in the success case the callback is simply not called.
@@ -234,25 +295,6 @@ class BluetoothDevice {
// In the success case this callback is not called.
typedef base::Callback<void(enum ConnectErrorCode)> ConnectErrorCallback;
- // Returns the services (as BluetoothServiceRecord objects) that this device
- // provides.
- typedef ScopedVector<BluetoothServiceRecord> ServiceRecordList;
- typedef base::Callback<void(const ServiceRecordList&)> ServiceRecordsCallback;
- virtual void GetServiceRecords(const ServiceRecordsCallback& callback,
- const ErrorCallback& error_callback) = 0;
-
- // Indicates whether this device provides the given service.
- virtual bool ProvidesServiceWithUUID(const std::string& uuid) const;
-
- // The ProvidesServiceCallback is used by ProvidesServiceWithName to indicate
- // whether or not a matching service was found.
- typedef base::Callback<void(bool)> ProvidesServiceCallback;
-
- // Indicates whether this device provides the given service.
- virtual void ProvidesServiceWithName(
- const std::string& name,
- const ProvidesServiceCallback& callback) = 0;
-
// Indicates whether the device is currently pairing and expecting a
// PIN Code to be returned.
virtual bool ExpectingPinCode() const = 0;
@@ -265,12 +307,6 @@ class BluetoothDevice {
// confirmation of a displayed passkey.
virtual bool ExpectingConfirmation() const = 0;
- // 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;
-
// Initiates a connection to the device, pairing first if necessary.
//
// Method calls will be made on the supplied object |pairing_delegate|
@@ -283,7 +319,7 @@ class BluetoothDevice {
// If the request fails, |error_callback| will be called; otherwise,
// |callback| is called when the request is complete.
// After calling Connect, CancelPairing should be called to cancel the pairing
- // process and release |pairing_delegate_| if user cancels the pairing and
+ // process and release the pairing delegate if user cancels the pairing and
// closes the pairing UI.
virtual void Connect(PairingDelegate* pairing_delegate,
const base::Closure& callback,
@@ -309,8 +345,8 @@ class BluetoothDevice {
// Rejects a pairing or connection request from a remote device.
virtual void RejectPairing() = 0;
- // Cancels a pairing or connection attempt to a remote device or release
- // |pairing_deleage_| and |agent_|.
+ // Cancels a pairing or connection attempt to a remote device, releasing
+ // the pairing delegate.
virtual void CancelPairing() = 0;
// Disconnects the device, terminating the low-level ACL connection
@@ -330,35 +366,55 @@ class BluetoothDevice {
// before that callback would be called.
virtual void Forget(const ErrorCallback& error_callback) = 0;
- // 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.
- virtual void ConnectToService(const std::string& service_uuid,
- const SocketCallback& callback) = 0;
-
- // Attempts to initiate an outgoing connection to this device for the profile
- // identified by |profile|, on success the profile's connection callback
- // will be called as well as |callback|; on failure |error_callback| will be
- // called.
- virtual void ConnectToProfile(BluetoothProfile* profile,
- const base::Closure& callback,
- const ErrorCallback& error_callback) = 0;
-
- // Sets the Out Of Band pairing data for this device to |data|. Exactly one
- // of |callback| or |error_callback| will be run.
- virtual void SetOutOfBandPairingData(
- const BluetoothOutOfBandPairingData& data,
- const base::Closure& callback,
- const ErrorCallback& error_callback) = 0;
-
- // Clears the Out Of Band pairing data for this device. Exactly one of
- // |callback| or |error_callback| will be run.
- virtual void ClearOutOfBandPairingData(
- const base::Closure& callback,
- const ErrorCallback& error_callback) = 0;
+ // Attempts to initiate an outgoing L2CAP or RFCOMM connection to the
+ // advertised service on this device matching |uuid|, performing an SDP lookup
+ // if necessary to determine the correct protocol and channel for the
+ // connection. |callback| will be called on a successful connection with a
+ // BluetoothSocket instance that is to be owned by the receiver.
+ // |error_callback| will be called on failure with a message indicating the
+ // cause.
+ typedef base::Callback<void(scoped_refptr<BluetoothSocket>)>
+ ConnectToServiceCallback;
+ typedef base::Callback<void(const std::string& message)>
+ ConnectToServiceErrorCallback;
+ virtual void ConnectToService(
+ const BluetoothUUID& uuid,
+ const ConnectToServiceCallback& callback,
+ const ConnectToServiceErrorCallback& error_callback) = 0;
+
+ // Opens a new GATT connection to this device. On success, a new
+ // BluetoothGattConnection will be handed to the caller via |callback|. On
+ // error, |error_callback| will be called. The connection will be kept alive,
+ // as long as there is at least one active GATT connection. In the case that
+ // the underlying connection gets terminated, either due to a call to
+ // BluetoothDevice::Disconnect or other unexpected circumstances, the
+ // returned BluetoothGattConnection will be automatically marked as inactive.
+ // To monitor the state of the connection, observe the
+ // BluetoothAdapter::Observer::DeviceChanged method.
+ typedef base::Callback<void(scoped_ptr<BluetoothGattConnection>)>
+ GattConnectionCallback;
+ virtual void CreateGattConnection(
+ const GattConnectionCallback& callback,
+ const ConnectErrorCallback& error_callback) = 0;
+
+ // Starts monitoring the connection properties, RSSI and TX power. These
+ // properties will be tracked, and updated when their values change. Exactly
+ // one of |callback| or |error_callback| will be run.
+ virtual void StartConnectionMonitor(const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // Returns the list of discovered GATT services.
+ virtual std::vector<BluetoothGattService*> GetGattServices() const;
+
+ // Returns the GATT service with device-specific identifier |identifier|.
+ // Returns NULL, if no such service exists.
+ virtual BluetoothGattService* GetGattService(
+ const std::string& identifier) const;
+
+ // Returns the |address| in the canoncial format: XX:XX:XX:XX:XX:XX, where
+ // each 'X' is a hex digit. If the input |address| is invalid, returns an
+ // empty string.
+ static std::string CanonicalizeAddress(const std::string& address);
protected:
BluetoothDevice();
@@ -366,10 +422,15 @@ class BluetoothDevice {
// Returns the internal name of the Bluetooth device, used by GetName().
virtual std::string GetDeviceName() const = 0;
+ // Mapping from the platform-specific GATT service identifiers to
+ // BluetoothGattService objects.
+ typedef std::map<std::string, BluetoothGattService*> GattServiceMap;
+ GattServiceMap gatt_services_;
+
private:
// Returns a localized string containing the device's bluetooth address and
// a device type for display when |name_| is empty.
- string16 GetAddressWithLocalizedDeviceTypeName() const;
+ base::string16 GetAddressWithLocalizedDeviceTypeName() const;
};
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_device_chromeos.cc b/chromium/device/bluetooth/bluetooth_device_chromeos.cc
index 76e20972ad9..2045a1edb21 100644
--- a/chromium/device/bluetooth/bluetooth_device_chromeos.cc
+++ b/chromium/device/bluetooth/bluetooth_device_chromeos.cc
@@ -4,44 +4,35 @@
#include "device/bluetooth/bluetooth_device_chromeos.h"
+#include <stdio.h>
+
#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "chromeos/dbus/bluetooth_adapter_client.h"
-#include "chromeos/dbus/bluetooth_agent_manager_client.h"
-#include "chromeos/dbus/bluetooth_agent_service_provider.h"
#include "chromeos/dbus/bluetooth_device_client.h"
+#include "chromeos/dbus/bluetooth_gatt_service_client.h"
#include "chromeos/dbus/bluetooth_input_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "dbus/bus.h"
#include "device/bluetooth/bluetooth_adapter_chromeos.h"
-#include "device/bluetooth/bluetooth_profile_chromeos.h"
+#include "device/bluetooth/bluetooth_gatt_connection_chromeos.h"
+#include "device/bluetooth/bluetooth_pairing_chromeos.h"
+#include "device/bluetooth/bluetooth_remote_gatt_service_chromeos.h"
#include "device/bluetooth/bluetooth_socket.h"
+#include "device/bluetooth/bluetooth_socket_chromeos.h"
+#include "device/bluetooth/bluetooth_socket_thread.h"
+#include "device/bluetooth/bluetooth_uuid.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
using device::BluetoothDevice;
+using device::BluetoothSocket;
+using device::BluetoothUUID;
namespace {
-// The agent path is relatively meaningless since BlueZ only supports one
-// at time and will fail in an attempt to register another with "Already Exists"
-// (which we fail in OnRegisterAgentError with ERROR_INPROGRESS).
-const char kAgentPath[] = "/org/chromium/bluetooth_agent";
-
-// Histogram enumerations for pairing methods.
-enum UMAPairingMethod {
- UMA_PAIRING_METHOD_NONE,
- UMA_PAIRING_METHOD_REQUEST_PINCODE,
- UMA_PAIRING_METHOD_REQUEST_PASSKEY,
- UMA_PAIRING_METHOD_DISPLAY_PINCODE,
- UMA_PAIRING_METHOD_DISPLAY_PASSKEY,
- UMA_PAIRING_METHOD_CONFIRM_PASSKEY,
- // NOTE: Add new pairing methods immediately above this line. Make sure to
- // update the enum list in tools/histogram/histograms.xml accordinly.
- UMA_PAIRING_METHOD_COUNT
-};
-
// Histogram enumerations for pairing results.
enum UMAPairingResult {
UMA_PAIRING_RESULT_SUCCESS,
@@ -59,35 +50,37 @@ enum UMAPairingResult {
};
void ParseModalias(const dbus::ObjectPath& object_path,
- uint16 *vendor_id,
- uint16 *product_id,
- uint16 *device_id) {
+ BluetoothDevice::VendorIDSource* vendor_id_source,
+ uint16* vendor_id,
+ uint16* product_id,
+ uint16* device_id) {
chromeos::BluetoothDeviceClient::Properties* properties =
chromeos::DBusThreadManager::Get()->GetBluetoothDeviceClient()->
GetProperties(object_path);
DCHECK(properties);
std::string modalias = properties->modalias.value();
- if (StartsWithASCII(modalias, "usb:", false) && modalias.length() == 19) {
- // usb:vXXXXpXXXXdXXXX
- if (modalias[4] == 'v' && vendor_id != NULL) {
- uint64 component = 0;
- base::HexStringToUInt64(modalias.substr(5, 4), &component);
- *vendor_id = component;
- }
-
- if (modalias[9] == 'p' && product_id != NULL) {
- uint64 component = 0;
- base::HexStringToUInt64(modalias.substr(10, 4), &component);
- *product_id = component;
- }
-
- if (modalias[14] == 'd' && device_id != NULL) {
- uint64 component = 0;
- base::HexStringToUInt64(modalias.substr(15, 4), &component);
- *device_id = component;
- }
+ BluetoothDevice::VendorIDSource source_value;
+ int vendor_value, product_value, device_value;
+
+ if (sscanf(modalias.c_str(), "bluetooth:v%04xp%04xd%04x",
+ &vendor_value, &product_value, &device_value) == 3) {
+ source_value = BluetoothDevice::VENDOR_ID_BLUETOOTH;
+ } else if (sscanf(modalias.c_str(), "usb:v%04xp%04xd%04x",
+ &vendor_value, &product_value, &device_value) == 3) {
+ source_value = BluetoothDevice::VENDOR_ID_USB;
+ } else {
+ return;
}
+
+ if (vendor_id_source != NULL)
+ *vendor_id_source = source_value;
+ if (vendor_id != NULL)
+ *vendor_id = vendor_value;
+ if (product_id != NULL)
+ *product_id = product_value;
+ if (device_id != NULL)
+ *device_id = device_value;
}
void RecordPairingResult(BluetoothDevice::ConnectErrorCode error_code) {
@@ -129,16 +122,53 @@ namespace chromeos {
BluetoothDeviceChromeOS::BluetoothDeviceChromeOS(
BluetoothAdapterChromeOS* adapter,
- const dbus::ObjectPath& object_path)
+ const dbus::ObjectPath& object_path,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<device::BluetoothSocketThread> socket_thread)
: adapter_(adapter),
object_path_(object_path),
num_connecting_calls_(0),
- pairing_delegate_(NULL),
- pairing_delegate_used_(false),
+ connection_monitor_started_(false),
+ ui_task_runner_(ui_task_runner),
+ socket_thread_(socket_thread),
weak_ptr_factory_(this) {
+ DBusThreadManager::Get()->GetBluetoothGattServiceClient()->AddObserver(this);
+
+ // Add all known GATT services.
+ const std::vector<dbus::ObjectPath> gatt_services =
+ DBusThreadManager::Get()->GetBluetoothGattServiceClient()->GetServices();
+ for (std::vector<dbus::ObjectPath>::const_iterator it = gatt_services.begin();
+ it != gatt_services.end(); ++it) {
+ GattServiceAdded(*it);
+ }
}
BluetoothDeviceChromeOS::~BluetoothDeviceChromeOS() {
+ DBusThreadManager::Get()->GetBluetoothGattServiceClient()->
+ RemoveObserver(this);
+
+ // Copy the GATT services list here and clear the original so that when we
+ // send GattServiceRemoved(), GetGattServices() returns no services.
+ GattServiceMap gatt_services = gatt_services_;
+ gatt_services_.clear();
+ for (GattServiceMap::iterator iter = gatt_services.begin();
+ iter != gatt_services.end(); ++iter) {
+ FOR_EACH_OBSERVER(BluetoothDevice::Observer, observers_,
+ GattServiceRemoved(this, iter->second));
+ delete iter->second;
+ }
+}
+
+void BluetoothDeviceChromeOS::AddObserver(
+ device::BluetoothDevice::Observer* observer) {
+ DCHECK(observer);
+ observers_.AddObserver(observer);
+}
+
+void BluetoothDeviceChromeOS::RemoveObserver(
+ device::BluetoothDevice::Observer* observer) {
+ DCHECK(observer);
+ observers_.RemoveObserver(observer);
}
uint32 BluetoothDeviceChromeOS::GetBluetoothClass() const {
@@ -165,27 +195,70 @@ std::string BluetoothDeviceChromeOS::GetAddress() const {
GetProperties(object_path_);
DCHECK(properties);
- return properties->address.value();
+ return CanonicalizeAddress(properties->address.value());
+}
+
+BluetoothDevice::VendorIDSource
+BluetoothDeviceChromeOS::GetVendorIDSource() const {
+ VendorIDSource vendor_id_source = VENDOR_ID_UNKNOWN;
+ ParseModalias(object_path_, &vendor_id_source, NULL, NULL, NULL);
+ return vendor_id_source;
}
uint16 BluetoothDeviceChromeOS::GetVendorID() const {
uint16 vendor_id = 0;
- ParseModalias(object_path_, &vendor_id, NULL, NULL);
+ ParseModalias(object_path_, NULL, &vendor_id, NULL, NULL);
return vendor_id;
}
uint16 BluetoothDeviceChromeOS::GetProductID() const {
uint16 product_id = 0;
- ParseModalias(object_path_, NULL, &product_id, NULL);
+ ParseModalias(object_path_, NULL, NULL, &product_id, NULL);
return product_id;
}
uint16 BluetoothDeviceChromeOS::GetDeviceID() const {
uint16 device_id = 0;
- ParseModalias(object_path_, NULL, NULL, &device_id);
+ ParseModalias(object_path_, NULL, NULL, NULL, &device_id);
return device_id;
}
+int BluetoothDeviceChromeOS::GetRSSI() const {
+ BluetoothDeviceClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothDeviceClient()->GetProperties(
+ object_path_);
+ DCHECK(properties);
+
+ if (!IsConnected()) {
+ NOTIMPLEMENTED();
+ return kUnknownPower;
+ }
+
+ return connection_monitor_started_ ? properties->connection_rssi.value()
+ : kUnknownPower;
+}
+
+int BluetoothDeviceChromeOS::GetCurrentHostTransmitPower() const {
+ BluetoothDeviceClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothDeviceClient()->GetProperties(
+ object_path_);
+ DCHECK(properties);
+
+ return IsConnected() && connection_monitor_started_
+ ? properties->connection_tx_power.value()
+ : kUnknownPower;
+}
+
+int BluetoothDeviceChromeOS::GetMaximumHostTransmitPower() const {
+ BluetoothDeviceClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothDeviceClient()->GetProperties(
+ object_path_);
+ DCHECK(properties);
+
+ return IsConnected() ? properties->connection_tx_power_max.value()
+ : kUnknownPower;
+}
+
bool BluetoothDeviceChromeOS::IsPaired() const {
BluetoothDeviceClient::Properties* properties =
DBusThreadManager::Get()->GetBluetoothDeviceClient()->
@@ -223,40 +296,33 @@ bool BluetoothDeviceChromeOS::IsConnecting() const {
return num_connecting_calls_ > 0;
}
-BluetoothDeviceChromeOS::ServiceList BluetoothDeviceChromeOS::GetServices()
- const {
+BluetoothDeviceChromeOS::UUIDList BluetoothDeviceChromeOS::GetUUIDs() const {
BluetoothDeviceClient::Properties* properties =
DBusThreadManager::Get()->GetBluetoothDeviceClient()->
GetProperties(object_path_);
DCHECK(properties);
- return properties->uuids.value();
-}
-
-void BluetoothDeviceChromeOS::GetServiceRecords(
- const ServiceRecordsCallback& callback,
- const ErrorCallback& error_callback) {
- // TODO(keybuk): not implemented; remove
- error_callback.Run();
-}
-
-void BluetoothDeviceChromeOS::ProvidesServiceWithName(
- const std::string& name,
- const ProvidesServiceCallback& callback) {
- // TODO(keybuk): not implemented; remove
- callback.Run(false);
+ std::vector<device::BluetoothUUID> uuids;
+ const std::vector<std::string> &dbus_uuids = properties->uuids.value();
+ for (std::vector<std::string>::const_iterator iter = dbus_uuids.begin();
+ iter != dbus_uuids.end(); ++iter) {
+ device::BluetoothUUID uuid(*iter);
+ DCHECK(uuid.IsValid());
+ uuids.push_back(uuid);
+ }
+ return uuids;
}
bool BluetoothDeviceChromeOS::ExpectingPinCode() const {
- return !pincode_callback_.is_null();
+ return pairing_.get() && pairing_->ExpectingPinCode();
}
bool BluetoothDeviceChromeOS::ExpectingPasskey() const {
- return !passkey_callback_.is_null();
+ return pairing_.get() && pairing_->ExpectingPasskey();
}
bool BluetoothDeviceChromeOS::ExpectingConfirmation() const {
- return !confirmation_callback_.is_null();
+ return pairing_.get() && pairing_->ExpectingConfirmation();
}
void BluetoothDeviceChromeOS::Connect(
@@ -274,77 +340,72 @@ void BluetoothDeviceChromeOS::Connect(
ConnectInternal(false, callback, error_callback);
} else {
// Initiate high-security connection with pairing.
- DCHECK(!pairing_delegate_);
- DCHECK(agent_.get() == NULL);
-
- pairing_delegate_ = pairing_delegate;
- pairing_delegate_used_ = false;
-
- // The agent path is relatively meaningless since BlueZ only supports
- // one per application at a time.
- dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
- agent_.reset(BluetoothAgentServiceProvider::Create(
- system_bus, dbus::ObjectPath(kAgentPath), this));
- DCHECK(agent_.get());
-
- VLOG(1) << object_path_.value() << ": Registering agent for pairing";
- DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
- RegisterAgent(
- dbus::ObjectPath(kAgentPath),
- bluetooth_agent_manager::kKeyboardDisplayCapability,
- base::Bind(&BluetoothDeviceChromeOS::OnRegisterAgent,
- weak_ptr_factory_.GetWeakPtr(),
- callback,
- error_callback),
- base::Bind(&BluetoothDeviceChromeOS::OnRegisterAgentError,
- weak_ptr_factory_.GetWeakPtr(),
- error_callback));
+ BeginPairing(pairing_delegate);
+
+ DBusThreadManager::Get()->GetBluetoothDeviceClient()->
+ Pair(object_path_,
+ base::Bind(&BluetoothDeviceChromeOS::OnPair,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback, error_callback),
+ base::Bind(&BluetoothDeviceChromeOS::OnPairError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
}
}
void BluetoothDeviceChromeOS::SetPinCode(const std::string& pincode) {
- if (!agent_.get() || pincode_callback_.is_null())
+ if (!pairing_.get())
return;
- pincode_callback_.Run(SUCCESS, pincode);
- pincode_callback_.Reset();
+ pairing_->SetPinCode(pincode);
}
void BluetoothDeviceChromeOS::SetPasskey(uint32 passkey) {
- if (!agent_.get() || passkey_callback_.is_null())
+ if (!pairing_.get())
return;
- passkey_callback_.Run(SUCCESS, passkey);
- passkey_callback_.Reset();
+ pairing_->SetPasskey(passkey);
}
void BluetoothDeviceChromeOS::ConfirmPairing() {
- if (!agent_.get() || confirmation_callback_.is_null())
+ if (!pairing_.get())
return;
- confirmation_callback_.Run(SUCCESS);
- confirmation_callback_.Reset();
+ pairing_->ConfirmPairing();
}
void BluetoothDeviceChromeOS::RejectPairing() {
- RunPairingCallbacks(REJECTED);
+ if (!pairing_.get())
+ return;
+
+ pairing_->RejectPairing();
}
void BluetoothDeviceChromeOS::CancelPairing() {
- // If there wasn't a callback in progress that we can reply to then we
- // have to send a CancelPairing() to the device instead.
- if (!RunPairingCallbacks(CANCELLED)) {
+ bool canceled = false;
+
+ // If there is a callback in progress that we can reply to then use that
+ // to cancel the current pairing request.
+ if (pairing_.get() && pairing_->CancelPairing())
+ canceled = true;
+
+ // If not we have to send an explicit CancelPairing() to the device instead.
+ if (!canceled) {
+ VLOG(1) << object_path_.value() << ": No pairing context or callback. "
+ << "Sending explicit cancel";
DBusThreadManager::Get()->GetBluetoothDeviceClient()->
CancelPairing(
object_path_,
base::Bind(&base::DoNothing),
base::Bind(&BluetoothDeviceChromeOS::OnCancelPairingError,
weak_ptr_factory_.GetWeakPtr()));
-
- // Since there's no calback to this method, it's possible that the pairing
- // delegate is going to be freed before things complete.
- UnregisterAgent();
}
+
+ // Since there is no callback to this method it's possible that the pairing
+ // delegate is going to be freed before things complete (indeed it's
+ // documented that this is the method you should call while freeing the
+ // pairing delegate), so clear our the context holding on to it.
+ EndPairing();
}
void BluetoothDeviceChromeOS::Disconnect(const base::Closure& callback,
@@ -365,7 +426,7 @@ void BluetoothDeviceChromeOS::Forget(const ErrorCallback& error_callback) {
VLOG(1) << object_path_.value() << ": Removing device";
DBusThreadManager::Get()->GetBluetoothAdapterClient()->
RemoveDevice(
- adapter_->object_path_,
+ adapter_->object_path(),
object_path_,
base::Bind(&base::DoNothing),
base::Bind(&BluetoothDeviceChromeOS::OnForgetError,
@@ -374,177 +435,107 @@ void BluetoothDeviceChromeOS::Forget(const ErrorCallback& error_callback) {
}
void BluetoothDeviceChromeOS::ConnectToService(
- const std::string& service_uuid,
- const SocketCallback& callback) {
- // TODO(keybuk): implement
- callback.Run(scoped_refptr<device::BluetoothSocket>());
-}
-
-void BluetoothDeviceChromeOS::ConnectToProfile(
- device::BluetoothProfile* profile,
- const base::Closure& callback,
- const ErrorCallback& error_callback) {
- BluetoothProfileChromeOS* profile_chromeos =
- static_cast<BluetoothProfileChromeOS*>(profile);
- VLOG(1) << object_path_.value() << ": Connecting profile: "
- << profile_chromeos->uuid();
- DBusThreadManager::Get()->GetBluetoothDeviceClient()->
- ConnectProfile(
- object_path_,
- profile_chromeos->uuid(),
- base::Bind(
- &BluetoothDeviceChromeOS::OnConnectProfile,
- weak_ptr_factory_.GetWeakPtr(),
- profile,
- callback),
- base::Bind(
- &BluetoothDeviceChromeOS::OnConnectProfileError,
- weak_ptr_factory_.GetWeakPtr(),
- profile,
- error_callback));
-}
-
-void BluetoothDeviceChromeOS::SetOutOfBandPairingData(
- const device::BluetoothOutOfBandPairingData& data,
- const base::Closure& callback,
- const ErrorCallback& error_callback) {
- // TODO(keybuk): implement
- error_callback.Run();
+ const BluetoothUUID& uuid,
+ const ConnectToServiceCallback& callback,
+ const ConnectToServiceErrorCallback& error_callback) {
+ VLOG(1) << object_path_.value() << ": Connecting to service: "
+ << uuid.canonical_value();
+ scoped_refptr<BluetoothSocketChromeOS> socket =
+ BluetoothSocketChromeOS::CreateBluetoothSocket(
+ ui_task_runner_,
+ socket_thread_,
+ NULL,
+ net::NetLog::Source());
+ socket->Connect(this, uuid,
+ base::Bind(callback, socket),
+ error_callback);
+}
+
+void BluetoothDeviceChromeOS::CreateGattConnection(
+ const GattConnectionCallback& callback,
+ const ConnectErrorCallback& error_callback) {
+ // TODO(armansito): Until there is a way to create a reference counted GATT
+ // connection in bluetoothd, simply do a regular connect.
+ Connect(NULL,
+ base::Bind(&BluetoothDeviceChromeOS::OnCreateGattConnection,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ error_callback);
}
-void BluetoothDeviceChromeOS::ClearOutOfBandPairingData(
+void BluetoothDeviceChromeOS::StartConnectionMonitor(
const base::Closure& callback,
const ErrorCallback& error_callback) {
- // TODO(keybuk): implement
- error_callback.Run();
-}
-
-
-void BluetoothDeviceChromeOS::Release() {
- DCHECK(agent_.get());
- DCHECK(pairing_delegate_);
- VLOG(1) << object_path_.value() << ": Release";
-
- pincode_callback_.Reset();
- passkey_callback_.Reset();
- confirmation_callback_.Reset();
-
- UnregisterAgent();
+ DBusThreadManager::Get()->GetBluetoothDeviceClient()->StartConnectionMonitor(
+ object_path_,
+ base::Bind(&BluetoothDeviceChromeOS::OnStartConnectionMonitor,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&BluetoothDeviceChromeOS::OnStartConnectionMonitorError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
}
-void BluetoothDeviceChromeOS::RequestPinCode(
- const dbus::ObjectPath& device_path,
- const PinCodeCallback& callback) {
- DCHECK(agent_.get());
- DCHECK(device_path == object_path_);
- VLOG(1) << object_path_.value() << ": RequestPinCode";
-
- UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
- UMA_PAIRING_METHOD_REQUEST_PINCODE,
- UMA_PAIRING_METHOD_COUNT);
-
- DCHECK(pairing_delegate_);
- DCHECK(pincode_callback_.is_null());
- pincode_callback_ = callback;
- pairing_delegate_->RequestPinCode(this);
- pairing_delegate_used_ = true;
+BluetoothPairingChromeOS* BluetoothDeviceChromeOS::BeginPairing(
+ BluetoothDevice::PairingDelegate* pairing_delegate) {
+ pairing_.reset(new BluetoothPairingChromeOS(this, pairing_delegate));
+ return pairing_.get();
}
-void BluetoothDeviceChromeOS::DisplayPinCode(
- const dbus::ObjectPath& device_path,
- const std::string& pincode) {
- DCHECK(agent_.get());
- DCHECK(device_path == object_path_);
- VLOG(1) << object_path_.value() << ": DisplayPinCode: " << pincode;
-
- UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
- UMA_PAIRING_METHOD_DISPLAY_PINCODE,
- UMA_PAIRING_METHOD_COUNT);
-
- DCHECK(pairing_delegate_);
- pairing_delegate_->DisplayPinCode(this, pincode);
- pairing_delegate_used_ = true;
+void BluetoothDeviceChromeOS::EndPairing() {
+ pairing_.reset();
}
-void BluetoothDeviceChromeOS::RequestPasskey(
- const dbus::ObjectPath& device_path,
- const PasskeyCallback& callback) {
- DCHECK(agent_.get());
- DCHECK(device_path == object_path_);
- VLOG(1) << object_path_.value() << ": RequestPasskey";
-
- UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
- UMA_PAIRING_METHOD_REQUEST_PASSKEY,
- UMA_PAIRING_METHOD_COUNT);
-
- DCHECK(pairing_delegate_);
- DCHECK(passkey_callback_.is_null());
- passkey_callback_ = callback;
- pairing_delegate_->RequestPasskey(this);
- pairing_delegate_used_ = true;
+BluetoothPairingChromeOS* BluetoothDeviceChromeOS::GetPairing() const {
+ return pairing_.get();
}
-void BluetoothDeviceChromeOS::DisplayPasskey(
- const dbus::ObjectPath& device_path,
- uint32 passkey,
- uint16 entered) {
- DCHECK(agent_.get());
- DCHECK(device_path == object_path_);
- VLOG(1) << object_path_.value() << ": DisplayPasskey: " << passkey
- << " (" << entered << " entered)";
-
- if (entered == 0)
- UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
- UMA_PAIRING_METHOD_DISPLAY_PASSKEY,
- UMA_PAIRING_METHOD_COUNT);
+void BluetoothDeviceChromeOS::GattServiceAdded(
+ const dbus::ObjectPath& object_path) {
+ if (GetGattService(object_path.value())) {
+ VLOG(1) << "Remote GATT service already exists: " << object_path.value();
+ return;
+ }
- DCHECK(pairing_delegate_);
- if (entered == 0)
- pairing_delegate_->DisplayPasskey(this, passkey);
- pairing_delegate_->KeysEntered(this, entered);
- pairing_delegate_used_ = true;
-}
+ BluetoothGattServiceClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothGattServiceClient()->
+ GetProperties(object_path);
+ DCHECK(properties);
+ if (properties->device.value() != object_path_) {
+ VLOG(2) << "Remote GATT service does not belong to this device.";
+ return;
+ }
-void BluetoothDeviceChromeOS::RequestConfirmation(
- const dbus::ObjectPath& device_path,
- uint32 passkey,
- const ConfirmationCallback& callback) {
- DCHECK(agent_.get());
- DCHECK(device_path == object_path_);
- VLOG(1) << object_path_.value() << ": RequestConfirmation: " << passkey;
+ VLOG(1) << "Adding new remote GATT service for device: " << GetAddress();
- UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
- UMA_PAIRING_METHOD_CONFIRM_PASSKEY,
- UMA_PAIRING_METHOD_COUNT);
+ BluetoothRemoteGattServiceChromeOS* service =
+ new BluetoothRemoteGattServiceChromeOS(adapter_, this, object_path);
- DCHECK(pairing_delegate_);
- DCHECK(confirmation_callback_.is_null());
- confirmation_callback_ = callback;
- pairing_delegate_->ConfirmPasskey(this, passkey);
- pairing_delegate_used_ = true;
-}
+ gatt_services_[service->GetIdentifier()] = service;
+ DCHECK(service->object_path() == object_path);
+ DCHECK(service->GetUUID().IsValid());
-void BluetoothDeviceChromeOS::RequestAuthorization(
- const dbus::ObjectPath& device_path,
- const ConfirmationCallback& callback) {
- // TODO(keybuk): implement
- callback.Run(CANCELLED);
+ FOR_EACH_OBSERVER(device::BluetoothDevice::Observer, observers_,
+ GattServiceAdded(this, service));
}
-void BluetoothDeviceChromeOS::AuthorizeService(
- const dbus::ObjectPath& device_path,
- const std::string& uuid,
- const ConfirmationCallback& callback) {
- // TODO(keybuk): implement
- callback.Run(CANCELLED);
-}
+void BluetoothDeviceChromeOS::GattServiceRemoved(
+ const dbus::ObjectPath& object_path) {
+ GattServiceMap::iterator iter = gatt_services_.find(object_path.value());
+ if (iter == gatt_services_.end()) {
+ VLOG(2) << "Unknown GATT service removed: " << object_path.value();
+ return;
+ }
-void BluetoothDeviceChromeOS::Cancel() {
- DCHECK(agent_.get());
- VLOG(1) << object_path_.value() << ": Cancel";
+ VLOG(1) << "Removing remote GATT service from device: " << GetAddress();
- DCHECK(pairing_delegate_);
- pairing_delegate_->DismissDisplayOrConfirm();
+ BluetoothRemoteGattServiceChromeOS* service =
+ static_cast<BluetoothRemoteGattServiceChromeOS*>(iter->second);
+ DCHECK(service->object_path() == object_path);
+ gatt_services_.erase(iter);
+ FOR_EACH_OBSERVER(device::BluetoothDevice::Observer, observers_,
+ GattServiceRemoved(this, service));
+ delete service;
}
void BluetoothDeviceChromeOS::ConnectInternal(
@@ -584,6 +575,14 @@ void BluetoothDeviceChromeOS::OnConnect(bool after_pairing,
callback.Run();
}
+void BluetoothDeviceChromeOS::OnCreateGattConnection(
+ const GattConnectionCallback& callback) {
+ scoped_ptr<device::BluetoothGattConnection> conn(
+ new BluetoothGattConnectionChromeOS(
+ adapter_, GetAddress(), object_path_));
+ callback.Run(conn.Pass());
+}
+
void BluetoothDeviceChromeOS::OnConnectError(
bool after_pairing,
const ConnectErrorCallback& error_callback,
@@ -613,56 +612,13 @@ void BluetoothDeviceChromeOS::OnConnectError(
error_callback.Run(error_code);
}
-void BluetoothDeviceChromeOS::OnRegisterAgent(
- const base::Closure& callback,
- const ConnectErrorCallback& error_callback) {
- VLOG(1) << object_path_.value() << ": Agent registered, now pairing";
-
- DBusThreadManager::Get()->GetBluetoothDeviceClient()->
- Pair(object_path_,
- base::Bind(&BluetoothDeviceChromeOS::OnPair,
- weak_ptr_factory_.GetWeakPtr(),
- callback, error_callback),
- base::Bind(&BluetoothDeviceChromeOS::OnPairError,
- weak_ptr_factory_.GetWeakPtr(),
- error_callback));
-}
-
-void BluetoothDeviceChromeOS::OnRegisterAgentError(
- const ConnectErrorCallback& error_callback,
- const std::string& error_name,
- const std::string& error_message) {
- if (--num_connecting_calls_ == 0)
- adapter_->NotifyDeviceChanged(this);
-
- DCHECK(num_connecting_calls_ >= 0);
- LOG(WARNING) << object_path_.value() << ": Failed to register agent: "
- << error_name << ": " << error_message;
- VLOG(1) << object_path_.value() << ": " << num_connecting_calls_
- << " still in progress";
-
- UnregisterAgent();
-
- // Determine the error code from error_name.
- ConnectErrorCode error_code = ERROR_UNKNOWN;
- if (error_name == bluetooth_agent_manager::kErrorAlreadyExists)
- error_code = ERROR_INPROGRESS;
-
- RecordPairingResult(error_code);
- error_callback.Run(error_code);
-}
-
void BluetoothDeviceChromeOS::OnPair(
const base::Closure& callback,
const ConnectErrorCallback& error_callback) {
VLOG(1) << object_path_.value() << ": Paired";
- if (!pairing_delegate_used_)
- UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
- UMA_PAIRING_METHOD_NONE,
- UMA_PAIRING_METHOD_COUNT);
- UnregisterAgent();
- SetTrusted();
+ EndPairing();
+
ConnectInternal(true, callback, error_callback);
}
@@ -679,7 +635,7 @@ void BluetoothDeviceChromeOS::OnPairError(
VLOG(1) << object_path_.value() << ": " << num_connecting_calls_
<< " still in progress";
- UnregisterAgent();
+ EndPairing();
// Determine the error code from error_name.
ConnectErrorCode error_code = ERROR_UNKNOWN;
@@ -725,36 +681,20 @@ void BluetoothDeviceChromeOS::OnSetTrusted(bool success) {
<< ": Failed to set device as trusted";
}
-void BluetoothDeviceChromeOS::UnregisterAgent() {
- if (!agent_.get())
- return;
-
- DCHECK(pairing_delegate_);
-
- DCHECK(pincode_callback_.is_null());
- DCHECK(passkey_callback_.is_null());
- DCHECK(confirmation_callback_.is_null());
-
- pairing_delegate_->DismissDisplayOrConfirm();
- pairing_delegate_ = NULL;
-
- agent_.reset();
-
- // Clean up after ourselves.
- VLOG(1) << object_path_.value() << ": Unregistering pairing agent";
- DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
- UnregisterAgent(
- dbus::ObjectPath(kAgentPath),
- base::Bind(&base::DoNothing),
- base::Bind(&BluetoothDeviceChromeOS::OnUnregisterAgentError,
- weak_ptr_factory_.GetWeakPtr()));
+void BluetoothDeviceChromeOS::OnStartConnectionMonitor(
+ const base::Closure& callback) {
+ connection_monitor_started_ = true;
+ callback.Run();
}
-void BluetoothDeviceChromeOS::OnUnregisterAgentError(
+void BluetoothDeviceChromeOS::OnStartConnectionMonitorError(
+ const ErrorCallback& error_callback,
const std::string& error_name,
const std::string& error_message) {
- LOG(WARNING) << object_path_.value() << ": Failed to unregister agent: "
- << error_name << ": " << error_message;
+ LOG(WARNING) << object_path_.value()
+ << ": Failed to start connection monitor: " << error_name << ": "
+ << error_message;
+ error_callback.Run();
}
void BluetoothDeviceChromeOS::OnDisconnect(const base::Closure& callback) {
@@ -780,53 +720,4 @@ void BluetoothDeviceChromeOS::OnForgetError(
error_callback.Run();
}
-bool BluetoothDeviceChromeOS::RunPairingCallbacks(Status status) {
- if (!agent_.get())
- return false;
-
- bool callback_run = false;
- if (!pincode_callback_.is_null()) {
- pincode_callback_.Run(status, "");
- pincode_callback_.Reset();
- callback_run = true;
- }
-
- if (!passkey_callback_.is_null()) {
- passkey_callback_.Run(status, 0);
- passkey_callback_.Reset();
- callback_run = true;
- }
-
- if (!confirmation_callback_.is_null()) {
- confirmation_callback_.Run(status);
- confirmation_callback_.Reset();
- callback_run = true;
- }
-
- return callback_run;
-}
-
-void BluetoothDeviceChromeOS::OnConnectProfile(
- device::BluetoothProfile* profile,
- const base::Closure& callback) {
- BluetoothProfileChromeOS* profile_chromeos =
- static_cast<BluetoothProfileChromeOS*>(profile);
- VLOG(1) << object_path_.value() << ": Profile connected: "
- << profile_chromeos->uuid();
- callback.Run();
-}
-
-void BluetoothDeviceChromeOS::OnConnectProfileError(
- device::BluetoothProfile* profile,
- const ErrorCallback& error_callback,
- const std::string& error_name,
- const std::string& error_message) {
- BluetoothProfileChromeOS* profile_chromeos =
- static_cast<BluetoothProfileChromeOS*>(profile);
- VLOG(1) << object_path_.value() << ": Profile connection failed: "
- << profile_chromeos->uuid() << ": "
- << error_name << ": " << error_message;
- error_callback.Run();
-}
-
} // namespace chromeos
diff --git a/chromium/device/bluetooth/bluetooth_device_chromeos.h b/chromium/device/bluetooth/bluetooth_device_chromeos.h
index fef7cfdcfae..b7247f5571a 100644
--- a/chromium/device/bluetooth/bluetooth_device_chromeos.h
+++ b/chromium/device/bluetooth/bluetooth_device_chromeos.h
@@ -7,40 +7,50 @@
#include <string>
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "chromeos/dbus/bluetooth_agent_service_provider.h"
+#include "base/observer_list.h"
+#include "base/sequenced_task_runner.h"
#include "chromeos/dbus/bluetooth_device_client.h"
+#include "chromeos/dbus/bluetooth_gatt_service_client.h"
#include "dbus/object_path.h"
#include "device/bluetooth/bluetooth_device.h"
+namespace device {
+class BluetoothSocketThread;
+} // namespace device
+
namespace chromeos {
class BluetoothAdapterChromeOS;
+class BluetoothPairingChromeOS;
// The BluetoothDeviceChromeOS class implements BluetoothDevice for the
// Chrome OS platform.
class BluetoothDeviceChromeOS
: public device::BluetoothDevice,
- private chromeos::BluetoothAgentServiceProvider::Delegate {
+ public BluetoothGattServiceClient::Observer {
public:
// BluetoothDevice override
+ virtual void AddObserver(
+ device::BluetoothDevice::Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(
+ device::BluetoothDevice::Observer* observer) OVERRIDE;
virtual uint32 GetBluetoothClass() const OVERRIDE;
virtual std::string GetAddress() const OVERRIDE;
+ virtual VendorIDSource GetVendorIDSource() const OVERRIDE;
virtual uint16 GetVendorID() const OVERRIDE;
virtual uint16 GetProductID() const OVERRIDE;
virtual uint16 GetDeviceID() const OVERRIDE;
+ virtual int GetRSSI() const OVERRIDE;
+ virtual int GetCurrentHostTransmitPower() const OVERRIDE;
+ virtual int GetMaximumHostTransmitPower() const OVERRIDE;
virtual bool IsPaired() const OVERRIDE;
virtual bool IsConnected() const OVERRIDE;
virtual bool IsConnectable() const OVERRIDE;
virtual bool IsConnecting() const OVERRIDE;
- virtual ServiceList GetServices() const OVERRIDE;
- virtual void GetServiceRecords(
- const ServiceRecordsCallback& callback,
- const ErrorCallback& error_callback) OVERRIDE;
- virtual void ProvidesServiceWithName(
- const std::string& name,
- const ProvidesServiceCallback& callback) OVERRIDE;
+ virtual UUIDList GetUUIDs() const OVERRIDE;
virtual bool ExpectingPinCode() const OVERRIDE;
virtual bool ExpectingPasskey() const OVERRIDE;
virtual bool ExpectingConfirmation() const OVERRIDE;
@@ -58,20 +68,32 @@ class BluetoothDeviceChromeOS
const ErrorCallback& error_callback) OVERRIDE;
virtual void Forget(const ErrorCallback& error_callback) OVERRIDE;
virtual void ConnectToService(
- const std::string& service_uuid,
- const SocketCallback& callback) OVERRIDE;
- virtual void ConnectToProfile(
- device::BluetoothProfile* profile,
- const base::Closure& callback,
- const ErrorCallback& error_callback) OVERRIDE;
- virtual void SetOutOfBandPairingData(
- const device::BluetoothOutOfBandPairingData& data,
- const base::Closure& callback,
- const ErrorCallback& error_callback) OVERRIDE;
- virtual void ClearOutOfBandPairingData(
+ const device::BluetoothUUID& uuid,
+ const ConnectToServiceCallback& callback,
+ const ConnectToServiceErrorCallback& error_callback) OVERRIDE;
+ virtual void CreateGattConnection(
+ const GattConnectionCallback& callback,
+ const ConnectErrorCallback& error_callback) OVERRIDE;
+ virtual void StartConnectionMonitor(
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
+ // Creates a pairing object with the given delegate |pairing_delegate| and
+ // establishes it as the pairing context for this device. All pairing-related
+ // method calls will be forwarded to this object until it is released.
+ BluetoothPairingChromeOS* BeginPairing(
+ BluetoothDevice::PairingDelegate* pairing_delegate);
+
+ // Releases the current pairing object, any pairing-related method calls will
+ // be ignored.
+ void EndPairing();
+
+ // Returns the current pairing object or NULL if no pairing is in progress.
+ BluetoothPairingChromeOS* GetPairing() const;
+
+ // Returns the object path of the device.
+ const dbus::ObjectPath& object_path() const { return object_path_; }
+
protected:
// BluetoothDevice override
virtual std::string GetDeviceName() const OVERRIDE;
@@ -79,31 +101,16 @@ class BluetoothDeviceChromeOS
private:
friend class BluetoothAdapterChromeOS;
- BluetoothDeviceChromeOS(BluetoothAdapterChromeOS* adapter,
- const dbus::ObjectPath& object_path);
+ BluetoothDeviceChromeOS(
+ BluetoothAdapterChromeOS* adapter,
+ const dbus::ObjectPath& object_path,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<device::BluetoothSocketThread> socket_thread);
virtual ~BluetoothDeviceChromeOS();
- // BluetoothAgentServiceProvider::Delegate override.
- virtual void Release() OVERRIDE;
- virtual void RequestPinCode(const dbus::ObjectPath& device_path,
- const PinCodeCallback& callback) OVERRIDE;
- virtual void DisplayPinCode(const dbus::ObjectPath& device_path,
- const std::string& pincode) OVERRIDE;
- virtual void RequestPasskey(const dbus::ObjectPath& device_path,
- const PasskeyCallback& callback) OVERRIDE;
- virtual void DisplayPasskey(const dbus::ObjectPath& device_path,
- uint32 passkey, uint16 entered) OVERRIDE;
- virtual void RequestConfirmation(const dbus::ObjectPath& device_path,
- uint32 passkey,
- const ConfirmationCallback& callback)
- OVERRIDE;
- virtual void RequestAuthorization(const dbus::ObjectPath& device_path,
- const ConfirmationCallback& callback)
- OVERRIDE;
- virtual void AuthorizeService(const dbus::ObjectPath& device_path,
- const std::string& uuid,
- const ConfirmationCallback& callback) OVERRIDE;
- virtual void Cancel() OVERRIDE;
+ // BluetoothGattServiceClient::Observer overrides.
+ virtual void GattServiceAdded(const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void GattServiceRemoved(const dbus::ObjectPath& object_path) OVERRIDE;
// Internal method to initiate a connection to this device, and methods called
// by dbus:: on completion of the D-Bus method call.
@@ -112,19 +119,12 @@ class BluetoothDeviceChromeOS
const ConnectErrorCallback& error_callback);
void OnConnect(bool after_pairing,
const base::Closure& callback);
+ void OnCreateGattConnection(const GattConnectionCallback& callback);
void OnConnectError(bool after_pairing,
const ConnectErrorCallback& error_callback,
const std::string& error_name,
const std::string& error_message);
- // Called by dbus:: on completion of the D-Bus method call to register the
- // pairing agent.
- void OnRegisterAgent(const base::Closure& callback,
- const ConnectErrorCallback& error_callback);
- void OnRegisterAgentError(const ConnectErrorCallback& error_callback,
- const std::string& error_name,
- const std::string& error_message);
-
// Called by dbus:: on completion of the D-Bus method call to pair the device.
void OnPair(const base::Closure& callback,
const ConnectErrorCallback& error_callback);
@@ -145,13 +145,6 @@ class BluetoothDeviceChromeOS
void SetTrusted();
void OnSetTrusted(bool success);
- // Internal method to unregister the pairing agent and method called by dbus::
- // on failure of the D-Bus method call. No completion call as success is
- // ignored.
- void UnregisterAgent();
- void OnUnregisterAgentError(const std::string& error_name,
- const std::string& error_message);
-
// Called by dbus:: on completion of the D-Bus method call to disconnect the
// device.
void OnDisconnect(const base::Closure& callback);
@@ -166,21 +159,12 @@ class BluetoothDeviceChromeOS
const std::string& error_name,
const std::string& error_message);
- // Run any outstanding pairing callbacks passing |status| as the result of
- // 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 BluetoothAdapterChromeOS
- const dbus::ObjectPath& object_path() const { return object_path_; }
+ // Called by dbus:: on completion of the D-Bus method call to start the
+ // connection monitor.
+ void OnStartConnectionMonitor(const base::Closure& callback);
+ void OnStartConnectionMonitorError(const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
// The adapter that owns this device instance.
BluetoothAdapterChromeOS* adapter_;
@@ -188,28 +172,24 @@ class BluetoothDeviceChromeOS
// The dbus object path of the device object.
dbus::ObjectPath object_path_;
+ // List of observers interested in event notifications from us.
+ ObserverList<device::BluetoothDevice::Observer> observers_;
+
// Number of ongoing calls to Connect().
int num_connecting_calls_;
+ // True if the connection monitor has been started, tracking the connection
+ // RSSI and TX power.
+ bool connection_monitor_started_;
+
+ // UI thread task runner and socket thread object used to create sockets.
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
+ scoped_refptr<device::BluetoothSocketThread> socket_thread_;
+
// During pairing this is set to an object that we don't own, but on which
// we can make method calls to request, display or confirm PIN Codes and
// Passkeys. Generally it is the object that owns this one.
- PairingDelegate* pairing_delegate_;
-
- // Flag to indicate whether a pairing delegate method has been called during
- // pairing.
- bool pairing_delegate_used_;
-
- // During pairing this is set to an instance of a D-Bus agent object
- // intialized with our own class as its delegate.
- scoped_ptr<BluetoothAgentServiceProvider> agent_;
-
- // During pairing these callbacks are set to those provided by method calls
- // made on us by |agent_| and are called by our own method calls such as
- // SetPinCode() and SetPasskey().
- PinCodeCallback pincode_callback_;
- PasskeyCallback passkey_callback_;
- ConfirmationCallback confirmation_callback_;
+ scoped_ptr<BluetoothPairingChromeOS> pairing_;
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
diff --git a/chromium/device/bluetooth/bluetooth_device_mac.h b/chromium/device/bluetooth/bluetooth_device_mac.h
index bc887b97aca..462cd35c344 100644
--- a/chromium/device/bluetooth/bluetooth_device_mac.h
+++ b/chromium/device/bluetooth/bluetooth_device_mac.h
@@ -5,16 +5,16 @@
#ifndef DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_MAC_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_MAC_H_
+#import <IOBluetooth/IOBluetooth.h>
+
#include <string>
#include "base/basictypes.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/observer_list.h"
#include "device/bluetooth/bluetooth_device.h"
-#ifdef __OBJC__
@class IOBluetoothDevice;
-#else
-class IOBluetoothDevice;
-#endif
namespace device {
@@ -24,22 +24,24 @@ class BluetoothDeviceMac : public BluetoothDevice {
virtual ~BluetoothDeviceMac();
// BluetoothDevice override
+ virtual void AddObserver(
+ device::BluetoothDevice::Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(
+ device::BluetoothDevice::Observer* observer) OVERRIDE;
virtual uint32 GetBluetoothClass() const OVERRIDE;
virtual std::string GetAddress() const OVERRIDE;
+ virtual VendorIDSource GetVendorIDSource() const OVERRIDE;
virtual uint16 GetVendorID() const OVERRIDE;
virtual uint16 GetProductID() const OVERRIDE;
virtual uint16 GetDeviceID() const OVERRIDE;
+ virtual int GetRSSI() const OVERRIDE;
+ virtual int GetCurrentHostTransmitPower() const OVERRIDE;
+ virtual int GetMaximumHostTransmitPower() const OVERRIDE;
virtual bool IsPaired() const OVERRIDE;
virtual bool IsConnected() const OVERRIDE;
virtual bool IsConnectable() const OVERRIDE;
virtual bool IsConnecting() const OVERRIDE;
- virtual ServiceList GetServices() const OVERRIDE;
- virtual void GetServiceRecords(
- const ServiceRecordsCallback& callback,
- const ErrorCallback& error_callback) OVERRIDE;
- virtual void ProvidesServiceWithName(
- const std::string& name,
- const ProvidesServiceCallback& callback) OVERRIDE;
+ virtual UUIDList GetUUIDs() const OVERRIDE;
virtual bool ExpectingPinCode() const OVERRIDE;
virtual bool ExpectingPasskey() const OVERRIDE;
virtual bool ExpectingConfirmation() const OVERRIDE;
@@ -57,20 +59,20 @@ class BluetoothDeviceMac : public BluetoothDevice {
const ErrorCallback& error_callback) OVERRIDE;
virtual void Forget(const ErrorCallback& error_callback) OVERRIDE;
virtual void ConnectToService(
- const std::string& service_uuid,
- const SocketCallback& callback) OVERRIDE;
- virtual void ConnectToProfile(
- device::BluetoothProfile* profile,
- const base::Closure& callback,
- const ErrorCallback& error_callback) OVERRIDE;
- virtual void SetOutOfBandPairingData(
- const BluetoothOutOfBandPairingData& data,
- const base::Closure& callback,
- const ErrorCallback& error_callback) OVERRIDE;
- virtual void ClearOutOfBandPairingData(
+ const BluetoothUUID& uuid,
+ const ConnectToServiceCallback& callback,
+ const ConnectToServiceErrorCallback& error_callback) OVERRIDE;
+ virtual void CreateGattConnection(
+ const GattConnectionCallback& callback,
+ const ConnectErrorCallback& error_callback) OVERRIDE;
+ virtual void StartConnectionMonitor(
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
+ // Returns the Bluetooth address for the |device|. The returned address has a
+ // normalized format (see below).
+ static std::string GetDeviceAddress(IOBluetoothDevice* device);
+
protected:
// BluetoothDevice override
virtual std::string GetDeviceName() const OVERRIDE;
@@ -78,7 +80,15 @@ class BluetoothDeviceMac : public BluetoothDevice {
private:
friend class BluetoothAdapterMac;
- IOBluetoothDevice* device_;
+ // Implementation to read the host's transmit power level of type
+ // |power_level_type|.
+ int GetHostTransmitPower(
+ BluetoothHCITransmitPowerLevelType power_level_type) const;
+
+ // List of observers interested in event notifications from us.
+ ObserverList<Observer> observers_;
+
+ base::scoped_nsobject<IOBluetoothDevice> device_;
DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceMac);
};
diff --git a/chromium/device/bluetooth/bluetooth_device_mac.mm b/chromium/device/bluetooth/bluetooth_device_mac.mm
index 39acbef4347..43d9fb66d6b 100644
--- a/chromium/device/bluetooth/bluetooth_device_mac.mm
+++ b/chromium/device/bluetooth/bluetooth_device_mac.mm
@@ -4,70 +4,75 @@
#include "device/bluetooth/bluetooth_device_mac.h"
-#include <IOBluetooth/Bluetooth.h>
-#import <IOBluetooth/objc/IOBluetoothDevice.h>
-#import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h>
-#import <IOBluetooth/objc/IOBluetoothSDPUUID.h>
-
#include <string>
#include "base/basictypes.h"
+#include "base/bind.h"
#include "base/hash.h"
+#include "base/mac/sdk_forward_declarations.h"
+#include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
+#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
-#include "device/bluetooth/bluetooth_out_of_band_pairing_data.h"
-#include "device/bluetooth/bluetooth_profile_mac.h"
-#include "device/bluetooth/bluetooth_service_record_mac.h"
#include "device/bluetooth/bluetooth_socket_mac.h"
-
-// Replicate specific 10.7 SDK declarations for building with prior SDKs.
-#if !defined(MAC_OS_X_VERSION_10_7) || \
- MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
-
-@interface IOBluetoothDevice (LionSDKDeclarations)
-- (NSString*)addressString;
-- (NSString*)name;
-- (unsigned int)classOfDevice;
-- (NSArray*)services;
+#include "device/bluetooth/bluetooth_uuid.h"
+
+// Undocumented API for accessing the Bluetooth transmit power level.
+// Similar to the API defined here [ http://goo.gl/20Q5vE ].
+@interface IOBluetoothHostController (UndocumentedAPI)
+- (IOReturn)
+ BluetoothHCIReadTransmitPowerLevel:(BluetoothConnectionHandle)connection
+ inType:(BluetoothHCITransmitPowerLevelType)type
+ outTransmitPowerLevel:(BluetoothHCITransmitPowerLevel*)level;
@end
-#endif // MAC_OS_X_VERSION_10_7
-
+namespace device {
namespace {
-// Converts |uuid| to a IOBluetoothSDPUUID instance.
-//
-// |uuid| must be in the format of XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
-IOBluetoothSDPUUID* GetIOBluetoothSDPUUID(const std::string& uuid) {
- DCHECK(uuid.size() == 36);
- DCHECK(uuid[8] == '-');
- DCHECK(uuid[13] == '-');
- DCHECK(uuid[18] == '-');
- DCHECK(uuid[23] == '-');
- std::string numbers_only = uuid;
- numbers_only.erase(23, 1);
- numbers_only.erase(18, 1);
- numbers_only.erase(13, 1);
- numbers_only.erase(8, 1);
- std::vector<uint8> uuid_bytes_vector;
- base::HexStringToBytes(numbers_only, &uuid_bytes_vector);
- DCHECK(uuid_bytes_vector.size() == 16);
-
- return [IOBluetoothSDPUUID uuidWithBytes:&uuid_bytes_vector[0]
- length:uuid_bytes_vector.size()];
+// Returns the first (should be, only) UUID contained within the
+// |service_class_data|. Returns an invalid (empty) UUID if none is found.
+BluetoothUUID ExtractUuid(IOBluetoothSDPDataElement* service_class_data) {
+ NSArray* inner_elements = [service_class_data getArrayValue];
+ IOBluetoothSDPUUID* sdp_uuid = nil;
+ for (IOBluetoothSDPDataElement* inner_element in inner_elements) {
+ if ([inner_element getTypeDescriptor] == kBluetoothSDPDataElementTypeUUID) {
+ sdp_uuid = [[inner_element getUUIDValue] getUUIDWithLength:16];
+ break;
+ }
+ }
+
+ if (!sdp_uuid)
+ return BluetoothUUID();
+
+ const uint8* uuid_bytes = reinterpret_cast<const uint8*>([sdp_uuid bytes]);
+ std::string uuid_str = base::HexEncode(uuid_bytes, 16);
+ DCHECK_EQ(uuid_str.size(), 32U);
+ uuid_str.insert(8, "-");
+ uuid_str.insert(13, "-");
+ uuid_str.insert(18, "-");
+ uuid_str.insert(23, "-");
+ return BluetoothUUID(uuid_str);
}
} // namespace
-namespace device {
-
BluetoothDeviceMac::BluetoothDeviceMac(IOBluetoothDevice* device)
- : BluetoothDevice(), device_([device retain]) {
+ : device_([device retain]) {
}
BluetoothDeviceMac::~BluetoothDeviceMac() {
- [device_ release];
+}
+
+void BluetoothDeviceMac::AddObserver(
+ device::BluetoothDevice::Observer* observer) {
+ DCHECK(observer);
+ observers_.AddObserver(observer);
+}
+
+void BluetoothDeviceMac::RemoveObserver(
+ device::BluetoothDevice::Observer* observer) {
+ DCHECK(observer);
+ observers_.RemoveObserver(observer);
}
uint32 BluetoothDeviceMac::GetBluetoothClass() const {
@@ -79,7 +84,11 @@ std::string BluetoothDeviceMac::GetDeviceName() const {
}
std::string BluetoothDeviceMac::GetAddress() const {
- return base::SysNSStringToUTF8([device_ addressString]);
+ return GetDeviceAddress(device_);
+}
+
+BluetoothDevice::VendorIDSource BluetoothDeviceMac::GetVendorIDSource() const {
+ return VENDOR_ID_UNKNOWN;
}
uint16 BluetoothDeviceMac::GetVendorID() const {
@@ -94,6 +103,30 @@ uint16 BluetoothDeviceMac::GetDeviceID() const {
return 0;
}
+int BluetoothDeviceMac::GetRSSI() const {
+ if (![device_ isConnected]) {
+ NOTIMPLEMENTED();
+ return kUnknownPower;
+ }
+
+ int rssi = [device_ rawRSSI];
+
+ // The API guarantees that +127 is returned in case the RSSI is not readable:
+ // http://goo.gl/bpURYv
+ if (rssi == 127)
+ return kUnknownPower;
+
+ return rssi;
+}
+
+int BluetoothDeviceMac::GetCurrentHostTransmitPower() const {
+ return GetHostTransmitPower(kReadCurrentTransmitPowerLevel);
+}
+
+int BluetoothDeviceMac::GetMaximumHostTransmitPower() const {
+ return GetHostTransmitPower(kReadMaximumTransmitPowerLevel);
+}
+
bool BluetoothDeviceMac::IsPaired() const {
return [device_ isPaired];
}
@@ -110,37 +143,20 @@ bool BluetoothDeviceMac::IsConnecting() const {
return false;
}
-// TODO(youngki): BluetoothServiceRecord is deprecated; implement this method
-// without using BluetoothServiceRecord.
-BluetoothDevice::ServiceList BluetoothDeviceMac::GetServices() const {
- ServiceList service_uuids;
- for (IOBluetoothSDPServiceRecord* service in [device_ services]) {
- BluetoothServiceRecordMac service_record(service);
- service_uuids.push_back(service_record.uuid());
+BluetoothDevice::UUIDList BluetoothDeviceMac::GetUUIDs() const {
+ UUIDList uuids;
+ for (IOBluetoothSDPServiceRecord* service_record in [device_ services]) {
+ IOBluetoothSDPDataElement* service_class_data =
+ [service_record getAttributeDataElement:
+ kBluetoothSDPAttributeIdentifierServiceClassIDList];
+ if ([service_class_data getTypeDescriptor] ==
+ kBluetoothSDPDataElementTypeDataElementSequence) {
+ BluetoothUUID uuid = ExtractUuid(service_class_data);
+ if (uuid.IsValid())
+ uuids.push_back(uuid);
+ }
}
- return service_uuids;
-}
-
-// NOTE(youngki): This method is deprecated; it will be removed soon.
-void BluetoothDeviceMac::GetServiceRecords(
- const ServiceRecordsCallback& callback,
- const ErrorCallback& error_callback) {
- ServiceRecordList service_record_list;
- for (IOBluetoothSDPServiceRecord* service in [device_ services]) {
- BluetoothServiceRecord* service_record =
- new BluetoothServiceRecordMac(service);
- service_record_list.push_back(service_record);
- }
-
- callback.Run(service_record_list);
-}
-
-// NOTE(youngki): This method is deprecated; it will be removed soon.
-void BluetoothDeviceMac::ProvidesServiceWithName(
- const std::string& name,
- const ProvidesServiceCallback& callback) {
- NOTIMPLEMENTED();
- callback.Run(false);
+ return uuids;
}
bool BluetoothDeviceMac::ExpectingPinCode() const {
@@ -185,9 +201,8 @@ void BluetoothDeviceMac::CancelPairing() {
NOTIMPLEMENTED();
}
-void BluetoothDeviceMac::Disconnect(
- const base::Closure& callback,
- const ErrorCallback& error_callback) {
+void BluetoothDeviceMac::Disconnect(const base::Closure& callback,
+ const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
@@ -196,40 +211,52 @@ void BluetoothDeviceMac::Forget(const ErrorCallback& error_callback) {
}
void BluetoothDeviceMac::ConnectToService(
- const std::string& service_uuid,
- const SocketCallback& callback) {
- IOBluetoothSDPServiceRecord* record =
- [device_ getServiceRecordForUUID:GetIOBluetoothSDPUUID(service_uuid)];
- if (record != nil) {
- BluetoothServiceRecordMac service_record(record);
- scoped_refptr<BluetoothSocket> socket(
- BluetoothSocketMac::CreateBluetoothSocket(service_record));
- if (socket.get() != NULL)
- callback.Run(socket);
- }
+ const BluetoothUUID& uuid,
+ const ConnectToServiceCallback& callback,
+ const ConnectToServiceErrorCallback& error_callback) {
+ scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
+ socket->Connect(
+ device_.get(), uuid, base::Bind(callback, socket), error_callback);
}
-void BluetoothDeviceMac::ConnectToProfile(
- device::BluetoothProfile* profile,
- const base::Closure& callback,
- const ErrorCallback& error_callback) {
- if (static_cast<BluetoothProfileMac*>(profile)->Connect(device_))
- callback.Run();
- else
- error_callback.Run();
+void BluetoothDeviceMac::CreateGattConnection(
+ const GattConnectionCallback& callback,
+ const ConnectErrorCallback& error_callback) {
+ // TODO(armansito): Implement.
+ error_callback.Run(ERROR_UNSUPPORTED_DEVICE);
}
-void BluetoothDeviceMac::SetOutOfBandPairingData(
- const BluetoothOutOfBandPairingData& data,
+void BluetoothDeviceMac::StartConnectionMonitor(
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
-void BluetoothDeviceMac::ClearOutOfBandPairingData(
- const base::Closure& callback,
- const ErrorCallback& error_callback) {
- NOTIMPLEMENTED();
+int BluetoothDeviceMac::GetHostTransmitPower(
+ BluetoothHCITransmitPowerLevelType power_level_type) const {
+ IOBluetoothHostController* controller =
+ [IOBluetoothHostController defaultController];
+
+ // Bail if the undocumented API is unavailable on this machine.
+ SEL selector = @selector(
+ BluetoothHCIReadTransmitPowerLevel:inType:outTransmitPowerLevel:);
+ if (![controller respondsToSelector:selector])
+ return kUnknownPower;
+
+ BluetoothHCITransmitPowerLevel power_level;
+ IOReturn result =
+ [controller BluetoothHCIReadTransmitPowerLevel:[device_ connectionHandle]
+ inType:power_level_type
+ outTransmitPowerLevel:&power_level];
+ if (result != kIOReturnSuccess)
+ return kUnknownPower;
+
+ return power_level;
+}
+
+// static
+std::string BluetoothDeviceMac::GetDeviceAddress(IOBluetoothDevice* device) {
+ return CanonicalizeAddress(base::SysNSStringToUTF8([device addressString]));
}
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_device_unittest.cc b/chromium/device/bluetooth/bluetooth_device_unittest.cc
new file mode 100644
index 00000000000..7fb50592536
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_device_unittest.cc
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_device.h"
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+TEST(BluetoothDeviceTest, CanonicalizeAddressFormat_AcceptsAllValidFormats) {
+ // There are three valid separators (':', '-', and none).
+ // Case shouldn't matter.
+ const char* const kValidFormats[] = {
+ "1A:2B:3C:4D:5E:6F",
+ "1a:2B:3c:4D:5e:6F",
+ "1a:2b:3c:4d:5e:6f",
+ "1A-2B-3C-4D-5E-6F",
+ "1a-2B-3c-4D-5e-6F",
+ "1a-2b-3c-4d-5e-6f",
+ "1A2B3C4D5E6F",
+ "1a2B3c4D5e6F",
+ "1a2b3c4d5e6f",
+ };
+
+ for (size_t i = 0; i < arraysize(kValidFormats); ++i) {
+ SCOPED_TRACE(std::string("Input format: '") + kValidFormats[i] + "'");
+ EXPECT_EQ("1A:2B:3C:4D:5E:6F",
+ BluetoothDevice::CanonicalizeAddress(kValidFormats[i]));
+ }
+}
+
+TEST(BluetoothDeviceTest, CanonicalizeAddressFormat_RejectsInvalidFormats) {
+ const char* const kValidFormats[] = {
+ // Empty string.
+ "",
+ // Too short.
+ "1A:2B:3C:4D:5E",
+ // Too long.
+ "1A:2B:3C:4D:5E:6F:70",
+ // Missing a separator.
+ "1A:2B:3C:4D:5E6F",
+ // Mixed separators.
+ "1A:2B-3C:4D-5E:6F",
+ // Invalid characters.
+ "1A:2B-3C:4D-5E:6X",
+ // Separators in the wrong place.
+ "1:A2:B3:C4:D5:E6F",
+ };
+
+ for (size_t i = 0; i < arraysize(kValidFormats); ++i) {
+ SCOPED_TRACE(std::string("Input format: '") + kValidFormats[i] + "'");
+ EXPECT_EQ(std::string(),
+ BluetoothDevice::CanonicalizeAddress(kValidFormats[i]));
+ }
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_device_win.cc b/chromium/device/bluetooth/bluetooth_device_win.cc
index bfd9bc61297..47376bcdae0 100644
--- a/chromium/device/bluetooth/bluetooth_device_win.cc
+++ b/chromium/device/bluetooth/bluetooth_device_win.cc
@@ -9,12 +9,13 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/scoped_vector.h"
+#include "base/sequenced_task_runner.h"
#include "base/strings/stringprintf.h"
-#include "device/bluetooth/bluetooth_out_of_band_pairing_data.h"
-#include "device/bluetooth/bluetooth_profile_win.h"
#include "device/bluetooth/bluetooth_service_record_win.h"
+#include "device/bluetooth/bluetooth_socket_thread.h"
#include "device/bluetooth/bluetooth_socket_win.h"
#include "device/bluetooth/bluetooth_task_manager_win.h"
+#include "device/bluetooth/bluetooth_uuid.h"
namespace {
@@ -25,10 +26,18 @@ const int kSdpBytesBufferSize = 1024;
namespace device {
BluetoothDeviceWin::BluetoothDeviceWin(
- const BluetoothTaskManagerWin::DeviceState& state)
- : BluetoothDevice() {
+ const BluetoothTaskManagerWin::DeviceState& state,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<BluetoothSocketThread> socket_thread,
+ net::NetLog* net_log,
+ const net::NetLog::Source& net_log_source)
+ : BluetoothDevice(),
+ ui_task_runner_(ui_task_runner),
+ socket_thread_(socket_thread),
+ net_log_(net_log),
+ net_log_source_(net_log_source) {
name_ = state.name;
- address_ = state.address;
+ address_ = CanonicalizeAddress(state.address);
bluetooth_class_ = state.bluetooth_class;
visible_ = state.visible;
connected_ = state.connected;
@@ -42,13 +51,13 @@ BluetoothDeviceWin::BluetoothDeviceWin(
std::copy((*iter)->sdp_bytes.begin(),
(*iter)->sdp_bytes.end(),
sdp_bytes_buffer);
- BluetoothServiceRecord* service_record = new BluetoothServiceRecordWin(
+ BluetoothServiceRecordWin* service_record = new BluetoothServiceRecordWin(
(*iter)->name,
(*iter)->address,
(*iter)->sdp_bytes.size(),
sdp_bytes_buffer);
service_record_list_.push_back(service_record);
- service_uuids_.push_back(service_record->uuid());
+ uuids_.push_back(service_record->uuid());
}
}
@@ -59,6 +68,19 @@ void BluetoothDeviceWin::SetVisible(bool visible) {
visible_ = visible;
}
+void BluetoothDeviceWin::AddObserver(
+ device::BluetoothDevice::Observer* observer) {
+ DCHECK(observer);
+ observers_.AddObserver(observer);
+}
+
+void BluetoothDeviceWin::RemoveObserver(
+ device::BluetoothDevice::Observer* observer) {
+ DCHECK(observer);
+ observers_.RemoveObserver(observer);
+}
+
+
uint32 BluetoothDeviceWin::GetBluetoothClass() const {
return bluetooth_class_;
}
@@ -71,6 +93,11 @@ std::string BluetoothDeviceWin::GetAddress() const {
return address_;
}
+BluetoothDevice::VendorIDSource
+BluetoothDeviceWin::GetVendorIDSource() const {
+ return VENDOR_ID_UNKNOWN;
+}
+
uint16 BluetoothDeviceWin::GetVendorID() const {
return 0;
}
@@ -83,6 +110,21 @@ uint16 BluetoothDeviceWin::GetDeviceID() const {
return 0;
}
+int BluetoothDeviceWin::GetRSSI() const {
+ NOTIMPLEMENTED();
+ return kUnknownPower;
+}
+
+int BluetoothDeviceWin::GetCurrentHostTransmitPower() const {
+ NOTIMPLEMENTED();
+ return kUnknownPower;
+}
+
+int BluetoothDeviceWin::GetMaximumHostTransmitPower() const {
+ NOTIMPLEMENTED();
+ return kUnknownPower;
+}
+
bool BluetoothDeviceWin::IsPaired() const {
return paired_;
}
@@ -99,28 +141,8 @@ bool BluetoothDeviceWin::IsConnecting() const {
return false;
}
-BluetoothDevice::ServiceList BluetoothDeviceWin::GetServices() const {
- return service_uuids_;
-}
-
-void BluetoothDeviceWin::GetServiceRecords(
- const ServiceRecordsCallback& callback,
- const ErrorCallback& error_callback) {
- callback.Run(service_record_list_);
-}
-
-void BluetoothDeviceWin::ProvidesServiceWithName(
- const std::string& name,
- const ProvidesServiceCallback& callback) {
- for (ServiceRecordList::const_iterator iter = service_record_list_.begin();
- iter != service_record_list_.end();
- ++iter) {
- if ((*iter)->name() == name) {
- callback.Run(true);
- return;
- }
- }
- callback.Run(false);
+BluetoothDevice::UUIDList BluetoothDeviceWin::GetUUIDs() const {
+ return uuids_;
}
bool BluetoothDeviceWin::ExpectingPinCode() const {
@@ -176,52 +198,34 @@ void BluetoothDeviceWin::Forget(const ErrorCallback& error_callback) {
}
void BluetoothDeviceWin::ConnectToService(
- const std::string& service_uuid,
- const SocketCallback& callback) {
- for (ServiceRecordList::const_iterator iter = service_record_list_.begin();
- iter != service_record_list_.end();
- ++iter) {
- if ((*iter)->uuid() == service_uuid) {
- // If multiple service records are found, use the first one that works.
- scoped_refptr<BluetoothSocket> socket(
- BluetoothSocketWin::CreateBluetoothSocket(**iter));
- if (socket.get() != NULL) {
- callback.Run(socket);
- return;
- }
- }
- }
+ const BluetoothUUID& uuid,
+ const ConnectToServiceCallback& callback,
+ const ConnectToServiceErrorCallback& error_callback) {
+ scoped_refptr<BluetoothSocketWin> socket(
+ BluetoothSocketWin::CreateBluetoothSocket(
+ ui_task_runner_, socket_thread_, NULL, net::NetLog::Source()));
+ socket->Connect(this, uuid, base::Bind(callback, socket), error_callback);
}
-void BluetoothDeviceWin::ConnectToProfile(
- device::BluetoothProfile* profile,
- const base::Closure& callback,
- const ErrorCallback& error_callback) {
- if (static_cast<BluetoothProfileWin*>(profile)->Connect(this))
- callback.Run();
- else
- error_callback.Run();
-}
-
-void BluetoothDeviceWin::SetOutOfBandPairingData(
- const BluetoothOutOfBandPairingData& data,
- const base::Closure& callback,
- const ErrorCallback& error_callback) {
- NOTIMPLEMENTED();
+void BluetoothDeviceWin::CreateGattConnection(
+ const GattConnectionCallback& callback,
+ const ConnectErrorCallback& error_callback) {
+ // TODO(armansito): Implement.
+ error_callback.Run(ERROR_UNSUPPORTED_DEVICE);
}
-void BluetoothDeviceWin::ClearOutOfBandPairingData(
+void BluetoothDeviceWin::StartConnectionMonitor(
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
-const BluetoothServiceRecord* BluetoothDeviceWin::GetServiceRecord(
- const std::string& uuid) const {
+const BluetoothServiceRecordWin* BluetoothDeviceWin::GetServiceRecord(
+ const device::BluetoothUUID& uuid) const {
for (ServiceRecordList::const_iterator iter = service_record_list_.begin();
iter != service_record_list_.end();
++iter) {
- if ((*iter)->uuid().compare(uuid) == 0)
+ if ((*iter)->uuid() == uuid)
return *iter;
}
return NULL;
diff --git a/chromium/device/bluetooth/bluetooth_device_win.h b/chromium/device/bluetooth/bluetooth_device_win.h
index 452f6f2b861..b403cf6dff4 100644
--- a/chromium/device/bluetooth/bluetooth_device_win.h
+++ b/chromium/device/bluetooth/bluetooth_device_win.h
@@ -9,37 +9,45 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/observer_list.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_task_manager_win.h"
namespace device {
class BluetoothAdapterWin;
-class BluetoothServiceRecord;
+class BluetoothServiceRecordWin;
+class BluetoothSocketThread;
class BluetoothDeviceWin : public BluetoothDevice {
public:
explicit BluetoothDeviceWin(
- const BluetoothTaskManagerWin::DeviceState& state);
+ const BluetoothTaskManagerWin::DeviceState& state,
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<BluetoothSocketThread> socket_thread,
+ net::NetLog* net_log,
+ const net::NetLog::Source& net_log_source);
virtual ~BluetoothDeviceWin();
// BluetoothDevice override
+ virtual void AddObserver(
+ device::BluetoothDevice::Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(
+ device::BluetoothDevice::Observer* observer) OVERRIDE;
virtual uint32 GetBluetoothClass() const OVERRIDE;
virtual std::string GetAddress() const OVERRIDE;
+ virtual VendorIDSource GetVendorIDSource() const OVERRIDE;
virtual uint16 GetVendorID() const OVERRIDE;
virtual uint16 GetProductID() const OVERRIDE;
virtual uint16 GetDeviceID() const OVERRIDE;
+ virtual int GetRSSI() const OVERRIDE;
+ virtual int GetCurrentHostTransmitPower() const OVERRIDE;
+ virtual int GetMaximumHostTransmitPower() const OVERRIDE;
virtual bool IsPaired() const OVERRIDE;
virtual bool IsConnected() const OVERRIDE;
virtual bool IsConnectable() const OVERRIDE;
virtual bool IsConnecting() const OVERRIDE;
- virtual ServiceList GetServices() const OVERRIDE;
- virtual void GetServiceRecords(
- const ServiceRecordsCallback& callback,
- const ErrorCallback& error_callback) OVERRIDE;
- virtual void ProvidesServiceWithName(
- const std::string& name,
- const ProvidesServiceCallback& callback) OVERRIDE;
+ virtual UUIDList GetUUIDs() const OVERRIDE;
virtual bool ExpectingPinCode() const OVERRIDE;
virtual bool ExpectingPasskey() const OVERRIDE;
virtual bool ExpectingConfirmation() const OVERRIDE;
@@ -57,21 +65,20 @@ class BluetoothDeviceWin : public BluetoothDevice {
const ErrorCallback& error_callback) OVERRIDE;
virtual void Forget(const ErrorCallback& error_callback) OVERRIDE;
virtual void ConnectToService(
- const std::string& service_uuid,
- const SocketCallback& callback) OVERRIDE;
- virtual void ConnectToProfile(
- device::BluetoothProfile* profile,
- const base::Closure& callback,
- const ErrorCallback& error_callback) OVERRIDE;
- virtual void SetOutOfBandPairingData(
- const BluetoothOutOfBandPairingData& data,
- const base::Closure& callback,
- const ErrorCallback& error_callback) OVERRIDE;
- virtual void ClearOutOfBandPairingData(
+ const BluetoothUUID& uuid,
+ const ConnectToServiceCallback& callback,
+ const ConnectToServiceErrorCallback& error_callback) OVERRIDE;
+ virtual void CreateGattConnection(
+ const GattConnectionCallback& callback,
+ const ConnectErrorCallback& error_callback) OVERRIDE;
+ virtual void StartConnectionMonitor(
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
- const BluetoothServiceRecord* GetServiceRecord(const std::string& uuid) const;
+ // Used by BluetoothProfileWin to retrieve the service record for the given
+ // |uuid|.
+ const BluetoothServiceRecordWin* GetServiceRecord(
+ const device::BluetoothUUID& uuid) const;
protected:
// BluetoothDevice override
@@ -84,6 +91,14 @@ class BluetoothDeviceWin : public BluetoothDevice {
// discovery.
void SetVisible(bool visible);
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
+ scoped_refptr<BluetoothSocketThread> socket_thread_;
+ net::NetLog* net_log_;
+ net::NetLog::Source net_log_source_;
+
+ // List of observers interested in event notifications from us.
+ ObserverList<Observer> observers_;
+
// The Bluetooth class of the device, a bitmask that may be decoded using
// https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
uint32 bluetooth_class_;
@@ -104,7 +119,10 @@ class BluetoothDeviceWin : public BluetoothDevice {
bool visible_;
// The services (identified by UUIDs) that this device provides.
- ServiceList service_uuids_;
+ UUIDList uuids_;
+
+ // The service records retrieved from SDP.
+ typedef ScopedVector<BluetoothServiceRecordWin> ServiceRecordList;
ServiceRecordList service_record_list_;
DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceWin);
diff --git a/chromium/device/bluetooth/bluetooth_device_win_unittest.cc b/chromium/device/bluetooth/bluetooth_device_win_unittest.cc
index 20be2ada182..48e87173118 100644
--- a/chromium/device/bluetooth/bluetooth_device_win_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_device_win_unittest.cc
@@ -6,10 +6,14 @@
#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
+#include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h"
+#include "base/test/test_simple_task_runner.h"
#include "device/bluetooth/bluetooth_device_win.h"
-#include "device/bluetooth/bluetooth_service_record.h"
+#include "device/bluetooth/bluetooth_service_record_win.h"
+#include "device/bluetooth/bluetooth_socket_thread.h"
#include "device/bluetooth/bluetooth_task_manager_win.h"
+#include "device/bluetooth/bluetooth_uuid.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -23,7 +27,7 @@ const char kTestAudioSdpBytes[] =
"35510900000a00010001090001350319110a09000435103506190100090019350619001909"
"010209000535031910020900093508350619110d090102090100250c417564696f20536f75"
"726365090311090001";
-const char kTestAudioSdpUuid[] = "0000110a-0000-1000-8000-00805f9b34fb";
+const device::BluetoothUUID kTestAudioSdpUuid("110a");
const char kTestVideoSdpName[] = "Video";
const char kTestVideoSdpAddress[] = "A0:10:0A:03:02:01";
@@ -31,7 +35,7 @@ const char kTestVideoSdpBytes[] =
"354b0900000a000100030900013506191112191203090004350c3503190100350519000308"
"0b090005350319100209000935083506191108090100090100250d566f6963652047617465"
"776179";
-const char kTestVideoSdpUuid[] = "00001112-0000-1000-8000-00805f9b34fb";
+const device::BluetoothUUID kTestVideoSdpUuid("1112");
} // namespace
@@ -39,13 +43,12 @@ namespace device {
class BluetoothDeviceWinTest : public testing::Test {
public:
- BluetoothDeviceWinTest()
- : error_called_(false),
- provides_service_with_name_(false) {
+ BluetoothDeviceWinTest() {
BluetoothTaskManagerWin::DeviceState device_state;
device_state.name = kDeviceName;
device_state.address = kDeviceAddress;
+ // Add device with audio/video services.
BluetoothTaskManagerWin::ServiceRecordState* audio_state =
new BluetoothTaskManagerWin::ServiceRecordState();
audio_state->name = kTestAudioSdpName;
@@ -60,84 +63,39 @@ class BluetoothDeviceWinTest : public testing::Test {
base::HexStringToBytes(kTestVideoSdpBytes, &video_state->sdp_bytes);
device_state.service_record_states.push_back(video_state);
- device_.reset(new BluetoothDeviceWin(device_state));
- }
-
- void GetServiceRecords(
- const BluetoothDevice::ServiceRecordList& service_record_list) {
- service_records_ = &service_record_list;
- }
-
- void OnError() {
- error_called_ = true;
- }
-
- void SetProvidesServiceWithName(bool provides_service_with_name) {
- provides_service_with_name_ = provides_service_with_name;
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner(
+ new base::TestSimpleTaskRunner());
+ scoped_refptr<BluetoothSocketThread> socket_thread(
+ BluetoothSocketThread::Get());
+ 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,
+ ui_task_runner,
+ socket_thread,
+ NULL,
+ net::NetLog::Source()));
}
protected:
scoped_ptr<BluetoothDevice> device_;
scoped_ptr<BluetoothDevice> empty_device_;
- const BluetoothDevice::ServiceRecordList* service_records_;
- bool error_called_;
- bool provides_service_with_name_;
};
-TEST_F(BluetoothDeviceWinTest, GetServices) {
- BluetoothDevice::ServiceList service_list = device_->GetServices();
-
- EXPECT_EQ(2, service_list.size());
- EXPECT_STREQ(kTestAudioSdpUuid, service_list[0].c_str());
- EXPECT_STREQ(kTestVideoSdpUuid, service_list[1].c_str());
-}
+TEST_F(BluetoothDeviceWinTest, GetUUIDs) {
+ BluetoothDevice::UUIDList uuids = device_->GetUUIDs();
-TEST_F(BluetoothDeviceWinTest, GetServiceRecords) {
- device_->GetServiceRecords(
- base::Bind(&BluetoothDeviceWinTest::GetServiceRecords,
- base::Unretained(this)),
- BluetoothDevice::ErrorCallback());
- ASSERT_TRUE(service_records_ != NULL);
- EXPECT_EQ(2, service_records_->size());
- EXPECT_STREQ(kTestAudioSdpUuid, (*service_records_)[0]->uuid().c_str());
- EXPECT_STREQ(kTestVideoSdpUuid, (*service_records_)[1]->uuid().c_str());
-
- BluetoothDeviceWin* device_win =
- reinterpret_cast<BluetoothDeviceWin*>(device_.get());
-
- const BluetoothServiceRecord* audio_device =
- device_win->GetServiceRecord(kTestAudioSdpUuid);
- ASSERT_TRUE(audio_device != NULL);
- EXPECT_EQ((*service_records_)[0], audio_device);
-
- const BluetoothServiceRecord* video_device =
- device_win->GetServiceRecord(kTestVideoSdpUuid);
- ASSERT_TRUE(video_device != NULL);
- EXPECT_EQ((*service_records_)[1], video_device);
-
- const BluetoothServiceRecord* invalid_device =
- device_win->GetServiceRecord(kTestVideoSdpAddress);
- EXPECT_TRUE(invalid_device == NULL);
-}
+ EXPECT_EQ(2, uuids.size());
+ EXPECT_EQ(kTestAudioSdpUuid, uuids[0]);
+ EXPECT_EQ(kTestVideoSdpUuid, uuids[1]);
-TEST_F(BluetoothDeviceWinTest, ProvidesServiceWithName) {
- device_->ProvidesServiceWithName(
- kTestAudioSdpName,
- base::Bind(&BluetoothDeviceWinTest::SetProvidesServiceWithName,
- base::Unretained(this)));
- EXPECT_TRUE(provides_service_with_name_);
-
- device_->ProvidesServiceWithName(
- kTestVideoSdpName,
- base::Bind(&BluetoothDeviceWinTest::SetProvidesServiceWithName,
- base::Unretained(this)));
- EXPECT_TRUE(provides_service_with_name_);
-
- device_->ProvidesServiceWithName(
- "name that does not exist",
- base::Bind(&BluetoothDeviceWinTest::SetProvidesServiceWithName,
- base::Unretained(this)));
- EXPECT_FALSE(provides_service_with_name_);
+ uuids = empty_device_->GetUUIDs();
+ EXPECT_EQ(0, uuids.size());
}
-} // namespace device \ No newline at end of file
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_discovery_manager_mac.h b/chromium/device/bluetooth/bluetooth_discovery_manager_mac.h
new file mode 100644
index 00000000000..575a033faf2
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_discovery_manager_mac.h
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_DISCOVERY_MANAGER_MAC_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_DISCOVERY_MANAGER_MAC_H_
+
+#include "base/macros.h"
+
+@class IOBluetoothDevice;
+
+namespace device {
+
+// Class used by BluetoothAdapterMac to manage classic and LE device discovery.
+// For Bluetooth Classic, this class is responsible for keeping device inquiry
+// running if device discovery is initiated.
+class BluetoothDiscoveryManagerMac {
+ public:
+ // Interface for being notified of events during a device discovery session.
+ class Observer {
+ public:
+ // Called when |manager| has found a device through classic device inquiry
+ // in the form of a IOBluetoothDevice.
+ virtual void DeviceFound(BluetoothDiscoveryManagerMac* manager,
+ IOBluetoothDevice* device) {}
+
+ // Called when device discovery is no longer running, due to either a call
+ // to BluetoothDiscoveryManagerMac::StopDiscovery or an unexpected reason,
+ // such as when a user disables the controller, in which case the value of
+ // |unexpected| will be true.
+ virtual void DiscoveryStopped(BluetoothDiscoveryManagerMac* manager,
+ bool unexpected) {}
+ };
+
+ virtual ~BluetoothDiscoveryManagerMac();
+
+ // Returns true, if discovery is currently being performed.
+ virtual bool IsDiscovering() const = 0;
+
+ // Initiates a discovery session. Returns true on success or if discovery
+ // is already running. Returns false on failure.
+ virtual bool StartDiscovery() = 0;
+
+ // Stops a discovery session. Returns true on success or if discovery is
+ // already not running. Returns false on failure.
+ virtual bool StopDiscovery() = 0;
+
+ // Creates a discovery manager for Bluetooth Classic device discovery with
+ // observer |observer|. Note that the life-time of |observer| should not
+ // end before that of the returned BluetoothDiscoveryManager, as that may
+ // lead to use after free errors.
+ static BluetoothDiscoveryManagerMac* CreateClassic(Observer* observer);
+
+ protected:
+ explicit BluetoothDiscoveryManagerMac(Observer* observer);
+
+ // Observer interested in notifications from us.
+ Observer* observer_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BluetoothDiscoveryManagerMac);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_DISCOVERY_MANAGER_MAC_H_
diff --git a/chromium/device/bluetooth/bluetooth_discovery_manager_mac.mm b/chromium/device/bluetooth/bluetooth_discovery_manager_mac.mm
new file mode 100644
index 00000000000..528e22dc77b
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_discovery_manager_mac.mm
@@ -0,0 +1,222 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_discovery_manager_mac.h"
+
+#import <IOBluetooth/objc/IOBluetoothDevice.h>
+#import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "base/mac/sdk_forward_declarations.h"
+#include "base/logging.h"
+
+namespace device {
+
+class BluetoothDiscoveryManagerMacClassic;
+
+} // namespace device
+
+// IOBluetoothDeviceInquiryDelegate implementation.
+@interface BluetoothDeviceInquiryDelegate
+ : NSObject<IOBluetoothDeviceInquiryDelegate> {
+ @private
+ device::BluetoothDiscoveryManagerMacClassic* manager_; // weak
+}
+
+- (id)initWithManager:(device::BluetoothDiscoveryManagerMacClassic*)manager;
+
+@end
+
+namespace device {
+
+// ementation of BluetoothDiscoveryManagerMac for Bluetooth classic device
+// discovery, using the IOBluetooth framework.
+class BluetoothDiscoveryManagerMacClassic
+ : public BluetoothDiscoveryManagerMac {
+ public:
+ explicit BluetoothDiscoveryManagerMacClassic(Observer* observer)
+ : BluetoothDiscoveryManagerMac(observer),
+ should_do_discovery_(false),
+ inquiry_running_(false),
+ inquiry_delegate_(
+ [[BluetoothDeviceInquiryDelegate alloc] initWithManager:this]),
+ inquiry_([[IOBluetoothDeviceInquiry alloc]
+ initWithDelegate:inquiry_delegate_]) {}
+
+ virtual ~BluetoothDiscoveryManagerMacClassic() {}
+
+ // BluetoothDiscoveryManagerMac override.
+ virtual bool IsDiscovering() const OVERRIDE { return should_do_discovery_; }
+
+ // BluetoothDiscoveryManagerMac override.
+ virtual bool StartDiscovery() OVERRIDE {
+ DVLOG(1) << "Bluetooth Classic: StartDiscovery";
+ DCHECK(!should_do_discovery_);
+
+ DVLOG(1) << "Discovery requested";
+ should_do_discovery_ = true;
+
+ if (inquiry_running_) {
+ DVLOG(1) << "Device inquiry already running";
+ return true;
+ }
+
+ DVLOG(1) << "Requesting to start device inquiry";
+ if ([inquiry_ start] != kIOReturnSuccess) {
+ DVLOG(1) << "Failed to start device inquiry";
+
+ // Set |should_do_discovery_| to false here. Since we're reporting an
+ // error, we're indicating that the adapter call StartDiscovery again
+ // if needed.
+ should_do_discovery_ = false;
+ return false;
+ }
+
+ DVLOG(1) << "Device inquiry start was successful";
+ return true;
+ }
+
+ // BluetoothDiscoveryManagerMac override.
+ virtual bool StopDiscovery() OVERRIDE {
+ DVLOG(1) << "Bluetooth Classic: StopDiscovery";
+ DCHECK(should_do_discovery_);
+
+ should_do_discovery_ = false;
+
+ if (!inquiry_running_) {
+ DVLOG(1) << "No device inquiry running; discovery stopped";
+ return true;
+ }
+
+ DVLOG(1) << "Requesting to stop device inquiry";
+ IOReturn status = [inquiry_ stop];
+ if (status == kIOReturnSuccess) {
+ DVLOG(1) << "Device inquiry stop was successful";
+ return true;
+ }
+
+ if (status == kIOReturnNotPermitted) {
+ DVLOG(1) << "Device inquiry was already stopped";
+ return true;
+ }
+
+ LOG(WARNING) << "Failed to stop device inquiry";
+ return false;
+ }
+
+ // Called by BluetoothDeviceInquiryDelegate.
+ void DeviceInquiryStarted(IOBluetoothDeviceInquiry* inquiry) {
+ DCHECK(!inquiry_running_);
+
+ DVLOG(1) << "Device inquiry started!";
+
+ // If discovery was requested to stop in the mean time, stop the inquiry.
+ if (!should_do_discovery_) {
+ DVLOG(1) << "Discovery stop was requested earlier. Stopping inquiry";
+ [inquiry stop];
+ return;
+ }
+
+ inquiry_running_ = true;
+ }
+
+ void DeviceFound(IOBluetoothDeviceInquiry* inquiry,
+ IOBluetoothDevice* device) {
+ DCHECK(observer_);
+ observer_->DeviceFound(this, device);
+ }
+
+ void DeviceInquiryComplete(IOBluetoothDeviceInquiry* inquiry,
+ IOReturn error,
+ bool aborted) {
+ DCHECK_EQ(inquiry_, inquiry);
+ DCHECK(observer_);
+ DVLOG(1) << "Device inquiry complete";
+ inquiry_running_ = false;
+
+ // If discovery is no longer desired, notify observers that discovery
+ // has stopped and return.
+ if (!should_do_discovery_) {
+ observer_->DiscoveryStopped(this, false /* unexpected */);
+ return;
+ }
+
+ // If discovery has stopped due to an unexpected reason, notify the
+ // observers and return.
+ if (error != kIOReturnSuccess) {
+ DVLOG(1) << "Inquiry has stopped with an error: " << error;
+ should_do_discovery_ = false;
+ observer_->DiscoveryStopped(this, true /* unexpected */);
+ return;
+ }
+
+ DVLOG(1) << "Restarting device inquiry";
+
+ if ([inquiry_ start] == kIOReturnSuccess) {
+ DVLOG(1) << "Device inquiry restart was successful";
+ return;
+ }
+
+ DVLOG(1) << "Failed to restart discovery";
+ should_do_discovery_ = false;
+ DCHECK(observer_);
+ observer_->DiscoveryStopped(this, true /* unexpected */);
+ }
+
+ private:
+ // The requested discovery state.
+ bool should_do_discovery_;
+
+ // The current inquiry state.
+ bool inquiry_running_;
+
+ // Objective-C objects for running and tracking device inquiry.
+ base::scoped_nsobject<BluetoothDeviceInquiryDelegate> inquiry_delegate_;
+ base::scoped_nsobject<IOBluetoothDeviceInquiry> inquiry_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothDiscoveryManagerMacClassic);
+};
+
+BluetoothDiscoveryManagerMac::BluetoothDiscoveryManagerMac(
+ Observer* observer) : observer_(observer) {
+ DCHECK(observer);
+}
+
+BluetoothDiscoveryManagerMac::~BluetoothDiscoveryManagerMac() {
+}
+
+// static
+BluetoothDiscoveryManagerMac* BluetoothDiscoveryManagerMac::CreateClassic(
+ Observer* observer) {
+ return new BluetoothDiscoveryManagerMacClassic(observer);
+}
+
+} // namespace device
+
+@implementation BluetoothDeviceInquiryDelegate
+
+- (id)initWithManager:
+ (device::BluetoothDiscoveryManagerMacClassic*)manager {
+ if ((self = [super init]))
+ manager_ = manager;
+
+ return self;
+}
+
+- (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender {
+ manager_->DeviceInquiryStarted(sender);
+}
+
+- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
+ device:(IOBluetoothDevice*)device {
+ manager_->DeviceFound(sender, device);
+}
+
+- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
+ error:(IOReturn)error
+ aborted:(BOOL)aborted {
+ manager_->DeviceInquiryComplete(sender, error, aborted);
+}
+
+@end
diff --git a/chromium/device/bluetooth/bluetooth_discovery_session.cc b/chromium/device/bluetooth/bluetooth_discovery_session.cc
new file mode 100644
index 00000000000..ba5f5fdba64
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_discovery_session.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_discovery_session.h"
+
+#include "base/bind.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+
+namespace device {
+
+BluetoothDiscoverySession::BluetoothDiscoverySession(
+ scoped_refptr<BluetoothAdapter> adapter)
+ : active_(true), adapter_(adapter), weak_ptr_factory_(this) {
+ DCHECK(adapter_.get());
+}
+
+BluetoothDiscoverySession::BluetoothDiscoverySession()
+ : active_(false), weak_ptr_factory_(this) {}
+
+BluetoothDiscoverySession::~BluetoothDiscoverySession() {
+ // |adapter_| may be NULL if this instance was initialized as a mock.
+ if (!adapter_.get()) {
+ DCHECK(!active_);
+ return;
+ }
+ Stop(base::Bind(&base::DoNothing), base::Bind(&base::DoNothing));
+ MarkAsInactive();
+}
+
+bool BluetoothDiscoverySession::IsActive() const {
+ return active_;
+}
+
+void BluetoothDiscoverySession::Stop(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (!active_) {
+ LOG(WARNING) << "Discovery session not active. Cannot stop.";
+ error_callback.Run();
+ return;
+ }
+ VLOG(1) << "Stopping device discovery session.";
+ DCHECK(adapter_.get());
+ adapter_->RemoveDiscoverySession(
+ base::Bind(&BluetoothDiscoverySession::OnStop,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ error_callback);
+}
+
+void BluetoothDiscoverySession::OnStop(const base::Closure& callback) {
+ MarkAsInactive();
+ callback.Run();
+}
+
+void BluetoothDiscoverySession::MarkAsInactive() {
+ if (!active_)
+ return;
+ active_ = false;
+ DCHECK(adapter_.get());
+ adapter_->DiscoverySessionBecameInactive(this);
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_discovery_session.h b/chromium/device/bluetooth/bluetooth_discovery_session.h
new file mode 100644
index 00000000000..666538b8e06
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_discovery_session.h
@@ -0,0 +1,90 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_DISCOVERY_SESSION_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_DISCOVERY_SESSION_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+
+namespace device {
+
+class BluetoothAdapter;
+
+// BluetoothDiscoverySession represents a current active or inactive device
+// discovery session. Instances of this class are obtained by calling
+// BluetoothAdapter::StartDiscoverySession. The Bluetooth adapter will be
+// constantly searching for nearby devices, as long as at least one instance
+// of an active BluetoothDiscoverySession exists. A BluetoothDiscoverySession is
+// considered active, as long as the adapter is discovering AND the owner of the
+// instance has not called BluetoothDiscoverySession::Stop. A
+// BluetoothDiscoverySession might unexpectedly become inactive, if the adapter
+// unexpectedly stops discovery. Users can implement the
+// AdapterDiscoveringChanged method of the BluetoothAdapter::Observer interface
+// to be notified of such a change and promptly request a new
+// BluetoothDiscoverySession if their existing sessions have become inactive.
+class BluetoothDiscoverySession {
+ public:
+ // The ErrorCallback is used by methods to asynchronously report errors.
+ typedef base::Closure ErrorCallback;
+
+ // Destructor automatically terminates the discovery session. If this
+ // results in a call to the underlying system to stop device discovery
+ // (i.e. this instance represents the last active discovery session),
+ // the call may not always succeed. To be notified of such failures,
+ // users are highly encouraged to call BluetoothDiscoverySession::Stop,
+ // instead of relying on the destructor.
+ virtual ~BluetoothDiscoverySession();
+
+ // Returns true if the session is active, false otherwise. If false, the
+ // adapter might still be discovering as there might still be other active
+ // sessions; this just means that this instance no longer has a say in
+ // whether or not discovery should continue. In this case, the application
+ // should request a new BluetoothDiscoverySession to make sure that device
+ // discovery continues.
+ virtual bool IsActive() const;
+
+ // Requests this discovery session instance to stop. If this instance is
+ // active, the session will stop. On success, |callback| is called and
+ // on error |error_callback| is called. After a successful invocation, the
+ // adapter may or may not stop device discovery, depending on whether or not
+ // other active discovery sessions are present. Users are highly encouraged
+ // to call this method to end a discovery session, instead of relying on the
+ // destructor, so that they can be notified of the result via the callback
+ // arguments.
+ virtual void Stop(const base::Closure& callback,
+ const ErrorCallback& error_callback);
+
+ protected:
+ BluetoothDiscoverySession(); // Called by mock.
+
+ private:
+ friend class BluetoothAdapter;
+ explicit BluetoothDiscoverySession(scoped_refptr<BluetoothAdapter> adapter);
+
+ // Internal callback invoked when a call to Stop has succeeded.
+ void OnStop(const base::Closure& callback);
+
+ // Marks this instance as inactive. Called by BluetoothAdapter to mark a
+ // session as inactive in the case of an unexpected change to the adapter
+ // discovery state.
+ void MarkAsInactive();
+
+ // Whether or not this instance represents an active discovery session.
+ bool active_;
+
+ // The adapter that created this instance.
+ scoped_refptr<BluetoothAdapter> adapter_;
+
+ // 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<BluetoothDiscoverySession> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothDiscoverySession);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_DISCOVERY_SESSION_H_
diff --git a/chromium/device/bluetooth/bluetooth_gatt_characteristic.cc b/chromium/device/bluetooth/bluetooth_gatt_characteristic.cc
new file mode 100644
index 00000000000..47182a5d9f9
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_characteristic.cc
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
+
+#include "base/logging.h"
+
+namespace device {
+
+BluetoothGattCharacteristic::BluetoothGattCharacteristic() {
+}
+
+BluetoothGattCharacteristic::~BluetoothGattCharacteristic() {
+}
+
+// static
+BluetoothGattCharacteristic* BluetoothGattCharacteristic::Create(
+ const BluetoothUUID& uuid,
+ const std::vector<uint8>& value,
+ Properties properties,
+ Permissions permissions) {
+ LOG(ERROR) << "Creating local GATT characteristics currently not supported.";
+ return NULL;
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_gatt_characteristic.h b/chromium/device/bluetooth/bluetooth_gatt_characteristic.h
new file mode 100644
index 00000000000..7b952e08d65
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_characteristic.h
@@ -0,0 +1,210 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_GATT_CHARACTERISTIC_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_GATT_CHARACTERISTIC_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace device {
+
+class BluetoothGattDescriptor;
+class BluetoothGattService;
+class BluetoothGattNotifySession;
+
+// BluetoothGattCharacteristic represents a local or remote GATT characteristic.
+// A GATT characteristic is a basic data element used to construct a GATT
+// service. Hence, instances of a BluetoothGattCharacteristic are associated
+// with a BluetoothGattService. There are two ways in which this class is used:
+//
+// 1. To represent GATT characteristics that belong to a service hosted by a
+// a remote device. In this case the characteristic will be constructed by
+// the subsystem.
+// 2. To represent GATT characteristics that belong to a locally hosted
+// service. To achieve this, users can construct instances of
+// BluetoothGattCharacteristic directly and add it to the desired
+// BluetoothGattService instance that represents a local service.
+class BluetoothGattCharacteristic {
+ public:
+ // Values representing the possible properties of a characteristic, which
+ // define how the characteristic can be used. Each of these properties serve
+ // a role as defined in the Bluetooth Specification.
+ // |kPropertyExtendedProperties| is a special property that, if present,
+ // indicates that there is a characteristic descriptor (namely the
+ // "Characteristic Extended Properties Descriptor" with UUID 0x2900) that
+ // contains additional properties pertaining to the characteristic.
+ // The properties "ReliableWrite| and |WriteAuxiliaries| are retrieved from
+ // that characteristic.
+ enum Property {
+ kPropertyNone = 0,
+ kPropertyBroadcast = 1 << 0,
+ kPropertyRead = 1 << 1,
+ kPropertyWriteWithoutResponse = 1 << 2,
+ kPropertyWrite = 1 << 3,
+ kPropertyNotify = 1 << 4,
+ kPropertyIndicate = 1 << 5,
+ kPropertyAuthenticatedSignedWrites = 1 << 6,
+ kPropertyExtendedProperties = 1 << 7,
+ kPropertyReliableWrite = 1 << 8,
+ kPropertyWritableAuxiliaries = 1 << 9
+ };
+ typedef uint32 Properties;
+
+ // Values representing read, write, and encryption permissions for a
+ // characteristic's value. While attribute permissions for all GATT
+ // definitions have been set by the Bluetooth specification, characteristic
+ // value permissions are left up to the higher-level profile.
+ //
+ // Attribute permissions are distinct from the characteristic properties. For
+ // example, a characteristic may have the property |kPropertyRead| to make
+ // clients know that it is possible to read the characteristic value and have
+ // the permission |kPermissionReadEncrypted| to require a secure connection.
+ // It is up to the application to properly specify the permissions and
+ // properties for a local characteristic.
+ enum Permission {
+ kPermissionNone = 0,
+ kPermissionRead = 1 << 0,
+ kPermissionWrite = 1 << 1,
+ kPermissionReadEncrypted = 1 << 2,
+ kPermissionWriteEncrypted = 1 << 3
+ };
+ typedef uint32 Permissions;
+
+ // The ErrorCallback is used by methods to asynchronously report errors.
+ typedef base::Closure ErrorCallback;
+
+ // The ValueCallback is used to return the value of a remote characteristic
+ // upon a read request.
+ typedef base::Callback<void(const std::vector<uint8>&)> ValueCallback;
+
+ // The NotifySessionCallback is used to return sessions after they have
+ // been successfully started.
+ typedef base::Callback<void(scoped_ptr<BluetoothGattNotifySession>)>
+ NotifySessionCallback;
+
+ // Constructs a BluetoothGattCharacteristic that can be associated with a
+ // local GATT service when the adapter is in the peripheral role. To
+ // associate the returned characteristic with a service, add it to a local
+ // service by calling BluetoothGattService::AddCharacteristic.
+ //
+ // This method constructs a characteristic with UUID |uuid|, initial cached
+ // value |value|, properties |properties|, and permissions |permissions|.
+ // |value| will be cached and returned for read requests and automatically set
+ // for write requests by default, unless an instance of
+ // BluetoothGattService::Delegate has been provided to the associated
+ // BluetoothGattService instance, in which case the delegate will handle read
+ // and write requests.
+ //
+ // NOTE: Don't explicitly set |kPropertyExtendedProperties| in |properties|.
+ // Instead, create and add a BluetoothGattDescriptor that represents the
+ // "Characteristic Extended Properties" descriptor and this will automatically
+ // set the correspoding bit in the characteristic's properties field. If
+ // |properties| has |kPropertyExtendedProperties| set, it will be ignored.
+ static BluetoothGattCharacteristic* Create(const BluetoothUUID& uuid,
+ const std::vector<uint8>& value,
+ Properties properties,
+ Permissions permissions);
+
+ // Identifier used to uniquely identify a GATT characteristic object. This is
+ // different from the characteristic UUID: while multiple characteristics with
+ // the same UUID can exist on a Bluetooth device, the identifier returned from
+ // this method is unique among all characteristics of a device. The contents
+ // of the identifier are platform specific.
+ virtual std::string GetIdentifier() const = 0;
+
+ // The Bluetooth-specific UUID of the characteristic.
+ virtual BluetoothUUID GetUUID() const = 0;
+
+ // Returns true, if this characteristic is hosted locally. If false, then this
+ // instance represents a remote GATT characteristic.
+ virtual bool IsLocal() const = 0;
+
+ // Returns the value of the characteristic. For remote characteristics, this
+ // is the most recently cached value. For local characteristics, this is the
+ // most recently updated value or the value retrieved from the delegate.
+ virtual const std::vector<uint8>& GetValue() const = 0;
+
+ // Returns a pointer to the GATT service this characteristic belongs to.
+ virtual BluetoothGattService* GetService() const = 0;
+
+ // Returns the bitmask of characteristic properties.
+ virtual Properties GetProperties() const = 0;
+
+ // Returns the bitmask of characteristic attribute permissions.
+ virtual Permissions GetPermissions() const = 0;
+
+ // Returns whether or not this characteristic is currently sending value
+ // updates in the form of a notification or indication.
+ virtual bool IsNotifying() const = 0;
+
+ // Returns the list of GATT characteristic descriptors that provide more
+ // information about this characteristic.
+ virtual std::vector<BluetoothGattDescriptor*>
+ GetDescriptors() const = 0;
+
+ // Returns the GATT characteristic descriptor with identifier |identifier| if
+ // it belongs to this GATT characteristic.
+ virtual BluetoothGattDescriptor* GetDescriptor(
+ const std::string& identifier) const = 0;
+
+ // Adds a characteristic descriptor to the locally hosted characteristic
+ // represented by this instance. This method only makes sense for local
+ // characteristics and won't have an effect if this instance represents a
+ // remote GATT service and will return false. This method takes ownership
+ // of |descriptor|.
+ virtual bool AddDescriptor(BluetoothGattDescriptor* descriptor) = 0;
+
+ // For locally hosted characteristics, updates the characteristic's value.
+ // This will update the value that is visible to remote devices and send out
+ // any notifications and indications that have been configured. This method
+ // can be used in place of, and in conjunction with,
+ // BluetoothGattService::Delegate methods to send updates to remote devices,
+ // or simply to set update the cached value for read requests without having
+ // to implement the delegate methods.
+ //
+ // This method only makes sense for local characteristics and does nothing and
+ // returns false if this instance represents a remote characteristic.
+ virtual bool UpdateValue(const std::vector<uint8>& value) = 0;
+
+ // Starts a notify session for the remote characteristic, if it supports
+ // notifications/indications. On success, the characteristic starts sending
+ // value notifications and |callback| is called with a session object whose
+ // ownership belongs to the caller. |error_callback| is called on errors.
+ virtual void StartNotifySession(const NotifySessionCallback& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // Sends a read request to a remote characteristic to read its value.
+ // |callback| is called to return the read value on success and
+ // |error_callback| is called for failures.
+ virtual void ReadRemoteCharacteristic(
+ const ValueCallback& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // Sends a write request to a remote characteristic, to modify the
+ // characteristic's value with the new value |new_value|. |callback| is
+ // called to signal success and |error_callback| for failures. This method
+ // only applies to remote characteristics and will fail for those that are
+ // locally hosted.
+ virtual void WriteRemoteCharacteristic(
+ const std::vector<uint8>& new_value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ protected:
+ BluetoothGattCharacteristic();
+ virtual ~BluetoothGattCharacteristic();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BluetoothGattCharacteristic);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_GATT_CHARACTERISTIC_H_
diff --git a/chromium/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc b/chromium/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc
new file mode 100644
index 00000000000..673ed5f35fb
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc
@@ -0,0 +1,1346 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "chromeos/dbus/fake_bluetooth_adapter_client.h"
+#include "chromeos/dbus/fake_bluetooth_agent_manager_client.h"
+#include "chromeos/dbus/fake_bluetooth_device_client.h"
+#include "chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h"
+#include "chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h"
+#include "chromeos/dbus/fake_bluetooth_gatt_service_client.h"
+#include "chromeos/dbus/fake_bluetooth_input_client.h"
+#include "chromeos/dbus/fake_dbus_thread_manager.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
+#include "device/bluetooth/bluetooth_gatt_connection.h"
+#include "device/bluetooth/bluetooth_gatt_descriptor.h"
+#include "device/bluetooth/bluetooth_gatt_notify_session.h"
+#include "device/bluetooth/bluetooth_gatt_service.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using device::BluetoothAdapter;
+using device::BluetoothDevice;
+using device::BluetoothGattCharacteristic;
+using device::BluetoothGattConnection;
+using device::BluetoothGattDescriptor;
+using device::BluetoothGattService;
+using device::BluetoothGattNotifySession;
+using device::BluetoothUUID;
+
+namespace chromeos {
+
+namespace {
+
+const BluetoothUUID kHeartRateMeasurementUUID(
+ FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID);
+const BluetoothUUID kBodySensorLocationUUID(
+ FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID);
+const BluetoothUUID kHeartRateControlPointUUID(
+ FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID);
+
+// Compares GATT characteristic/descriptor values. Returns true, if the values
+// are equal.
+bool ValuesEqual(const std::vector<uint8>& value0,
+ const std::vector<uint8>& value1) {
+ if (value0.size() != value1.size())
+ return false;
+ for (size_t i = 0; i < value0.size(); ++i)
+ if (value0[i] != value1[i])
+ return false;
+ return true;
+}
+
+class TestDeviceObserver : public BluetoothDevice::Observer {
+ public:
+ TestDeviceObserver(scoped_refptr<BluetoothAdapter> adapter,
+ BluetoothDevice* device)
+ : gatt_service_added_count_(0),
+ gatt_service_removed_count_(0),
+ device_address_(device->GetAddress()),
+ adapter_(adapter) {
+ device->AddObserver(this);
+ }
+
+ virtual ~TestDeviceObserver() {
+ BluetoothDevice* device = adapter_->GetDevice(device_address_);
+ if (device)
+ device->RemoveObserver(this);
+ }
+
+ // BluetoothDevice::Observer overrides.
+ virtual void GattServiceAdded(
+ BluetoothDevice* device,
+ BluetoothGattService* service) OVERRIDE {
+ ASSERT_EQ(device_address_, device->GetAddress());
+
+ ++gatt_service_added_count_;
+ last_gatt_service_id_ = service->GetIdentifier();
+ last_gatt_service_uuid_ = service->GetUUID();
+
+ EXPECT_FALSE(service->IsLocal());
+ EXPECT_TRUE(service->IsPrimary());
+
+ EXPECT_EQ(device->GetGattService(last_gatt_service_id_), service);
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattServiceRemoved(
+ BluetoothDevice* device,
+ BluetoothGattService* service) OVERRIDE {
+ ASSERT_EQ(device_address_, device->GetAddress());
+
+ ++gatt_service_removed_count_;
+ last_gatt_service_id_ = service->GetIdentifier();
+ last_gatt_service_uuid_ = service->GetUUID();
+
+ EXPECT_FALSE(service->IsLocal());
+ EXPECT_TRUE(service->IsPrimary());
+
+ // The device should return NULL for this service.
+ EXPECT_FALSE(device->GetGattService(last_gatt_service_id_));
+
+ QuitMessageLoop();
+ }
+
+ int gatt_service_added_count_;
+ int gatt_service_removed_count_;
+ std::string last_gatt_service_id_;
+ BluetoothUUID last_gatt_service_uuid_;
+
+ private:
+ // Some tests use a message loop since background processing is simulated;
+ // break out of those loops.
+ void QuitMessageLoop() {
+ if (base::MessageLoop::current() &&
+ base::MessageLoop::current()->is_running())
+ base::MessageLoop::current()->Quit();
+ }
+
+ std::string device_address_;
+ scoped_refptr<BluetoothAdapter> adapter_;
+};
+
+class TestGattServiceObserver : public BluetoothGattService::Observer {
+ public:
+ TestGattServiceObserver(scoped_refptr<BluetoothAdapter> adapter,
+ BluetoothDevice* device,
+ BluetoothGattService* service)
+ : gatt_service_changed_count_(0),
+ gatt_characteristic_added_count_(0),
+ gatt_characteristic_removed_count_(0),
+ gatt_characteristic_value_changed_count_(0),
+ gatt_descriptor_added_count_(0),
+ gatt_descriptor_removed_count_(0),
+ gatt_descriptor_value_changed_count_(0),
+ device_address_(device->GetAddress()),
+ gatt_service_id_(service->GetIdentifier()),
+ adapter_(adapter) {
+ service->AddObserver(this);
+ }
+
+ virtual ~TestGattServiceObserver() {
+ // See if either the device or the service even exist.
+ BluetoothDevice* device = adapter_->GetDevice(device_address_);
+ if (!device)
+ return;
+
+ BluetoothGattService* service = device->GetGattService(gatt_service_id_);
+ if (!service)
+ return;
+
+ service->RemoveObserver(this);
+ }
+
+ // BluetoothGattService::Observer overrides.
+ virtual void GattServiceChanged(BluetoothGattService* service) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, service->GetIdentifier());
+ ++gatt_service_changed_count_;
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattCharacteristicAdded(
+ BluetoothGattService* service,
+ BluetoothGattCharacteristic* characteristic) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, service->GetIdentifier());
+
+ ++gatt_characteristic_added_count_;
+ last_gatt_characteristic_id_ = characteristic->GetIdentifier();
+ last_gatt_characteristic_uuid_ = characteristic->GetUUID();
+
+ EXPECT_EQ(service->GetCharacteristic(last_gatt_characteristic_id_),
+ characteristic);
+ EXPECT_EQ(service, characteristic->GetService());
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattCharacteristicRemoved(
+ BluetoothGattService* service,
+ BluetoothGattCharacteristic* characteristic) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, service->GetIdentifier());
+
+ ++gatt_characteristic_removed_count_;
+ last_gatt_characteristic_id_ = characteristic->GetIdentifier();
+ last_gatt_characteristic_uuid_ = characteristic->GetUUID();
+
+ // The service should return NULL for this characteristic.
+ EXPECT_FALSE(service->GetCharacteristic(last_gatt_characteristic_id_));
+ EXPECT_EQ(service, characteristic->GetService());
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattCharacteristicValueChanged(
+ BluetoothGattService* service,
+ BluetoothGattCharacteristic* characteristic,
+ const std::vector<uint8>& value) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, service->GetIdentifier());
+
+ ++gatt_characteristic_value_changed_count_;
+ last_gatt_characteristic_id_ = characteristic->GetIdentifier();
+ last_gatt_characteristic_uuid_ = characteristic->GetUUID();
+ last_changed_characteristic_value_ = value;
+
+ EXPECT_EQ(service->GetCharacteristic(last_gatt_characteristic_id_),
+ characteristic);
+ EXPECT_EQ(service, characteristic->GetService());
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattDescriptorAdded(
+ BluetoothGattCharacteristic* characteristic,
+ BluetoothGattDescriptor* descriptor) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, characteristic->GetService()->GetIdentifier());
+
+ ++gatt_descriptor_added_count_;
+ last_gatt_descriptor_id_ = descriptor->GetIdentifier();
+ last_gatt_descriptor_uuid_ = descriptor->GetUUID();
+
+ EXPECT_EQ(characteristic->GetDescriptor(last_gatt_descriptor_id_),
+ descriptor);
+ EXPECT_EQ(characteristic, descriptor->GetCharacteristic());
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattDescriptorRemoved(
+ BluetoothGattCharacteristic* characteristic,
+ BluetoothGattDescriptor* descriptor) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, characteristic->GetService()->GetIdentifier());
+
+ ++gatt_descriptor_removed_count_;
+ last_gatt_descriptor_id_ = descriptor->GetIdentifier();
+ last_gatt_descriptor_uuid_ = descriptor->GetUUID();
+
+ // The characteristic should return NULL for this descriptor..
+ EXPECT_FALSE(characteristic->GetDescriptor(last_gatt_descriptor_id_));
+ EXPECT_EQ(characteristic, descriptor->GetCharacteristic());
+
+ QuitMessageLoop();
+ }
+
+ virtual void GattDescriptorValueChanged(
+ BluetoothGattCharacteristic* characteristic,
+ BluetoothGattDescriptor* descriptor,
+ const std::vector<uint8>& value) OVERRIDE {
+ ASSERT_EQ(gatt_service_id_, characteristic->GetService()->GetIdentifier());
+
+ ++gatt_descriptor_value_changed_count_;
+ last_gatt_descriptor_id_ = descriptor->GetIdentifier();
+ last_gatt_descriptor_uuid_ = descriptor->GetUUID();
+ last_changed_descriptor_value_ = value;
+
+ EXPECT_EQ(characteristic->GetDescriptor(last_gatt_descriptor_id_),
+ descriptor);
+ EXPECT_EQ(characteristic, descriptor->GetCharacteristic());
+
+ QuitMessageLoop();
+ }
+
+ int gatt_service_changed_count_;
+ int gatt_characteristic_added_count_;
+ int gatt_characteristic_removed_count_;
+ int gatt_characteristic_value_changed_count_;
+ int gatt_descriptor_added_count_;
+ int gatt_descriptor_removed_count_;
+ int gatt_descriptor_value_changed_count_;
+ std::string last_gatt_characteristic_id_;
+ BluetoothUUID last_gatt_characteristic_uuid_;
+ std::vector<uint8> last_changed_characteristic_value_;
+ std::string last_gatt_descriptor_id_;
+ BluetoothUUID last_gatt_descriptor_uuid_;
+ std::vector<uint8> last_changed_descriptor_value_;
+
+ private:
+ // Some tests use a message loop since background processing is simulated;
+ // break out of those loops.
+ void QuitMessageLoop() {
+ if (base::MessageLoop::current() &&
+ base::MessageLoop::current()->is_running())
+ base::MessageLoop::current()->Quit();
+ }
+
+ std::string device_address_;
+ std::string gatt_service_id_;
+ scoped_refptr<BluetoothAdapter> adapter_;
+};
+
+} // namespace
+
+class BluetoothGattChromeOSTest : public testing::Test {
+ public:
+ BluetoothGattChromeOSTest()
+ : fake_bluetooth_gatt_service_client_(NULL),
+ success_callback_count_(0),
+ error_callback_count_(0) {
+ }
+
+ virtual void SetUp() {
+ FakeDBusThreadManager* fake_dbus_thread_manager = new FakeDBusThreadManager;
+ fake_bluetooth_device_client_ = new FakeBluetoothDeviceClient;
+ fake_bluetooth_gatt_service_client_ =
+ new FakeBluetoothGattServiceClient;
+ fake_bluetooth_gatt_characteristic_client_ =
+ new FakeBluetoothGattCharacteristicClient;
+ fake_bluetooth_gatt_descriptor_client_ =
+ new FakeBluetoothGattDescriptorClient;
+ fake_dbus_thread_manager->SetBluetoothDeviceClient(
+ scoped_ptr<BluetoothDeviceClient>(
+ fake_bluetooth_device_client_));
+ fake_dbus_thread_manager->SetBluetoothGattServiceClient(
+ scoped_ptr<BluetoothGattServiceClient>(
+ fake_bluetooth_gatt_service_client_));
+ fake_dbus_thread_manager->SetBluetoothGattCharacteristicClient(
+ scoped_ptr<BluetoothGattCharacteristicClient>(
+ fake_bluetooth_gatt_characteristic_client_));
+ fake_dbus_thread_manager->SetBluetoothGattDescriptorClient(
+ scoped_ptr<BluetoothGattDescriptorClient>(
+ fake_bluetooth_gatt_descriptor_client_));
+ fake_dbus_thread_manager->SetBluetoothAdapterClient(
+ scoped_ptr<BluetoothAdapterClient>(new FakeBluetoothAdapterClient));
+ fake_dbus_thread_manager->SetBluetoothInputClient(
+ scoped_ptr<BluetoothInputClient>(new FakeBluetoothInputClient));
+ fake_dbus_thread_manager->SetBluetoothAgentManagerClient(
+ scoped_ptr<BluetoothAgentManagerClient>(
+ new FakeBluetoothAgentManagerClient));
+ DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager);
+
+ GetAdapter();
+
+ adapter_->SetPowered(
+ true,
+ base::Bind(&base::DoNothing),
+ base::Bind(&base::DoNothing));
+ ASSERT_TRUE(adapter_->IsPowered());
+ }
+
+ virtual void TearDown() {
+ adapter_ = NULL;
+ update_sessions_.clear();
+ gatt_conn_.reset();
+ DBusThreadManager::Shutdown();
+ }
+
+ void GetAdapter() {
+ device::BluetoothAdapterFactory::GetAdapter(
+ base::Bind(&BluetoothGattChromeOSTest::AdapterCallback,
+ base::Unretained(this)));
+ ASSERT_TRUE(adapter_.get() != NULL);
+ ASSERT_TRUE(adapter_->IsInitialized());
+ ASSERT_TRUE(adapter_->IsPresent());
+ }
+
+ void AdapterCallback(scoped_refptr<BluetoothAdapter> adapter) {
+ adapter_ = adapter;
+ }
+
+ void SuccessCallback() {
+ ++success_callback_count_;
+ }
+
+ void ValueCallback(const std::vector<uint8>& value) {
+ ++success_callback_count_;
+ last_read_value_ = value;
+ }
+
+ void GattConnectionCallback(scoped_ptr<BluetoothGattConnection> conn) {
+ ++success_callback_count_;
+ gatt_conn_ = conn.Pass();
+ }
+
+ void NotifySessionCallback(scoped_ptr<BluetoothGattNotifySession> session) {
+ ++success_callback_count_;
+ update_sessions_.push_back(session.release());
+ QuitMessageLoop();
+ }
+
+ void ErrorCallback() {
+ ++error_callback_count_;
+ }
+
+ void DBusErrorCallback(const std::string& error_name,
+ const std::string& error_message) {
+ ++error_callback_count_;
+ }
+
+ void ConnectErrorCallback(BluetoothDevice::ConnectErrorCode error) {
+ ++error_callback_count_;
+ }
+
+ protected:
+ void QuitMessageLoop() {
+ if (base::MessageLoop::current() &&
+ base::MessageLoop::current()->is_running())
+ base::MessageLoop::current()->Quit();
+ }
+
+ base::MessageLoop message_loop_;
+
+ FakeBluetoothDeviceClient* fake_bluetooth_device_client_;
+ FakeBluetoothGattServiceClient* fake_bluetooth_gatt_service_client_;
+ FakeBluetoothGattCharacteristicClient*
+ fake_bluetooth_gatt_characteristic_client_;
+ FakeBluetoothGattDescriptorClient* fake_bluetooth_gatt_descriptor_client_;
+ scoped_ptr<device::BluetoothGattConnection> gatt_conn_;
+ ScopedVector<BluetoothGattNotifySession> update_sessions_;
+ scoped_refptr<BluetoothAdapter> adapter_;
+
+ int success_callback_count_;
+ int error_callback_count_;
+ std::vector<uint8> last_read_value_;
+};
+
+TEST_F(BluetoothGattChromeOSTest, GattConnection) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+ ASSERT_FALSE(device->IsConnected());
+ ASSERT_FALSE(gatt_conn_.get());
+ ASSERT_EQ(0, success_callback_count_);
+ ASSERT_EQ(0, error_callback_count_);
+
+ device->CreateGattConnection(
+ base::Bind(&BluetoothGattChromeOSTest::GattConnectionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(device->IsConnected());
+ ASSERT_TRUE(gatt_conn_.get());
+ EXPECT_TRUE(gatt_conn_->IsConnected());
+ EXPECT_EQ(FakeBluetoothDeviceClient::kLowEnergyAddress,
+ gatt_conn_->GetDeviceAddress());
+
+ gatt_conn_->Disconnect(
+ base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(device->IsConnected());
+ EXPECT_FALSE(gatt_conn_->IsConnected());
+
+ device->CreateGattConnection(
+ base::Bind(&BluetoothGattChromeOSTest::GattConnectionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(3, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(device->IsConnected());
+ ASSERT_TRUE(gatt_conn_.get());
+ EXPECT_TRUE(gatt_conn_->IsConnected());
+
+ device->Disconnect(
+ base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(4, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ ASSERT_TRUE(gatt_conn_.get());
+ EXPECT_FALSE(gatt_conn_->IsConnected());
+
+ device->CreateGattConnection(
+ base::Bind(&BluetoothGattChromeOSTest::GattConnectionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(5, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(device->IsConnected());
+ EXPECT_TRUE(gatt_conn_->IsConnected());
+
+ fake_bluetooth_device_client_->RemoveDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_TRUE(gatt_conn_.get());
+ EXPECT_FALSE(gatt_conn_->IsConnected());
+}
+
+TEST_F(BluetoothGattChromeOSTest, GattServiceAddedAndRemoved) {
+ // Create a fake LE device. We store the device pointer here because this is a
+ // test. It's unsafe to do this in production as the device might get deleted.
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+ EXPECT_EQ(0, observer.gatt_service_added_count_);
+ EXPECT_EQ(0, observer.gatt_service_removed_count_);
+ EXPECT_TRUE(observer.last_gatt_service_id_.empty());
+ EXPECT_FALSE(observer.last_gatt_service_uuid_.IsValid());
+ EXPECT_TRUE(device->GetGattServices().empty());
+
+ // Expose the fake Heart Rate Service.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ EXPECT_EQ(1, observer.gatt_service_added_count_);
+ EXPECT_EQ(0, observer.gatt_service_removed_count_);
+ EXPECT_FALSE(observer.last_gatt_service_id_.empty());
+ EXPECT_EQ(1U, device->GetGattServices().size());
+ EXPECT_EQ(
+ BluetoothUUID(FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
+ observer.last_gatt_service_uuid_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+ EXPECT_FALSE(service->IsLocal());
+ EXPECT_TRUE(service->IsPrimary());
+ EXPECT_EQ(service, device->GetGattServices()[0]);
+ EXPECT_EQ(service, device->GetGattService(service->GetIdentifier()));
+
+ EXPECT_EQ(observer.last_gatt_service_uuid_, service->GetUUID());
+
+ // Hide the service.
+ observer.last_gatt_service_uuid_ = BluetoothUUID();
+ observer.last_gatt_service_id_.clear();
+ fake_bluetooth_gatt_service_client_->HideHeartRateService();
+
+ EXPECT_EQ(1, observer.gatt_service_added_count_);
+ EXPECT_EQ(1, observer.gatt_service_removed_count_);
+ EXPECT_FALSE(observer.last_gatt_service_id_.empty());
+ EXPECT_TRUE(device->GetGattServices().empty());
+ EXPECT_EQ(
+ BluetoothUUID(FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
+ observer.last_gatt_service_uuid_);
+
+ EXPECT_EQ(NULL, device->GetGattService(observer.last_gatt_service_id_));
+
+ // Expose the service again.
+ observer.last_gatt_service_uuid_ = BluetoothUUID();
+ observer.last_gatt_service_id_.clear();
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ EXPECT_EQ(2, observer.gatt_service_added_count_);
+ EXPECT_EQ(1, observer.gatt_service_removed_count_);
+ EXPECT_FALSE(observer.last_gatt_service_id_.empty());
+ EXPECT_EQ(1U, device->GetGattServices().size());
+ EXPECT_EQ(
+ BluetoothUUID(FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
+ observer.last_gatt_service_uuid_);
+
+ // The object |service| points to should have been deallocated. |device|
+ // should contain a brand new instance.
+ service = device->GetGattService(observer.last_gatt_service_id_);
+ EXPECT_EQ(service, device->GetGattServices()[0]);
+ EXPECT_FALSE(service->IsLocal());
+ EXPECT_TRUE(service->IsPrimary());
+
+ EXPECT_EQ(observer.last_gatt_service_uuid_, service->GetUUID());
+
+ // Remove the device. The observer should be notified of the removed service.
+ // |device| becomes invalid after this.
+ observer.last_gatt_service_uuid_ = BluetoothUUID();
+ observer.last_gatt_service_id_.clear();
+ fake_bluetooth_device_client_->RemoveDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+
+ EXPECT_EQ(2, observer.gatt_service_added_count_);
+ EXPECT_EQ(2, observer.gatt_service_removed_count_);
+ EXPECT_FALSE(observer.last_gatt_service_id_.empty());
+ EXPECT_EQ(
+ BluetoothUUID(FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
+ observer.last_gatt_service_uuid_);
+ EXPECT_EQ(
+ NULL, adapter_->GetDevice(FakeBluetoothDeviceClient::kLowEnergyAddress));
+}
+
+TEST_F(BluetoothGattChromeOSTest, GattCharacteristicAddedAndRemoved) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_added_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_removed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(service->GetCharacteristics().empty());
+
+ // Run the message loop so that the characteristics appear.
+ base::MessageLoop::current()->Run();
+
+ // 3 characteristics should appear. Only 1 of the characteristics sends
+ // value changed signals. Service changed should be fired once for
+ // descriptor added.
+ EXPECT_EQ(4, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(3, service_observer.gatt_characteristic_added_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_removed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(3U, service->GetCharacteristics().size());
+
+ // Hide the characteristics. 3 removed signals should be received.
+ fake_bluetooth_gatt_characteristic_client_->HideHeartRateCharacteristics();
+ EXPECT_EQ(8, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(3, service_observer.gatt_characteristic_added_count_);
+ EXPECT_EQ(3, service_observer.gatt_characteristic_removed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(service->GetCharacteristics().empty());
+
+ // Re-expose the heart rate characteristics.
+ fake_bluetooth_gatt_characteristic_client_->ExposeHeartRateCharacteristics(
+ fake_bluetooth_gatt_service_client_->GetHeartRateServicePath());
+ EXPECT_EQ(12, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(6, service_observer.gatt_characteristic_added_count_);
+ EXPECT_EQ(3, service_observer.gatt_characteristic_removed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(3U, service->GetCharacteristics().size());
+
+ // Hide the service. All characteristics should disappear.
+ fake_bluetooth_gatt_service_client_->HideHeartRateService();
+ EXPECT_EQ(16, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(6, service_observer.gatt_characteristic_added_count_);
+ EXPECT_EQ(6, service_observer.gatt_characteristic_removed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+}
+
+TEST_F(BluetoothGattChromeOSTest, GattDescriptorAddedAndRemoved) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(0, service_observer.gatt_descriptor_added_count_);
+ EXPECT_EQ(0, service_observer.gatt_descriptor_removed_count_);
+ EXPECT_EQ(0, service_observer.gatt_descriptor_value_changed_count_);
+
+ EXPECT_TRUE(service->GetCharacteristics().empty());
+
+ // Run the message loop so that the characteristics appear.
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(4, service_observer.gatt_service_changed_count_);
+
+ // Only the Heart Rate Measurement characteristic has a descriptor.
+ EXPECT_EQ(1, service_observer.gatt_descriptor_added_count_);
+ EXPECT_EQ(0, service_observer.gatt_descriptor_removed_count_);
+ EXPECT_EQ(0, service_observer.gatt_descriptor_value_changed_count_);
+
+ BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetBodySensorLocationPath().value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_TRUE(characteristic->GetDescriptors().empty());
+
+ characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateControlPointPath().value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_TRUE(characteristic->GetDescriptors().empty());
+
+ characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateMeasurementPath().value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_EQ(1U, characteristic->GetDescriptors().size());
+
+ BluetoothGattDescriptor* descriptor = characteristic->GetDescriptors()[0];
+ EXPECT_FALSE(descriptor->IsLocal());
+ EXPECT_EQ(BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid(),
+ descriptor->GetUUID());
+ EXPECT_EQ(descriptor->GetUUID(),
+ service_observer.last_gatt_descriptor_uuid_);
+ EXPECT_EQ(descriptor->GetIdentifier(),
+ service_observer.last_gatt_descriptor_id_);
+
+ // Hide the descriptor.
+ fake_bluetooth_gatt_descriptor_client_->HideDescriptor(
+ dbus::ObjectPath(descriptor->GetIdentifier()));
+ EXPECT_TRUE(characteristic->GetDescriptors().empty());
+ EXPECT_EQ(5, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(1, service_observer.gatt_descriptor_added_count_);
+ EXPECT_EQ(1, service_observer.gatt_descriptor_removed_count_);
+ EXPECT_EQ(0, service_observer.gatt_descriptor_value_changed_count_);
+
+ // Expose the descriptor again.
+ service_observer.last_gatt_descriptor_id_.clear();
+ service_observer.last_gatt_descriptor_uuid_ = BluetoothUUID();
+ fake_bluetooth_gatt_descriptor_client_->ExposeDescriptor(
+ dbus::ObjectPath(characteristic->GetIdentifier()),
+ FakeBluetoothGattDescriptorClient::
+ kClientCharacteristicConfigurationUUID);
+ EXPECT_EQ(6, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(1U, characteristic->GetDescriptors().size());
+ EXPECT_EQ(2, service_observer.gatt_descriptor_added_count_);
+ EXPECT_EQ(1, service_observer.gatt_descriptor_removed_count_);
+ EXPECT_EQ(0, service_observer.gatt_descriptor_value_changed_count_);
+
+ descriptor = characteristic->GetDescriptors()[0];
+ EXPECT_FALSE(descriptor->IsLocal());
+ EXPECT_EQ(BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid(),
+ descriptor->GetUUID());
+ EXPECT_EQ(descriptor->GetUUID(), service_observer.last_gatt_descriptor_uuid_);
+ EXPECT_EQ(descriptor->GetIdentifier(),
+ service_observer.last_gatt_descriptor_id_);
+}
+
+TEST_F(BluetoothGattChromeOSTest, AdapterAddedAfterGattService) {
+ // This unit test tests that all remote GATT objects are created for D-Bus
+ // objects that were already exposed.
+ adapter_ = NULL;
+ ASSERT_FALSE(device::BluetoothAdapterFactory::HasSharedInstanceForTesting());
+
+ // Create the fake D-Bus objects.
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ while (!fake_bluetooth_gatt_characteristic_client_->IsHeartRateVisible())
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(fake_bluetooth_gatt_service_client_->IsHeartRateVisible());
+ ASSERT_TRUE(fake_bluetooth_gatt_characteristic_client_->IsHeartRateVisible());
+
+ // Create the adapter. This should create all the GATT objects.
+ GetAdapter();
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+ EXPECT_EQ(1U, device->GetGattServices().size());
+
+ BluetoothGattService* service = device->GetGattServices()[0];
+ ASSERT_TRUE(service);
+ EXPECT_FALSE(service->IsLocal());
+ EXPECT_TRUE(service->IsPrimary());
+ EXPECT_EQ(
+ BluetoothUUID(FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
+ service->GetUUID());
+ EXPECT_EQ(service, device->GetGattServices()[0]);
+ EXPECT_EQ(service, device->GetGattService(service->GetIdentifier()));
+ EXPECT_FALSE(service->IsLocal());
+ EXPECT_EQ(3U, service->GetCharacteristics().size());
+
+ BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetBodySensorLocationPath().value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_EQ(
+ BluetoothUUID(FakeBluetoothGattCharacteristicClient::
+ kBodySensorLocationUUID),
+ characteristic->GetUUID());
+ EXPECT_FALSE(characteristic->IsLocal());
+ EXPECT_TRUE(characteristic->GetDescriptors().empty());
+
+ characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateControlPointPath().value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_EQ(
+ BluetoothUUID(FakeBluetoothGattCharacteristicClient::
+ kHeartRateControlPointUUID),
+ characteristic->GetUUID());
+ EXPECT_FALSE(characteristic->IsLocal());
+ EXPECT_TRUE(characteristic->GetDescriptors().empty());
+
+ characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateMeasurementPath().value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_EQ(
+ BluetoothUUID(FakeBluetoothGattCharacteristicClient::
+ kHeartRateMeasurementUUID),
+ characteristic->GetUUID());
+ EXPECT_FALSE(characteristic->IsLocal());
+ EXPECT_EQ(1U, characteristic->GetDescriptors().size());
+
+ BluetoothGattDescriptor* descriptor = characteristic->GetDescriptors()[0];
+ ASSERT_TRUE(descriptor);
+ EXPECT_EQ(BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid(),
+ descriptor->GetUUID());
+ EXPECT_FALSE(descriptor->IsLocal());
+}
+
+TEST_F(BluetoothGattChromeOSTest, GattCharacteristicValue) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+
+ // Run the message loop so that the characteristics appear.
+ base::MessageLoop::current()->Run();
+
+ // Issue write request to non-writeable characteristics.
+ service_observer.last_gatt_characteristic_id_.clear();
+ service_observer.last_gatt_characteristic_uuid_ = BluetoothUUID();
+
+ std::vector<uint8> write_value;
+ write_value.push_back(0x01);
+ BluetoothGattCharacteristic* characteristic =
+ service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateMeasurementPath().value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_FALSE(characteristic->IsNotifying());
+ EXPECT_EQ(fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateMeasurementPath().value(),
+ characteristic->GetIdentifier());
+ EXPECT_EQ(kHeartRateMeasurementUUID, characteristic->GetUUID());
+ characteristic->WriteRemoteCharacteristic(
+ write_value,
+ base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_TRUE(service_observer.last_gatt_characteristic_id_.empty());
+ EXPECT_FALSE(service_observer.last_gatt_characteristic_uuid_.IsValid());
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+
+ characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetBodySensorLocationPath().value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_EQ(fake_bluetooth_gatt_characteristic_client_->
+ GetBodySensorLocationPath().value(),
+ characteristic->GetIdentifier());
+ EXPECT_EQ(kBodySensorLocationUUID, characteristic->GetUUID());
+ characteristic->WriteRemoteCharacteristic(
+ write_value,
+ base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_TRUE(service_observer.last_gatt_characteristic_id_.empty());
+ EXPECT_FALSE(service_observer.last_gatt_characteristic_uuid_.IsValid());
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(2, error_callback_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+
+ // Issue write request to writeable characteristic. The "Body Sensor Location"
+ // characteristic does not send notifications and WriteValue does not result
+ // in a CharacteristicValueChanged event, thus no such event should be
+ // received.
+ characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateControlPointPath().value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_EQ(fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateControlPointPath().value(),
+ characteristic->GetIdentifier());
+ EXPECT_EQ(kHeartRateControlPointUUID, characteristic->GetUUID());
+ characteristic->WriteRemoteCharacteristic(
+ write_value,
+ base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_TRUE(service_observer.last_gatt_characteristic_id_.empty());
+ EXPECT_FALSE(service_observer.last_gatt_characteristic_uuid_.IsValid());
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(2, error_callback_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+
+ // Issue a read request. A successful read results in a
+ // CharacteristicValueChanged notification.
+ characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetBodySensorLocationPath().value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_EQ(fake_bluetooth_gatt_characteristic_client_->
+ GetBodySensorLocationPath().value(),
+ characteristic->GetIdentifier());
+ EXPECT_EQ(kBodySensorLocationUUID, characteristic->GetUUID());
+ characteristic->ReadRemoteCharacteristic(
+ base::Bind(&BluetoothGattChromeOSTest::ValueCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+ EXPECT_EQ(2, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(ValuesEqual(characteristic->GetValue(), last_read_value_));
+}
+
+TEST_F(BluetoothGattChromeOSTest, GattCharacteristicProperties) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_TRUE(service->GetCharacteristics().empty());
+
+ // Run the message loop so that the characteristics appear.
+ base::MessageLoop::current()->Run();
+
+ BluetoothGattCharacteristic *characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetBodySensorLocationPath().value());
+ EXPECT_EQ(BluetoothGattCharacteristic::kPropertyRead,
+ characteristic->GetProperties());
+
+ characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateControlPointPath().value());
+ EXPECT_EQ(BluetoothGattCharacteristic::kPropertyWrite,
+ characteristic->GetProperties());
+
+ characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateMeasurementPath().value());
+ EXPECT_EQ(BluetoothGattCharacteristic::kPropertyNotify,
+ characteristic->GetProperties());
+}
+
+TEST_F(BluetoothGattChromeOSTest, GattDescriptorValue) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(0, service_observer.gatt_descriptor_value_changed_count_);
+ EXPECT_TRUE(service->GetCharacteristics().empty());
+
+ // Run the message loop so that the characteristics appear.
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(4, service_observer.gatt_service_changed_count_);
+
+ // Only the Heart Rate Measurement characteristic has a descriptor.
+ BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->
+ GetHeartRateMeasurementPath().value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_EQ(1U, characteristic->GetDescriptors().size());
+
+ BluetoothGattDescriptor* descriptor = characteristic->GetDescriptors()[0];
+ EXPECT_FALSE(descriptor->IsLocal());
+ EXPECT_EQ(BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid(),
+ descriptor->GetUUID());
+
+ std::vector<uint8> desc_value;
+ desc_value.push_back(1);
+ desc_value.push_back(0);
+
+ /* The cached value will be empty until the first read request */
+ EXPECT_FALSE(ValuesEqual(desc_value, descriptor->GetValue()));
+ EXPECT_TRUE(descriptor->GetValue().empty());
+
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(last_read_value_.empty());
+
+ // Read value. GattDescriptorValueChanged event will be sent after a
+ // successful read.
+ descriptor->ReadRemoteDescriptor(
+ base::Bind(&BluetoothGattChromeOSTest::ValueCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_TRUE(ValuesEqual(last_read_value_, descriptor->GetValue()));
+ EXPECT_TRUE(ValuesEqual(desc_value, descriptor->GetValue()));
+ EXPECT_EQ(4, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(1, service_observer.gatt_descriptor_value_changed_count_);
+
+ // Write value. Writes to this descriptor will fail.
+ desc_value[0] = 0x03;
+ descriptor->WriteRemoteDescriptor(
+ desc_value,
+ base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_TRUE(ValuesEqual(last_read_value_, descriptor->GetValue()));
+ EXPECT_FALSE(ValuesEqual(desc_value, descriptor->GetValue()));
+ EXPECT_EQ(4, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(1, service_observer.gatt_descriptor_value_changed_count_);
+
+ // Read new value.
+ descriptor->ReadRemoteDescriptor(
+ base::Bind(&BluetoothGattChromeOSTest::ValueCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_TRUE(ValuesEqual(last_read_value_, descriptor->GetValue()));
+ EXPECT_FALSE(ValuesEqual(desc_value, descriptor->GetValue()));
+ EXPECT_EQ(4, service_observer.gatt_service_changed_count_);
+ EXPECT_EQ(2, service_observer.gatt_descriptor_value_changed_count_);
+}
+
+TEST_F(BluetoothGattChromeOSTest, NotifySessions) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device =
+ adapter_->GetDevice(FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+
+ // Run the message loop so that the characteristics appear.
+ base::MessageLoop::current()->Run();
+
+ BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath()
+ .value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_FALSE(characteristic->IsNotifying());
+ EXPECT_TRUE(update_sessions_.empty());
+
+ // Request to start notifications.
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ // The operation still hasn't completed but we should have received the first
+ // notification.
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(update_sessions_.empty());
+
+ // Send a two more requests, which should get queued.
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(update_sessions_.empty());
+ EXPECT_TRUE(characteristic->IsNotifying());
+
+ // Run the main loop. The initial call should complete. The queued call should
+ // succeed immediately.
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(3, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(3U, update_sessions_.size());
+
+ // Notifications should be getting sent regularly now.
+ base::MessageLoop::current()->Run();
+ EXPECT_GT(service_observer.gatt_characteristic_value_changed_count_, 1);
+
+ // Stop one of the sessions. The session should become inactive but the
+ // characteristic should still be notifying.
+ BluetoothGattNotifySession* session = update_sessions_[0];
+ EXPECT_TRUE(session->IsActive());
+ session->Stop(base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(4, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_FALSE(session->IsActive());
+ EXPECT_EQ(characteristic->GetIdentifier(),
+ session->GetCharacteristicIdentifier());
+ EXPECT_TRUE(characteristic->IsNotifying());
+
+ // Delete another session. Characteristic should still be notifying.
+ update_sessions_.pop_back();
+ EXPECT_EQ(2U, update_sessions_.size());
+ EXPECT_TRUE(characteristic->IsNotifying());
+ EXPECT_FALSE(update_sessions_[0]->IsActive());
+ EXPECT_TRUE(update_sessions_[1]->IsActive());
+
+ // Clear the last session.
+ update_sessions_.clear();
+ EXPECT_TRUE(update_sessions_.empty());
+ EXPECT_FALSE(characteristic->IsNotifying());
+
+ success_callback_count_ = 0;
+ service_observer.gatt_characteristic_value_changed_count_ = 0;
+
+ // Enable notifications again.
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(update_sessions_.empty());
+ EXPECT_TRUE(characteristic->IsNotifying());
+
+ // Run the message loop. Notifications should begin.
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(1U, update_sessions_.size());
+ EXPECT_TRUE(update_sessions_[0]->IsActive());
+ EXPECT_TRUE(characteristic->IsNotifying());
+
+ // Check that notifications are happening.
+ base::MessageLoop::current()->Run();
+ EXPECT_GT(service_observer.gatt_characteristic_value_changed_count_, 1);
+
+ // Request another session. This should return immediately.
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(2U, update_sessions_.size());
+ EXPECT_TRUE(update_sessions_[0]->IsActive());
+ EXPECT_TRUE(update_sessions_[1]->IsActive());
+ EXPECT_TRUE(characteristic->IsNotifying());
+
+ // Hide the characteristic. The sessions should become inactive.
+ fake_bluetooth_gatt_characteristic_client_->HideHeartRateCharacteristics();
+ EXPECT_EQ(2U, update_sessions_.size());
+ EXPECT_FALSE(update_sessions_[0]->IsActive());
+ EXPECT_FALSE(update_sessions_[1]->IsActive());
+}
+
+TEST_F(BluetoothGattChromeOSTest, NotifySessionsMadeInactive) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device =
+ adapter_->GetDevice(FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+
+ // Run the message loop so that the characteristics appear.
+ base::MessageLoop::current()->Run();
+
+ BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath()
+ .value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_FALSE(characteristic->IsNotifying());
+ EXPECT_TRUE(update_sessions_.empty());
+
+ // Send several requests to start notifications.
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ // The operation still hasn't completed but we should have received the first
+ // notification.
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(characteristic->IsNotifying());
+ EXPECT_TRUE(update_sessions_.empty());
+
+ // Run the main loop. The initial call should complete. The queued calls
+ // should succeed immediately.
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(4, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(characteristic->IsNotifying());
+ EXPECT_EQ(4U, update_sessions_.size());
+
+ for (int i = 0; i < 4; i++)
+ EXPECT_TRUE(update_sessions_[0]->IsActive());
+
+ // Stop notifications directly through the client. The sessions should get
+ // marked as inactive.
+ fake_bluetooth_gatt_characteristic_client_->StopNotify(
+ fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath(),
+ base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(5, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_FALSE(characteristic->IsNotifying());
+ EXPECT_EQ(4U, update_sessions_.size());
+
+ for (int i = 0; i < 4; i++)
+ EXPECT_FALSE(update_sessions_[0]->IsActive());
+
+ // It should be possible to restart notifications and the call should reset
+ // the session count and make a request through the client.
+ update_sessions_.clear();
+ success_callback_count_ = 0;
+ service_observer.gatt_characteristic_value_changed_count_ = 0;
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(characteristic->IsNotifying());
+ EXPECT_TRUE(update_sessions_.empty());
+
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(characteristic->IsNotifying());
+ EXPECT_EQ(1U, update_sessions_.size());
+ EXPECT_TRUE(update_sessions_[0]->IsActive());
+}
+
+} // namespace chromeos
diff --git a/chromium/device/bluetooth/bluetooth_gatt_connection.cc b/chromium/device/bluetooth/bluetooth_gatt_connection.cc
new file mode 100644
index 00000000000..721391e8d1a
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_connection.cc
@@ -0,0 +1,15 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_gatt_connection.h"
+
+namespace device {
+
+BluetoothGattConnection::BluetoothGattConnection() {
+}
+
+BluetoothGattConnection::~BluetoothGattConnection() {
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_gatt_connection.h b/chromium/device/bluetooth/bluetooth_gatt_connection.h
new file mode 100644
index 00000000000..da630434879
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_connection.h
@@ -0,0 +1,50 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_GATT_CONNECTION_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_GATT_CONNECTION_H_
+
+#include <string>
+
+#include "base/callback.h"
+
+namespace device {
+
+// BluetoothGattConnection represents a GATT connection to a Bluetooth device
+// that has GATT services. Instances are obtained from a BluetoothDevice,
+// and the connection is kept alive as long as there is at least one
+// active BluetoothGattConnection object. BluetoothGattConnection objects
+// automatically update themselves, when the connection is terminated by the
+// operating system (e.g. due to user action).
+class BluetoothGattConnection {
+ public:
+ // Destructor automatically closes this GATT connection. If this is the last
+ // remaining GATT connection and this results in a call to the OS, that call
+ // may not always succeed. Users can make an explicit call to
+ // BluetoothGattConnection::Close to make sure that they are notified of
+ // a possible error via the callback.
+ virtual ~BluetoothGattConnection();
+
+ // Returns the Bluetooth address of the device that this connection is open
+ // to.
+ virtual std::string GetDeviceAddress() const = 0;
+
+ // Returns true if this connection is open.
+ virtual bool IsConnected() = 0;
+
+ // Disconnects this GATT connection and calls |callback| upon completion.
+ // After a successful invocation, the device may still remain connected due to
+ // other GATT connections.
+ virtual void Disconnect(const base::Closure& callback) = 0;
+
+ protected:
+ BluetoothGattConnection();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BluetoothGattConnection);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_GATT_CONNECTION_H_
diff --git a/chromium/device/bluetooth/bluetooth_gatt_connection_chromeos.cc b/chromium/device/bluetooth/bluetooth_gatt_connection_chromeos.cc
new file mode 100644
index 00000000000..94b7555170b
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_connection_chromeos.cc
@@ -0,0 +1,107 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_gatt_connection_chromeos.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_device.h"
+
+namespace chromeos {
+
+BluetoothGattConnectionChromeOS::BluetoothGattConnectionChromeOS(
+ scoped_refptr<device::BluetoothAdapter> adapter,
+ const std::string& device_address,
+ const dbus::ObjectPath& object_path)
+ : connected_(true),
+ adapter_(adapter),
+ device_address_(device_address),
+ object_path_(object_path) {
+ DCHECK(adapter_.get());
+ DCHECK(!device_address_.empty());
+ DCHECK(object_path_.IsValid());
+
+ DBusThreadManager::Get()->GetBluetoothDeviceClient()->AddObserver(this);
+}
+
+BluetoothGattConnectionChromeOS::~BluetoothGattConnectionChromeOS() {
+ DBusThreadManager::Get()->GetBluetoothDeviceClient()->RemoveObserver(this);
+ Disconnect(base::Bind(&base::DoNothing));
+}
+
+std::string BluetoothGattConnectionChromeOS::GetDeviceAddress() const {
+ return device_address_;
+}
+
+bool BluetoothGattConnectionChromeOS::IsConnected() {
+ // Lazily determine the activity state of the connection. If already
+ // marked as inactive, then return false. Otherwise, explicitly mark
+ // |connected_| as false if the device is removed or disconnected. We do this,
+ // so that if this method is called during a call to DeviceRemoved or
+ // DeviceChanged somewhere else, it returns the correct status.
+ if (!connected_)
+ return false;
+
+ BluetoothDeviceClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothDeviceClient()->
+ GetProperties(object_path_);
+ if (!properties || !properties->connected.value())
+ connected_ = false;
+
+ return connected_;
+}
+
+void BluetoothGattConnectionChromeOS::Disconnect(
+ const base::Closure& callback) {
+ if (!connected_) {
+ VLOG(1) << "Connection already inactive.";
+ callback.Run();
+ return;
+ }
+
+ // TODO(armansito): There isn't currently a good way to manage the ownership
+ // of a connection between Chrome and bluetoothd plugins/profiles. Until
+ // a proper reference count is kept by bluetoothd, we might unwittingly kill
+ // a connection that is managed by the daemon (e.g. HoG). For now, just return
+ // success to indicate that this BluetoothGattConnection is no longer active,
+ // even though the underlying connection won't actually be disconnected. This
+ // technically doesn't violate the contract put forth by this API.
+ connected_ = false;
+ callback.Run();
+}
+
+void BluetoothGattConnectionChromeOS::DeviceRemoved(
+ const dbus::ObjectPath& object_path) {
+ if (object_path != object_path_)
+ return;
+
+ connected_ = false;
+}
+
+void BluetoothGattConnectionChromeOS::DevicePropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) {
+ if (object_path != object_path_)
+ return;
+
+ if (!connected_)
+ return;
+
+ BluetoothDeviceClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothDeviceClient()->
+ GetProperties(object_path_);
+
+ if (!properties) {
+ connected_ = false;
+ return;
+ }
+
+ if (property_name == properties->connected.name() &&
+ !properties->connected.value())
+ connected_ = false;
+}
+
+} // namespace chromeos
diff --git a/chromium/device/bluetooth/bluetooth_gatt_connection_chromeos.h b/chromium/device/bluetooth/bluetooth_gatt_connection_chromeos.h
new file mode 100644
index 00000000000..beade19d17b
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_connection_chromeos.h
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_GATT_CONNECTION_CHROMEOS_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_GATT_CONNECTION_CHROMEOS_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "chromeos/dbus/bluetooth_device_client.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_gatt_connection.h"
+
+namespace device {
+
+class BluetoothAdapter;
+
+} // namespace device
+
+namespace chromeos {
+
+// BluetoothGattConnectionChromeOS implements BluetoothGattConnection for the
+// Chrome OS platform.
+class BluetoothGattConnectionChromeOS
+ : public device::BluetoothGattConnection,
+ public BluetoothDeviceClient::Observer {
+ public:
+ explicit BluetoothGattConnectionChromeOS(
+ scoped_refptr<device::BluetoothAdapter> adapter,
+ const std::string& device_address,
+ const dbus::ObjectPath& object_path);
+ virtual ~BluetoothGattConnectionChromeOS();
+
+ // BluetoothGattConnection overrides.
+ virtual std::string GetDeviceAddress() const OVERRIDE;
+ virtual bool IsConnected() OVERRIDE;
+ virtual void Disconnect(const base::Closure& callback) OVERRIDE;
+
+ private:
+
+ // chromeos::BluetoothDeviceClient::Observer overrides.
+ virtual void DeviceRemoved(const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void DevicePropertyChanged(const dbus::ObjectPath& object_path,
+ const std::string& property_name) OVERRIDE;
+
+ // True, if the connection is currently active.
+ bool connected_;
+
+ // The Bluetooth adapter that this connection is associated with.
+ scoped_refptr<device::BluetoothAdapter> adapter_;
+
+ // Bluetooth address of the underlying device.
+ std::string device_address_;
+
+ // D-Bus object path of the underlying device. This is used to filter observer
+ // events.
+ dbus::ObjectPath object_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothGattConnectionChromeOS);
+};
+
+} // namespace chromeos
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_GATT_CONNECTION_CHROMEOS_H_
diff --git a/chromium/device/bluetooth/bluetooth_gatt_descriptor.cc b/chromium/device/bluetooth/bluetooth_gatt_descriptor.cc
new file mode 100644
index 00000000000..5d3673b5ea3
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_descriptor.cc
@@ -0,0 +1,91 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_gatt_descriptor.h"
+
+#include <vector>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace device {
+namespace {
+
+struct UUIDs {
+ UUIDs() : uuids_(MakeUUIDVector()) {}
+
+ const std::vector<BluetoothUUID> uuids_;
+
+ private:
+ static std::vector<BluetoothUUID> MakeUUIDVector() {
+ std::vector<BluetoothUUID> uuids;
+ static const char* const strings[] = {
+ "0x2900", "0x2901", "0x2902", "0x2903", "0x2904", "0x2905"
+ };
+
+ for (size_t i = 0; i < arraysize(strings); ++i)
+ uuids.push_back(BluetoothUUID(strings[i]));
+
+ return uuids;
+ }
+};
+
+base::LazyInstance<const UUIDs>::Leaky g_uuids = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+// static
+const BluetoothUUID&
+BluetoothGattDescriptor::CharacteristicExtendedPropertiesUuid() {
+ return g_uuids.Get().uuids_[0];
+}
+
+// static
+const BluetoothUUID&
+BluetoothGattDescriptor::CharacteristicUserDescriptionUuid() {
+ return g_uuids.Get().uuids_[1];
+}
+
+// static
+const BluetoothUUID&
+BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid() {
+ return g_uuids.Get().uuids_[2];
+}
+
+// static
+const BluetoothUUID&
+BluetoothGattDescriptor::ServerCharacteristicConfigurationUuid() {
+ return g_uuids.Get().uuids_[3];
+}
+
+// static
+const BluetoothUUID&
+BluetoothGattDescriptor::CharacteristicPresentationFormatUuid() {
+ return g_uuids.Get().uuids_[4];
+}
+
+// static
+const BluetoothUUID&
+BluetoothGattDescriptor::CharacteristicAggregateFormatUuid() {
+ return g_uuids.Get().uuids_[5];
+}
+
+BluetoothGattDescriptor::BluetoothGattDescriptor() {
+}
+
+BluetoothGattDescriptor::~BluetoothGattDescriptor() {
+}
+
+// static
+BluetoothGattDescriptor* BluetoothGattDescriptor::Create(
+ const BluetoothUUID& uuid,
+ const std::vector<uint8>& value,
+ BluetoothGattCharacteristic::Permissions permissions) {
+ LOG(ERROR) << "Creating local GATT characteristic descriptors currently not "
+ << "supported.";
+ return NULL;
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_gatt_descriptor.h b/chromium/device/bluetooth/bluetooth_gatt_descriptor.h
new file mode 100644
index 00000000000..e98ef29f754
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_descriptor.h
@@ -0,0 +1,188 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_GATT_DESCRIPTOR_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_GATT_DESCRIPTOR_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace device {
+
+// BluetoothGattDescriptor represents a local or remote GATT characteristic
+// descriptor. A GATT characteristic descriptor provides further information
+// about a characteristic's value. They can be used to describe the
+// characteristic's features or to control certain behaviors.
+class BluetoothGattDescriptor {
+ public:
+ // The Bluetooth Specification declares several predefined descriptors that
+ // profiles can use. The following are definitions for the list of UUIDs
+ // and descriptions of the characteristic descriptors that they represent.
+ // Possible values for and further information on each descriptor can be found
+ // in Core v4.0, Volume 3, Part G, Section 3.3.3. All of these desciptors are
+ // optional and may not be present for a given characteristic.
+
+ // The "Characteristic Extended Properties" descriptor. This defines
+ // additional "Characteristic Properties" which cannot fit into the allocated
+ // single octet property field of a characteristic. The value is a bit field
+ // and the two predefined bits, as per Bluetooth Core Specification v4.0, are:
+ //
+ // - Reliable Write: 0x0001
+ // - Writable Auxiliaries: 0x0002
+ //
+ static const BluetoothUUID& CharacteristicExtendedPropertiesUuid();
+
+ // The "Characteristic User Description" descriptor defines a UTF-8 string of
+ // variable size that is a user textual description of the associated
+ // characteristic's value. There can be only one instance of this descriptor
+ // per characteristic. This descriptor can be written to if the "Writable
+ // Auxiliaries" bit of the Characteristic Properties (via the "Characteristic
+ // Extended Properties" descriptor) has been set.
+ static const BluetoothUUID& CharacteristicUserDescriptionUuid();
+
+ // The "Client Characteristic Configuration" descriptor defines how the
+ // characteristic may be configured by a specific client. A server-side
+ // instance of this descriptor exists for each client that has bonded with
+ // the server and the value can be read and written by that client only. As
+ // of Core v4.0, this descriptor is used by clients to set up notifications
+ // and indications from a characteristic. The value is a bit field and the
+ // predefined bits are:
+ //
+ // - Default: 0x0000
+ // - Notification: 0x0001
+ // - Indication: 0x0002
+ //
+ static const BluetoothUUID& ClientCharacteristicConfigurationUuid();
+
+ // The "Server Characteristic Configuration" descriptor defines how the
+ // characteristic may be configured for the server. There is one instance
+ // of this descriptor for all clients and setting the value of this descriptor
+ // affects its configuration for all clients. As of Core v4.0, this descriptor
+ // is used to set up the server to broadcast the characteristic value if
+ // advertising resources are available. The value is a bit field and the
+ // predefined bits are:
+ //
+ // - Default: 0x0000
+ // - Broadcast: 0x0001
+ //
+ static const BluetoothUUID& ServerCharacteristicConfigurationUuid();
+
+ // The "Characteristic Presentation Format" declaration defines the format of
+ // the Characteristic Value. The value is composed of 7 octets which are
+ // divided into groups that represent different semantic meanings. For a
+ // detailed description of how the value of this descriptor should be
+ // interpreted, refer to Core v4.0, Volume 3, Part G, Section 3.3.3.5. If more
+ // than one declaration of this descriptor exists for a characteristic, then a
+ // "Characteristic Aggregate Format" descriptor must also exist for that
+ // characteristic.
+ static const BluetoothUUID& CharacteristicPresentationFormatUuid();
+
+ // The "Characteristic Aggregate Format" descriptor defines the format of an
+ // aggragated characteristic value. In GATT's underlying protocol, ATT, each
+ // attribute is identified by a handle that is unique for the hosting server.
+ // Multiple characteristics can share the same instance(s) of a
+ // "Characteristic Presentation Format" descriptor. The value of the
+ // "Characteristic Aggregate Format" descriptor contains a list of handles
+ // that each refer to a "Characteristic Presentation Format" descriptor that
+ // is used by that characteristic. Hence, exactly one instance of this
+ // descriptor must exist if more than one "Characteristic Presentation Format"
+ // descriptors exist for a characteristic.
+ //
+ // Applications that are using the device::Bluetooth API do not have access to
+ // the underlying handles and shouldn't use this descriptor to determine which
+ // "Characteristic Presentation Format" desciptors belong to a characteristic.
+ // The API will construct a BluetoothGattDescriptor object for each instance
+ // of "Characteristic Presentation Format" descriptor per instance of
+ // BluetoothGattCharacteristic that represents a remote characteristic.
+ // Similarly for local characteristics, implementations DO NOT need to create
+ // an instance of BluetoothGattDescriptor for this descriptor as this will be
+ // handled by the subsystem.
+ static const BluetoothUUID& CharacteristicAggregateFormatUuid();
+
+ // The ErrorCallback is used by methods to asynchronously report errors.
+ typedef base::Closure ErrorCallback;
+
+ // The ValueCallback is used to return the value of a remote characteristic
+ // descriptor upon a read request.
+ typedef base::Callback<void(const std::vector<uint8>&)> ValueCallback;
+
+ // Constructs a BluetoothGattDescriptor that can be associated with a local
+ // GATT characteristic when the adapter is in the peripheral role. To
+ // associate the returned descriptor with a characteristic, add it to a local
+ // characteristic by calling BluetoothGattCharacteristic::AddDescriptor.
+ //
+ // This method constructs a characteristic descriptor with UUID |uuid| and the
+ // initial cached value |value|. |value| will be cached and returned for read
+ // requests and automatically modified for write requests by default, unless
+ // an instance of BluetoothGattService::Delegate has been provided to the
+ // associated BluetoothGattService instance, in which case the delegate will
+ // handle the read and write requests.
+ //
+ // Currently, only custom UUIDs, |kCharacteristicDescriptionUuid|, and
+ // |kCharacteristicPresentationFormat| are supported for locally hosted
+ // descriptors. This method will return NULL if |uuid| is any one of the
+ // unsupported predefined descriptor UUIDs.
+ static BluetoothGattDescriptor* Create(
+ const BluetoothUUID& uuid,
+ const std::vector<uint8>& value,
+ BluetoothGattCharacteristic::Permissions permissions);
+
+ // Identifier used to uniquely identify a GATT descriptorobject. This is
+ // different from the descriptor UUID: while multiple descriptors with the
+ // same UUID can exist on a Bluetooth device, the identifier returned from
+ // this method is unique among all descriptors of a device. The contents of
+ // the identifier are platform specific.
+ virtual std::string GetIdentifier() const = 0;
+
+ // The Bluetooth-specific UUID of the characteristic descriptor.
+ virtual BluetoothUUID GetUUID() const = 0;
+
+ // Returns true, if this characteristic descriptor is hosted locally. If
+ // false, then this instance represents a remote descriptor.
+ virtual bool IsLocal() const = 0;
+
+ // Returns the value of the descriptor. For remote descriptors, this is the
+ // most recently cached value of the remote descriptor. For local descriptors
+ // this is the most recently updated value or the value retrieved from the
+ // delegate.
+ virtual const std::vector<uint8>& GetValue() const = 0;
+
+ // Returns a pointer to the GATT characteristic that this characteristic
+ // descriptor belongs to.
+ virtual BluetoothGattCharacteristic* GetCharacteristic() const = 0;
+
+ // Returns the bitmask of characteristic descriptor attribute permissions.
+ virtual BluetoothGattCharacteristic::Permissions GetPermissions() const = 0;
+
+ // Sends a read request to a remote characteristic descriptor to read its
+ // value. |callback| is called to return the read value on success and
+ // |error_callback| is called for failures.
+ virtual void ReadRemoteDescriptor(const ValueCallback& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // Sends a write request to a remote characteristic descriptor, to modify the
+ // value of the descriptor with the new value |new_value|. |callback| is
+ // called to signal success and |error_callback| for failures. This method
+ // only applies to remote descriptors and will fail for those that are locally
+ // hosted.
+ virtual void WriteRemoteDescriptor(
+ const std::vector<uint8>& new_value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ protected:
+ BluetoothGattDescriptor();
+ virtual ~BluetoothGattDescriptor();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BluetoothGattDescriptor);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_GATT_DESCRIPTOR_H_
diff --git a/chromium/device/bluetooth/bluetooth_gatt_notify_session.cc b/chromium/device/bluetooth/bluetooth_gatt_notify_session.cc
new file mode 100644
index 00000000000..3c9a2423880
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_notify_session.cc
@@ -0,0 +1,15 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_gatt_notify_session.h"
+
+namespace device {
+
+BluetoothGattNotifySession::BluetoothGattNotifySession() {
+}
+
+BluetoothGattNotifySession::~BluetoothGattNotifySession() {
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_gatt_notify_session.h b/chromium/device/bluetooth/bluetooth_gatt_notify_session.h
new file mode 100644
index 00000000000..9837e3a3cc3
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_notify_session.h
@@ -0,0 +1,44 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_GATT_NOTIFY_SESSION_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_GATT_NOTIFY_SESSION_H_
+
+#include <string>
+
+#include "base/callback.h"
+
+namespace device {
+
+// A BluetoothGattNotifySession represents an active session for listening
+// to value updates from GATT characteristics that support notifications and/or
+// indications. Instances are obtained by calling
+// BluetoothGattCharacteristic::StartNotifySession.
+class BluetoothGattNotifySession {
+ public:
+ // Destructor autmatically stops this session.
+ virtual ~BluetoothGattNotifySession();
+
+ // Returns the identifier of the associated characteristic.
+ virtual std::string GetCharacteristicIdentifier() const = 0;
+
+ // Returns true if this session is active.
+ virtual bool IsActive() = 0;
+
+ // Stops this session and calls |callback| upon completion. This won't
+ // necessarily stop value updates from the characteristic -- since updates
+ // are shared among BluetoothGattNotifySession instances -- but it will
+ // terminate this session.
+ virtual void Stop(const base::Closure& callback) = 0;
+
+ protected:
+ BluetoothGattNotifySession();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BluetoothGattNotifySession);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_GATT_NOTIFY_SESSION_H_
diff --git a/chromium/device/bluetooth/bluetooth_gatt_notify_session_chromeos.cc b/chromium/device/bluetooth/bluetooth_gatt_notify_session_chromeos.cc
new file mode 100644
index 00000000000..ba7b84314e1
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_notify_session_chromeos.cc
@@ -0,0 +1,132 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_gatt_notify_session_chromeos.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_gatt_service.h"
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h"
+
+namespace chromeos {
+
+BluetoothGattNotifySessionChromeOS::BluetoothGattNotifySessionChromeOS(
+ scoped_refptr<device::BluetoothAdapter> adapter,
+ const std::string& device_address,
+ const std::string& service_identifier,
+ const std::string& characteristic_identifier,
+ const dbus::ObjectPath& characteristic_path)
+ : active_(true),
+ adapter_(adapter),
+ device_address_(device_address),
+ service_id_(service_identifier),
+ characteristic_id_(characteristic_identifier),
+ object_path_(characteristic_path) {
+ DCHECK(adapter_.get());
+ DCHECK(!device_address_.empty());
+ DCHECK(!service_id_.empty());
+ DCHECK(!characteristic_id_.empty());
+ DCHECK(object_path_.IsValid());
+
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->AddObserver(
+ this);
+}
+
+BluetoothGattNotifySessionChromeOS::~BluetoothGattNotifySessionChromeOS() {
+ DBusThreadManager::Get()
+ ->GetBluetoothGattCharacteristicClient()
+ ->RemoveObserver(this);
+ Stop(base::Bind(&base::DoNothing));
+}
+
+std::string BluetoothGattNotifySessionChromeOS::GetCharacteristicIdentifier()
+ const {
+ return characteristic_id_;
+}
+
+bool BluetoothGattNotifySessionChromeOS::IsActive() {
+ // Determine if the session is active. If |active_| is false, then it's
+ // been explicitly marked, so return false.
+ if (!active_)
+ return false;
+
+ // The fact that |active_| is true doesn't mean that the session is
+ // actually active, since the characteristic might have stopped sending
+ // notifications yet this method was called before we processed the
+ // observer event (e.g. because somebody else called this method in their
+ // BluetoothGattCharacteristicClient::Observer implementation, which was
+ // called before ours). Check the client to see if notifications are still
+ // being sent.
+ BluetoothGattCharacteristicClient::Properties* properties =
+ DBusThreadManager::Get()
+ ->GetBluetoothGattCharacteristicClient()
+ ->GetProperties(object_path_);
+ if (!properties || !properties->notifying.value())
+ active_ = false;
+
+ return active_;
+}
+
+void BluetoothGattNotifySessionChromeOS::Stop(const base::Closure& callback) {
+ if (!active_) {
+ VLOG(1) << "Notify session already inactive.";
+ callback.Run();
+ return;
+ }
+
+ // Mark this session as inactive no matter what.
+ active_ = false;
+
+ device::BluetoothDevice* device = adapter_->GetDevice(device_address_);
+ if (!device)
+ return;
+
+ device::BluetoothGattService* service = device->GetGattService(service_id_);
+ if (!service)
+ return;
+
+ BluetoothRemoteGattCharacteristicChromeOS* chrc =
+ static_cast<BluetoothRemoteGattCharacteristicChromeOS*>(
+ service->GetCharacteristic(characteristic_id_));
+ if (!chrc)
+ return;
+
+ chrc->RemoveNotifySession(callback);
+}
+
+void BluetoothGattNotifySessionChromeOS::GattCharacteristicRemoved(
+ const dbus::ObjectPath& object_path) {
+ if (object_path != object_path_)
+ return;
+
+ active_ = false;
+}
+
+void BluetoothGattNotifySessionChromeOS::GattCharacteristicPropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) {
+ if (object_path != object_path_)
+ return;
+
+ if (!active_)
+ return;
+
+ BluetoothGattCharacteristicClient::Properties* properties =
+ DBusThreadManager::Get()
+ ->GetBluetoothGattCharacteristicClient()
+ ->GetProperties(object_path_);
+ if (!properties) {
+ active_ = false;
+ return;
+ }
+
+ if (property_name == properties->notifying.name() &&
+ !properties->notifying.value())
+ active_ = false;
+}
+
+} // namespace chromeos
diff --git a/chromium/device/bluetooth/bluetooth_gatt_notify_session_chromeos.h b/chromium/device/bluetooth/bluetooth_gatt_notify_session_chromeos.h
new file mode 100644
index 00000000000..1202fd8e7db
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_notify_session_chromeos.h
@@ -0,0 +1,78 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_GATT_NOTIFY_SESSION_CHROMEOS_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_GATT_NOTIFY_SESSION_CHROMEOS_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "chromeos/dbus/bluetooth_gatt_characteristic_client.h"
+#include "device/bluetooth/bluetooth_gatt_notify_session.h"
+
+namespace device {
+
+class BluetoothAdapter;
+
+} // namespace device
+
+namespace chromeos {
+
+class BluetoothRemoteGattCharacteristicChromeOS;
+
+// BluetoothGattNotifySessionChromeOS implements
+// BluetoothGattNotifySession for the Chrome OS platform.
+class BluetoothGattNotifySessionChromeOS
+ : public device::BluetoothGattNotifySession,
+ public BluetoothGattCharacteristicClient::Observer {
+ public:
+ virtual ~BluetoothGattNotifySessionChromeOS();
+
+ // BluetoothGattNotifySession overrides.
+ virtual std::string GetCharacteristicIdentifier() const OVERRIDE;
+ virtual bool IsActive() OVERRIDE;
+ virtual void Stop(const base::Closure& callback) OVERRIDE;
+
+ private:
+ friend class BluetoothRemoteGattCharacteristicChromeOS;
+
+ explicit BluetoothGattNotifySessionChromeOS(
+ scoped_refptr<device::BluetoothAdapter> adapter,
+ const std::string& device_address,
+ const std::string& service_identifier,
+ const std::string& characteristic_identifier,
+ const dbus::ObjectPath& characteristic_path);
+
+ // BluetoothGattCharacteristicClient::Observer overrides.
+ virtual void GattCharacteristicRemoved(
+ const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void GattCharacteristicPropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) OVERRIDE;
+
+ // True, if this session is currently active.
+ bool active_;
+
+ // The Bluetooth adapter that this session is associated with.
+ scoped_refptr<device::BluetoothAdapter> adapter_;
+
+ // The Bluetooth address of the device hosting the characteristic.
+ std::string device_address_;
+
+ // The GATT service that the characteristic belongs to.
+ std::string service_id_;
+
+ // Identifier of the associated characteristic.
+ std::string characteristic_id_;
+
+ // D-Bus object path of the associated characteristic. This is used to filter
+ // observer events.
+ dbus::ObjectPath object_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothGattNotifySessionChromeOS);
+};
+
+} // namespace chromeos
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_GATT_NOTIFY_SESSION_CHROMEOS_H_
diff --git a/chromium/device/bluetooth/bluetooth_gatt_service.cc b/chromium/device/bluetooth/bluetooth_gatt_service.cc
new file mode 100644
index 00000000000..461a63cf784
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_service.cc
@@ -0,0 +1,26 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_gatt_service.h"
+
+#include "base/logging.h"
+
+namespace device {
+
+BluetoothGattService::BluetoothGattService() {
+}
+
+BluetoothGattService::~BluetoothGattService() {
+}
+
+// static
+BluetoothGattService* BluetoothGattService::Create(
+ const BluetoothUUID& uuid,
+ bool is_primary,
+ Delegate* delegate) {
+ LOG(ERROR) << "Creating local GATT services currently not supported.";
+ return NULL;
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_gatt_service.h b/chromium/device/bluetooth/bluetooth_gatt_service.h
new file mode 100644
index 00000000000..16f4ae5f9e1
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_gatt_service.h
@@ -0,0 +1,307 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_GATT_SERVICE_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_GATT_SERVICE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace device {
+
+class BluetoothDevice;
+class BluetoothGattCharacteristic;
+class BluetoothGattDescriptor;
+
+// BluetoothGattService represents a local or remote GATT service. A GATT
+// service is hosted by a peripheral and represents a collection of data in
+// the form of GATT characteristics and a set of included GATT services if this
+// service is what is called "a primary service".
+//
+// Instances of the BluetoothGattService class are used for two functions:
+// 1. To represent GATT attribute hierarchies that have been received from a
+// remote Bluetooth GATT peripheral. Such BluetoothGattService instances
+// are constructed and owned by a BluetoothDevice.
+//
+// 2. To represent a locally hosted GATT attribute hierarchy when the local
+// adapter is used in the "peripheral" role. Such instances are meant to be
+// constructed directly and registered. Once registered, a GATT attribute
+// hierarchy will be visible to remote devices in the "central" role.
+class BluetoothGattService {
+ public:
+ // The Delegate class is used to send certain events that need to be handled
+ // when the device is in peripheral mode. The delegate handles read and write
+ // requests that are issued from remote clients.
+ class Delegate {
+ public:
+ // Callbacks used for communicating GATT request responses.
+ typedef base::Callback<void(const std::vector<uint8>)> ValueCallback;
+ typedef base::Closure ErrorCallback;
+
+ // Called when a remote device in the central role requests to read the
+ // value of the characteristic |characteristic| starting at offset |offset|.
+ // This method is only called if the characteristic was specified as
+ // readable and any authentication and authorization challanges were
+ // satisfied by the remote device.
+ //
+ // To respond to the request with success and return the requested value,
+ // the delegate must invoke |callback| with the value. Doing so will
+ // automatically update the value property of |characteristic|. To respond
+ // to the request with failure (e.g. if an invalid offset was given),
+ // delegates must invoke |error_callback|. If neither callback parameter is
+ // invoked, the request will time out and result in an error. Therefore,
+ // delegates MUST invoke either |callback| or |error_callback|.
+ virtual void OnCharacteristicReadRequest(
+ const BluetoothGattService* service,
+ const BluetoothGattCharacteristic* characteristic,
+ int offset,
+ const ValueCallback& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // Called when a remote device in the central role requests to write the
+ // value of the characteristic |characteristic| starting at offset |offset|.
+ // This method is only called if the characteristic was specified as
+ // writeable and any authentication and authorization challanges were
+ // satisfied by the remote device.
+ //
+ // To respond to the request with success the delegate must invoke
+ // |callback| with the new value of the characteristic. Doing so will
+ // automatically update the value property of |characteristic|. To respond
+ // to the request with failure (e.g. if an invalid offset was given),
+ // delegates must invoke |error_callback|. If neither callback parameter is
+ // invoked, the request will time out and result in an error. Therefore,
+ // delegates MUST invoke either |callback| or |error_callback|.
+ virtual void OnCharacteristicWriteRequest(
+ const BluetoothGattService* service,
+ const BluetoothGattCharacteristic* characteristic,
+ const std::vector<uint8>& value,
+ int offset,
+ const ValueCallback& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // Called when a remote device in the central role requests to read the
+ // value of the descriptor |descriptor| starting at offset |offset|.
+ // This method is only called if the characteristic was specified as
+ // readable and any authentication and authorization challanges were
+ // satisfied by the remote device.
+ //
+ // To respond to the request with success and return the requested value,
+ // the delegate must invoke |callback| with the value. Doing so will
+ // automatically update the value property of |descriptor|. To respond
+ // to the request with failure (e.g. if an invalid offset was given),
+ // delegates must invoke |error_callback|. If neither callback parameter is
+ // invoked, the request will time out and result in an error. Therefore,
+ // delegates MUST invoke either |callback| or |error_callback|.
+ virtual void OnDescriptorReadRequest(
+ const BluetoothGattService* service,
+ const BluetoothGattDescriptor* descriptor,
+ int offset,
+ const ValueCallback& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // Called when a remote device in the central role requests to write the
+ // value of the descriptor |descriptor| starting at offset |offset|.
+ // This method is only called if the characteristic was specified as
+ // writeable and any authentication and authorization challanges were
+ // satisfied by the remote device.
+ //
+ // To respond to the request with success the delegate must invoke
+ // |callback| with the new value of the descriptor. Doing so will
+ // automatically update the value property of |descriptor|. To respond
+ // to the request with failure (e.g. if an invalid offset was given),
+ // delegates must invoke |error_callback|. If neither callback parameter is
+ // invoked, the request will time out and result in an error. Therefore,
+ // delegates MUST invoke either |callback| or |error_callback|.
+ virtual void OnDescriptorWriteRequest(
+ const BluetoothGattService* service,
+ const BluetoothGattDescriptor* descriptor,
+ const std::vector<uint8>& value,
+ int offset,
+ const ValueCallback& callback,
+ const ErrorCallback& error_callback) = 0;
+ };
+
+ // Interface for observing changes from a BluetoothGattService. Properties
+ // of remote services are received asynchronously. The Observer interface can
+ // be used to be notified when the initial values of a service are received
+ // as well as when successive changes occur during its life cycle.
+ class Observer {
+ public:
+ // Called when properties of the remote GATT service |service| have changed.
+ // This will get called for properties such as UUID, as well as for changes
+ // to the list of known characteristics and included services. Observers
+ // should read all GATT characteristic and descriptors objects and do any
+ // necessary set up required for a changed service. This method may be
+ // called several times, especially when the service is discovered for the
+ // first time. It will be called for each characteristic and characteristic
+ // descriptor that is discovered or removed. Hence this method should be
+ // used to check whether or not all characteristics of a service have been
+ // discovered that correspond to the profile implemented by the Observer.
+ virtual void GattServiceChanged(BluetoothGattService* service) {}
+
+ // Called when the remote GATT characteristic |characteristic| belonging to
+ // GATT service |service| has been discovered. Use this to issue any initial
+ // read/write requests to the characteristic but don't cache the pointer as
+ // it may become invalid. Instead, use the specially assigned identifier
+ // to obtain a characteristic and cache that identifier as necessary, as it
+ // can be used to retrieve the characteristic from its GATT service. The
+ // number of characteristics with the same UUID belonging to a service
+ // depends on the particular profile the remote device implements, hence the
+ // client of a GATT based profile will usually operate on the whole set of
+ // characteristics and not just one.
+ //
+ // This method will always be followed by a call to GattServiceChanged,
+ // which can be used by observers to get all the characteristics of a
+ // service and perform the necessary updates. GattCharacteristicAdded exists
+ // mostly for convenience.
+ virtual void GattCharacteristicAdded(
+ BluetoothGattService* service,
+ BluetoothGattCharacteristic* characteristic) {}
+
+ // Called when a GATT characteristic |characteristic| belonging to GATT
+ // service |service| has been removed. This method is for convenience
+ // and will be followed by a call to GattServiceChanged (except when called
+ // after the service gets removed) which should be used for bootstrapping a
+ // GATT based profile. See the documentation of GattCharacteristicAdded and
+ // GattServiceChanged for more information. Try to obtain the service from
+ // its device to see whether or not the service has been removed.
+ virtual void GattCharacteristicRemoved(
+ BluetoothGattService* service,
+ BluetoothGattCharacteristic* characteristic) {}
+
+ // Called when the remote GATT characteristic descriptor |descriptor|
+ // belonging to characteristic |characteristic| has been discovered. Don't
+ // cache the arguments as the pointers may become invalid. Instead, use the
+ // specially assigned identifier to obtain a descriptor and cache that
+ // identifier as necessary.
+ //
+ // This method will always be followed by a call to GattServiceChanged,
+ // which can be used by observers to get all the characteristics of a
+ // service and perform the necessary updates. GattDescriptorAdded exists
+ // mostly for convenience.
+ virtual void GattDescriptorAdded(
+ BluetoothGattCharacteristic* characteristic,
+ BluetoothGattDescriptor* descriptor) {}
+
+ // Called when a GATT characteristic descriptor |descriptor| belonging to
+ // characteristic |characteristic| has been removed. This method is for
+ // convenience and will be followed by a call to GattServiceChanged (except
+ // when called after the service gets removed).
+ virtual void GattDescriptorRemoved(
+ BluetoothGattCharacteristic* characteristic,
+ BluetoothGattDescriptor* descriptor) {}
+
+ // Called when the value of a characteristic has changed. This might be a
+ // result of a read/write request to, or a notification/indication from, a
+ // remote GATT characteristic.
+ virtual void GattCharacteristicValueChanged(
+ BluetoothGattService* service,
+ BluetoothGattCharacteristic* characteristic,
+ const std::vector<uint8>& value) {}
+
+ // Called when the value of a characteristic descriptor has been updated.
+ virtual void GattDescriptorValueChanged(
+ BluetoothGattCharacteristic* characteristic,
+ BluetoothGattDescriptor* descriptor,
+ const std::vector<uint8>& value) {}
+ };
+
+ // The ErrorCallback is used by methods to asynchronously report errors.
+ typedef base::Closure ErrorCallback;
+
+ virtual ~BluetoothGattService();
+
+ // Adds and removes observers for events on this GATT service. If monitoring
+ // multiple services, check the |service| parameter of observer methods to
+ // determine which service is issuing the event.
+ virtual void AddObserver(Observer* observer) = 0;
+ virtual void RemoveObserver(Observer* observer) = 0;
+
+ // Constructs a BluetoothGattService that can be locally hosted when the local
+ // adapter is in the peripheral role. The resulting object can then be made
+ // available by calling the "Register" method. This method constructs a
+ // service with UUID |uuid|. Whether the constructed service is primary or
+ // secondary is determined by |is_primary|. |delegate| is used to send certain
+ // peripheral role events. If |delegate| is NULL, then this service will
+ // employ a default behavior when responding to read and write requests based
+ // on the cached value of its characteristics and descriptors at a given time.
+ static BluetoothGattService* Create(const BluetoothUUID& uuid,
+ bool is_primary,
+ Delegate* delegate);
+
+ // Identifier used to uniquely identify a GATT service object. This is
+ // different from the service UUID: while multiple services with the same UUID
+ // can exist on a Bluetooth device, the identifier returned from this method
+ // is unique among all services of a device. The contents of the identifier
+ // are platform specific.
+ virtual std::string GetIdentifier() const = 0;
+
+ // The Bluetooth-specific UUID of the service.
+ virtual BluetoothUUID GetUUID() const = 0;
+
+ // Returns true, if this service hosted locally. If false, then this service
+ // represents a remote GATT service.
+ virtual bool IsLocal() const = 0;
+
+ // Indicates whether the type of this service is primary or secondary. A
+ // primary service describes the primary function of the peripheral that
+ // hosts it, while a secondary service only makes sense in the presence of a
+ // primary service. A primary service may include other primary or secondary
+ // services.
+ virtual bool IsPrimary() const = 0;
+
+ // Returns the BluetoothDevice that this GATT service was received from, which
+ // also owns this service. Local services always return NULL.
+ virtual BluetoothDevice* GetDevice() const = 0;
+
+ // List of characteristics that belong to this service.
+ virtual std::vector<BluetoothGattCharacteristic*>
+ GetCharacteristics() const = 0;
+
+ // List of GATT services that are included by this service.
+ virtual std::vector<BluetoothGattService*>
+ GetIncludedServices() const = 0;
+
+ // Returns the GATT characteristic with identifier |identifier| if it belongs
+ // to this GATT service.
+ virtual BluetoothGattCharacteristic* GetCharacteristic(
+ const std::string& identifier) const = 0;
+
+ // Adds characteristics and included services to the local attribute hierarchy
+ // represented by this service. These methods only make sense for local
+ // services and won't have an effect if this instance represents a remote
+ // GATT service and will return false. While ownership of added
+ // characteristics are taken over by the service, ownership of an included
+ // service is not taken.
+ virtual bool AddCharacteristic(
+ BluetoothGattCharacteristic* characteristic) = 0;
+ virtual bool AddIncludedService(BluetoothGattService* service) = 0;
+
+ // Registers this GATT service. Calling Register will make this service and
+ // all of its associated attributes available on the local adapters GATT
+ // database and the service UUID will be advertised to nearby devices if the
+ // local adapter is discoverable. Call Unregister to make this service no
+ // longer available.
+ //
+ // These methods only make sense for services that are local and will hence
+ // fail if this instance represents a remote GATT service. |callback| is
+ // called to denote success and |error_callback| to denote failure.
+ virtual void Register(const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+ virtual void Unregister(const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ protected:
+ BluetoothGattService();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BluetoothGattService);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_GATT_SERVICE_H_
diff --git a/chromium/device/bluetooth/bluetooth_l2cap_channel_mac.h b/chromium/device/bluetooth/bluetooth_l2cap_channel_mac.h
new file mode 100644
index 00000000000..f843608d204
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_l2cap_channel_mac.h
@@ -0,0 +1,69 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_L2CAP_CHANNEL_MAC_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_L2CAP_CHANNEL_MAC_H_
+
+#import <IOBluetooth/IOBluetooth.h>
+#import <IOKit/IOReturn.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "device/bluetooth/bluetooth_channel_mac.h"
+
+@class BluetoothL2capChannelDelegate;
+
+namespace device {
+
+class BluetoothL2capChannelMac : public BluetoothChannelMac {
+ public:
+ // Creates a new L2CAP channel wrapper with the given |socket| and native
+ // |channel|.
+ // NOTE: The |channel| is expected to already be retained.
+ BluetoothL2capChannelMac(BluetoothSocketMac* socket,
+ IOBluetoothL2CAPChannel* channel);
+ virtual ~BluetoothL2capChannelMac();
+
+ // Opens a new L2CAP channel with Channel ID |channel_id| to the target
+ // |device|. Returns the opened channel and sets |status| to kIOReturnSuccess
+ // if the open process was successfully started (or if an existing L2CAP
+ // channel was found). Otherwise, sets |status| to an error status.
+ static scoped_ptr<BluetoothL2capChannelMac> OpenAsync(
+ BluetoothSocketMac* socket,
+ IOBluetoothDevice* device,
+ BluetoothL2CAPPSM psm,
+ IOReturn* status);
+
+ // BluetoothChannelMac:
+ virtual void SetSocket(BluetoothSocketMac* socket) OVERRIDE;
+ virtual IOBluetoothDevice* GetDevice() OVERRIDE;
+ virtual uint16_t GetOutgoingMTU() OVERRIDE;
+ virtual IOReturn WriteAsync(void* data,
+ uint16_t length,
+ void* refcon) OVERRIDE;
+
+ void OnChannelOpenComplete(IOBluetoothL2CAPChannel* channel,
+ IOReturn status);
+ void OnChannelClosed(IOBluetoothL2CAPChannel* channel);
+ void OnChannelDataReceived(IOBluetoothL2CAPChannel* channel,
+ void* data,
+ size_t length);
+ void OnChannelWriteComplete(IOBluetoothL2CAPChannel* channel,
+ void* refcon,
+ IOReturn status);
+
+ private:
+ // The wrapped native L2CAP channel.
+ base::scoped_nsobject<IOBluetoothL2CAPChannel> channel_;
+
+ // The delegate for the native channel.
+ base::scoped_nsobject<BluetoothL2capChannelDelegate> delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothL2capChannelMac);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_L2CAP_CHANNEL_MAC_H_
diff --git a/chromium/device/bluetooth/bluetooth_l2cap_channel_mac.mm b/chromium/device/bluetooth/bluetooth_l2cap_channel_mac.mm
new file mode 100644
index 00000000000..2f38f3f89df
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_l2cap_channel_mac.mm
@@ -0,0 +1,173 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_l2cap_channel_mac.h"
+
+#include "base/logging.h"
+#include "base/mac/sdk_forward_declarations.h"
+#include "device/bluetooth/bluetooth_device_mac.h"
+#include "device/bluetooth/bluetooth_socket_mac.h"
+
+// A simple delegate class for an open L2CAP channel that forwards methods to
+// its wrapped |channel_|.
+@interface BluetoothL2capChannelDelegate
+ : NSObject <IOBluetoothL2CAPChannelDelegate> {
+ @private
+ device::BluetoothL2capChannelMac* channel_; // weak
+}
+
+- (id)initWithChannel:(device::BluetoothL2capChannelMac*)channel;
+
+@end
+
+@implementation BluetoothL2capChannelDelegate
+
+- (id)initWithChannel:(device::BluetoothL2capChannelMac*)channel {
+ if ((self = [super init]))
+ channel_ = channel;
+
+ return self;
+}
+
+- (void)l2capChannelOpenComplete:(IOBluetoothL2CAPChannel*)l2capChannel
+ status:(IOReturn)error {
+ channel_->OnChannelOpenComplete(l2capChannel, error);
+}
+
+- (void)l2capChannelWriteComplete:(IOBluetoothL2CAPChannel*)l2capChannel
+ refcon:(void*)refcon
+ status:(IOReturn)error {
+ channel_->OnChannelWriteComplete(l2capChannel, refcon, error);
+}
+
+- (void)l2capChannelData:(IOBluetoothL2CAPChannel*)l2capChannel
+ data:(void*)dataPointer
+ length:(size_t)dataLength {
+ channel_->OnChannelDataReceived(l2capChannel, dataPointer, dataLength);
+}
+
+- (void)l2capChannelClosed:(IOBluetoothL2CAPChannel*)l2capChannel {
+ channel_->OnChannelClosed(l2capChannel);
+}
+
+// These methods are marked as optional in the 10.8 SDK, but not in the 10.6
+// SDK. These empty implementations can be removed once we drop the 10.6 SDK.
+- (void)l2capChannelReconfigured:(IOBluetoothL2CAPChannel*)l2capChannel {
+}
+- (void)l2capChannelQueueSpaceAvailable:(IOBluetoothL2CAPChannel*)l2capChannel {
+}
+
+@end
+
+namespace device {
+
+BluetoothL2capChannelMac::BluetoothL2capChannelMac(
+ BluetoothSocketMac* socket,
+ IOBluetoothL2CAPChannel* channel)
+ : channel_(channel),
+ delegate_(nil) {
+ SetSocket(socket);
+}
+
+BluetoothL2capChannelMac::~BluetoothL2capChannelMac() {
+ [channel_ setDelegate:nil];
+ [channel_ closeChannel];
+}
+
+// static
+scoped_ptr<BluetoothL2capChannelMac> BluetoothL2capChannelMac::OpenAsync(
+ BluetoothSocketMac* socket,
+ IOBluetoothDevice* device,
+ BluetoothL2CAPPSM psm,
+ IOReturn* status) {
+ DCHECK(socket);
+ scoped_ptr<BluetoothL2capChannelMac> channel(
+ new BluetoothL2capChannelMac(socket, nil));
+
+ // Retain the delegate, because IOBluetoothDevice's
+ // |-openL2CAPChannelAsync:withPSM:delegate:| assumes that it can take
+ // ownership of the delegate without calling |-retain| on it...
+ DCHECK(channel->delegate_);
+ [channel->delegate_ retain];
+ IOBluetoothL2CAPChannel* l2cap_channel;
+ *status = [device openL2CAPChannelAsync:&l2cap_channel
+ withPSM:psm
+ delegate:channel->delegate_];
+ if (*status == kIOReturnSuccess)
+ channel->channel_.reset([l2cap_channel retain]);
+ else
+ channel.reset();
+
+ return channel.Pass();
+}
+
+void BluetoothL2capChannelMac::SetSocket(BluetoothSocketMac* socket) {
+ BluetoothChannelMac::SetSocket(socket);
+ if (!this->socket())
+ return;
+
+ // Now that the socket is set, it's safe to associate a delegate, which can
+ // call back to the socket.
+ DCHECK(!delegate_);
+ delegate_.reset(
+ [[BluetoothL2capChannelDelegate alloc] initWithChannel:this]);
+ [channel_ setDelegate:delegate_];
+}
+
+IOBluetoothDevice* BluetoothL2capChannelMac::GetDevice() {
+ return [channel_ getDevice];
+}
+
+uint16_t BluetoothL2capChannelMac::GetOutgoingMTU() {
+ return [channel_ outgoingMTU];
+}
+
+IOReturn BluetoothL2capChannelMac::WriteAsync(void* data,
+ uint16_t length,
+ void* refcon) {
+ DCHECK_LE(length, GetOutgoingMTU());
+ return [channel_ writeAsync:data length:length refcon:refcon];
+}
+
+void BluetoothL2capChannelMac::OnChannelOpenComplete(
+ IOBluetoothL2CAPChannel* channel,
+ IOReturn status) {
+ if (channel_) {
+ DCHECK_EQ(channel_, channel);
+ } else {
+ // The (potentially) asynchronous connection occurred synchronously.
+ // Should only be reachable from OpenAsync().
+ DCHECK_EQ(status, kIOReturnSuccess);
+ }
+
+ socket()->OnChannelOpenComplete(
+ BluetoothDeviceMac::GetDeviceAddress([channel getDevice]), status);
+}
+
+void BluetoothL2capChannelMac::OnChannelClosed(
+ IOBluetoothL2CAPChannel* channel) {
+ DCHECK_EQ(channel_, channel);
+ socket()->OnChannelClosed();
+}
+
+void BluetoothL2capChannelMac::OnChannelDataReceived(
+ IOBluetoothL2CAPChannel* channel,
+ void* data,
+ size_t length) {
+ DCHECK_EQ(channel_, channel);
+ socket()->OnChannelDataReceived(data, length);
+}
+
+void BluetoothL2capChannelMac::OnChannelWriteComplete(
+ IOBluetoothL2CAPChannel* channel,
+ void* refcon,
+ IOReturn status) {
+ // Note: We use "CHECK" below to ensure we never run into unforeseen
+ // occurrences of asynchronous callbacks, which could lead to data
+ // corruption.
+ CHECK_EQ(channel_, channel);
+ socket()->OnChannelWriteComplete(refcon, status);
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_out_of_band_pairing_data.h b/chromium/device/bluetooth/bluetooth_out_of_band_pairing_data.h
deleted file mode 100644
index 1b45bb0b9d2..00000000000
--- a/chromium/device/bluetooth/bluetooth_out_of_band_pairing_data.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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 DEVICE_BLUETOOTH_BLUETOOTH_OUT_OF_BAND_PAIRING_DATA_H_
-#define DEVICE_BLUETOOTH_BLUETOOTH_OUT_OF_BAND_PAIRING_DATA_H_
-
-#include "base/basictypes.h"
-
-namespace device {
-
-const size_t kBluetoothOutOfBandPairingDataSize = 16;
-
-// A simple structure representing the data required to perform Out Of Band
-// Pairing. See
-// http://mclean-linsky.net/joel/cv/Simple%20Pairing_WP_V10r00.pdf
-struct BluetoothOutOfBandPairingData {
- // Simple Pairing Hash C.
- uint8 hash[kBluetoothOutOfBandPairingDataSize];
-
- // Simple Pairing Randomizer R.
- uint8 randomizer[kBluetoothOutOfBandPairingDataSize];
-};
-
-} // namespace device
-
-#endif // DEVICE_BLUETOOTH_BLUETOOTH_OUT_OF_BAND_PAIRING_DATA_H_
diff --git a/chromium/device/bluetooth/bluetooth_pairing_chromeos.cc b/chromium/device/bluetooth/bluetooth_pairing_chromeos.cc
new file mode 100644
index 00000000000..dc73f6d7f6c
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_pairing_chromeos.cc
@@ -0,0 +1,273 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_pairing_chromeos.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_device_chromeos.h"
+
+using device::BluetoothDevice;
+
+namespace {
+
+// Histogram enumerations for pairing methods.
+enum UMAPairingMethod {
+ UMA_PAIRING_METHOD_NONE,
+ UMA_PAIRING_METHOD_REQUEST_PINCODE,
+ UMA_PAIRING_METHOD_REQUEST_PASSKEY,
+ UMA_PAIRING_METHOD_DISPLAY_PINCODE,
+ UMA_PAIRING_METHOD_DISPLAY_PASSKEY,
+ UMA_PAIRING_METHOD_CONFIRM_PASSKEY,
+ // NOTE: Add new pairing methods immediately above this line. Make sure to
+ // update the enum list in tools/histogram/histograms.xml accordingly.
+ UMA_PAIRING_METHOD_COUNT
+};
+
+// Number of keys that will be entered for a passkey, six digits plus the
+// final enter.
+const uint16 kPasskeyMaxKeysEntered = 7;
+
+} // namespace
+
+namespace chromeos {
+
+BluetoothPairingChromeOS::BluetoothPairingChromeOS(
+ BluetoothDeviceChromeOS* device,
+ BluetoothDevice::PairingDelegate* pairing_delegate)
+ : device_(device),
+ pairing_delegate_(pairing_delegate),
+ pairing_delegate_used_(false) {
+ VLOG(1) << "Created BluetoothPairingChromeOS for "
+ << device_->GetAddress();
+}
+
+BluetoothPairingChromeOS::~BluetoothPairingChromeOS() {
+ VLOG(1) << "Destroying BluetoothPairingChromeOS for "
+ << device_->GetAddress();
+
+ if (!pairing_delegate_used_) {
+ UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+ UMA_PAIRING_METHOD_NONE,
+ UMA_PAIRING_METHOD_COUNT);
+ }
+
+ if (!pincode_callback_.is_null()) {
+ pincode_callback_.Run(
+ BluetoothAgentServiceProvider::Delegate::CANCELLED, "");
+ }
+
+ if (!passkey_callback_.is_null()) {
+ passkey_callback_.Run(
+ BluetoothAgentServiceProvider::Delegate::CANCELLED, 0);
+ }
+
+ if (!confirmation_callback_.is_null()) {
+ confirmation_callback_.Run(
+ BluetoothAgentServiceProvider::Delegate::CANCELLED);
+ }
+
+ pairing_delegate_ = NULL;
+}
+
+void BluetoothPairingChromeOS::RequestPinCode(
+ const BluetoothAgentServiceProvider::Delegate::PinCodeCallback& callback) {
+ UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+ UMA_PAIRING_METHOD_REQUEST_PINCODE,
+ UMA_PAIRING_METHOD_COUNT);
+
+ ResetCallbacks();
+ pincode_callback_ = callback;
+ pairing_delegate_used_ = true;
+ pairing_delegate_->RequestPinCode(device_);
+}
+
+bool BluetoothPairingChromeOS::ExpectingPinCode() const {
+ return !pincode_callback_.is_null();
+}
+
+void BluetoothPairingChromeOS::SetPinCode(const std::string& pincode) {
+ if (pincode_callback_.is_null())
+ return;
+
+ pincode_callback_.Run(BluetoothAgentServiceProvider::Delegate::SUCCESS,
+ pincode);
+ pincode_callback_.Reset();
+
+ // If this is not an outgoing connection to the device, clean up the pairing
+ // context since the pairing is done. The outgoing connection case is cleaned
+ // up in the callback for the underlying Pair() call.
+ if (!device_->IsConnecting())
+ device_->EndPairing();
+}
+
+void BluetoothPairingChromeOS::DisplayPinCode(const std::string& pincode) {
+ UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+ UMA_PAIRING_METHOD_DISPLAY_PINCODE,
+ UMA_PAIRING_METHOD_COUNT);
+
+ ResetCallbacks();
+ pairing_delegate_used_ = true;
+ pairing_delegate_->DisplayPinCode(device_, pincode);
+
+ // If this is not an outgoing connection to the device, the pairing context
+ // needs to be cleaned up again as there's no reliable indication of
+ // completion of incoming pairing.
+ if (!device_->IsConnecting())
+ device_->EndPairing();
+}
+
+void BluetoothPairingChromeOS::RequestPasskey(
+ const BluetoothAgentServiceProvider::Delegate::PasskeyCallback& callback) {
+ UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+ UMA_PAIRING_METHOD_REQUEST_PASSKEY,
+ UMA_PAIRING_METHOD_COUNT);
+
+ ResetCallbacks();
+ passkey_callback_ = callback;
+ pairing_delegate_used_ = true;
+ pairing_delegate_->RequestPasskey(device_);
+}
+
+bool BluetoothPairingChromeOS::ExpectingPasskey() const {
+ return !passkey_callback_.is_null();
+}
+
+void BluetoothPairingChromeOS::SetPasskey(uint32 passkey) {
+ if (passkey_callback_.is_null())
+ return;
+
+ passkey_callback_.Run(BluetoothAgentServiceProvider::Delegate::SUCCESS,
+ passkey);
+ passkey_callback_.Reset();
+
+ // If this is not an outgoing connection to the device, clean up the pairing
+ // context since the pairing is done. The outgoing connection case is cleaned
+ // up in the callback for the underlying Pair() call.
+ if (!device_->IsConnecting())
+ device_->EndPairing();
+}
+
+void BluetoothPairingChromeOS::DisplayPasskey(uint32 passkey) {
+ UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+ UMA_PAIRING_METHOD_DISPLAY_PASSKEY,
+ UMA_PAIRING_METHOD_COUNT);
+
+ ResetCallbacks();
+ pairing_delegate_used_ = true;
+ pairing_delegate_->DisplayPasskey(device_, passkey);
+
+}
+
+void BluetoothPairingChromeOS::KeysEntered(uint16 entered) {
+ pairing_delegate_used_ = true;
+ pairing_delegate_->KeysEntered(device_, entered);
+
+ // If this is not an outgoing connection to the device, the pairing context
+ // needs to be cleaned up again as there's no reliable indication of
+ // completion of incoming pairing.
+ if (entered >= kPasskeyMaxKeysEntered && !device_->IsConnecting())
+ device_->EndPairing();
+}
+
+void BluetoothPairingChromeOS::RequestConfirmation(
+ uint32 passkey,
+ const BluetoothAgentServiceProvider::Delegate::ConfirmationCallback&
+ callback) {
+ UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+ UMA_PAIRING_METHOD_CONFIRM_PASSKEY,
+ UMA_PAIRING_METHOD_COUNT);
+
+ ResetCallbacks();
+ confirmation_callback_ = callback;
+ pairing_delegate_used_ = true;
+ pairing_delegate_->ConfirmPasskey(device_, passkey);
+}
+
+void BluetoothPairingChromeOS::RequestAuthorization(
+ const BluetoothAgentServiceProvider::Delegate::ConfirmationCallback&
+ callback) {
+ UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+ UMA_PAIRING_METHOD_NONE,
+ UMA_PAIRING_METHOD_COUNT);
+
+ ResetCallbacks();
+ confirmation_callback_ = callback;
+ pairing_delegate_used_ = true;
+ pairing_delegate_->AuthorizePairing(device_);
+}
+
+bool BluetoothPairingChromeOS::ExpectingConfirmation() const {
+ return !confirmation_callback_.is_null();
+}
+
+void BluetoothPairingChromeOS::ConfirmPairing() {
+ if (confirmation_callback_.is_null())
+ return;
+
+ confirmation_callback_.Run(BluetoothAgentServiceProvider::Delegate::SUCCESS);
+ confirmation_callback_.Reset();
+
+ // If this is not an outgoing connection to the device, clean up the pairing
+ // context since the pairing is done. The outgoing connection case is cleaned
+ // up in the callback for the underlying Pair() call.
+ if (!device_->IsConnecting())
+ device_->EndPairing();
+}
+
+bool BluetoothPairingChromeOS::RejectPairing() {
+ return RunPairingCallbacks(
+ BluetoothAgentServiceProvider::Delegate::REJECTED);
+}
+
+bool BluetoothPairingChromeOS::CancelPairing() {
+ return RunPairingCallbacks(
+ BluetoothAgentServiceProvider::Delegate::CANCELLED);
+}
+
+BluetoothDevice::PairingDelegate*
+BluetoothPairingChromeOS::GetPairingDelegate() const {
+ return pairing_delegate_;
+}
+
+void BluetoothPairingChromeOS::ResetCallbacks() {
+ pincode_callback_.Reset();
+ passkey_callback_.Reset();
+ confirmation_callback_.Reset();
+}
+
+bool BluetoothPairingChromeOS::RunPairingCallbacks(
+ BluetoothAgentServiceProvider::Delegate::Status status) {
+ pairing_delegate_used_ = true;
+
+ bool callback_run = false;
+ if (!pincode_callback_.is_null()) {
+ pincode_callback_.Run(status, "");
+ pincode_callback_.Reset();
+ callback_run = true;
+ }
+
+ if (!passkey_callback_.is_null()) {
+ passkey_callback_.Run(status, 0);
+ passkey_callback_.Reset();
+ callback_run = true;
+ }
+
+ if (!confirmation_callback_.is_null()) {
+ confirmation_callback_.Run(status);
+ confirmation_callback_.Reset();
+ callback_run = true;
+ }
+
+ // If this is not an outgoing connection to the device, clean up the pairing
+ // context since the pairing is done. The outgoing connection case is cleaned
+ // up in the callback for the underlying Pair() call.
+ if (!device_->IsConnecting())
+ device_->EndPairing();
+
+ return callback_run;
+}
+
+} // namespace chromeos
diff --git a/chromium/device/bluetooth/bluetooth_pairing_chromeos.h b/chromium/device/bluetooth/bluetooth_pairing_chromeos.h
new file mode 100644
index 00000000000..ae906bc88ac
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_pairing_chromeos.h
@@ -0,0 +1,146 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_PAIRING_CHROMEOS_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_PAIRING_CHROMEOS_H_
+
+#include "chromeos/dbus/bluetooth_agent_service_provider.h"
+#include "device/bluetooth/bluetooth_device.h"
+
+namespace chromeos {
+
+class BluetoothDeviceChromeOS;
+
+// The BluetoothPairingChromeOS class encapsulates the logic for an individual
+// device pairing, acting as a bridge between BluetoothAdapterChromeOS which
+// communicates with the underlying Controller and Host Subsystem, and
+// BluetoothDeviceChromeOS which presents the pairing logic to the application.
+class BluetoothPairingChromeOS {
+ public:
+ BluetoothPairingChromeOS(
+ BluetoothDeviceChromeOS* device,
+ device::BluetoothDevice::PairingDelegate* pairing_delegate);
+ ~BluetoothPairingChromeOS();
+
+ // Indicates whether the device is currently pairing and expecting a
+ // Passkey to be returned.
+ bool ExpectingPasskey() const;
+
+ // Indicates whether the device is currently pairing and expecting
+ // confirmation of a displayed passkey.
+ bool ExpectingConfirmation() const;
+
+ // Requests a PIN code for the current device from the current pairing
+ // delegate, the SetPinCode(), RejectPairing() and CancelPairing() method
+ // calls on this object are translated into the appropriate response to
+ // |callback|.
+ void RequestPinCode(
+ const BluetoothAgentServiceProvider::Delegate::PinCodeCallback& callback);
+
+ // Indicates whether the device is currently pairing and expecting a
+ // PIN Code to be returned.
+ bool ExpectingPinCode() const;
+
+ // Sends the PIN code |pincode| to the remote device during pairing.
+ //
+ // PIN Codes are generally required for Bluetooth 2.0 and earlier devices
+ // for which there is no automatic pairing or special handling.
+ void SetPinCode(const std::string& pincode);
+
+ // Requests a PIN code for the current device be displayed by the current
+ // pairing delegate. No response is expected from the delegate.
+ void DisplayPinCode(const std::string& pincode);
+
+ // Requests a Passkey for the current device from the current pairing
+ // delegate, the SetPasskey(), RejectPairing() and CancelPairing() method
+ // calls on this object are translated into the appropriate response to
+ // |callback|.
+ void RequestPasskey(
+ const BluetoothAgentServiceProvider::Delegate::PasskeyCallback& callback);
+
+ // Sends the Passkey |passkey| to the remote device during pairing.
+ //
+ // Passkeys are generally required for Bluetooth 2.1 and later devices
+ // which cannot provide input or display on their own, and don't accept
+ // passkey-less pairing, and are a numeric in the range 0-999999.
+ void SetPasskey(uint32 passkey);
+
+ // Requests a Passkey for the current device be displayed by the current
+ // pairing delegate. No response is expected from the delegate.
+ void DisplayPasskey(uint32 passkey);
+
+ // Informs the current pairing delegate that |entered| keys have been
+ // provided to the remote device since the DisplayPasskey() call. No
+ // response is expected from the delegate.
+ void KeysEntered(uint16 entered);
+
+ // Requests confirmation that |passkey| is displayed on the current device
+ // from the current pairing delegate. The ConfirmPairing(), RejectPairing()
+ // and CancelPairing() method calls on this object are translated into the
+ // appropriate response to |callback|.
+ void RequestConfirmation(
+ uint32 passkey,
+ const BluetoothAgentServiceProvider::Delegate::ConfirmationCallback&
+ callback);
+
+ // Requests authorization that the current device be allowed to pair with
+ // this device from the current pairing delegate. The ConfirmPairing(),
+ // RejectPairing() and CancelPairing() method calls on this object are
+ // translated into the appropriate response to |callback|.
+ void RequestAuthorization(
+ const BluetoothAgentServiceProvider::Delegate::ConfirmationCallback&
+ callback);
+
+ // Confirms to the remote device during pairing that a passkey provided by
+ // the ConfirmPasskey() delegate call is displayed on both devices.
+ void ConfirmPairing();
+
+ // Rejects a pairing or connection request from a remote device, returns
+ // false if there was no way to reject the pairing.
+ bool RejectPairing();
+
+ // Cancels a pairing or connection attempt to a remote device, returns
+ // false if there was no way to cancel the pairing.
+ bool CancelPairing();
+
+ // Returns the pairing delegate being used by this pairing object.
+ device::BluetoothDevice::PairingDelegate* GetPairingDelegate() const;
+
+ private:
+ // Internal method to reset the current set of callbacks because a new
+ // request has arrived that supercedes them.
+ void ResetCallbacks();
+
+ // Internal method to respond to the relevant callback for a RejectPairing
+ // or CancelPairing call.
+ bool RunPairingCallbacks(
+ BluetoothAgentServiceProvider::Delegate::Status status);
+
+ // The underlying BluetoothDeviceChromeOS that owns this pairing context.
+ BluetoothDeviceChromeOS* device_;
+
+ // UI Pairing Delegate to make method calls on, this must live as long as
+ // the object capturing the PairingContext.
+ device::BluetoothDevice::PairingDelegate* pairing_delegate_;
+
+ // Flag to indicate whether any pairing delegate method has been called
+ // during pairing. Used to determine whether we need to log the
+ // "no pairing interaction" metric.
+ bool pairing_delegate_used_;
+
+ // During pairing these callbacks are set to those provided by method calls
+ // made on the BluetoothAdapterChromeOS instance by its respective
+ // BluetoothAgentServiceProvider instance, and are called by our own
+ // method calls such as SetPinCode() and SetPasskey().
+ BluetoothAgentServiceProvider::Delegate::PinCodeCallback pincode_callback_;
+ BluetoothAgentServiceProvider::Delegate::PasskeyCallback passkey_callback_;
+ BluetoothAgentServiceProvider::Delegate::ConfirmationCallback
+ confirmation_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothPairingChromeOS);
+};
+
+} // namespace chromeos
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_PAIRING_CHROMEOS_H_
diff --git a/chromium/device/bluetooth/bluetooth_profile.cc b/chromium/device/bluetooth/bluetooth_profile.cc
deleted file mode 100644
index 0d9a7572970..00000000000
--- a/chromium/device/bluetooth/bluetooth_profile.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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.h"
-
-#if defined(OS_CHROMEOS)
-#include "device/bluetooth/bluetooth_profile_chromeos.h"
-#elif defined(OS_MACOSX)
-#include "base/mac/mac_util.h"
-#include "device/bluetooth/bluetooth_profile_mac.h"
-#elif defined(OS_WIN)
-#include "device/bluetooth/bluetooth_profile_win.h"
-#endif
-
-#include <string>
-
-namespace device {
-
-BluetoothProfile::Options::Options()
- : channel(0),
- psm(0),
- require_authentication(false),
- require_authorization(false),
- auto_connect(false),
- version(0),
- features(0) {
-}
-
-BluetoothProfile::Options::~Options() {
-
-}
-
-
-BluetoothProfile::BluetoothProfile() {
-
-}
-
-BluetoothProfile::~BluetoothProfile() {
-
-}
-
-
-// static
-void BluetoothProfile::Register(const std::string& uuid,
- const Options& options,
- const ProfileCallback& callback) {
-#if defined(OS_CHROMEOS)
- chromeos::BluetoothProfileChromeOS* profile = NULL;
- profile = new chromeos::BluetoothProfileChromeOS();
- profile->Init(uuid, options, callback);
-#elif defined(OS_MACOSX)
- BluetoothProfile* profile = NULL;
-
- 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);
- callback.Run(profile);
-#else
- callback.Run(NULL);
-#endif
-}
-
-} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_profile.h b/chromium/device/bluetooth/bluetooth_profile.h
deleted file mode 100644
index 4ff2480c21d..00000000000
--- a/chromium/device/bluetooth/bluetooth_profile.h
+++ /dev/null
@@ -1,109 +0,0 @@
-// 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_H_
-#define DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-
-namespace device {
-
-class BluetoothDevice;
-class BluetoothProfileMac;
-class BluetoothSocket;
-class MockBluetoothProfile;
-
-// BluetoothProfile represents an implementation of either a client or server
-// of a particular specified profile (aka service or protocol in other
-// standards).
-//
-// Profile implementations are created by registering them through the static
-// BluetoothProfile::Register() method and are always identified by a UUID
-// which in any method may be specified in the short or long form.
-//
-// The lifecycle of BluetoothProfile instances is managed by the implementation
-// but they are guaranteed to exist once provided to a Register() callback until
-// the instance's Unregister() method is called, so others may hold on to
-// pointers to them.
-class BluetoothProfile {
- public:
- // Options used to register a BluetoothProfile object.
- struct Options {
- Options();
- ~Options();
-
- // Human readable name of the Profile, e.g. "Health Device".
- // Exported in the adapter's SDP or GATT tables where relevant.
- std::string name;
-
- // RFCOMM channel used by the profile.
- // Exported in the adapter's SDP or GATT tables where relevant.
- uint16 channel;
-
- // L2CAP PSM number.
- // Exported in the adapter's SDP or GATT tables where relevant.
- uint16 psm;
-
- // Specifies whether pairing (and encryption) is required to be able to
- // connect. Defaults to false.
- bool require_authentication;
-
- // Specifies whether user authorization is required to be able to connect.
- // Defaults to false.
- bool require_authorization;
-
- // Specifies whether this profile will be automatically connected if any
- // other profile of a device conects to the host.
- // Defaults to false.
- bool auto_connect;
-
- // Implemented version of the profile.
- // Exported in the adapter's SDP or GATT tables where relevant.
- uint16 version;
-
- // Implemented feature set of the profile.
- // Exported in the adapter's SDP or GATT tables where relevant.
- uint16 features;
- };
-
- // Register an implementation of the profile with UUID |uuid| and
- // additional details specified in |options|. The corresponding profile
- // object will be created and returned by |callback|. If the profile cannot
- // be registered, NULL will be passed.
- //
- // This pointer is not owned by the receiver, but will not be freed unless
- // its Unregister() method is called.
- typedef base::Callback<void(BluetoothProfile*)> ProfileCallback;
- static void Register(const std::string& uuid,
- const Options& options,
- const ProfileCallback& callback);
-
- // Unregister the profile. This deletes the profile object so, once called,
- // any pointers to the profile should be discarded.
- virtual void Unregister() = 0;
-
- // Set the connection callback for the profile to |callback|, any successful
- // connection initiated by BluetoothDevice::ConnectToProfile() or incoming
- // connections from devices, will have a BluetoothSocket created and passed
- // to this callback.
- //
- // The socket will be closed when all references are released; none of the
- // BluetoothProfile, or BluetoothAdapter or BluetoothDevice objects are
- // guaranteed to hold a reference so this may outlive all of them.
- typedef base::Callback<void(
- const BluetoothDevice*,
- scoped_refptr<BluetoothSocket>)> ConnectionCallback;
- virtual void SetConnectionCallback(const ConnectionCallback& callback) = 0;
-
- protected:
- BluetoothProfile();
- virtual ~BluetoothProfile();
-};
-
-} // namespace device
-
-#endif // DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_H_
diff --git a/chromium/device/bluetooth/bluetooth_profile_chromeos.cc b/chromium/device/bluetooth/bluetooth_profile_chromeos.cc
deleted file mode 100644
index 31e165e8daa..00000000000
--- a/chromium/device/bluetooth/bluetooth_profile_chromeos.cc
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright 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_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/message_loop.h"
-#include "base/strings/string_util.h"
-#include "base/task_runner_util.h"
-#include "base/threading/thread_restrictions.h"
-#include "base/threading/worker_pool.h"
-#include "chromeos/dbus/bluetooth_profile_manager_client.h"
-#include "chromeos/dbus/bluetooth_profile_service_provider.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "dbus/bus.h"
-#include "dbus/file_descriptor.h"
-#include "dbus/object_path.h"
-#include "device/bluetooth/bluetooth_adapter_chromeos.h"
-#include "device/bluetooth/bluetooth_adapter_factory.h"
-#include "device/bluetooth/bluetooth_device.h"
-#include "device/bluetooth/bluetooth_device_chromeos.h"
-#include "device/bluetooth/bluetooth_profile.h"
-#include "device/bluetooth/bluetooth_socket.h"
-#include "device/bluetooth/bluetooth_socket_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 {
-
-BluetoothProfileChromeOS::BluetoothProfileChromeOS()
- : weak_ptr_factory_(this) {
-}
-
-BluetoothProfileChromeOS::~BluetoothProfileChromeOS() {
- DCHECK(object_path_.value().empty());
- DCHECK(profile_.get() == NULL);
-}
-
-void BluetoothProfileChromeOS::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;
-
- BluetoothProfileManagerClient::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;
- base::ReplaceChars(uuid, ":-", "_", &uuid_path);
-
- object_path_ = dbus::ObjectPath("/org/chromium/bluetooth_profile/" +
- uuid_path);
-
- dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
- profile_.reset(BluetoothProfileServiceProvider::Create(
- system_bus, object_path_, this));
- DCHECK(profile_.get());
-
- VLOG(1) << object_path_.value() << ": Register profile";
- DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
- RegisterProfile(
- object_path_,
- uuid,
- bluetooth_options,
- base::Bind(&BluetoothProfileChromeOS::OnRegisterProfile,
- weak_ptr_factory_.GetWeakPtr(),
- callback),
- base::Bind(&BluetoothProfileChromeOS::OnRegisterProfileError,
- weak_ptr_factory_.GetWeakPtr(),
- callback));
-}
-
-void BluetoothProfileChromeOS::Unregister() {
- DCHECK(!object_path_.value().empty());
- DCHECK(profile_.get());
-
- profile_.reset();
-
- VLOG(1) << object_path_.value() << ": Unregister profile";
- DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
- UnregisterProfile(
- object_path_,
- base::Bind(&BluetoothProfileChromeOS::OnUnregisterProfile,
- weak_ptr_factory_.GetWeakPtr()),
- base::Bind(&BluetoothProfileChromeOS::OnUnregisterProfileError,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void BluetoothProfileChromeOS::SetConnectionCallback(
- const ConnectionCallback& callback) {
- connection_callback_ = callback;
-}
-
-void BluetoothProfileChromeOS::Release() {
- VLOG(1) << object_path_.value() << ": Release";
-}
-
-void BluetoothProfileChromeOS::NewConnection(
- const dbus::ObjectPath& device_path,
- scoped_ptr<dbus::FileDescriptor> fd,
- const BluetoothProfileServiceProvider::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).get(),
- FROM_HERE,
- base::Bind(&CheckValidity, base::Passed(&fd)),
- base::Bind(&BluetoothProfileChromeOS::GetAdapter,
- weak_ptr_factory_.GetWeakPtr(),
- device_path,
- options,
- callback));
-}
-
-void BluetoothProfileChromeOS::RequestDisconnection(
- const dbus::ObjectPath& device_path,
- const ConfirmationCallback& callback) {
- VLOG(1) << object_path_.value() << ": Request disconnection";
- callback.Run(SUCCESS);
-}
-
-void BluetoothProfileChromeOS::Cancel() {
- VLOG(1) << object_path_.value() << ": Cancel";
-}
-
-void BluetoothProfileChromeOS::OnRegisterProfile(
- const ProfileCallback& callback) {
- VLOG(1) << object_path_.value() << ": Profile registered";
- callback.Run(this);
-}
-
-void BluetoothProfileChromeOS::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 BluetoothProfileChromeOS::OnUnregisterProfile() {
- VLOG(1) << object_path_.value() << ": Profile unregistered";
- object_path_ = dbus::ObjectPath("");
- delete this;
-}
-
-void BluetoothProfileChromeOS::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 BluetoothProfileChromeOS::GetAdapter(
- const dbus::ObjectPath& device_path,
- const BluetoothProfileServiceProvider::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(&BluetoothProfileChromeOS::OnGetAdapter,
- weak_ptr_factory_.GetWeakPtr(),
- device_path,
- options,
- callback,
- base::Passed(&fd)));
-}
-
-void BluetoothProfileChromeOS::OnGetAdapter(
- const dbus::ObjectPath& device_path,
- const BluetoothProfileServiceProvider::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);
-
- BluetoothDeviceChromeOS* device =
- static_cast<BluetoothAdapterChromeOS*>(adapter.get())->
- GetDeviceWithPath(device_path);
- DCHECK(device);
-
- scoped_refptr<BluetoothSocket> socket((
- BluetoothSocketChromeOS::Create(fd.get())));
- connection_callback_.Run(device, socket);
-}
-
-} // namespace chromeos
diff --git a/chromium/device/bluetooth/bluetooth_profile_chromeos.h b/chromium/device/bluetooth/bluetooth_profile_chromeos.h
deleted file mode 100644
index c13951e168a..00000000000
--- a/chromium/device/bluetooth/bluetooth_profile_chromeos.h
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 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_CHROMEOS_H_
-#define DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_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/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 BluetoothProfileChromeOS class implements BluetoothProfile for the
-// Chrome OS platform.
-class CHROMEOS_EXPORT BluetoothProfileChromeOS
- : public device::BluetoothProfile,
- private BluetoothProfileServiceProvider::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;
-
- BluetoothProfileChromeOS();
- virtual ~BluetoothProfileChromeOS();
-
- // 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);
-
- // BluetoothProfileServiceProvider::Delegate override.
- virtual void Release() OVERRIDE;
- virtual void NewConnection(
- const dbus::ObjectPath& device_path,
- scoped_ptr<dbus::FileDescriptor> fd,
- const BluetoothProfileServiceProvider::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 BluetoothProfileServiceProvider::Delegate::Options& options,
- const ConfirmationCallback& callback,
- scoped_ptr<dbus::FileDescriptor> fd);
- void OnGetAdapter(
- const dbus::ObjectPath& device_path,
- const BluetoothProfileServiceProvider::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<BluetoothProfileServiceProvider> 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<BluetoothProfileChromeOS> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(BluetoothProfileChromeOS);
-};
-
-} // namespace chromeos
-
-#endif // DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_CHROMEOS_H_
diff --git a/chromium/device/bluetooth/bluetooth_profile_chromeos_unittest.cc b/chromium/device/bluetooth/bluetooth_profile_chromeos_unittest.cc
deleted file mode 100644
index 1792cdac807..00000000000
--- a/chromium/device/bluetooth/bluetooth_profile_chromeos_unittest.cc
+++ /dev/null
@@ -1,369 +0,0 @@
-// 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/message_loop/message_loop.h"
-#include "chromeos/dbus/fake_bluetooth_adapter_client.h"
-#include "chromeos/dbus/fake_bluetooth_device_client.h"
-#include "chromeos/dbus/fake_bluetooth_input_client.h"
-#include "chromeos/dbus/fake_bluetooth_profile_manager_client.h"
-#include "chromeos/dbus/fake_bluetooth_profile_service_provider.h"
-#include "chromeos/dbus/fake_dbus_thread_manager.h"
-#include "device/bluetooth/bluetooth_adapter.h"
-#include "device/bluetooth/bluetooth_adapter_chromeos.h"
-#include "device/bluetooth/bluetooth_adapter_factory.h"
-#include "device/bluetooth/bluetooth_device.h"
-#include "device/bluetooth/bluetooth_device_chromeos.h"
-#include "device/bluetooth/bluetooth_profile.h"
-#include "device/bluetooth/bluetooth_profile_chromeos.h"
-#include "device/bluetooth/bluetooth_socket.h"
-#include "device/bluetooth/bluetooth_socket_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_(base::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() {
- FakeDBusThreadManager* fake_dbus_thread_manager = new FakeDBusThreadManager;
- fake_bluetooth_profile_manager_client_ =
- new FakeBluetoothProfileManagerClient;
- fake_dbus_thread_manager->SetBluetoothProfileManagerClient(
- scoped_ptr<BluetoothProfileManagerClient>(
- fake_bluetooth_profile_manager_client_));
- fake_dbus_thread_manager->SetBluetoothAdapterClient(
- scoped_ptr<BluetoothAdapterClient>(new FakeBluetoothAdapterClient));
- fake_dbus_thread_manager->SetBluetoothDeviceClient(
- scoped_ptr<BluetoothDeviceClient>(new FakeBluetoothDeviceClient));
- fake_dbus_thread_manager->SetBluetoothInputClient(
- scoped_ptr<BluetoothInputClient>(new FakeBluetoothInputClient));
- DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager);
-
- device::BluetoothAdapterFactory::GetAdapter(
- base::Bind(&BluetoothProfileChromeOSTest::AdapterCallback,
- base::Unretained(this)));
- ASSERT_TRUE(adapter_.get() != 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_;
- 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.get());
- 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.get(), base_buffer->size());
- success = socket->Send(write_buffer.get());
- 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.get());
- } 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.get(), base_buffer->size());
- success = socket->Send(write_buffer.get());
- 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.get());
- 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.get());
- 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.get(), base_buffer->size());
- success = socket->Send(write_buffer.get());
- 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.get());
- } 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.get(), base_buffer->size());
- success = socket->Send(write_buffer.get());
- 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.get());
- 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/chromium/device/bluetooth/bluetooth_profile_mac.h b/chromium/device/bluetooth/bluetooth_profile_mac.h
deleted file mode 100644
index 0085efdeaea..00000000000
--- a/chromium/device/bluetooth/bluetooth_profile_mac.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 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_MAC_H_
-#define DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_MAC_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/callback.h"
-#include "device/bluetooth/bluetooth_profile.h"
-
-#ifdef __OBJC__
-@class IOBluetoothDevice;
-#else
-class IOBluetoothDevice;
-#endif
-
-namespace device {
-
-class BluetoothProfileMac : public BluetoothProfile {
- public:
- // BluetoothProfile override.
- virtual void Unregister() OVERRIDE;
- virtual void SetConnectionCallback(
- const ConnectionCallback& callback) OVERRIDE;
-
- // Makes an outgoing connection to |device|.
- // This method runs |socket_callback_| with the socket and returns true if the
- // connection is made successfully.
- bool Connect(IOBluetoothDevice* device);
-
- private:
- friend BluetoothProfile;
-
- BluetoothProfileMac(const std::string& uuid, const std::string& name);
- virtual ~BluetoothProfileMac();
-
- const std::string uuid_;
- const std::string name_;
- ConnectionCallback connection_callback_;
-};
-
-} // namespace device
-
-#endif // DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_MAC_H_
diff --git a/chromium/device/bluetooth/bluetooth_profile_mac.mm b/chromium/device/bluetooth/bluetooth_profile_mac.mm
deleted file mode 100644
index 4956651eac6..00000000000
--- a/chromium/device/bluetooth/bluetooth_profile_mac.mm
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 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_mac.h"
-
-#import <IOBluetooth/objc/IOBluetoothDevice.h>
-#import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h>
-#import <IOBluetooth/objc/IOBluetoothSDPUUID.h>
-
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "base/memory/ref_counted.h"
-#include "base/strings/string_number_conversions.h"
-#include "device/bluetooth/bluetooth_device_mac.h"
-#include "device/bluetooth/bluetooth_socket_mac.h"
-
-namespace {
-
-// Converts |uuid| to a IOBluetoothSDPUUID instance.
-//
-// |uuid| must be in the format of XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
-IOBluetoothSDPUUID* GetIOBluetoothSDPUUID(const std::string& uuid) {
- DCHECK(uuid.size() == 36);
- DCHECK(uuid[8] == '-');
- DCHECK(uuid[13] == '-');
- DCHECK(uuid[18] == '-');
- DCHECK(uuid[23] == '-');
- std::string numbers_only = uuid;
- numbers_only.erase(23, 1);
- numbers_only.erase(18, 1);
- numbers_only.erase(13, 1);
- numbers_only.erase(8, 1);
- std::vector<uint8> uuid_bytes_vector;
- base::HexStringToBytes(numbers_only, &uuid_bytes_vector);
- DCHECK(uuid_bytes_vector.size() == 16);
-
- return [IOBluetoothSDPUUID uuidWithBytes:&uuid_bytes_vector[0]
- length:uuid_bytes_vector.size()];
-}
-
-} // namespace
-
-namespace device {
-
-BluetoothProfileMac::BluetoothProfileMac(const std::string& uuid,
- const std::string& name)
- : BluetoothProfile(), uuid_(uuid), name_(name) {
-}
-
-BluetoothProfileMac::~BluetoothProfileMac() {
-}
-
-void BluetoothProfileMac::Unregister() {
- delete this;
-}
-
-void BluetoothProfileMac::SetConnectionCallback(
- const ConnectionCallback& callback) {
- connection_callback_ = callback;
-}
-
-bool BluetoothProfileMac::Connect(IOBluetoothDevice* device) {
- if (connection_callback_.is_null())
- return false;
-
- IOBluetoothSDPServiceRecord* record =
- [device getServiceRecordForUUID:GetIOBluetoothSDPUUID(uuid_)];
- if (record != nil) {
- scoped_refptr<BluetoothSocket> socket(
- BluetoothSocketMac::CreateBluetoothSocket(record));
- if (socket.get() != NULL) {
- BluetoothDeviceMac device_mac(device);
- connection_callback_.Run(&device_mac, socket);
- return true;
- }
- }
- return false;
-}
-
-} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_profile_win.cc b/chromium/device/bluetooth/bluetooth_profile_win.cc
deleted file mode 100644
index f0729bb4fb7..00000000000
--- a/chromium/device/bluetooth/bluetooth_profile_win.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 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_win.h"
-
-#include "base/memory/ref_counted.h"
-#include "device/bluetooth/bluetooth_device_win.h"
-#include "device/bluetooth/bluetooth_service_record.h"
-#include "device/bluetooth/bluetooth_socket_win.h"
-
-namespace device {
-
-BluetoothProfileWin::BluetoothProfileWin(const std::string& uuid,
- const std::string& name)
- : BluetoothProfile(), uuid_(uuid), name_(name) {
-}
-
-BluetoothProfileWin::~BluetoothProfileWin() {
-}
-
-void BluetoothProfileWin::Unregister() {
- delete this;
-}
-
-void BluetoothProfileWin::SetConnectionCallback(
- const ConnectionCallback& callback) {
- connection_callback_ = callback;
-}
-
-bool BluetoothProfileWin::Connect(const BluetoothDeviceWin* device) {
- if (connection_callback_.is_null())
- return false;
-
- const BluetoothServiceRecord* record = device->GetServiceRecord(uuid_);
- if (record) {
- scoped_refptr<BluetoothSocket> socket(
- BluetoothSocketWin::CreateBluetoothSocket(*record));
- if (socket.get() != NULL) {
- connection_callback_.Run(device, socket);
- return true;
- }
- }
- return false;
-}
-
-} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_profile_win.h b/chromium/device/bluetooth/bluetooth_profile_win.h
deleted file mode 100644
index a102bf5069a..00000000000
--- a/chromium/device/bluetooth/bluetooth_profile_win.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 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_WIN_H_
-#define DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_WIN_H_
-
-#include <string>
-
-#include "device/bluetooth/bluetooth_profile.h"
-
-namespace device {
-
-class BluetoothDeviceWin;
-
-class BluetoothProfileWin : public BluetoothProfile {
- public:
- // BluetoothProfile override.
- virtual void Unregister() OVERRIDE;
- virtual void SetConnectionCallback(
- const ConnectionCallback& callback) OVERRIDE;
-
- bool Connect(const BluetoothDeviceWin* device);
-
- private:
- friend BluetoothProfile;
-
- BluetoothProfileWin(const std::string& uuid, const std::string& name);
- virtual ~BluetoothProfileWin();
-
- const std::string uuid_;
- const std::string name_;
- ConnectionCallback connection_callback_;
-};
-
-} // namespace device
-
-#endif // DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_WIN_H_
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc
new file mode 100644
index 00000000000..83231b9f651
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc
@@ -0,0 +1,479 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_gatt_notify_session_chromeos.h"
+#include "device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.h"
+#include "device/bluetooth/bluetooth_remote_gatt_service_chromeos.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+namespace {
+
+// Stream operator for logging vector<uint8>.
+std::ostream& operator<<(std::ostream& out, const std::vector<uint8> bytes) {
+ out << "[";
+ for (std::vector<uint8>::const_iterator iter = bytes.begin();
+ iter != bytes.end(); ++iter) {
+ out << base::StringPrintf("%02X", *iter);
+ }
+ return out << "]";
+}
+
+} // namespace
+
+BluetoothRemoteGattCharacteristicChromeOS::
+ BluetoothRemoteGattCharacteristicChromeOS(
+ BluetoothRemoteGattServiceChromeOS* service,
+ const dbus::ObjectPath& object_path)
+ : object_path_(object_path),
+ service_(service),
+ num_notify_sessions_(0),
+ notify_call_pending_(false),
+ weak_ptr_factory_(this) {
+ VLOG(1) << "Creating remote GATT characteristic with identifier: "
+ << GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
+ AddObserver(this);
+ DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
+ AddObserver(this);
+
+ // Add all known GATT characteristic descriptors.
+ const std::vector<dbus::ObjectPath>& gatt_descs =
+ DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
+ GetDescriptors();
+ for (std::vector<dbus::ObjectPath>::const_iterator iter = gatt_descs.begin();
+ iter != gatt_descs.end(); ++iter)
+ GattDescriptorAdded(*iter);
+}
+
+BluetoothRemoteGattCharacteristicChromeOS::
+ ~BluetoothRemoteGattCharacteristicChromeOS() {
+ DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
+ RemoveObserver(this);
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
+ RemoveObserver(this);
+
+ // Clean up all the descriptors. There isn't much point in notifying service
+ // observers for each descriptor that gets removed, so just delete them.
+ for (DescriptorMap::iterator iter = descriptors_.begin();
+ iter != descriptors_.end(); ++iter)
+ delete iter->second;
+
+ // Report an error for all pending calls to StartNotifySession.
+ while (!pending_start_notify_calls_.empty()) {
+ PendingStartNotifyCall callbacks = pending_start_notify_calls_.front();
+ pending_start_notify_calls_.pop();
+ callbacks.second.Run();
+ }
+}
+
+std::string BluetoothRemoteGattCharacteristicChromeOS::GetIdentifier() const {
+ return object_path_.value();
+}
+
+device::BluetoothUUID
+BluetoothRemoteGattCharacteristicChromeOS::GetUUID() const {
+ BluetoothGattCharacteristicClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
+ GetProperties(object_path_);
+ DCHECK(properties);
+ return device::BluetoothUUID(properties->uuid.value());
+}
+
+bool BluetoothRemoteGattCharacteristicChromeOS::IsLocal() const {
+ return false;
+}
+
+const std::vector<uint8>&
+BluetoothRemoteGattCharacteristicChromeOS::GetValue() const {
+ return cached_value_;
+}
+
+device::BluetoothGattService*
+BluetoothRemoteGattCharacteristicChromeOS::GetService() const {
+ return service_;
+}
+
+device::BluetoothGattCharacteristic::Properties
+BluetoothRemoteGattCharacteristicChromeOS::GetProperties() const {
+ BluetoothGattCharacteristicClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
+ GetProperties(object_path_);
+ DCHECK(properties);
+
+ Properties props = kPropertyNone;
+ const std::vector<std::string>& flags = properties->flags.value();
+ for (std::vector<std::string>::const_iterator iter = flags.begin();
+ iter != flags.end();
+ ++iter) {
+ if (*iter == bluetooth_gatt_characteristic::kFlagBroadcast)
+ props |= kPropertyBroadcast;
+ if (*iter == bluetooth_gatt_characteristic::kFlagRead)
+ props |= kPropertyRead;
+ if (*iter == bluetooth_gatt_characteristic::kFlagWriteWithoutResponse)
+ props |= kPropertyWriteWithoutResponse;
+ if (*iter == bluetooth_gatt_characteristic::kFlagWrite)
+ props |= kPropertyWrite;
+ if (*iter == bluetooth_gatt_characteristic::kFlagNotify)
+ props |= kPropertyNotify;
+ if (*iter == bluetooth_gatt_characteristic::kFlagIndicate)
+ props |= kPropertyIndicate;
+ if (*iter == bluetooth_gatt_characteristic::kFlagAuthenticatedSignedWrites)
+ props |= kPropertyAuthenticatedSignedWrites;
+ if (*iter == bluetooth_gatt_characteristic::kFlagExtendedProperties)
+ props |= kPropertyExtendedProperties;
+ if (*iter == bluetooth_gatt_characteristic::kFlagReliableWrite)
+ props |= kPropertyReliableWrite;
+ if (*iter == bluetooth_gatt_characteristic::kFlagWritableAuxiliaries)
+ props |= kPropertyWritableAuxiliaries;
+ }
+
+ return props;
+}
+
+device::BluetoothGattCharacteristic::Permissions
+BluetoothRemoteGattCharacteristicChromeOS::GetPermissions() const {
+ // TODO(armansito): Once BlueZ defines the permissions, return the correct
+ // values here.
+ return kPermissionNone;
+}
+
+bool BluetoothRemoteGattCharacteristicChromeOS::IsNotifying() const {
+ BluetoothGattCharacteristicClient::Properties* properties =
+ DBusThreadManager::Get()
+ ->GetBluetoothGattCharacteristicClient()
+ ->GetProperties(object_path_);
+ DCHECK(properties);
+
+ return properties->notifying.value();
+}
+
+std::vector<device::BluetoothGattDescriptor*>
+BluetoothRemoteGattCharacteristicChromeOS::GetDescriptors() const {
+ std::vector<device::BluetoothGattDescriptor*> descriptors;
+ for (DescriptorMap::const_iterator iter = descriptors_.begin();
+ iter != descriptors_.end(); ++iter)
+ descriptors.push_back(iter->second);
+ return descriptors;
+}
+
+device::BluetoothGattDescriptor*
+BluetoothRemoteGattCharacteristicChromeOS::GetDescriptor(
+ const std::string& identifier) const {
+ DescriptorMap::const_iterator iter =
+ descriptors_.find(dbus::ObjectPath(identifier));
+ if (iter == descriptors_.end())
+ return NULL;
+ return iter->second;
+}
+
+bool BluetoothRemoteGattCharacteristicChromeOS::AddDescriptor(
+ device::BluetoothGattDescriptor* descriptor) {
+ VLOG(1) << "Descriptors cannot be added to a remote GATT characteristic.";
+ return false;
+}
+
+bool BluetoothRemoteGattCharacteristicChromeOS::UpdateValue(
+ const std::vector<uint8>& value) {
+ VLOG(1) << "Cannot update the value of a remote GATT characteristic.";
+ return false;
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::ReadRemoteCharacteristic(
+ const ValueCallback& callback,
+ const ErrorCallback& error_callback) {
+ VLOG(1) << "Sending GATT characteristic read request to characteristic: "
+ << GetIdentifier() << ", UUID: " << GetUUID().canonical_value()
+ << ".";
+
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->ReadValue(
+ object_path_,
+ base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnValueSuccess,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::WriteRemoteCharacteristic(
+ const std::vector<uint8>& new_value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ VLOG(1) << "Sending GATT characteristic write request to characteristic: "
+ << GetIdentifier() << ", UUID: " << GetUUID().canonical_value()
+ << ", with value: " << new_value << ".";
+
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->WriteValue(
+ object_path_,
+ new_value,
+ callback,
+ base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::StartNotifySession(
+ const NotifySessionCallback& callback,
+ const ErrorCallback& error_callback) {
+ VLOG(1) << __func__;
+
+ if (num_notify_sessions_ > 0) {
+ // The characteristic might have stopped notifying even though the session
+ // count is nonzero. This means that notifications stopped outside of our
+ // control and we should reset the count. If the characteristic is still
+ // notifying, then return success. Otherwise, reset the count and treat
+ // this call as if the count were 0.
+ if (IsNotifying()) {
+ // Check for overflows, though unlikely.
+ if (num_notify_sessions_ == std::numeric_limits<size_t>::max()) {
+ error_callback.Run();
+ return;
+ }
+
+ ++num_notify_sessions_;
+ DCHECK(service_);
+ DCHECK(service_->GetDevice());
+ scoped_ptr<device::BluetoothGattNotifySession> session(
+ new BluetoothGattNotifySessionChromeOS(
+ service_->GetAdapter(),
+ service_->GetDevice()->GetAddress(),
+ service_->GetIdentifier(),
+ GetIdentifier(),
+ object_path_));
+ callback.Run(session.Pass());
+ return;
+ }
+
+ num_notify_sessions_ = 0;
+ }
+
+ // Queue the callbacks if there is a pending call to bluetoothd.
+ if (notify_call_pending_) {
+ pending_start_notify_calls_.push(std::make_pair(callback, error_callback));
+ return;
+ }
+
+ notify_call_pending_ = true;
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->StartNotify(
+ object_path_,
+ base::Bind(
+ &BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifySuccess,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifyError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::RemoveNotifySession(
+ const base::Closure& callback) {
+ VLOG(1) << __func__;
+
+ if (num_notify_sessions_ > 1) {
+ DCHECK(!notify_call_pending_);
+ --num_notify_sessions_;
+ callback.Run();
+ return;
+ }
+
+ // Notifications may have stopped outside our control. If the characteristic
+ // is no longer notifying, return success.
+ if (!IsNotifying()) {
+ num_notify_sessions_ = 0;
+ callback.Run();
+ return;
+ }
+
+ if (notify_call_pending_ || num_notify_sessions_ == 0) {
+ callback.Run();
+ return;
+ }
+
+ DCHECK(num_notify_sessions_ == 1);
+ notify_call_pending_ = true;
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->StopNotify(
+ object_path_,
+ base::Bind(
+ &BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifySuccess,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifyError,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback));
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::GattCharacteristicValueUpdated(
+ const dbus::ObjectPath& object_path,
+ const std::vector<uint8>& value) {
+ if (object_path != object_path_)
+ return;
+
+ cached_value_ = value;
+
+ VLOG(1) << "GATT characteristic value has changed: " << object_path.value()
+ << ": " << value;
+ DCHECK(service_);
+ service_->NotifyCharacteristicValueChanged(this, value);
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::GattDescriptorAdded(
+ const dbus::ObjectPath& object_path) {
+ if (descriptors_.find(object_path) != descriptors_.end()) {
+ VLOG(1) << "Remote GATT characteristic descriptor already exists: "
+ << object_path.value();
+ return;
+ }
+
+ BluetoothGattDescriptorClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
+ GetProperties(object_path);
+ DCHECK(properties);
+ if (properties->characteristic.value() != object_path_) {
+ VLOG(2) << "Remote GATT descriptor does not belong to this characteristic.";
+ return;
+ }
+
+ VLOG(1) << "Adding new remote GATT descriptor for GATT characteristic: "
+ << GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
+
+ BluetoothRemoteGattDescriptorChromeOS* descriptor =
+ new BluetoothRemoteGattDescriptorChromeOS(this, object_path);
+ descriptors_[object_path] = descriptor;
+ DCHECK(descriptor->GetIdentifier() == object_path.value());
+ DCHECK(descriptor->GetUUID().IsValid());
+ DCHECK(service_);
+
+ service_->NotifyDescriptorAddedOrRemoved(this, descriptor, true /* added */);
+ service_->NotifyServiceChanged();
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::GattDescriptorRemoved(
+ const dbus::ObjectPath& object_path) {
+ DescriptorMap::iterator iter = descriptors_.find(object_path);
+ if (iter == descriptors_.end()) {
+ VLOG(2) << "Unknown descriptor removed: " << object_path.value();
+ return;
+ }
+
+ VLOG(1) << "Removing remote GATT descriptor from characteristic: "
+ << GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
+
+ BluetoothRemoteGattDescriptorChromeOS* descriptor = iter->second;
+ DCHECK(descriptor->object_path() == object_path);
+ descriptors_.erase(iter);
+
+ service_->NotifyDescriptorAddedOrRemoved(this, descriptor, false /* added */);
+ delete descriptor;
+
+ DCHECK(service_);
+
+ service_->NotifyServiceChanged();
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::OnValueSuccess(
+ const ValueCallback& callback,
+ const std::vector<uint8>& value) {
+ VLOG(1) << "Characteristic value read: " << value;
+ cached_value_ = value;
+
+ DCHECK(service_);
+ service_->NotifyCharacteristicValueChanged(this, cached_value_);
+
+ callback.Run(value);
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::OnError(
+ const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ VLOG(1) << "Operation failed: " << error_name << ", message: "
+ << error_message;
+ error_callback.Run();
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifySuccess(
+ const NotifySessionCallback& callback) {
+ VLOG(1) << "Started notifications from characteristic: "
+ << object_path_.value();
+ DCHECK(num_notify_sessions_ == 0);
+ DCHECK(notify_call_pending_);
+
+ ++num_notify_sessions_;
+ notify_call_pending_ = false;
+
+ // Invoke the queued callbacks for this operation.
+ DCHECK(service_);
+ DCHECK(service_->GetDevice());
+ scoped_ptr<device::BluetoothGattNotifySession> session(
+ new BluetoothGattNotifySessionChromeOS(
+ service_->GetAdapter(),
+ service_->GetDevice()->GetAddress(),
+ service_->GetIdentifier(),
+ GetIdentifier(),
+ object_path_));
+ callback.Run(session.Pass());
+
+ ProcessStartNotifyQueue();
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifyError(
+ const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ VLOG(1) << "Failed to start notifications from characteristic: "
+ << object_path_.value() << ": " << error_name << ", "
+ << error_message;
+ DCHECK(num_notify_sessions_ == 0);
+ DCHECK(notify_call_pending_);
+
+ notify_call_pending_ = false;
+ error_callback.Run();
+
+ ProcessStartNotifyQueue();
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifySuccess(
+ const base::Closure& callback) {
+ DCHECK(notify_call_pending_);
+ DCHECK(num_notify_sessions_ == 1);
+
+ notify_call_pending_ = false;
+ --num_notify_sessions_;
+ callback.Run();
+
+ ProcessStartNotifyQueue();
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifyError(
+ const base::Closure& callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ VLOG(1) << "Call to stop notifications failed for characteristic: "
+ << object_path_.value() << ": " << error_name << ", "
+ << error_message;
+
+ // Since this is a best effort operation, treat this as success.
+ OnStopNotifySuccess(callback);
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::ProcessStartNotifyQueue() {
+ while (!pending_start_notify_calls_.empty()) {
+ PendingStartNotifyCall callbacks = pending_start_notify_calls_.front();
+ pending_start_notify_calls_.pop();
+ StartNotifySession(callbacks.first, callbacks.second);
+ }
+}
+
+} // namespace chromeos
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h
new file mode 100644
index 00000000000..26fda0b9b1f
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h
@@ -0,0 +1,169 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_CHROMEOS_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_CHROMEOS_H_
+
+#include <map>
+#include <queue>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "chromeos/dbus/bluetooth_gatt_characteristic_client.h"
+#include "chromeos/dbus/bluetooth_gatt_descriptor_client.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace device {
+
+class BluetoothGattDescriptor;
+class BluetoothGattService;
+
+} // namespace device
+
+namespace chromeos {
+
+class BluetoothRemoteGattDescriptorChromeOS;
+class BluetoothRemoteGattServiceChromeOS;
+
+// The BluetoothRemoteGattCharacteristicChromeOS class implements
+// BluetoothGattCharacteristic for remote GATT characteristics on the Chrome OS
+// platform.
+class BluetoothRemoteGattCharacteristicChromeOS
+ : public device::BluetoothGattCharacteristic,
+ public BluetoothGattCharacteristicClient::Observer,
+ public BluetoothGattDescriptorClient::Observer {
+ public:
+ // device::BluetoothGattCharacteristic overrides.
+ virtual std::string GetIdentifier() const OVERRIDE;
+ virtual device::BluetoothUUID GetUUID() const OVERRIDE;
+ virtual bool IsLocal() const OVERRIDE;
+ virtual const std::vector<uint8>& GetValue() const OVERRIDE;
+ virtual device::BluetoothGattService* GetService() const OVERRIDE;
+ virtual Properties GetProperties() const OVERRIDE;
+ virtual Permissions GetPermissions() const OVERRIDE;
+ virtual bool IsNotifying() const OVERRIDE;
+ virtual std::vector<device::BluetoothGattDescriptor*>
+ GetDescriptors() const OVERRIDE;
+ virtual device::BluetoothGattDescriptor* GetDescriptor(
+ const std::string& identifier) const OVERRIDE;
+ virtual bool AddDescriptor(
+ device::BluetoothGattDescriptor* descriptor) OVERRIDE;
+ virtual bool UpdateValue(const std::vector<uint8>& value) OVERRIDE;
+ virtual void ReadRemoteCharacteristic(
+ const ValueCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual void WriteRemoteCharacteristic(
+ const std::vector<uint8>& new_value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual void StartNotifySession(const NotifySessionCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+
+ // Removes one value update session and invokes |callback| on completion. This
+ // decrements the session reference count by 1 and if the number reaches 0,
+ // makes a call to the subsystem to stop notifications from this
+ // characteristic.
+ void RemoveNotifySession(const base::Closure& callback);
+
+ // Object path of the underlying D-Bus characteristic.
+ const dbus::ObjectPath& object_path() const { return object_path_; }
+
+ private:
+ friend class BluetoothRemoteGattServiceChromeOS;
+
+ BluetoothRemoteGattCharacteristicChromeOS(
+ BluetoothRemoteGattServiceChromeOS* service,
+ const dbus::ObjectPath& object_path);
+ virtual ~BluetoothRemoteGattCharacteristicChromeOS();
+
+ // BluetoothGattCharacteristicClient::Observer overrides.
+ virtual void GattCharacteristicValueUpdated(
+ const dbus::ObjectPath& object_path,
+ const std::vector<uint8>& value) OVERRIDE;
+
+ // BluetoothGattDescriptorClient::Observer overrides.
+ virtual void GattDescriptorAdded(
+ const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void GattDescriptorRemoved(
+ const dbus::ObjectPath& object_path) OVERRIDE;
+
+ // Called by dbus:: on successful completion of a request to read
+ // the characteristic value.
+ void OnValueSuccess(const ValueCallback& callback,
+ const std::vector<uint8>& value);
+
+ // Called by dbus:: on unsuccessful completion of a request to read or write
+ // the characteristic value.
+ void OnError(const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Called by dbus:: on successful completion of a request to start
+ // notifications.
+ void OnStartNotifySuccess(const NotifySessionCallback& callback);
+
+ // Called by dbus:: on unsuccessful completion of a request to start
+ // notifications.
+ void OnStartNotifyError(const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Called by dbus:: on successful completion of a request to stop
+ // notifications.
+ void OnStopNotifySuccess(const base::Closure& callback);
+
+ // Called by dbus:: on unsuccessful completion of a request to stop
+ // notifications.
+ void OnStopNotifyError(const base::Closure& callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Calls StartNotifySession for each queued request.
+ void ProcessStartNotifyQueue();
+
+ // Object path of the D-Bus characteristic object.
+ dbus::ObjectPath object_path_;
+
+ // The GATT service this GATT characteristic belongs to.
+ BluetoothRemoteGattServiceChromeOS* service_;
+
+ // The cached characteristic value based on the most recent read or
+ // notification.
+ std::vector<uint8> cached_value_;
+
+ // The total number of currently active value update sessions.
+ size_t num_notify_sessions_;
+
+ // Calls to StartNotifySession that are pending. This can happen during the
+ // first remote call to start notifications.
+ typedef std::pair<NotifySessionCallback, ErrorCallback>
+ PendingStartNotifyCall;
+ std::queue<PendingStartNotifyCall> pending_start_notify_calls_;
+
+ // True, if a Start or Stop notify call to bluetoothd is currently pending.
+ bool notify_call_pending_;
+
+ // Mapping from GATT descriptor object paths to descriptor objects owned by
+ // this characteristic. Since the Chrome OS implementation uses object paths
+ // as unique identifiers, we also use this mapping to return descriptors by
+ // identifier.
+ typedef std::map<dbus::ObjectPath, BluetoothRemoteGattDescriptorChromeOS*>
+ DescriptorMap;
+ DescriptorMap descriptors_;
+
+ // 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<BluetoothRemoteGattCharacteristicChromeOS>
+ weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattCharacteristicChromeOS);
+};
+
+} // namespace chromeos
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_CHROMEOS_H_
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.cc
new file mode 100644
index 00000000000..8978cc55812
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.cc
@@ -0,0 +1,137 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "chromeos/dbus/bluetooth_gatt_descriptor_client.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h"
+#include "device/bluetooth/bluetooth_remote_gatt_service_chromeos.h"
+
+namespace chromeos {
+
+namespace {
+
+// Stream operator for logging vector<uint8>.
+std::ostream& operator<<(std::ostream& out, const std::vector<uint8> bytes) {
+ out << "[";
+ for (std::vector<uint8>::const_iterator iter = bytes.begin();
+ iter != bytes.end(); ++iter) {
+ out << base::StringPrintf("%02X", *iter);
+ }
+ return out << "]";
+}
+
+} // namespace
+
+BluetoothRemoteGattDescriptorChromeOS::BluetoothRemoteGattDescriptorChromeOS(
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic,
+ const dbus::ObjectPath& object_path)
+ : object_path_(object_path),
+ characteristic_(characteristic),
+ weak_ptr_factory_(this) {
+ VLOG(1) << "Creating remote GATT descriptor with identifier: "
+ << GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
+}
+
+BluetoothRemoteGattDescriptorChromeOS::
+ ~BluetoothRemoteGattDescriptorChromeOS() {
+}
+
+std::string BluetoothRemoteGattDescriptorChromeOS::GetIdentifier() const {
+ return object_path_.value();
+}
+
+device::BluetoothUUID BluetoothRemoteGattDescriptorChromeOS::GetUUID() const {
+ BluetoothGattDescriptorClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->
+ GetProperties(object_path_);
+ DCHECK(properties);
+ return device::BluetoothUUID(properties->uuid.value());
+}
+
+bool BluetoothRemoteGattDescriptorChromeOS::IsLocal() const {
+ return false;
+}
+
+const std::vector<uint8>&
+BluetoothRemoteGattDescriptorChromeOS::GetValue() const {
+ return cached_value_;
+}
+
+device::BluetoothGattCharacteristic*
+BluetoothRemoteGattDescriptorChromeOS::GetCharacteristic() const {
+ return characteristic_;
+}
+
+device::BluetoothGattCharacteristic::Permissions
+BluetoothRemoteGattDescriptorChromeOS::GetPermissions() const {
+ // TODO(armansito): Once BlueZ defines the permissions, return the correct
+ // values here.
+ return device::BluetoothGattCharacteristic::kPermissionNone;
+}
+
+void BluetoothRemoteGattDescriptorChromeOS::ReadRemoteDescriptor(
+ const ValueCallback& callback,
+ const ErrorCallback& error_callback) {
+ VLOG(1) << "Sending GATT characteristic descriptor read request to "
+ << "descriptor: " << GetIdentifier() << ", UUID: "
+ << GetUUID().canonical_value();
+
+ DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->ReadValue(
+ object_path_,
+ base::Bind(&BluetoothRemoteGattDescriptorChromeOS::OnValueSuccess,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&BluetoothRemoteGattDescriptorChromeOS::OnError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+}
+
+void BluetoothRemoteGattDescriptorChromeOS::WriteRemoteDescriptor(
+ const std::vector<uint8>& new_value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ VLOG(1) << "Sending GATT characteristic descriptor write request to "
+ << "characteristic: " << GetIdentifier() << ", UUID: "
+ << GetUUID().canonical_value() << ", with value: "
+ << new_value << ".";
+
+ DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()->WriteValue(
+ object_path_,
+ new_value,
+ callback,
+ base::Bind(&BluetoothRemoteGattDescriptorChromeOS::OnError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+}
+
+void BluetoothRemoteGattDescriptorChromeOS::OnValueSuccess(
+ const ValueCallback& callback,
+ const std::vector<uint8>& value) {
+ VLOG(1) << "Descriptor value read: " << value;
+ cached_value_ = value;
+
+ DCHECK(characteristic_);
+ BluetoothRemoteGattServiceChromeOS* service =
+ static_cast<BluetoothRemoteGattServiceChromeOS*>(
+ characteristic_->GetService());
+ DCHECK(service);
+ service->NotifyDescriptorValueChanged(characteristic_, this, value);
+ callback.Run(value);
+}
+
+void BluetoothRemoteGattDescriptorChromeOS::OnError(
+ const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ VLOG(1) << "Operation failed: " << error_name
+ << ", message: " << error_message;
+ error_callback.Run();
+}
+
+} // namespace chromeos
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.h b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.h
new file mode 100644
index 00000000000..d4ea1e68217
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.h
@@ -0,0 +1,89 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_DESCRIPTOR_CHROMEOS_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_DESCRIPTOR_CHROMEOS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_gatt_descriptor.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace device {
+
+class BluetoothGattCharacteristic;
+
+} // namespace device
+
+namespace chromeos {
+
+class BluetoothRemoteGattCharacteristicChromeOS;
+
+// The BluetoothRemoteGattDescriptorChromeOS class implements
+// BluetoothGattDescriptor for remote GATT characteristic descriptors on the
+// Chrome OS platform.
+class BluetoothRemoteGattDescriptorChromeOS
+ : public device::BluetoothGattDescriptor {
+ public:
+ // device::BluetoothGattDescriptor overrides.
+ virtual std::string GetIdentifier() const OVERRIDE;
+ virtual device::BluetoothUUID GetUUID() const OVERRIDE;
+ virtual bool IsLocal() const OVERRIDE;
+ virtual const std::vector<uint8>& GetValue() const OVERRIDE;
+ virtual device::BluetoothGattCharacteristic*
+ GetCharacteristic() const OVERRIDE;
+ virtual device::BluetoothGattCharacteristic::Permissions
+ GetPermissions() const OVERRIDE;
+ virtual void ReadRemoteDescriptor(
+ const ValueCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual void WriteRemoteDescriptor(
+ const std::vector<uint8>& new_value,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+
+ // Object path of the underlying D-Bus characteristic.
+ const dbus::ObjectPath& object_path() const { return object_path_; }
+
+ private:
+ friend class BluetoothRemoteGattCharacteristicChromeOS;
+
+ BluetoothRemoteGattDescriptorChromeOS(
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic,
+ const dbus::ObjectPath& object_path);
+ virtual ~BluetoothRemoteGattDescriptorChromeOS();
+
+ // Called by dbus:: on successful completion of a request to read
+ // the descriptor value.
+ void OnValueSuccess(const ValueCallback& callback,
+ const std::vector<uint8>& value);
+
+ // Called by dbus:: on unsuccessful completion of a request to read or write
+ // the descriptor value.
+ void OnError(const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Object path of the D-Bus descriptor object.
+ dbus::ObjectPath object_path_;
+
+ // The GATT characteristic this descriptor belongs to.
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic_;
+
+ // The cached characteristic value based on the most recent read request.
+ std::vector<uint8> cached_value_;
+
+ // 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<BluetoothRemoteGattDescriptorChromeOS> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattDescriptorChromeOS);
+};
+
+} // namespace chromeos
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_DESCRIPTOR_CHROMEOS_H_
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service_chromeos.cc b/chromium/device/bluetooth/bluetooth_remote_gatt_service_chromeos.cc
new file mode 100644
index 00000000000..70382703187
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_chromeos.cc
@@ -0,0 +1,286 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_remote_gatt_service_chromeos.h"
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "chromeos/dbus/bluetooth_gatt_service_client.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "device/bluetooth/bluetooth_adapter_chromeos.h"
+#include "device/bluetooth/bluetooth_device_chromeos.h"
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h"
+#include "device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.h"
+
+namespace chromeos {
+
+BluetoothRemoteGattServiceChromeOS::BluetoothRemoteGattServiceChromeOS(
+ BluetoothAdapterChromeOS* adapter,
+ BluetoothDeviceChromeOS* device,
+ const dbus::ObjectPath& object_path)
+ : object_path_(object_path),
+ adapter_(adapter),
+ device_(device),
+ weak_ptr_factory_(this) {
+ VLOG(1) << "Creating remote GATT service with identifier: "
+ << object_path.value() << ", UUID: " << GetUUID().canonical_value();
+ DCHECK(adapter_);
+
+ DBusThreadManager::Get()->GetBluetoothGattServiceClient()->AddObserver(this);
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
+ AddObserver(this);
+
+ // Add all known GATT characteristics.
+ const std::vector<dbus::ObjectPath>& gatt_chars =
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
+ GetCharacteristics();
+ for (std::vector<dbus::ObjectPath>::const_iterator iter = gatt_chars.begin();
+ iter != gatt_chars.end(); ++iter)
+ GattCharacteristicAdded(*iter);
+}
+
+BluetoothRemoteGattServiceChromeOS::~BluetoothRemoteGattServiceChromeOS() {
+ DBusThreadManager::Get()->GetBluetoothGattServiceClient()->
+ RemoveObserver(this);
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
+ RemoveObserver(this);
+
+ // Clean up all the characteristics. Copy the characteristics list here and
+ // clear the original so that when we send GattCharacteristicRemoved(),
+ // GetCharacteristics() returns no characteristics.
+ CharacteristicMap characteristics = characteristics_;
+ characteristics_.clear();
+ for (CharacteristicMap::iterator iter = characteristics.begin();
+ iter != characteristics.end(); ++iter) {
+ FOR_EACH_OBSERVER(device::BluetoothGattService::Observer, observers_,
+ GattCharacteristicRemoved(this, iter->second));
+ delete iter->second;
+ }
+}
+
+void BluetoothRemoteGattServiceChromeOS::AddObserver(
+ device::BluetoothGattService::Observer* observer) {
+ DCHECK(observer);
+ observers_.AddObserver(observer);
+}
+
+void BluetoothRemoteGattServiceChromeOS::RemoveObserver(
+ device::BluetoothGattService::Observer* observer) {
+ DCHECK(observer);
+ observers_.RemoveObserver(observer);
+}
+
+std::string BluetoothRemoteGattServiceChromeOS::GetIdentifier() const {
+ return object_path_.value();
+}
+
+device::BluetoothUUID BluetoothRemoteGattServiceChromeOS::GetUUID() const {
+ BluetoothGattServiceClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothGattServiceClient()->
+ GetProperties(object_path_);
+ DCHECK(properties);
+ return device::BluetoothUUID(properties->uuid.value());
+}
+
+bool BluetoothRemoteGattServiceChromeOS::IsLocal() const {
+ return false;
+}
+
+bool BluetoothRemoteGattServiceChromeOS::IsPrimary() const {
+ BluetoothGattServiceClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothGattServiceClient()->
+ GetProperties(object_path_);
+ DCHECK(properties);
+ return properties->primary.value();
+}
+
+device::BluetoothDevice* BluetoothRemoteGattServiceChromeOS::GetDevice() const {
+ return device_;
+}
+
+std::vector<device::BluetoothGattCharacteristic*>
+BluetoothRemoteGattServiceChromeOS::GetCharacteristics() const {
+ std::vector<device::BluetoothGattCharacteristic*> characteristics;
+ for (CharacteristicMap::const_iterator iter = characteristics_.begin();
+ iter != characteristics_.end(); ++iter) {
+ characteristics.push_back(iter->second);
+ }
+ return characteristics;
+}
+
+std::vector<device::BluetoothGattService*>
+BluetoothRemoteGattServiceChromeOS::GetIncludedServices() const {
+ // TODO(armansito): Return the actual included services here.
+ return std::vector<device::BluetoothGattService*>();
+}
+
+device::BluetoothGattCharacteristic*
+BluetoothRemoteGattServiceChromeOS::GetCharacteristic(
+ const std::string& identifier) const {
+ CharacteristicMap::const_iterator iter =
+ characteristics_.find(dbus::ObjectPath(identifier));
+ if (iter == characteristics_.end())
+ return NULL;
+ return iter->second;
+}
+
+bool BluetoothRemoteGattServiceChromeOS::AddCharacteristic(
+ device::BluetoothGattCharacteristic* characteristic) {
+ VLOG(1) << "Characteristics cannot be added to a remote GATT service.";
+ return false;
+}
+
+bool BluetoothRemoteGattServiceChromeOS::AddIncludedService(
+ device::BluetoothGattService* service) {
+ VLOG(1) << "Included services cannot be added to a remote GATT service.";
+ return false;
+}
+
+void BluetoothRemoteGattServiceChromeOS::Register(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ VLOG(1) << "A remote GATT service cannot be registered.";
+ error_callback.Run();
+}
+
+void BluetoothRemoteGattServiceChromeOS::Unregister(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ VLOG(1) << "A remote GATT service cannot be unregistered.";
+ error_callback.Run();
+}
+
+scoped_refptr<device::BluetoothAdapter>
+BluetoothRemoteGattServiceChromeOS::GetAdapter() const {
+ return adapter_;
+}
+
+void BluetoothRemoteGattServiceChromeOS::NotifyServiceChanged() {
+ FOR_EACH_OBSERVER(device::BluetoothGattService::Observer, observers_,
+ GattServiceChanged(this));
+}
+
+void BluetoothRemoteGattServiceChromeOS::NotifyCharacteristicValueChanged(
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic,
+ const std::vector<uint8>& value) {
+ DCHECK(characteristic->GetService() == this);
+ FOR_EACH_OBSERVER(
+ device::BluetoothGattService::Observer,
+ observers_,
+ GattCharacteristicValueChanged(this, characteristic, value));
+}
+
+void BluetoothRemoteGattServiceChromeOS::NotifyDescriptorAddedOrRemoved(
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic,
+ BluetoothRemoteGattDescriptorChromeOS* descriptor,
+ bool added) {
+ DCHECK(characteristic->GetService() == this);
+ DCHECK(descriptor->GetCharacteristic() == characteristic);
+ if (added) {
+ FOR_EACH_OBSERVER(device::BluetoothGattService::Observer, observers_,
+ GattDescriptorAdded(characteristic, descriptor));
+ return;
+ }
+ FOR_EACH_OBSERVER(device::BluetoothGattService::Observer, observers_,
+ GattDescriptorRemoved(characteristic, descriptor));
+}
+
+void BluetoothRemoteGattServiceChromeOS::NotifyDescriptorValueChanged(
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic,
+ BluetoothRemoteGattDescriptorChromeOS* descriptor,
+ const std::vector<uint8>& value) {
+ DCHECK(characteristic->GetService() == this);
+ DCHECK(descriptor->GetCharacteristic() == characteristic);
+ FOR_EACH_OBSERVER(
+ device::BluetoothGattService::Observer, observers_,
+ GattDescriptorValueChanged(characteristic, descriptor, value));
+}
+
+void BluetoothRemoteGattServiceChromeOS::GattServicePropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name){
+ if (object_path != object_path_)
+ return;
+
+ VLOG(1) << "Service property changed: " << object_path.value();
+ NotifyServiceChanged();
+}
+
+void BluetoothRemoteGattServiceChromeOS::GattCharacteristicAdded(
+ const dbus::ObjectPath& object_path) {
+ if (characteristics_.find(object_path) != characteristics_.end()) {
+ VLOG(1) << "Remote GATT characteristic already exists: "
+ << object_path.value();
+ return;
+ }
+
+ BluetoothGattCharacteristicClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
+ GetProperties(object_path);
+ DCHECK(properties);
+ if (properties->service.value() != object_path_) {
+ VLOG(2) << "Remote GATT characteristic does not belong to this service.";
+ return;
+ }
+
+ VLOG(1) << "Adding new remote GATT characteristic for GATT service: "
+ << GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
+
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic =
+ new BluetoothRemoteGattCharacteristicChromeOS(this, object_path);
+ characteristics_[object_path] = characteristic;
+ DCHECK(characteristic->GetIdentifier() == object_path.value());
+ DCHECK(characteristic->GetUUID().IsValid());
+
+ FOR_EACH_OBSERVER(device::BluetoothGattService::Observer, observers_,
+ GattCharacteristicAdded(this, characteristic));
+ NotifyServiceChanged();
+}
+
+void BluetoothRemoteGattServiceChromeOS::GattCharacteristicRemoved(
+ const dbus::ObjectPath& object_path) {
+ CharacteristicMap::iterator iter = characteristics_.find(object_path);
+ if (iter == characteristics_.end()) {
+ VLOG(2) << "Unknown GATT characteristic removed: " << object_path.value();
+ return;
+ }
+
+ VLOG(1) << "Removing remote GATT characteristic from service: "
+ << GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
+
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic = iter->second;
+ DCHECK(characteristic->object_path() == object_path);
+ characteristics_.erase(iter);
+
+ FOR_EACH_OBSERVER(device::BluetoothGattService::Observer, observers_,
+ GattCharacteristicRemoved(this, characteristic));
+ NotifyServiceChanged();
+
+ delete characteristic;
+}
+
+void BluetoothRemoteGattServiceChromeOS::GattCharacteristicPropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) {
+ if (characteristics_.find(object_path) == characteristics_.end()) {
+ VLOG(2) << "Properties of unknown characteristic changed";
+ return;
+ }
+
+ // We may receive a property changed event in certain cases, e.g. when the
+ // characteristic "Flags" property has been updated with values from the
+ // "Characteristic Extended Properties" descriptor. In this case, kick off
+ // a service changed observer event to let observers refresh the
+ // characteristics.
+ BluetoothGattCharacteristicClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
+ GetProperties(object_path);
+ DCHECK(properties);
+ if (property_name != properties->flags.name())
+ return;
+
+ NotifyServiceChanged();
+}
+
+} // namespace chromeos
diff --git a/chromium/device/bluetooth/bluetooth_remote_gatt_service_chromeos.h b/chromium/device/bluetooth/bluetooth_remote_gatt_service_chromeos.h
new file mode 100644
index 00000000000..ce481ec993d
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_remote_gatt_service_chromeos.h
@@ -0,0 +1,158 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_SERVICE_CHROMEOS_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_SERVICE_CHROMEOS_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "chromeos/dbus/bluetooth_gatt_characteristic_client.h"
+#include "chromeos/dbus/bluetooth_gatt_service_client.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_gatt_service.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace device {
+
+class BluetoothAdapter;
+class BluetoothGattCharacteristic;
+
+} // namespace device
+
+namespace chromeos {
+
+class BluetoothAdapterChromeOS;
+class BluetoothDeviceChromeOS;
+class BluetoothRemoteGattCharacteristicChromeOS;
+class BluetoothRemoteGattDescriptorChromeOS;
+
+// The BluetoothRemoteGattServiceChromeOS class implements BluetootGattService
+// for remote GATT services on the the Chrome OS platform.
+class BluetoothRemoteGattServiceChromeOS
+ : public device::BluetoothGattService,
+ public BluetoothGattServiceClient::Observer,
+ public BluetoothGattCharacteristicClient::Observer {
+ public:
+ // device::BluetoothGattService overrides.
+ virtual void AddObserver(
+ device::BluetoothGattService::Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(
+ device::BluetoothGattService::Observer* observer) OVERRIDE;
+ virtual std::string GetIdentifier() const OVERRIDE;
+ virtual device::BluetoothUUID GetUUID() const OVERRIDE;
+ virtual bool IsLocal() const OVERRIDE;
+ virtual bool IsPrimary() const OVERRIDE;
+ virtual device::BluetoothDevice* GetDevice() const OVERRIDE;
+ virtual std::vector<device::BluetoothGattCharacteristic*>
+ GetCharacteristics() const OVERRIDE;
+ virtual std::vector<device::BluetoothGattService*>
+ GetIncludedServices() const OVERRIDE;
+ virtual device::BluetoothGattCharacteristic* GetCharacteristic(
+ const std::string& identifier) const OVERRIDE;
+ virtual bool AddCharacteristic(
+ device::BluetoothGattCharacteristic* characteristic) OVERRIDE;
+ virtual bool AddIncludedService(
+ device::BluetoothGattService* service) OVERRIDE;
+ virtual void Register(const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual void Unregister(const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+
+ // Object path of the underlying service.
+ const dbus::ObjectPath& object_path() const { return object_path_; }
+
+ // Returns the adapter associated with this service.
+ scoped_refptr<device::BluetoothAdapter> GetAdapter() const;
+
+ // Notifies its observers that the GATT service has changed. This is mainly
+ // used by BluetoothRemoteGattCharacteristicChromeOS instances to notify
+ // service observers when characteristic descriptors get added and removed.
+ void NotifyServiceChanged();
+
+ // Notifies its observers that the value of a characteristic has changed.
+ // Called by BluetoothRemoteGattCharacteristicChromeOS instances to notify
+ // service observers when their cached value is updated after a successful
+ // read request or when a "ValueUpdated" signal is received.
+ void NotifyCharacteristicValueChanged(
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic,
+ const std::vector<uint8>& value);
+
+ // Notifies its observers that a descriptor |descriptor| belonging to
+ // characteristic |characteristic| has been added or removed. This is used
+ // by BluetoothRemoteGattCharacteristicChromeOS instances to notify service
+ // observers when characteristic descriptors get added and removed. If |added|
+ // is true, an "Added" event will be sent. Otherwise, a "Removed" event will
+ // be sent.
+ void NotifyDescriptorAddedOrRemoved(
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic,
+ BluetoothRemoteGattDescriptorChromeOS* descriptor,
+ bool added);
+
+ // Notifies its observers that the value of a descriptor has changed. Called
+ // by BluetoothRemoteGattDescriptorChromeOS instances to notify service
+ // observers when their cached value gets updated after a read request.
+ void NotifyDescriptorValueChanged(
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic,
+ BluetoothRemoteGattDescriptorChromeOS* descriptor,
+ const std::vector<uint8>& value);
+
+ private:
+ friend class BluetoothDeviceChromeOS;
+
+ BluetoothRemoteGattServiceChromeOS(BluetoothAdapterChromeOS* adapter,
+ BluetoothDeviceChromeOS* device,
+ const dbus::ObjectPath& object_path);
+ virtual ~BluetoothRemoteGattServiceChromeOS();
+
+ // BluetoothGattServiceClient::Observer override.
+ virtual void GattServicePropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) OVERRIDE;
+
+ // BluetoothGattCharacteristicClient::Observer override.
+ virtual void GattCharacteristicAdded(
+ const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void GattCharacteristicRemoved(
+ const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void GattCharacteristicPropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) OVERRIDE;
+
+ // Object path of the GATT service.
+ dbus::ObjectPath object_path_;
+
+ // List of observers interested in event notifications from us.
+ ObserverList<device::BluetoothGattService::Observer> observers_;
+
+ // The adapter associated with this service. It's ok to store a raw pointer
+ // here since |adapter_| indirectly owns this instance.
+ BluetoothAdapterChromeOS* adapter_;
+
+ // The device this GATT service belongs to. It's ok to store a raw pointer
+ // here since |device_| owns this instance.
+ BluetoothDeviceChromeOS* device_;
+
+ // Mapping from GATT characteristic object paths to characteristic objects.
+ // owned by this service. Since the Chrome OS implementation uses object
+ // paths as unique identifiers, we also use this mapping to return
+ // characteristics by identifier.
+ typedef std::map<dbus::ObjectPath, BluetoothRemoteGattCharacteristicChromeOS*>
+ CharacteristicMap;
+ CharacteristicMap characteristics_;
+
+ // 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<BluetoothRemoteGattServiceChromeOS> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattServiceChromeOS);
+};
+
+} // namespace chromeos
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_SERVICE_CHROMEOS_H_
diff --git a/chromium/device/bluetooth/bluetooth_rfcomm_channel_mac.h b/chromium/device/bluetooth/bluetooth_rfcomm_channel_mac.h
new file mode 100644
index 00000000000..cc64010b1d9
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_rfcomm_channel_mac.h
@@ -0,0 +1,69 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_RFCOMM_CHANNEL_MAC_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_RFCOMM_CHANNEL_MAC_H_
+
+#import <IOBluetooth/IOBluetooth.h>
+#import <IOKit/IOReturn.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "device/bluetooth/bluetooth_channel_mac.h"
+
+@class BluetoothRfcommChannelDelegate;
+
+namespace device {
+
+class BluetoothRfcommChannelMac : public BluetoothChannelMac {
+ public:
+ // Creates a new RFCOMM channel wrapper with the given |socket| and native
+ // |channel|.
+ // NOTE: The |channel| is expected to already be retained.
+ BluetoothRfcommChannelMac(BluetoothSocketMac* socket,
+ IOBluetoothRFCOMMChannel* channel);
+ virtual ~BluetoothRfcommChannelMac();
+
+ // Opens a new RFCOMM channel with Channel ID |channel_id| to the target
+ // |device|. Returns the opened channel and sets |status| to kIOReturnSuccess
+ // if the open process was successfully started (or if an existing RFCOMM
+ // channel was found). Otherwise, sets |status| to an error status.
+ static scoped_ptr<BluetoothRfcommChannelMac> OpenAsync(
+ BluetoothSocketMac* socket,
+ IOBluetoothDevice* device,
+ BluetoothRFCOMMChannelID channel_id,
+ IOReturn* status);
+
+ // BluetoothChannelMac:
+ virtual void SetSocket(BluetoothSocketMac* socket) OVERRIDE;
+ virtual IOBluetoothDevice* GetDevice() OVERRIDE;
+ virtual uint16_t GetOutgoingMTU() OVERRIDE;
+ virtual IOReturn WriteAsync(void* data,
+ uint16_t length,
+ void* refcon) OVERRIDE;
+
+ void OnChannelOpenComplete(IOBluetoothRFCOMMChannel* channel,
+ IOReturn status);
+ void OnChannelClosed(IOBluetoothRFCOMMChannel* channel);
+ void OnChannelDataReceived(IOBluetoothRFCOMMChannel* channel,
+ void* data,
+ size_t length);
+ void OnChannelWriteComplete(IOBluetoothRFCOMMChannel* channel,
+ void* refcon,
+ IOReturn status);
+
+ private:
+ // The wrapped native RFCOMM channel.
+ base::scoped_nsobject<IOBluetoothRFCOMMChannel> channel_;
+
+ // The delegate for the native channel.
+ base::scoped_nsobject<BluetoothRfcommChannelDelegate> delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothRfcommChannelMac);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_RFCOMM_CHANNEL_MAC_H_
diff --git a/chromium/device/bluetooth/bluetooth_rfcomm_channel_mac.mm b/chromium/device/bluetooth/bluetooth_rfcomm_channel_mac.mm
new file mode 100644
index 00000000000..693a2965857
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_rfcomm_channel_mac.mm
@@ -0,0 +1,168 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_rfcomm_channel_mac.h"
+
+#include "base/logging.h"
+#include "device/bluetooth/bluetooth_device_mac.h"
+#include "device/bluetooth/bluetooth_socket_mac.h"
+
+// A simple delegate class for an open RFCOMM channel that forwards methods to
+// its wrapped |channel_|.
+@interface BluetoothRfcommChannelDelegate
+ : NSObject <IOBluetoothRFCOMMChannelDelegate> {
+ @private
+ device::BluetoothRfcommChannelMac* channel_; // weak
+}
+
+- (id)initWithChannel:(device::BluetoothRfcommChannelMac*)channel;
+
+@end
+
+@implementation BluetoothRfcommChannelDelegate
+
+- (id)initWithChannel:(device::BluetoothRfcommChannelMac*)channel {
+ if ((self = [super init]))
+ channel_ = channel;
+
+ return self;
+}
+
+- (void)rfcommChannelOpenComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
+ status:(IOReturn)error {
+ channel_->OnChannelOpenComplete(rfcommChannel, error);
+}
+
+- (void)rfcommChannelWriteComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
+ refcon:(void*)refcon
+ status:(IOReturn)error {
+ channel_->OnChannelWriteComplete(rfcommChannel, refcon, error);
+}
+
+- (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel
+ data:(void*)dataPointer
+ length:(size_t)dataLength {
+ channel_->OnChannelDataReceived(rfcommChannel, dataPointer, dataLength);
+}
+
+- (void)rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel {
+ channel_->OnChannelClosed(rfcommChannel);
+}
+
+@end
+
+namespace device {
+
+BluetoothRfcommChannelMac::BluetoothRfcommChannelMac(
+ BluetoothSocketMac* socket,
+ IOBluetoothRFCOMMChannel* channel)
+ : channel_(channel),
+ delegate_(nil) {
+ SetSocket(socket);
+}
+
+BluetoothRfcommChannelMac::~BluetoothRfcommChannelMac() {
+ [channel_ setDelegate:nil];
+ [channel_ closeChannel];
+}
+
+// static
+scoped_ptr<BluetoothRfcommChannelMac> BluetoothRfcommChannelMac::OpenAsync(
+ BluetoothSocketMac* socket,
+ IOBluetoothDevice* device,
+ BluetoothRFCOMMChannelID channel_id,
+ IOReturn* status) {
+ DCHECK(socket);
+ scoped_ptr<BluetoothRfcommChannelMac> channel(
+ new BluetoothRfcommChannelMac(socket, nil));
+
+ // Retain the delegate, because IOBluetoothDevice's
+ // |-openRFCOMMChannelAsync:withChannelID:delegate:| assumes that it can take
+ // ownership of the delegate without calling |-retain| on it...
+ DCHECK(channel->delegate_);
+ [channel->delegate_ retain];
+ IOBluetoothRFCOMMChannel* rfcomm_channel;
+ *status = [device openRFCOMMChannelAsync:&rfcomm_channel
+ withChannelID:channel_id
+ delegate:channel->delegate_];
+ if (*status == kIOReturnSuccess) {
+ // Note: No need to retain the |rfcomm_channel| -- the returned channel is
+ // already retained.
+ channel->channel_.reset(rfcomm_channel);
+ } else {
+ channel.reset();
+ }
+
+ return channel.Pass();
+}
+
+void BluetoothRfcommChannelMac::SetSocket(BluetoothSocketMac* socket) {
+ BluetoothChannelMac::SetSocket(socket);
+ if (!this->socket())
+ return;
+
+ // Now that the socket is set, it's safe to associate a delegate, which can
+ // call back to the socket.
+ DCHECK(!delegate_);
+ delegate_.reset(
+ [[BluetoothRfcommChannelDelegate alloc] initWithChannel:this]);
+ [channel_ setDelegate:delegate_];
+}
+
+IOBluetoothDevice* BluetoothRfcommChannelMac::GetDevice() {
+ return [channel_ getDevice];
+}
+
+uint16_t BluetoothRfcommChannelMac::GetOutgoingMTU() {
+ return [channel_ getMTU];
+}
+
+IOReturn BluetoothRfcommChannelMac::WriteAsync(void* data,
+ uint16_t length,
+ void* refcon) {
+ DCHECK_LE(length, GetOutgoingMTU());
+ return [channel_ writeAsync:data length:length refcon:refcon];
+}
+
+void BluetoothRfcommChannelMac::OnChannelOpenComplete(
+ IOBluetoothRFCOMMChannel* channel,
+ IOReturn status) {
+ if (channel_) {
+ DCHECK_EQ(channel_, channel);
+ } else {
+ // The (potentially) asynchronous connection occurred synchronously.
+ // Should only be reachable from OpenAsync().
+ DCHECK_EQ(status, kIOReturnSuccess);
+ }
+
+ socket()->OnChannelOpenComplete(
+ BluetoothDeviceMac::GetDeviceAddress([channel getDevice]), status);
+}
+
+void BluetoothRfcommChannelMac::OnChannelClosed(
+ IOBluetoothRFCOMMChannel* channel) {
+ DCHECK_EQ(channel_, channel);
+ socket()->OnChannelClosed();
+}
+
+void BluetoothRfcommChannelMac::OnChannelDataReceived(
+ IOBluetoothRFCOMMChannel* channel,
+ void* data,
+ size_t length) {
+ DCHECK_EQ(channel_, channel);
+ socket()->OnChannelDataReceived(data, length);
+}
+
+void BluetoothRfcommChannelMac::OnChannelWriteComplete(
+ IOBluetoothRFCOMMChannel* channel,
+ void* refcon,
+ IOReturn status) {
+ // Note: We use "CHECK" below to ensure we never run into unforeseen
+ // occurrences of asynchronous callbacks, which could lead to data
+ // corruption.
+ CHECK_EQ(channel_, channel);
+ socket()->OnChannelWriteComplete(refcon, status);
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_service_record.cc b/chromium/device/bluetooth/bluetooth_service_record.cc
deleted file mode 100644
index aa973735f74..00000000000
--- a/chromium/device/bluetooth/bluetooth_service_record.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// 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 "device/bluetooth/bluetooth_service_record.h"
-
-namespace device {
-
-BluetoothServiceRecord::BluetoothServiceRecord() {
-}
-
-BluetoothServiceRecord::~BluetoothServiceRecord() {
-}
-
-} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_service_record.h b/chromium/device/bluetooth/bluetooth_service_record.h
deleted file mode 100644
index eebdb6ad376..00000000000
--- a/chromium/device/bluetooth/bluetooth_service_record.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// 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 DEVICE_BLUETOOTH_BLUETOOTH_SERVICE_RECORD_H_
-#define DEVICE_BLUETOOTH_BLUETOOTH_SERVICE_RECORD_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-
-namespace device {
-
-// BluetoothServiceRecord represents an SDP service record.
-//
-// This implementation is currently incomplete: it only supports those fields
-// that have been necessary so far.
-class BluetoothServiceRecord {
- public:
- virtual ~BluetoothServiceRecord();
-
- // The human-readable name of this service.
- const std::string& name() const { return name_; }
-
- // The address of the BluetoothDevice providing this service.
- const std::string& address() const { return address_; }
-
- // The UUID of the service. This field may be empty if no UUID was
- // specified in the service record.
- const std::string& uuid() const { return uuid_; }
-
- // Indicates if this service supports HID.
- bool SupportsHid() const { return supports_hid_; }
-
- // For HID services, returns the HIDReconnectInitiate attribute. For non-HID
- // or unknown services defaults to true.
- bool hid_reconnect_initiate() const { return hid_reconnect_initiate_; }
-
- // For HID services, returns the HIDNormallyConnectable attribute. For non-HID
- // or unknown services defaults to true.
- bool hid_normally_connectable() const { return hid_normally_connectable_; }
-
- // 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 rfcomm_channel() const { return rfcomm_channel_; }
-
- protected:
- BluetoothServiceRecord();
-
- std::string address_;
- std::string name_;
- std::string uuid_;
-
- bool supports_hid_;
- bool hid_reconnect_initiate_;
- bool hid_normally_connectable_;
-
- bool supports_rfcomm_;
- uint8 rfcomm_channel_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(BluetoothServiceRecord);
-};
-
-} // namespace device
-
-#endif // DEVICE_BLUETOOTH_BLUETOOTH_SERVICE_RECORD_H_
diff --git a/chromium/device/bluetooth/bluetooth_service_record_mac.h b/chromium/device/bluetooth/bluetooth_service_record_mac.h
deleted file mode 100644
index 13eb56bf7b7..00000000000
--- a/chromium/device/bluetooth/bluetooth_service_record_mac.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 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_SERVICE_RECORD_MAC_H_
-#define DEVICE_BLUETOOTH_BLUETOOTH_SERVICE_RECORD_MAC_H_
-
-#include "device/bluetooth/bluetooth_service_record.h"
-
-#ifdef __OBJC__
-@class IOBluetoothDevice;
-@class IOBluetoothSDPServiceRecord;
-#else
-class IOBluetoothDevice;
-class IOBluetoothSDPServiceRecord;
-#endif
-
-namespace device {
-
-class BluetoothServiceRecordMac : public BluetoothServiceRecord {
- public:
- explicit BluetoothServiceRecordMac(IOBluetoothSDPServiceRecord* record);
- virtual ~BluetoothServiceRecordMac();
-
- IOBluetoothDevice* GetIOBluetoothDevice() const {
- return device_;
- }
-
- private:
- IOBluetoothDevice* device_;
-
- DISALLOW_COPY_AND_ASSIGN(BluetoothServiceRecordMac);
-};
-
-} // namespace device
-
-#endif // DEVICE_BLUETOOTH_BLUETOOTH_SERVICE_RECORD_MAC_H_
diff --git a/chromium/device/bluetooth/bluetooth_service_record_mac.mm b/chromium/device/bluetooth/bluetooth_service_record_mac.mm
deleted file mode 100644
index 306d5e5ec39..00000000000
--- a/chromium/device/bluetooth/bluetooth_service_record_mac.mm
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 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_service_record_mac.h"
-
-#include <IOBluetooth/Bluetooth.h>
-#import <IOBluetooth/IOBluetoothUtilities.h>
-#import <IOBluetooth/objc/IOBluetoothDevice.h>
-#import <IOBluetooth/objc/IOBluetoothSDPDataElement.h>
-#import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h>
-#import <IOBluetooth/objc/IOBluetoothSDPUUID.h>
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/sys_string_conversions.h"
-
-namespace {
-
-void ExtractUuid(IOBluetoothSDPDataElement* service_class_data,
- std::string* uuid) {
- NSArray* inner_elements = [service_class_data getArrayValue];
- IOBluetoothSDPUUID* sdp_uuid = nil;
- for (IOBluetoothSDPDataElement* inner_element in inner_elements) {
- if ([inner_element getTypeDescriptor] == kBluetoothSDPDataElementTypeUUID) {
- sdp_uuid = [[inner_element getUUIDValue] getUUIDWithLength:16];
- break;
- }
- }
- if (sdp_uuid != nil) {
- const uint8* uuid_bytes = reinterpret_cast<const uint8*>([sdp_uuid bytes]);
- *uuid = base::StringPrintf(
- "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
- uuid_bytes[0], uuid_bytes[1], uuid_bytes[2], uuid_bytes[3],
- uuid_bytes[4], uuid_bytes[5], uuid_bytes[6], uuid_bytes[7],
- uuid_bytes[8], uuid_bytes[9], uuid_bytes[10], uuid_bytes[11],
- uuid_bytes[12], uuid_bytes[13], uuid_bytes[14], uuid_bytes[15]);
- }
-}
-
-} // namespace
-
-namespace device {
-
-BluetoothServiceRecordMac::BluetoothServiceRecordMac(
- IOBluetoothSDPServiceRecord* record)
- : BluetoothServiceRecord() {
- name_ = base::SysNSStringToUTF8([record getServiceName]);
- device_ = [[record device] retain];
- address_ = base::SysNSStringToUTF8(IOBluetoothNSStringFromDeviceAddress(
- [device_ getAddress]));
-
- // TODO(youngki): Extract these values from |record|.
- supports_hid_ = false;
- hid_reconnect_initiate_ = false;
- hid_normally_connectable_ = false;
-
- supports_rfcomm_ =
- [record getRFCOMMChannelID:&rfcomm_channel_] == kIOReturnSuccess;
-
- const BluetoothSDPServiceAttributeID service_class_id = 1;
-
- IOBluetoothSDPDataElement* service_class_data =
- [record getAttributeDataElement:service_class_id];
- if ([service_class_data getTypeDescriptor] ==
- kBluetoothSDPDataElementTypeDataElementSequence) {
- ExtractUuid(service_class_data, &uuid_);
- }
-}
-
-BluetoothServiceRecordMac::~BluetoothServiceRecordMac() {
- [device_ release];
-}
-
-} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_service_record_mac_unittest.mm b/chromium/device/bluetooth/bluetooth_service_record_mac_unittest.mm
deleted file mode 100644
index 22e4d1a3cfc..00000000000
--- a/chromium/device/bluetooth/bluetooth_service_record_mac_unittest.mm
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 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_service_record_mac.h"
-
-#import <IOBluetooth/objc/IOBluetoothSDPDataElement.h>
-#import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h>
-#import <IOBluetooth/objc/IOBluetoothSDPUUID.h>
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/sys_string_conversions.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-const BluetoothSDPServiceAttributeID kServiceClassIDAttributeId = 0x0001;
-const BluetoothSDPServiceAttributeID kProtocolDescriptorListAttributeId =
- 0x0004;
-const BluetoothSDPServiceAttributeID kServiceNameAttributeId = 0x0100;
-
-const uint8 kRfcommChannel = 0x0c;
-const char kServiceName[] = "Headset Audio Gateway";
-
-const char kExpectedRfcommUuid[] = "01234567-89ab-cdef-0123-456789abcdef";
-const char kExpectedSerialUuid[] = "00001101-0000-1000-8000-00805f9b34fb";
-
-const int kMaxUuidSize = 16;
-
-} // namespace
-
-namespace device {
-
-class BluetoothServiceRecordMacTest : public testing::Test {
- public:
-
- IOBluetoothSDPUUID* ConvertUuid(const char* uuid_hex_char, size_t uuid_size) {
- std::vector<uint8> uuid_bytes_vector;
- uint8 uuid_buffer[kMaxUuidSize];
- base::HexStringToBytes(uuid_hex_char, &uuid_bytes_vector);
- std::copy(uuid_bytes_vector.begin(),
- uuid_bytes_vector.end(),
- uuid_buffer);
- return [IOBluetoothSDPUUID uuidWithBytes:uuid_buffer length:uuid_size];
- }
-
- IOBluetoothSDPDataElement* GetServiceClassId(IOBluetoothSDPUUID* uuid) {
- IOBluetoothSDPDataElement* uuid_element =
- [IOBluetoothSDPDataElement withElementValue:uuid];
- return [IOBluetoothSDPDataElement
- withElementValue:[NSArray arrayWithObject:uuid_element]];
- }
-
- IOBluetoothSDPDataElement* GetProtocolDescriptorList(bool supports_rfcomm) {
- NSMutableArray* protocol_descriptor_list_sequence = [NSMutableArray array];
-
- const uint8 l2cap_uuid_bytes[] = { 0x01, 0x00 };
-
- IOBluetoothSDPUUID* l2cap_uuid =
- [IOBluetoothSDPUUID uuidWithBytes:l2cap_uuid_bytes length:2];
- [protocol_descriptor_list_sequence
- addObject:[NSArray arrayWithObject:l2cap_uuid]];
-
- if (supports_rfcomm) {
- const uint8 rfcomm_uuid_bytes[] = { 0x00, 0x03 };
- IOBluetoothSDPUUID* rfcomm_uuid =
- [IOBluetoothSDPUUID uuidWithBytes:rfcomm_uuid_bytes length:2];
- NSNumber* rfcomm_channel =
- [NSNumber numberWithUnsignedChar:kRfcommChannel];
- [protocol_descriptor_list_sequence
- addObject:[NSArray
- arrayWithObjects:rfcomm_uuid, rfcomm_channel, nil]];
- }
-
- return [IOBluetoothSDPDataElement
- withElementValue:protocol_descriptor_list_sequence];
- }
-
- IOBluetoothSDPDataElement* GetServiceName(const std::string& service_name) {
- return [IOBluetoothSDPDataElement
- withElementValue:base::SysUTF8ToNSString(service_name)];
- }
-
- IOBluetoothSDPServiceRecord* GetServiceRecord(
- IOBluetoothSDPUUID* uuid, bool supports_rfcomm) {
- NSMutableDictionary* service_attrs = [NSMutableDictionary dictionary];
-
- if (uuid != nil) {
- [service_attrs
- setObject:GetServiceClassId(uuid)
- forKey:[NSNumber numberWithInt:kServiceClassIDAttributeId]];
- }
- [service_attrs
- setObject:GetProtocolDescriptorList(supports_rfcomm)
- forKey:[NSNumber numberWithInt:kProtocolDescriptorListAttributeId]];
- [service_attrs
- setObject:GetServiceName(kServiceName)
- forKey:[NSNumber numberWithInt:kServiceNameAttributeId]];
-
- return [IOBluetoothSDPServiceRecord withServiceDictionary:service_attrs
- device:nil];
- }
-};
-
-TEST_F(BluetoothServiceRecordMacTest, RfcommService) {
- const char rfcomm_uuid_bytes[] = "0123456789abcdef0123456789abcdef";
- IOBluetoothSDPUUID* rfcomm_uuid =
- ConvertUuid(rfcomm_uuid_bytes, sizeof(rfcomm_uuid_bytes) / 2);
-
- BluetoothServiceRecordMac record(GetServiceRecord(rfcomm_uuid, true));
- EXPECT_EQ(kServiceName, record.name());
- EXPECT_TRUE(record.SupportsRfcomm());
- EXPECT_EQ(kRfcommChannel, record.rfcomm_channel());
- EXPECT_EQ(kExpectedRfcommUuid, record.uuid());
-}
-
-TEST_F(BluetoothServiceRecordMacTest, ShortUuid) {
- const char short_uuid_bytes[] = "1101";
- IOBluetoothSDPUUID* short_uuid =
- ConvertUuid(short_uuid_bytes, sizeof(short_uuid_bytes) / 2);
-
- BluetoothServiceRecordMac record(GetServiceRecord(short_uuid, false));
- EXPECT_EQ(kExpectedSerialUuid, record.uuid());
-}
-
-TEST_F(BluetoothServiceRecordMacTest, MediumUuid) {
- const char medium_uuid_bytes[] = "00001101";
- IOBluetoothSDPUUID* medium_uuid =
- ConvertUuid(medium_uuid_bytes, sizeof(medium_uuid_bytes) / 2);
-
- BluetoothServiceRecordMac record(GetServiceRecord(medium_uuid, false));
- EXPECT_EQ(kExpectedSerialUuid, record.uuid());
-}
-
-TEST_F(BluetoothServiceRecordMacTest, UpperCaseUuid) {
- const char upper_case_uuid_bytes[] = "0123456789ABCDEF0123456789ABCDEF";
- IOBluetoothSDPUUID* upper_case_uuid =
- ConvertUuid(upper_case_uuid_bytes, sizeof(upper_case_uuid_bytes) / 2);
-
- BluetoothServiceRecordMac record(GetServiceRecord(upper_case_uuid, false));
- EXPECT_EQ(kExpectedRfcommUuid, record.uuid());
-}
-
-TEST_F(BluetoothServiceRecordMacTest, InvalidUuid) {
- BluetoothServiceRecordMac record(GetServiceRecord(nil, false));
- EXPECT_EQ("", record.uuid());
-}
-
-} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_service_record_win.cc b/chromium/device/bluetooth/bluetooth_service_record_win.cc
index e0ba750f297..48f87232ab4 100644
--- a/chromium/device/bluetooth/bluetooth_service_record_win.cc
+++ b/chromium/device/bluetooth/bluetooth_service_record_win.cc
@@ -10,7 +10,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "device/bluetooth/bluetooth_init_win.h"
-#include "device/bluetooth/bluetooth_utils.h"
+#include "device/bluetooth/bluetooth_uuid.h"
namespace {
@@ -61,7 +61,8 @@ void ExtractChannels(const SDP_ELEMENT_DATA& protocol_descriptor_list_data,
}
}
-void ExtractUuid(const SDP_ELEMENT_DATA& uuid_data, std::string* uuid) {
+void ExtractUuid(const SDP_ELEMENT_DATA& uuid_data,
+ device::BluetoothUUID* uuid) {
HBLUETOOTH_CONTAINER_ELEMENT inner_uuid_element = NULL;
SDP_ELEMENT_DATA inner_uuid_data;
if (AdvanceToSdpType(uuid_data,
@@ -71,13 +72,13 @@ void ExtractUuid(const SDP_ELEMENT_DATA& uuid_data, std::string* uuid) {
if (inner_uuid_data.specificType == SDP_ST_UUID16) {
std::string uuid_hex =
base::StringPrintf("%04x", inner_uuid_data.data.uuid16);
- *uuid = device::bluetooth_utils::CanonicalUuid(uuid_hex);
+ *uuid = device::BluetoothUUID(uuid_hex);
} else if (inner_uuid_data.specificType == SDP_ST_UUID32) {
std::string uuid_hex =
base::StringPrintf("%08x", inner_uuid_data.data.uuid32);
- *uuid = device::bluetooth_utils::CanonicalUuid(uuid_hex);
+ *uuid = device::BluetoothUUID(uuid_hex);
} else if (inner_uuid_data.specificType == SDP_ST_UUID128) {
- *uuid = base::StringPrintf(
+ *uuid = device::BluetoothUUID(base::StringPrintf(
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
inner_uuid_data.data.uuid128.Data1,
inner_uuid_data.data.uuid128.Data2,
@@ -89,9 +90,9 @@ void ExtractUuid(const SDP_ELEMENT_DATA& uuid_data, std::string* uuid) {
inner_uuid_data.data.uuid128.Data4[4],
inner_uuid_data.data.uuid128.Data4[5],
inner_uuid_data.data.uuid128.Data4[6],
- inner_uuid_data.data.uuid128.Data4[7]);
+ inner_uuid_data.data.uuid128.Data4[7]));
} else {
- uuid->clear();
+ *uuid = device::BluetoothUUID();
}
}
}
@@ -143,8 +144,8 @@ BluetoothServiceRecordWin::BluetoothServiceRecordWin(
blob_size,
kUuidId,
&uuid_data)) {
- ExtractUuid(uuid_data, &uuid_);
+ ExtractUuid(uuid_data, &uuid_);
}
}
-} // namespace device \ No newline at end of file
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_service_record_win.h b/chromium/device/bluetooth/bluetooth_service_record_win.h
index b9f03312ba8..5f2b8f87f71 100644
--- a/chromium/device/bluetooth/bluetooth_service_record_win.h
+++ b/chromium/device/bluetooth/bluetooth_service_record_win.h
@@ -9,23 +9,44 @@
#include "base/basictypes.h"
#include "device/bluetooth/bluetooth_init_win.h"
-#include "device/bluetooth/bluetooth_service_record.h"
+#include "device/bluetooth/bluetooth_uuid.h"
namespace device {
-class BluetoothServiceRecordWin : public BluetoothServiceRecord {
+class BluetoothServiceRecordWin {
public:
BluetoothServiceRecordWin(const std::string& name,
const std::string& address,
uint64 blob_size,
uint8* blob_data);
- BTH_ADDR bth_addr() const {
- return bth_addr_;
- }
+ BTH_ADDR bth_addr() const { return bth_addr_; }
+
+ // The human-readable name of this service.
+ const std::string& name() const { return name_; }
+
+ // The address of the BluetoothDevice providing this service.
+ const std::string& address() const { return address_; }
+
+ // The UUID of the service. This field may be empty if no UUID was
+ // specified in the service record.
+ const BluetoothUUID& uuid() const { return uuid_; }
+
+ // 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 rfcomm_channel() const { return rfcomm_channel_; }
private:
BTH_ADDR bth_addr_;
+ std::string address_;
+ std::string name_;
+ BluetoothUUID uuid_;
+
+ bool supports_rfcomm_;
+ uint8 rfcomm_channel_;
DISALLOW_COPY_AND_ASSIGN(BluetoothServiceRecordWin);
};
diff --git a/chromium/device/bluetooth/bluetooth_service_record_win_unittest.cc b/chromium/device/bluetooth/bluetooth_service_record_win_unittest.cc
index 804b6200304..c9d44c6ba00 100644
--- a/chromium/device/bluetooth/bluetooth_service_record_win_unittest.cc
+++ b/chromium/device/bluetooth/bluetooth_service_record_win_unittest.cc
@@ -5,6 +5,7 @@
#include "base/basictypes.h"
#include "base/strings/string_number_conversions.h"
#include "device/bluetooth/bluetooth_service_record_win.h"
+#include "device/bluetooth/bluetooth_uuid.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -14,14 +15,14 @@ const char kTestNoRfcommSdpBytes[] =
"010209000535031910020900093508350619110d090102090100250c417564696f20536f75"
"726365090311090001";
const int kTestNoRfcommSdpBytesSize = sizeof(kTestNoRfcommSdpBytes) / 2;
-const char kTestNoRfcommSdpUuid[] = "0000110a-0000-1000-8000-00805f9b34fb";
+const device::BluetoothUUID kTestNoRfcommSdpUuid("110a");
const char kTestRfcommSdpBytes[] =
"354b0900000a000100030900013506191112191203090004350c3503190100350519000308"
"0b090005350319100209000935083506191108090100090100250d566f6963652047617465"
"776179";
const int kTestRfcommSdpBytesSize = sizeof(kTestRfcommSdpBytes) / 2;
-const char kTestRfcommSdpUuid[] = "00001112-0000-1000-8000-00805f9b34fb";
+const device::BluetoothUUID kTestRfcommSdpUuid("1112");
const int kTestRfcommChannel = 11;
} // namespace
@@ -46,7 +47,7 @@ TEST_F(BluetoothServiceRecordWinTest, NoRfcommSdp) {
"01:02:03:0A:10:A0",
kTestNoRfcommSdpBytesSize,
sdp_bytes_array);
- EXPECT_STREQ(kTestNoRfcommSdpUuid, service_record.uuid().c_str());
+ EXPECT_EQ(kTestNoRfcommSdpUuid, service_record.uuid());
EXPECT_FALSE(service_record.SupportsRfcomm());
}
@@ -58,7 +59,7 @@ TEST_F(BluetoothServiceRecordWinTest, RfcommSdp) {
"01:02:03:0A:10:A0",
kTestRfcommSdpBytesSize,
sdp_bytes_array);
- EXPECT_STREQ(kTestRfcommSdpUuid, service_record.uuid().c_str());
+ EXPECT_EQ(kTestRfcommSdpUuid, service_record.uuid());
EXPECT_TRUE(service_record.SupportsRfcomm());
EXPECT_EQ(kTestRfcommChannel, service_record.rfcomm_channel());
}
@@ -73,4 +74,4 @@ TEST_F(BluetoothServiceRecordWinTest, BthAddr) {
EXPECT_EQ(1108152553632, service_record.bth_addr());
}
-} // namespace device \ No newline at end of file
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_socket.cc b/chromium/device/bluetooth/bluetooth_socket.cc
new file mode 100644
index 00000000000..26981ab382d
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_socket.cc
@@ -0,0 +1,11 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_socket.h"
+
+namespace device {
+
+BluetoothSocket::~BluetoothSocket() {}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_socket.h b/chromium/device/bluetooth/bluetooth_socket.h
index 6ae349e9ac9..1c7e50facb3 100644
--- a/chromium/device/bluetooth/bluetooth_socket.h
+++ b/chromium/device/bluetooth/bluetooth_socket.h
@@ -7,38 +7,77 @@
#include <string>
+#include "base/callback.h"
#include "base/memory/ref_counted.h"
namespace net {
-
-class DrainableIOBuffer;
-class GrowableIOBuffer;
-
+class IOBuffer;
} // namespace net
namespace device {
+class BluetoothDevice;
+class BluetoothUUID;
+
// BluetoothSocket 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> {
+// creation. In terms of threading, platform specific implementations may
+// differ slightly, but platform independent consumers must guarantee calling
+// various instance methods on the same thread as the thread used at
+// construction time -- platform specific implementation are resonsible for
+// marshalling calls to a different thread if required.
+class BluetoothSocket : public base::RefCountedThreadSafe<BluetoothSocket> {
public:
- // Receives data from the socket and stores it in |buffer|. It returns whether
- // the reception has been successful. If it fails, the caller can get the
- // error message through |GetLastErrorMessage()|.
- virtual bool Receive(net::GrowableIOBuffer* buffer) = 0;
+ enum ErrorReason { kSystemError, kIOPending, kDisconnected };
+
+ typedef base::Callback<void(int)> SendCompletionCallback;
+ typedef base::Callback<void(int, scoped_refptr<net::IOBuffer> io_buffer)>
+ ReceiveCompletionCallback;
+ typedef base::Callback<void(const BluetoothDevice* device,
+ scoped_refptr<BluetoothSocket> socket)>
+ AcceptCompletionCallback;
+ typedef base::Callback<void(const std::string& error_message)>
+ ErrorCompletionCallback;
+ typedef base::Callback<void(ErrorReason, const std::string& error_message)>
+ ReceiveErrorCompletionCallback;
+
+ // Destroys resources associated with the socket. After calling this method,
+ // it is illegal to call any method on this socket instance (except for the
+ // desctrutor via Release).
+ virtual void Close() = 0;
+
+ // Gracefully disconnects the socket and calls |callback| upon completion.
+ // There is no failure case, as this is a best effort operation.
+ virtual void Disconnect(const base::Closure& success_callback) = 0;
+
+ // Receives data from the socket and calls |success_callback| when data is
+ // available. |buffer_size| specifies the maximum number of bytes that can be
+ // received. If an error occurs, calls |error_callback| with a reason and an
+ // error message.
+ virtual void Receive(
+ int buffer_size,
+ const ReceiveCompletionCallback& success_callback,
+ const ReceiveErrorCompletionCallback& error_callback) = 0;
- // Sends |buffer| to the socket. It returns whether the sending has been
- // successful. If it fails, the caller can get the error message through
- // |GetLastErrorMessage()|.
- virtual bool Send(net::DrainableIOBuffer* buffer) = 0;
+ // Sends |buffer| to the socket and calls |success_callback| when data has
+ // been successfully sent. |buffer_size| is the number of bytes contained in
+ // |buffer|. If an error occurs, calls |error_callback| with an error message.
+ virtual void Send(scoped_refptr<net::IOBuffer> buffer,
+ int buffer_size,
+ const SendCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) = 0;
- virtual std::string GetLastErrorMessage() const = 0;
+ // Accepts a pending client connection from the socket and calls
+ // |success_callback| on completion, passing a new BluetoothSocket instance
+ // for the new client. If an error occurs, calls |error_callback| with a
+ // reason and an error message.
+ virtual void Accept(const AcceptCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) = 0;
protected:
- friend class base::RefCounted<BluetoothSocket>;
- virtual ~BluetoothSocket() {}
+ friend class base::RefCountedThreadSafe<BluetoothSocket>;
+ virtual ~BluetoothSocket();
};
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_socket_chromeos.cc b/chromium/device/bluetooth/bluetooth_socket_chromeos.cc
index 3a881d12813..9e6ba35f524 100644
--- a/chromium/device/bluetooth/bluetooth_socket_chromeos.cc
+++ b/chromium/device/bluetooth/bluetooth_socket_chromeos.cc
@@ -4,164 +4,586 @@
#include "device/bluetooth/bluetooth_socket_chromeos.h"
-#include <errno.h>
-#include <poll.h>
-#include <unistd.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
+#include <queue>
#include <string>
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
#include "base/logging.h"
+#include "base/memory/linked_ptr.h"
#include "base/memory/ref_counted.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/safe_strerror_posix.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_util.h"
+#include "base/task_runner_util.h"
#include "base/threading/thread_restrictions.h"
+#include "base/threading/worker_pool.h"
+#include "chromeos/dbus/bluetooth_device_client.h"
+#include "chromeos/dbus/bluetooth_profile_manager_client.h"
+#include "chromeos/dbus/bluetooth_profile_service_provider.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "dbus/bus.h"
#include "dbus/file_descriptor.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_adapter_chromeos.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_device_chromeos.h"
#include "device/bluetooth/bluetooth_socket.h"
-#include "net/base/io_buffer.h"
+#include "device/bluetooth/bluetooth_socket_net.h"
+#include "device/bluetooth/bluetooth_socket_thread.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+using device::BluetoothAdapter;
+using device::BluetoothDevice;
+using device::BluetoothSocketThread;
+using device::BluetoothUUID;
+
+namespace {
+
+const char kAcceptFailed[] = "Failed to accept connection.";
+const char kInvalidUUID[] = "Invalid UUID";
+const char kSocketNotListening[] = "Socket is not listening.";
+
+} // namespace
namespace chromeos {
-BluetoothSocketChromeOS::BluetoothSocketChromeOS(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;
+// static
+scoped_refptr<BluetoothSocketChromeOS>
+BluetoothSocketChromeOS::CreateBluetoothSocket(
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<BluetoothSocketThread> socket_thread,
+ net::NetLog* net_log,
+ const net::NetLog::Source& source) {
+ DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
+
+ return make_scoped_refptr(
+ new BluetoothSocketChromeOS(
+ ui_task_runner, socket_thread, net_log, source));
+}
+
+BluetoothSocketChromeOS::AcceptRequest::AcceptRequest() {}
+
+BluetoothSocketChromeOS::AcceptRequest::~AcceptRequest() {}
+
+BluetoothSocketChromeOS::ConnectionRequest::ConnectionRequest()
+ : accepting(false),
+ cancelled(false) {}
+
+BluetoothSocketChromeOS::ConnectionRequest::~ConnectionRequest() {}
+
+BluetoothSocketChromeOS::BluetoothSocketChromeOS(
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<BluetoothSocketThread> socket_thread,
+ net::NetLog* net_log,
+ const net::NetLog::Source& source)
+ : BluetoothSocketNet(ui_task_runner, socket_thread, net_log, source) {
+}
+
+BluetoothSocketChromeOS::~BluetoothSocketChromeOS() {
+ DCHECK(object_path_.value().empty());
+ DCHECK(profile_.get() == NULL);
+
+ if (adapter_.get()) {
+ adapter_->RemoveObserver(this);
+ adapter_ = NULL;
+ }
+}
+
+void BluetoothSocketChromeOS::Connect(
+ const BluetoothDeviceChromeOS* device,
+ const BluetoothUUID& uuid,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(object_path_.value().empty());
+ DCHECK(!profile_.get());
+
+ if (!uuid.IsValid()) {
+ error_callback.Run(kInvalidUUID);
+ return;
+ }
+
+ device_address_ = device->GetAddress();
+ device_path_ = device->object_path();
+ uuid_ = uuid;
+ options_.reset(new BluetoothProfileManagerClient::Options());
+
+ RegisterProfile(success_callback, error_callback);
+}
+
+void BluetoothSocketChromeOS::Listen(
+ scoped_refptr<BluetoothAdapter> adapter,
+ SocketType socket_type,
+ const BluetoothUUID& uuid,
+ int psm_or_channel,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(object_path_.value().empty());
+ DCHECK(!profile_.get());
+
+ if (!uuid.IsValid()) {
+ error_callback.Run(kInvalidUUID);
+ return;
+ }
+
+ adapter_ = adapter;
+ adapter_->AddObserver(this);
+
+ uuid_ = uuid;
+ options_.reset(new BluetoothProfileManagerClient::Options());
+
+ switch (socket_type) {
+ case kRfcomm:
+ options_->channel.reset(new uint16(
+ psm_or_channel == BluetoothAdapter::kChannelAuto
+ ? 0 : psm_or_channel));
+ break;
+ case kL2cap:
+ options_->psm.reset(new uint16(
+ psm_or_channel == BluetoothAdapter::kPsmAuto
+ ? 0 : psm_or_channel));
+ break;
+ default:
+ NOTREACHED();
}
- if (optval == SOCK_DGRAM || optval == SOCK_SEQPACKET) {
- socket_type_ = L2CAP;
+ RegisterProfile(success_callback, error_callback);
+}
+
+void BluetoothSocketChromeOS::Close() {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+ if (profile_)
+ UnregisterProfile();
+
+ if (!device_path_.value().empty()) {
+ BluetoothSocketNet::Close();
} else {
- socket_type_ = RFCOMM;
+ DoCloseListening();
}
}
-BluetoothSocketChromeOS::~BluetoothSocketChromeOS() {
- close(fd_);
+void BluetoothSocketChromeOS::Disconnect(const base::Closure& callback) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+ if (profile_)
+ UnregisterProfile();
+
+ if (!device_path_.value().empty()) {
+ BluetoothSocketNet::Disconnect(callback);
+ } else {
+ DoCloseListening();
+ callback.Run();
+ }
}
-bool BluetoothSocketChromeOS::Receive(net::GrowableIOBuffer *buffer) {
- base::ThreadRestrictions::AssertIOAllowed();
+void BluetoothSocketChromeOS::Accept(
+ const AcceptCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
- 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;
- }
+ if (!device_path_.value().empty()) {
+ error_callback.Run(kSocketNotListening);
+ return;
+ }
- // 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;
- }
- }
+ // Only one pending accept at a time
+ if (accept_request_.get()) {
+ error_callback.Run(net::ErrorToString(net::ERR_IO_PENDING));
+ return;
+ }
- buffer->SetCapacity(count);
+ accept_request_.reset(new AcceptRequest);
+ accept_request_->success_callback = success_callback;
+ accept_request_->error_callback = error_callback;
+
+ if (connection_request_queue_.size() >= 1) {
+ AcceptConnectionRequest();
+ }
+}
+
+void BluetoothSocketChromeOS::RegisterProfile(
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(object_path_.value().empty());
+ DCHECK(!profile_.get());
+
+ // The object path is relatively meaningless, but has to be unique, so for
+ // connecting profiles use a combination of the device address and profile
+ // UUID.
+ std::string device_address_path, uuid_path;
+ base::ReplaceChars(device_address_, ":-", "_", &device_address_path);
+ base::ReplaceChars(uuid_.canonical_value(), ":-", "_", &uuid_path);
+ if (!device_address_path.empty()) {
+ object_path_ = dbus::ObjectPath("/org/chromium/bluetooth_profile/" +
+ device_address_path + "/" + uuid_path);
} 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;
+ object_path_ = dbus::ObjectPath("/org/chromium/bluetooth_profile/" +
+ uuid_path);
+ }
+
+ // Create the service provider for the profile object.
+ dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
+ profile_.reset(BluetoothProfileServiceProvider::Create(
+ system_bus, object_path_, this));
+ DCHECK(profile_.get());
+
+ // Before reaching out to the Bluetooth Daemon to register a listening socket,
+ // make sure it's actually running. If not, report success and carry on;
+ // the profile will be registered when the daemon becomes available.
+ if (adapter_ && !adapter_->IsPresent()) {
+ VLOG(1) << object_path_.value() << ": Delaying profile registration.";
+ success_callback.Run();
+ return;
+ }
+
+ VLOG(1) << object_path_.value() << ": Registering profile.";
+ DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
+ RegisterProfile(
+ object_path_,
+ uuid_.canonical_value(),
+ *options_,
+ base::Bind(&BluetoothSocketChromeOS::OnRegisterProfile,
+ this,
+ success_callback,
+ error_callback),
+ base::Bind(&BluetoothSocketChromeOS::OnRegisterProfileError,
+ this,
+ error_callback));
+}
+
+void BluetoothSocketChromeOS::OnRegisterProfile(
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ if (!device_path_.value().empty()) {
+ VLOG(1) << object_path_.value() << ": Profile registered, connecting to "
+ << device_path_.value();
+
+ DBusThreadManager::Get()->GetBluetoothDeviceClient()->
+ ConnectProfile(
+ device_path_,
+ uuid_.canonical_value(),
+ base::Bind(
+ &BluetoothSocketChromeOS::OnConnectProfile,
+ this,
+ success_callback),
+ base::Bind(
+ &BluetoothSocketChromeOS::OnConnectProfileError,
+ this,
+ error_callback));
+ } else {
+ VLOG(1) << object_path_.value() << ": Profile registered.";
+ success_callback.Run();
+ }
+}
+
+void BluetoothSocketChromeOS::OnRegisterProfileError(
+ const ErrorCompletionCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ LOG(WARNING) << object_path_.value() << ": Failed to register profile: "
+ << error_name << ": " << error_message;
+ error_callback.Run(error_message);
+}
+
+void BluetoothSocketChromeOS::OnConnectProfile(
+ const base::Closure& success_callback) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ VLOG(1) << object_path_.value() << ": Profile connected.";
+ UnregisterProfile();
+ success_callback.Run();
+}
+
+void BluetoothSocketChromeOS::OnConnectProfileError(
+ const ErrorCompletionCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ LOG(WARNING) << object_path_.value() << ": Failed to connect profile: "
+ << error_name << ": " << error_message;
+ UnregisterProfile();
+ error_callback.Run(error_message);
+}
+
+void BluetoothSocketChromeOS::AdapterPresentChanged(BluetoothAdapter* adapter,
+ bool present) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(!object_path_.value().empty());
+ DCHECK(profile_.get());
+
+ if (!present)
+ return;
+
+ VLOG(1) << object_path_.value() << ": Re-register profile.";
+ DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
+ RegisterProfile(
+ object_path_,
+ uuid_.canonical_value(),
+ *options_,
+ base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfile,
+ this),
+ base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfileError,
+ this));
+}
+
+void BluetoothSocketChromeOS::OnInternalRegisterProfile() {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+ VLOG(1) << object_path_.value() << ": Profile re-registered";
+}
+
+void BluetoothSocketChromeOS::OnInternalRegisterProfileError(
+ const std::string& error_name,
+ const std::string& error_message) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+ // It's okay if the profile already exists, it means we registered it on
+ // initialization.
+ if (error_name == bluetooth_profile_manager::kErrorAlreadyExists)
+ return;
+
+ LOG(WARNING) << object_path_.value() << ": Failed to re-register profile: "
+ << error_name << ": " << error_message;
+}
+
+void BluetoothSocketChromeOS::Released() {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ VLOG(1) << object_path_.value() << ": Release";
+}
+
+void BluetoothSocketChromeOS::NewConnection(
+ const dbus::ObjectPath& device_path,
+ scoped_ptr<dbus::FileDescriptor> fd,
+ const BluetoothProfileServiceProvider::Delegate::Options& options,
+ const ConfirmationCallback& callback) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ VLOG(1) << object_path_.value() << ": New connection from device: "
+ << device_path.value();
+
+ if (!device_path_.value().empty()) {
+ DCHECK(device_path_ == device_path);
+
+ socket_thread()->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &BluetoothSocketChromeOS::DoNewConnection,
+ this,
+ device_path_,
+ base::Passed(&fd),
+ options,
+ callback));
+ } else {
+ linked_ptr<ConnectionRequest> request(new ConnectionRequest());
+ request->device_path = device_path;
+ request->fd = fd.Pass();
+ request->options = options;
+ request->callback = callback;
+
+ connection_request_queue_.push(request);
+ VLOG(1) << object_path_.value() << ": Connection is now pending.";
+ if (accept_request_) {
+ AcceptConnectionRequest();
}
}
+}
- if (bytes_read == 0 && socket_type_ == RFCOMM) {
- // TODO(keybuk, youngki): Agree a common way to flag disconnected.
- error_message_ = "Disconnected";
- return false;
+void BluetoothSocketChromeOS::RequestDisconnection(
+ const dbus::ObjectPath& device_path,
+ const ConfirmationCallback& callback) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ VLOG(1) << object_path_.value() << ": Request disconnection";
+ callback.Run(SUCCESS);
+}
+
+void BluetoothSocketChromeOS::Cancel() {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ VLOG(1) << object_path_.value() << ": Cancel";
+
+ if (!connection_request_queue_.size())
+ return;
+
+ // If the front request is being accepted mark it as cancelled, otherwise
+ // just pop it from the queue.
+ linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
+ if (!request->accepting) {
+ request->cancelled = true;
+ } else {
+ connection_request_queue_.pop();
}
+}
+
+void BluetoothSocketChromeOS::AcceptConnectionRequest() {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(accept_request_.get());
+ DCHECK(connection_request_queue_.size() >= 1);
- return true;
+ VLOG(1) << object_path_.value() << ": Accepting pending connection.";
+
+ linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
+ request->accepting = true;
+
+ BluetoothDeviceChromeOS* device =
+ static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
+ GetDeviceWithPath(request->device_path);
+ DCHECK(device);
+
+ scoped_refptr<BluetoothSocketChromeOS> client_socket =
+ BluetoothSocketChromeOS::CreateBluetoothSocket(
+ ui_task_runner(),
+ socket_thread(),
+ net_log(),
+ source());
+
+ client_socket->device_address_ = device->GetAddress();
+ client_socket->device_path_ = request->device_path;
+ client_socket->uuid_ = uuid_;
+
+ socket_thread()->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &BluetoothSocketChromeOS::DoNewConnection,
+ client_socket,
+ request->device_path,
+ base::Passed(&request->fd),
+ request->options,
+ base::Bind(&BluetoothSocketChromeOS::OnNewConnection,
+ this,
+ client_socket,
+ request->callback)));
}
-bool BluetoothSocketChromeOS::Send(net::DrainableIOBuffer *buffer) {
+void BluetoothSocketChromeOS::DoNewConnection(
+ const dbus::ObjectPath& device_path,
+ scoped_ptr<dbus::FileDescriptor> fd,
+ const BluetoothProfileServiceProvider::Delegate::Options& options,
+ const ConfirmationCallback& callback) {
+ DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread());
base::ThreadRestrictions::AssertIOAllowed();
+ fd->CheckValidity();
- 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;
- }
+ VLOG(1) << object_path_.value() << ": Validity check complete.";
+ if (!fd->is_valid()) {
+ LOG(WARNING) << object_path_.value() << " :" << fd->value()
+ << ": Invalid file descriptor received from Bluetooth Daemon.";
+ ui_task_runner()->PostTask(FROM_HERE,
+ base::Bind(callback, REJECTED));;
+ return;
+ }
+
+ if (tcp_socket()) {
+ LOG(WARNING) << object_path_.value() << ": Already connected";
+ ui_task_runner()->PostTask(FROM_HERE,
+ base::Bind(callback, REJECTED));;
+ return;
+ }
+
+ ResetTCPSocket();
+
+ // Note: We don't have a meaningful |IPEndPoint|, but that is ok since the
+ // TCPSocket implementation does not actually require one.
+ int net_result = tcp_socket()->AdoptConnectedSocket(fd->value(),
+ net::IPEndPoint());
+ if (net_result != net::OK) {
+ LOG(WARNING) << object_path_.value() << ": Error adopting socket: "
+ << std::string(net::ErrorToString(net_result));
+ ui_task_runner()->PostTask(FROM_HERE,
+ base::Bind(callback, REJECTED));;
+ return;
+ }
+
+ VLOG(2) << object_path_.value() << ": Taking descriptor, confirming success.";
+ fd->TakeValue();
+ ui_task_runner()->PostTask(FROM_HERE,
+ base::Bind(callback, SUCCESS));;
+}
+
+void BluetoothSocketChromeOS::OnNewConnection(
+ scoped_refptr<BluetoothSocket> socket,
+ const ConfirmationCallback& callback,
+ Status status) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(accept_request_.get());
+ DCHECK(connection_request_queue_.size() >= 1);
+
+ linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
+ if (status == SUCCESS && !request->cancelled) {
+ BluetoothDeviceChromeOS* device =
+ static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
+ GetDeviceWithPath(request->device_path);
+ DCHECK(device);
+
+ accept_request_->success_callback.Run(device, socket);
+ } else {
+ accept_request_->error_callback.Run(kAcceptFailed);
}
- return true;
+ accept_request_.reset(NULL);
+ connection_request_queue_.pop();
+
+ callback.Run(status);
}
-std::string BluetoothSocketChromeOS::GetLastErrorMessage() const {
- return error_message_;
+void BluetoothSocketChromeOS::DoCloseListening() {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+ if (accept_request_) {
+ accept_request_->error_callback.Run(
+ net::ErrorToString(net::ERR_CONNECTION_CLOSED));
+ accept_request_.reset(NULL);
+ }
+
+ while (connection_request_queue_.size() > 0) {
+ linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
+ request->callback.Run(REJECTED);
+ connection_request_queue_.pop();
+ }
}
-// static
-scoped_refptr<device::BluetoothSocket> BluetoothSocketChromeOS::Create(
- dbus::FileDescriptor* fd) {
- DCHECK(fd->is_valid());
+void BluetoothSocketChromeOS::UnregisterProfile() {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(!object_path_.value().empty());
+ DCHECK(profile_.get());
+
+ VLOG(1) << object_path_.value() << ": Unregister profile";
+ DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
+ UnregisterProfile(
+ object_path_,
+ base::Bind(&BluetoothSocketChromeOS::OnUnregisterProfile,
+ this,
+ object_path_),
+ base::Bind(&BluetoothSocketChromeOS::OnUnregisterProfileError,
+ this,
+ object_path_));
+
+ profile_.reset();
+ object_path_ = dbus::ObjectPath("");
+}
+
+void BluetoothSocketChromeOS::OnUnregisterProfile(
+ const dbus::ObjectPath& object_path) {
+ VLOG(1) << object_path.value() << ": Profile unregistered";
+}
+
+void BluetoothSocketChromeOS::OnUnregisterProfileError(
+ const dbus::ObjectPath& object_path,
+ const std::string& error_name,
+ const std::string& error_message) {
+ // It's okay if the profile doesn't exist, it means we haven't registered it
+ // yet.
+ if (error_name == bluetooth_profile_manager::kErrorDoesNotExist)
+ return;
- BluetoothSocketChromeOS* bluetooth_socket =
- new BluetoothSocketChromeOS(fd->TakeValue());;
- return scoped_refptr<BluetoothSocketChromeOS>(bluetooth_socket);
+ LOG(WARNING) << object_path_.value() << ": Failed to unregister profile: "
+ << error_name << ": " << error_message;
}
} // namespace chromeos
diff --git a/chromium/device/bluetooth/bluetooth_socket_chromeos.h b/chromium/device/bluetooth/bluetooth_socket_chromeos.h
index 1c52c679f0e..43f512666eb 100644
--- a/chromium/device/bluetooth/bluetooth_socket_chromeos.h
+++ b/chromium/device/bluetooth/bluetooth_socket_chromeos.h
@@ -5,65 +5,200 @@
#ifndef DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_CHROMEOS_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_CHROMEOS_H_
+#include <queue>
#include <string>
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/bluetooth_profile_manager_client.h"
+#include "chromeos/dbus/bluetooth_profile_service_provider.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_socket.h"
+#include "device/bluetooth/bluetooth_socket_net.h"
+#include "device/bluetooth/bluetooth_uuid.h"
namespace dbus {
-
class FileDescriptor;
-
} // namespace dbus
-namespace net {
-
-class DrainableIOBuffer;
-class GrowableIOBuffer;
-
-} // namespace net
-
namespace chromeos {
+class BluetoothDeviceChromeOS;
+
// The BluetoothSocketChromeOS class implements BluetoothSocket for the
// Chrome OS platform.
class CHROMEOS_EXPORT BluetoothSocketChromeOS
- : public device::BluetoothSocket {
+ : public device::BluetoothSocketNet,
+ public device::BluetoothAdapter::Observer,
+ public BluetoothProfileServiceProvider::Delegate {
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);
+ static scoped_refptr<BluetoothSocketChromeOS> CreateBluetoothSocket(
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<device::BluetoothSocketThread> socket_thread,
+ net::NetLog* net_log,
+ const net::NetLog::Source& source);
+
+ // Connects this socket to the service on |device| published as UUID |uuid|,
+ // the underlying protocol and PSM or Channel is obtained through service
+ // discovery. On a successful connection the socket properties will be updated
+ // and |success_callback| called. On failure |error_callback| will be called
+ // with a message explaining the cause of the failure.
+ virtual void Connect(const BluetoothDeviceChromeOS* device,
+ const device::BluetoothUUID& uuid,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback);
+
+ // Listens using this socket using a service published on |adapter|. The
+ // service is either RFCOMM or L2CAP depending on |socket_type| and published
+ // as UUID |uuid|. The |psm_or_channel| argument is interpreted according to
+ // |socket_type|. |success_callback| will be called if the service is
+ // successfully registered, |error_callback| on failure with a message
+ // explaining the cause.
+ enum SocketType { kRfcomm, kL2cap };
+ virtual void Listen(scoped_refptr<device::BluetoothAdapter> adapter,
+ SocketType socket_type,
+ const device::BluetoothUUID& uuid,
+ int psm_or_channel,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback);
+
+ // BluetoothSocket:
+ virtual void Close() OVERRIDE;
+ virtual void Disconnect(const base::Closure& callback) OVERRIDE;
+ virtual void Accept(const AcceptCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) OVERRIDE;
+
+ // Returns the object path of the socket.
+ const dbus::ObjectPath& object_path() const { return object_path_; }
protected:
virtual ~BluetoothSocketChromeOS();
private:
- BluetoothSocketChromeOS(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
+ BluetoothSocketChromeOS(
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<device::BluetoothSocketThread> socket_thread,
+ net::NetLog* net_log,
+ const net::NetLog::Source& source);
+
+ // Register the underlying profile client object with the Bluetooth Daemon.
+ void RegisterProfile(const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback);
+ void OnRegisterProfile(const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback);
+ void OnRegisterProfileError(const ErrorCompletionCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Called by dbus:: on completion of the ConnectProfile() method.
+ void OnConnectProfile(const base::Closure& success_callback);
+ void OnConnectProfileError(const ErrorCompletionCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // BluetoothAdapter::Observer:
+ virtual void AdapterPresentChanged(device::BluetoothAdapter* adapter,
+ bool present) OVERRIDE;
+
+ // Called by dbus:: on completion of the RegisterProfile() method call
+ // triggered as a result of the adapter becoming present again.
+ void OnInternalRegisterProfile();
+ void OnInternalRegisterProfileError(const std::string& error_name,
+ const std::string& error_message);
+
+ // BluetoothProfileServiceProvider::Delegate:
+ virtual void Released() OVERRIDE;
+ virtual void NewConnection(
+ const dbus::ObjectPath& device_path,
+ scoped_ptr<dbus::FileDescriptor> fd,
+ const BluetoothProfileServiceProvider::Delegate::Options& options,
+ const ConfirmationCallback& callback) OVERRIDE;
+ virtual void RequestDisconnection(
+ const dbus::ObjectPath& device_path,
+ const ConfirmationCallback& callback) OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+
+ // Method run to accept a single incoming connection.
+ void AcceptConnectionRequest();
+
+ // Method run on the socket thread to validate the file descriptor of a new
+ // connection and set up the underlying net::TCPSocket() for it.
+ void DoNewConnection(
+ const dbus::ObjectPath& device_path,
+ scoped_ptr<dbus::FileDescriptor> fd,
+ const BluetoothProfileServiceProvider::Delegate::Options& options,
+ const ConfirmationCallback& callback);
+
+ // Method run on the UI thread after a new connection has been accepted and
+ // a socket allocated in |socket|. Takes care of calling the Accept()
+ // callback and |callback| with the right arguments based on |status|.
+ void OnNewConnection(scoped_refptr<BluetoothSocket> socket,
+ const ConfirmationCallback& callback,
+ Status status);
+
+ // Method run on the socket thread with a valid file descriptor |fd|, once
+ // complete calls |callback| on the UI thread with an appropriate argument
+ // indicating success or failure.
+ void DoConnect(scoped_ptr<dbus::FileDescriptor> fd,
+ const ConfirmationCallback& callback);
+
+ // Method run to clean-up a listening socket.
+ void DoCloseListening();
+
+ // Unregister the underlying profile client object from the Bluetooth Daemon.
+ void UnregisterProfile();
+ void OnUnregisterProfile(const dbus::ObjectPath& object_path);
+ void OnUnregisterProfileError(const dbus::ObjectPath& object_path,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Adapter the profile is registered against; this is only present when the
+ // socket is listening.
+ scoped_refptr<device::BluetoothAdapter> adapter_;
+
+ // Address and D-Bus object path of the device being connected to, empty and
+ // ignored if the socket is listening.
+ std::string device_address_;
+ dbus::ObjectPath device_path_;
+
+ // UUID of the profile being connected to, or listening on.
+ device::BluetoothUUID uuid_;
+
+ // Copy of the profile options used for registering the profile.
+ scoped_ptr<BluetoothProfileManagerClient::Options> options_;
+
+ // 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<BluetoothProfileServiceProvider> profile_;
+
+ // Pending request to an Accept() call.
+ struct AcceptRequest {
+ AcceptRequest();
+ ~AcceptRequest();
+
+ AcceptCompletionCallback success_callback;
+ ErrorCompletionCallback error_callback;
};
-
- // 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_;
+ scoped_ptr<AcceptRequest> accept_request_;
+
+ // Queue of incoming connection requests.
+ struct ConnectionRequest {
+ ConnectionRequest();
+ ~ConnectionRequest();
+
+ dbus::ObjectPath device_path;
+ scoped_ptr<dbus::FileDescriptor> fd;
+ BluetoothProfileServiceProvider::Delegate::Options options;
+ ConfirmationCallback callback;
+ bool accepting;
+ bool cancelled;
+ };
+ std::queue<linked_ptr<ConnectionRequest> > connection_request_queue_;
DISALLOW_COPY_AND_ASSIGN(BluetoothSocketChromeOS);
};
diff --git a/chromium/device/bluetooth/bluetooth_socket_chromeos_unittest.cc b/chromium/device/bluetooth/bluetooth_socket_chromeos_unittest.cc
new file mode 100644
index 00000000000..7c868371c47
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_socket_chromeos_unittest.cc
@@ -0,0 +1,513 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "chromeos/dbus/fake_bluetooth_adapter_client.h"
+#include "chromeos/dbus/fake_bluetooth_agent_manager_client.h"
+#include "chromeos/dbus/fake_bluetooth_device_client.h"
+#include "chromeos/dbus/fake_bluetooth_gatt_service_client.h"
+#include "chromeos/dbus/fake_bluetooth_input_client.h"
+#include "chromeos/dbus/fake_bluetooth_profile_manager_client.h"
+#include "chromeos/dbus/fake_dbus_thread_manager.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_adapter_chromeos.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_device_chromeos.h"
+#include "device/bluetooth/bluetooth_socket.h"
+#include "device/bluetooth/bluetooth_socket_chromeos.h"
+#include "device/bluetooth/bluetooth_socket_thread.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using device::BluetoothAdapter;
+using device::BluetoothDevice;
+using device::BluetoothSocket;
+using device::BluetoothSocketThread;
+using device::BluetoothUUID;
+
+namespace {
+
+void DoNothingDBusErrorCallback(const std::string& error_name,
+ const std::string& error_message) {}
+
+} // namespace
+
+namespace chromeos {
+
+class BluetoothSocketChromeOSTest : public testing::Test {
+ public:
+ BluetoothSocketChromeOSTest()
+ : success_callback_count_(0),
+ error_callback_count_(0),
+ last_bytes_sent_(0),
+ last_bytes_received_(0),
+ last_reason_(BluetoothSocket::kSystemError) {}
+
+ virtual void SetUp() OVERRIDE {
+ scoped_ptr<FakeDBusThreadManager> fake_dbus_thread_manager(
+ new FakeDBusThreadManager);
+
+ fake_dbus_thread_manager->SetBluetoothAdapterClient(
+ scoped_ptr<BluetoothAdapterClient>(new FakeBluetoothAdapterClient));
+ fake_dbus_thread_manager->SetBluetoothAgentManagerClient(
+ scoped_ptr<BluetoothAgentManagerClient>(
+ new FakeBluetoothAgentManagerClient));
+ fake_dbus_thread_manager->SetBluetoothDeviceClient(
+ scoped_ptr<BluetoothDeviceClient>(new FakeBluetoothDeviceClient));
+ fake_dbus_thread_manager->SetBluetoothGattServiceClient(
+ scoped_ptr<BluetoothGattServiceClient>(
+ new FakeBluetoothGattServiceClient));
+ fake_dbus_thread_manager->SetBluetoothInputClient(
+ scoped_ptr<BluetoothInputClient>(new FakeBluetoothInputClient));
+ fake_dbus_thread_manager->SetBluetoothProfileManagerClient(
+ scoped_ptr<BluetoothProfileManagerClient>(
+ new FakeBluetoothProfileManagerClient));
+
+ DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager.release());
+ BluetoothSocketThread::Get();
+
+ // Grab a pointer to the adapter.
+ device::BluetoothAdapterFactory::GetAdapter(
+ base::Bind(&BluetoothSocketChromeOSTest::AdapterCallback,
+ base::Unretained(this)));
+ ASSERT_TRUE(adapter_.get() != NULL);
+ ASSERT_TRUE(adapter_->IsInitialized());
+ ASSERT_TRUE(adapter_->IsPresent());
+
+ // Turn on the adapter.
+ adapter_->SetPowered(
+ true,
+ base::Bind(&base::DoNothing),
+ base::Bind(&base::DoNothing));
+ ASSERT_TRUE(adapter_->IsPowered());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ adapter_ = NULL;
+ BluetoothSocketThread::CleanupForTesting();
+ DBusThreadManager::Shutdown();
+ }
+
+ void AdapterCallback(scoped_refptr<BluetoothAdapter> adapter) {
+ adapter_ = adapter;
+ }
+
+ void SuccessCallback() {
+ ++success_callback_count_;
+ message_loop_.Quit();
+ }
+
+ void ErrorCallback(const std::string& message) {
+ ++error_callback_count_;
+ last_message_ = message;
+
+ message_loop_.Quit();
+ }
+
+ void ConnectToServiceSuccessCallback(scoped_refptr<BluetoothSocket> socket) {
+ ++success_callback_count_;
+ last_socket_ = socket;
+
+ message_loop_.Quit();
+ }
+
+ void SendSuccessCallback(int bytes_sent) {
+ ++success_callback_count_;
+ last_bytes_sent_ = bytes_sent;
+
+ message_loop_.Quit();
+ }
+
+ void ReceiveSuccessCallback(int bytes_received,
+ scoped_refptr<net::IOBuffer> io_buffer) {
+ ++success_callback_count_;
+ last_bytes_received_ = bytes_received;
+ last_io_buffer_ = io_buffer;
+
+ message_loop_.Quit();
+ }
+
+ void ReceiveErrorCallback(BluetoothSocket::ErrorReason reason,
+ const std::string& error_message) {
+ ++error_callback_count_;
+ last_reason_ = reason;
+ last_message_ = error_message;
+
+ message_loop_.Quit();
+ }
+
+ void CreateServiceSuccessCallback(scoped_refptr<BluetoothSocket> socket) {
+ ++success_callback_count_;
+ last_socket_ = socket;
+ }
+
+ void AcceptSuccessCallback(const BluetoothDevice* device,
+ scoped_refptr<BluetoothSocket> socket) {
+ ++success_callback_count_;
+ last_device_ = device;
+ last_socket_ = socket;
+
+ message_loop_.Quit();
+ }
+
+ void ImmediateSuccessCallback() {
+ ++success_callback_count_;
+ }
+
+ protected:
+ base::MessageLoop message_loop_;
+
+ scoped_refptr<BluetoothAdapter> adapter_;
+
+ unsigned int success_callback_count_;
+ unsigned int error_callback_count_;
+
+ std::string last_message_;
+ scoped_refptr<BluetoothSocket> last_socket_;
+ int last_bytes_sent_;
+ int last_bytes_received_;
+ scoped_refptr<net::IOBuffer> last_io_buffer_;
+ BluetoothSocket::ErrorReason last_reason_;
+ const BluetoothDevice* last_device_;
+};
+
+TEST_F(BluetoothSocketChromeOSTest, Connect) {
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kPairedDeviceAddress);
+ ASSERT_TRUE(device != NULL);
+
+ device->ConnectToService(
+ BluetoothUUID(FakeBluetoothProfileManagerClient::kRfcommUuid),
+ base::Bind(&BluetoothSocketChromeOSTest::ConnectToServiceSuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothSocketChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ message_loop_.Run();
+
+ EXPECT_EQ(1U, success_callback_count_);
+ EXPECT_EQ(0U, error_callback_count_);
+ EXPECT_TRUE(last_socket_.get() != NULL);
+
+ // Take ownership of the socket for the remainder of the test.
+ scoped_refptr<BluetoothSocket> socket = last_socket_;
+ last_socket_ = NULL;
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+
+ // Send data to the socket, expect all of the data to be sent.
+ scoped_refptr<net::StringIOBuffer> write_buffer(
+ new net::StringIOBuffer("test"));
+
+ socket->Send(write_buffer.get(), write_buffer->size(),
+ base::Bind(&BluetoothSocketChromeOSTest::SendSuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothSocketChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+
+ EXPECT_EQ(1U, success_callback_count_);
+ EXPECT_EQ(0U, error_callback_count_);
+ EXPECT_EQ(last_bytes_sent_, write_buffer->size());
+
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+
+ // Receive data from the socket, and fetch the buffer from the callback; since
+ // the fake is an echo server, we expect to receive what we wrote.
+ socket->Receive(
+ 4096,
+ base::Bind(&BluetoothSocketChromeOSTest::ReceiveSuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothSocketChromeOSTest::ReceiveErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+
+ EXPECT_EQ(1U, success_callback_count_);
+ EXPECT_EQ(0U, error_callback_count_);
+ EXPECT_EQ(4, last_bytes_received_);
+ EXPECT_TRUE(last_io_buffer_.get() != NULL);
+
+ // Take ownership of the received buffer.
+ scoped_refptr<net::IOBuffer> read_buffer = last_io_buffer_;
+ last_io_buffer_ = NULL;
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+
+ std::string data = std::string(read_buffer->data(), last_bytes_received_);
+ EXPECT_EQ("test", data);
+
+ read_buffer = NULL;
+
+ // Receive data again; the socket will have been closed, this should cause a
+ // disconnected error to be returned via the error callback.
+ socket->Receive(
+ 4096,
+ base::Bind(&BluetoothSocketChromeOSTest::ReceiveSuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothSocketChromeOSTest::ReceiveErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+
+ EXPECT_EQ(0U, success_callback_count_);
+ EXPECT_EQ(1U, error_callback_count_);
+ EXPECT_EQ(BluetoothSocket::kDisconnected, last_reason_);
+ EXPECT_EQ(net::ErrorToString(net::OK), last_message_);
+
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+
+ // Send data again; since the socket is closed we should get a system error
+ // equivalent to the connection reset error.
+ write_buffer = new net::StringIOBuffer("second test");
+
+ socket->Send(write_buffer.get(), write_buffer->size(),
+ base::Bind(&BluetoothSocketChromeOSTest::SendSuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothSocketChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ message_loop_.Run();
+
+ EXPECT_EQ(0U, success_callback_count_);
+ EXPECT_EQ(1U, error_callback_count_);
+ EXPECT_EQ(net::ErrorToString(net::ERR_CONNECTION_RESET), last_message_);
+
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+
+ // Close our end of the socket.
+ socket->Disconnect(base::Bind(&BluetoothSocketChromeOSTest::SuccessCallback,
+ base::Unretained(this)));
+
+ message_loop_.Run();
+ EXPECT_EQ(1U, success_callback_count_);
+}
+
+TEST_F(BluetoothSocketChromeOSTest, Listen) {
+ adapter_->CreateRfcommService(
+ BluetoothUUID(FakeBluetoothProfileManagerClient::kRfcommUuid),
+ BluetoothAdapter::kChannelAuto,
+ base::Bind(&BluetoothSocketChromeOSTest::CreateServiceSuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothSocketChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1U, success_callback_count_);
+ EXPECT_EQ(0U, error_callback_count_);
+ EXPECT_TRUE(last_socket_.get() != NULL);
+
+ // Take ownership of the socket for the remainder of the test.
+ scoped_refptr<BluetoothSocket> server_socket = last_socket_;
+ last_socket_ = NULL;
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+
+ // Simulate an incoming connection by just calling the ConnectProfile method
+ // of the underlying fake device client (from the BlueZ point of view,
+ // outgoing and incoming look the same).
+ //
+ // This is done before the Accept() call to simulate a pending call at the
+ // point that Accept() is called.
+ FakeBluetoothDeviceClient* fake_bluetooth_device_client =
+ static_cast<FakeBluetoothDeviceClient*>(
+ DBusThreadManager::Get()->GetBluetoothDeviceClient());
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kPairedDeviceAddress);
+ ASSERT_TRUE(device != NULL);
+ fake_bluetooth_device_client->ConnectProfile(
+ static_cast<BluetoothDeviceChromeOS*>(device)->object_path(),
+ FakeBluetoothProfileManagerClient::kRfcommUuid,
+ base::Bind(&base::DoNothing),
+ base::Bind(&DoNothingDBusErrorCallback));
+
+ server_socket->Accept(
+ base::Bind(&BluetoothSocketChromeOSTest::AcceptSuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothSocketChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ message_loop_.Run();
+
+ EXPECT_EQ(1U, success_callback_count_);
+ EXPECT_EQ(0U, error_callback_count_);
+ EXPECT_TRUE(last_socket_.get() != NULL);
+
+ // Take ownership of the client socket for the remainder of the test.
+ scoped_refptr<BluetoothSocket> client_socket = last_socket_;
+ last_socket_ = NULL;
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+
+ // Close our end of the client socket.
+ client_socket->Disconnect(
+ base::Bind(&BluetoothSocketChromeOSTest::SuccessCallback,
+ base::Unretained(this)));
+
+ message_loop_.Run();
+
+ EXPECT_EQ(1U, success_callback_count_);
+ client_socket = NULL;
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+
+ // Run a second connection test, this time calling Accept() before the
+ // incoming connection comes in.
+ server_socket->Accept(
+ base::Bind(&BluetoothSocketChromeOSTest::AcceptSuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothSocketChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ fake_bluetooth_device_client->ConnectProfile(
+ static_cast<BluetoothDeviceChromeOS*>(device)->object_path(),
+ FakeBluetoothProfileManagerClient::kRfcommUuid,
+ base::Bind(&base::DoNothing),
+ base::Bind(&DoNothingDBusErrorCallback));
+
+ message_loop_.Run();
+
+ EXPECT_EQ(1U, success_callback_count_);
+ EXPECT_EQ(0U, error_callback_count_);
+ EXPECT_TRUE(last_socket_.get() != NULL);
+
+ // Take ownership of the client socket for the remainder of the test.
+ client_socket = last_socket_;
+ last_socket_ = NULL;
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+
+ // Close our end of the client socket.
+ client_socket->Disconnect(
+ base::Bind(&BluetoothSocketChromeOSTest::SuccessCallback,
+ base::Unretained(this)));
+
+ message_loop_.Run();
+
+ EXPECT_EQ(1U, success_callback_count_);
+ client_socket = NULL;
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+
+ // Now close the server socket.
+ server_socket->Disconnect(
+ base::Bind(&BluetoothSocketChromeOSTest::ImmediateSuccessCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1U, success_callback_count_);
+}
+
+TEST_F(BluetoothSocketChromeOSTest, ListenBeforeAdapterStart) {
+ // Start off with an invisible adapter, register the profile, then make
+ // the adapter visible.
+ FakeBluetoothAdapterClient* fake_bluetooth_adapter_client =
+ static_cast<FakeBluetoothAdapterClient*>(
+ DBusThreadManager::Get()->GetBluetoothAdapterClient());
+ fake_bluetooth_adapter_client->SetVisible(false);
+
+ adapter_->CreateRfcommService(
+ BluetoothUUID(FakeBluetoothProfileManagerClient::kRfcommUuid),
+ BluetoothAdapter::kChannelAuto,
+ base::Bind(&BluetoothSocketChromeOSTest::CreateServiceSuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothSocketChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1U, success_callback_count_);
+ EXPECT_EQ(0U, error_callback_count_);
+ EXPECT_TRUE(last_socket_.get() != NULL);
+
+ // Take ownership of the socket for the remainder of the test.
+ scoped_refptr<BluetoothSocket> socket = last_socket_;
+ last_socket_ = NULL;
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+
+ // But there shouldn't be a profile registered yet.
+ FakeBluetoothProfileManagerClient* fake_bluetooth_profile_manager_client =
+ static_cast<FakeBluetoothProfileManagerClient*>(
+ DBusThreadManager::Get()->GetBluetoothProfileManagerClient());
+ FakeBluetoothProfileServiceProvider* profile_service_provider =
+ fake_bluetooth_profile_manager_client->GetProfileServiceProvider(
+ FakeBluetoothProfileManagerClient::kRfcommUuid);
+ EXPECT_TRUE(profile_service_provider == NULL);
+
+ // Make the adapter visible. This should register a profile.
+ fake_bluetooth_adapter_client->SetVisible(true);
+
+ profile_service_provider =
+ fake_bluetooth_profile_manager_client->GetProfileServiceProvider(
+ FakeBluetoothProfileManagerClient::kRfcommUuid);
+ EXPECT_TRUE(profile_service_provider != NULL);
+
+ // Cleanup the socket.
+ socket->Disconnect(
+ base::Bind(&BluetoothSocketChromeOSTest::ImmediateSuccessCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1U, success_callback_count_);
+}
+
+TEST_F(BluetoothSocketChromeOSTest, ListenAcrossAdapterRestart) {
+ // The fake adapter starts off visible by default.
+ FakeBluetoothAdapterClient* fake_bluetooth_adapter_client =
+ static_cast<FakeBluetoothAdapterClient*>(
+ DBusThreadManager::Get()->GetBluetoothAdapterClient());
+
+ adapter_->CreateRfcommService(
+ BluetoothUUID(FakeBluetoothProfileManagerClient::kRfcommUuid),
+ BluetoothAdapter::kChannelAuto,
+ base::Bind(&BluetoothSocketChromeOSTest::CreateServiceSuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothSocketChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1U, success_callback_count_);
+ EXPECT_EQ(0U, error_callback_count_);
+ EXPECT_TRUE(last_socket_.get() != NULL);
+
+ // Take ownership of the socket for the remainder of the test.
+ scoped_refptr<BluetoothSocket> socket = last_socket_;
+ last_socket_ = NULL;
+ success_callback_count_ = 0;
+ error_callback_count_ = 0;
+
+ // Make sure the profile was registered with the daemon.
+ FakeBluetoothProfileManagerClient* fake_bluetooth_profile_manager_client =
+ static_cast<FakeBluetoothProfileManagerClient*>(
+ DBusThreadManager::Get()->GetBluetoothProfileManagerClient());
+ FakeBluetoothProfileServiceProvider* profile_service_provider =
+ fake_bluetooth_profile_manager_client->GetProfileServiceProvider(
+ FakeBluetoothProfileManagerClient::kRfcommUuid);
+ EXPECT_TRUE(profile_service_provider != NULL);
+
+ // Make the adapter invisible, and fiddle with the profile fake to unregister
+ // the profile since this doesn't happen automatically.
+ fake_bluetooth_adapter_client->SetVisible(false);
+ fake_bluetooth_profile_manager_client->UnregisterProfile(
+ static_cast<BluetoothSocketChromeOS*>(socket.get())->object_path(),
+ base::Bind(&base::DoNothing),
+ base::Bind(&DoNothingDBusErrorCallback));
+
+ // Then make the adapter visible again. This should re-register the profile.
+ fake_bluetooth_adapter_client->SetVisible(true);
+
+ profile_service_provider =
+ fake_bluetooth_profile_manager_client->GetProfileServiceProvider(
+ FakeBluetoothProfileManagerClient::kRfcommUuid);
+ EXPECT_TRUE(profile_service_provider != NULL);
+
+ // Cleanup the socket.
+ socket->Disconnect(
+ base::Bind(&BluetoothSocketChromeOSTest::ImmediateSuccessCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1U, success_callback_count_);
+}
+
+} // namespace chromeos
diff --git a/chromium/device/bluetooth/bluetooth_socket_mac.h b/chromium/device/bluetooth/bluetooth_socket_mac.h
index 4d7bfb7e018..bbb107d8a38 100644
--- a/chromium/device/bluetooth/bluetooth_socket_mac.h
+++ b/chromium/device/bluetooth/bluetooth_socket_mac.h
@@ -5,65 +5,193 @@
#ifndef DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_
+#include <queue>
#include <string>
+#import <IOBluetooth/IOBluetooth.h>
+#import <IOKit/IOReturn.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "base/memory/linked_ptr.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
#include "device/bluetooth/bluetooth_socket.h"
+#include "device/bluetooth/bluetooth_uuid.h"
-#ifdef __OBJC__
-@class BluetoothRFCOMMChannelDelegate;
-@class IOBluetoothRFCOMMChannel;
-@class IOBluetoothSDPServiceRecord;
-#else
-class BluetoothRFCOMMChannelDelegate;
-class IOBluetoothRFCOMMChannel;
-class IOBluetoothSDPServiceRecord;
-#endif
+@class BluetoothRfcommConnectionListener;
+@class BluetoothL2capConnectionListener;
namespace net {
-
-class DrainableIOBuffer;
-class GrowableIOBuffer;
-
-} // namespace net
+class IOBuffer;
+class IOBufferWithSize;
+}
namespace device {
-class BluetoothServiceRecord;
+class BluetoothAdapterMac;
+class BluetoothChannelMac;
-// This class is an implementation of BluetoothSocket class for OSX platform.
+// Implements the BluetoothSocket class for the Mac OS X platform.
class BluetoothSocketMac : public BluetoothSocket {
public:
- // TODO(youngki): This method is deprecated; remove this method when
- // BluetoothServiceRecord is removed.
- static scoped_refptr<BluetoothSocket> CreateBluetoothSocket(
- const BluetoothServiceRecord& service_record);
+ static scoped_refptr<BluetoothSocketMac> CreateSocket();
+
+ // Connects this socket to the service on |device| published as UUID |uuid|.
+ // The underlying protocol and PSM or Channel is obtained through service
+ // discovery. On a successful connection, the socket properties will be
+ // updated and |success_callback| called. On failure, |error_callback| will be
+ // called with a message explaining the cause of failure.
+ void Connect(IOBluetoothDevice* device,
+ const BluetoothUUID& uuid,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback);
+
+ // Listens for incoming RFCOMM connections using this socket: Publishes an
+ // RFCOMM service on the |adapter| as UUID |uuid| with Channel |channel_id|.
+ // |success_callback| will be called if the service is successfully
+ // registered, |error_callback| on failure with a message explaining the
+ // cause.
+ void ListenUsingRfcomm(scoped_refptr<BluetoothAdapterMac> adapter,
+ const BluetoothUUID& uuid,
+ int channel_id,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback);
+
+ // Listens for incoming L2CAP connections using this socket: Publishes an
+ // L2CAP service on the |adapter| as UUID |uuid| with PSM |psm|.
+ // |success_callback| will be called if the service is successfully
+ // registered, |error_callback| on failure with a message explaining the
+ // cause.
+ void ListenUsingL2cap(scoped_refptr<BluetoothAdapterMac> adapter,
+ const BluetoothUUID& uuid,
+ int psm,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback);
+
+ // BluetoothSocket:
+ virtual void Close() OVERRIDE;
+ virtual void Disconnect(const base::Closure& callback) OVERRIDE;
+ virtual void Receive(
+ int /* buffer_size */,
+ const ReceiveCompletionCallback& success_callback,
+ const ReceiveErrorCompletionCallback& error_callback) OVERRIDE;
+ virtual void Send(scoped_refptr<net::IOBuffer> buffer,
+ int buffer_size,
+ const SendCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) OVERRIDE;
+ virtual void Accept(const AcceptCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) OVERRIDE;
+
+ // Callback that is invoked when the OS completes an SDP query.
+ // |status| is the returned status from the SDP query, |device| is the
+ // IOBluetoothDevice for which the query was made. The remaining
+ // parameters are those from |Connect()|.
+ void OnSDPQueryComplete(
+ IOReturn status,
+ IOBluetoothDevice* device,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback);
+
+ // Called by BluetoothRfcommConnectionListener and
+ // BluetoothL2capConnectionListener.
+ void OnChannelOpened(scoped_ptr<BluetoothChannelMac> channel);
+
+ // Called by |channel_|.
+ // Note: OnChannelOpenComplete might be called before the |channel_| is set.
+ void OnChannelOpenComplete(const std::string& device_address,
+ IOReturn status);
+ void OnChannelClosed();
+ void OnChannelDataReceived(void* data, size_t length);
+ void OnChannelWriteComplete(void* refcon, IOReturn status);
+
+ private:
+ struct AcceptRequest {
+ AcceptRequest();
+ ~AcceptRequest();
+
+ AcceptCompletionCallback success_callback;
+ ErrorCompletionCallback error_callback;
+ };
+
+ struct SendRequest {
+ SendRequest();
+ ~SendRequest();
+ int buffer_size;
+ SendCompletionCallback success_callback;
+ ErrorCompletionCallback error_callback;
+ IOReturn status;
+ int active_async_writes;
+ bool error_signaled;
+ };
+
+ struct ReceiveCallbacks {
+ ReceiveCallbacks();
+ ~ReceiveCallbacks();
+ ReceiveCompletionCallback success_callback;
+ ReceiveErrorCompletionCallback error_callback;
+ };
+
+ struct ConnectCallbacks {
+ ConnectCallbacks();
+ ~ConnectCallbacks();
+ base::Closure success_callback;
+ ErrorCompletionCallback error_callback;
+ };
+
+ BluetoothSocketMac();
+ virtual ~BluetoothSocketMac();
- static scoped_refptr<BluetoothSocket> CreateBluetoothSocket(
- IOBluetoothSDPServiceRecord* record);
+ // Accepts a single incoming connection.
+ void AcceptConnectionRequest();
- // BluetoothSocket override
- virtual bool Receive(net::GrowableIOBuffer* buffer) OVERRIDE;
- virtual bool Send(net::DrainableIOBuffer* buffer) OVERRIDE;
- virtual std::string GetLastErrorMessage() const OVERRIDE;
+ void ReleaseChannel();
+ void ReleaseListener();
- // called by BluetoothRFCOMMChannelDelegate.
- void OnDataReceived(IOBluetoothRFCOMMChannel* rfcomm_channel,
- void* data,
- size_t length);
+ bool is_connecting() const { return connect_callbacks_; }
- protected:
- virtual ~BluetoothSocketMac();
+ // Used to verify that all methods are called on the same thread.
+ base::ThreadChecker thread_checker_;
- private:
- explicit BluetoothSocketMac(IOBluetoothRFCOMMChannel* rfcomm_channel);
+ // Adapter the socket is registered against. This is only present when the
+ // socket is listening.
+ scoped_refptr<BluetoothAdapterMac> adapter_;
+
+ // UUID of the profile being connected to, or that the socket is listening on.
+ device::BluetoothUUID uuid_;
+
+ // Simple helpers that register for OS notifications and forward them to
+ // |this| profile.
+ base::scoped_nsobject<BluetoothRfcommConnectionListener>
+ rfcomm_connection_listener_;
+ base::scoped_nsobject<BluetoothL2capConnectionListener>
+ l2cap_connection_listener_;
+
+ // A handle to the service record registered in the system SDP server.
+ // Used to eventually unregister the service.
+ BluetoothSDPServiceRecordHandle service_record_handle_;
+
+ // The channel used to issue commands.
+ scoped_ptr<BluetoothChannelMac> channel_;
+
+ // Connection callbacks -- when a pending async connection is active.
+ scoped_ptr<ConnectCallbacks> connect_callbacks_;
+
+ // Packets received while there is no pending "receive" callback.
+ std::queue<scoped_refptr<net::IOBufferWithSize> > receive_queue_;
+
+ // Receive callbacks -- when a receive call is active.
+ scoped_ptr<ReceiveCallbacks> receive_callbacks_;
+
+ // Send queue -- one entry per pending send operation.
+ std::queue<linked_ptr<SendRequest>> send_queue_;
- void ResetIncomingDataBuffer();
+ // The pending request to an Accept() call, or null if there is no pending
+ // request.
+ scoped_ptr<AcceptRequest> accept_request_;
- IOBluetoothRFCOMMChannel* rfcomm_channel_;
- BluetoothRFCOMMChannelDelegate* delegate_;
- scoped_refptr<net::GrowableIOBuffer> incoming_data_buffer_;
- std::string error_message_;
+ // Queue of incoming connections.
+ std::queue<linked_ptr<BluetoothChannelMac>> accept_queue_;
DISALLOW_COPY_AND_ASSIGN(BluetoothSocketMac);
};
diff --git a/chromium/device/bluetooth/bluetooth_socket_mac.mm b/chromium/device/bluetooth/bluetooth_socket_mac.mm
index 3751441f521..e428ba5a7f0 100644
--- a/chromium/device/bluetooth/bluetooth_socket_mac.mm
+++ b/chromium/device/bluetooth/bluetooth_socket_mac.mm
@@ -4,179 +4,935 @@
#include "device/bluetooth/bluetooth_socket_mac.h"
-#import <IOBluetooth/objc/IOBluetoothDevice.h>
-#import <IOBluetooth/objc/IOBluetoothRFCOMMChannel.h>
-#import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h>
+#import <IOBluetooth/IOBluetooth.h>
#include <limits>
+#include <sstream>
#include <string>
#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/mac/scoped_cftyperef.h"
#include "base/memory/ref_counted.h"
+#include "base/numerics/safe_conversions.h"
#include "base/strings/stringprintf.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/sys_string_conversions.h"
-#include "device/bluetooth/bluetooth_service_record.h"
-#include "device/bluetooth/bluetooth_service_record_mac.h"
+#include "base/threading/thread_restrictions.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_adapter_mac.h"
+#include "device/bluetooth/bluetooth_channel_mac.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_device_mac.h"
+#include "device/bluetooth/bluetooth_l2cap_channel_mac.h"
+#include "device/bluetooth/bluetooth_rfcomm_channel_mac.h"
#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
// Replicate specific 10.7 SDK declarations for building with prior SDKs.
#if !defined(MAC_OS_X_VERSION_10_7) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
@interface IOBluetoothDevice (LionSDKDeclarations)
-- (NSString*)addressString;
+- (IOReturn)performSDPQuery:(id)target uuids:(NSArray*)uuids;
@end
#endif // MAC_OS_X_VERSION_10_7
-@interface BluetoothRFCOMMChannelDelegate
- : NSObject <IOBluetoothRFCOMMChannelDelegate> {
+using device::BluetoothSocket;
+
+// A simple helper class that forwards SDP query completed notifications to its
+// wrapped |socket_|.
+@interface SDPQueryListener : NSObject {
+ @private
+ // The socket that registered for notifications.
+ scoped_refptr<device::BluetoothSocketMac> socket_;
+
+ // Callbacks associated with the request that triggered this SDP query.
+ base::Closure success_callback_;
+ BluetoothSocket::ErrorCompletionCallback error_callback_;
+
+ // The device being queried.
+ IOBluetoothDevice* device_; // weak
+}
+
+- (id)initWithSocket:(scoped_refptr<device::BluetoothSocketMac>)socket
+ device:(IOBluetoothDevice*)device
+ success_callback:(base::Closure)success_callback
+ error_callback:(BluetoothSocket::ErrorCompletionCallback)error_callback;
+- (void)sdpQueryComplete:(IOBluetoothDevice*)device status:(IOReturn)status;
+
+@end
+
+@implementation SDPQueryListener
+
+- (id)initWithSocket:(scoped_refptr<device::BluetoothSocketMac>)socket
+ device:(IOBluetoothDevice*)device
+ success_callback:(base::Closure)success_callback
+ error_callback:(BluetoothSocket::ErrorCompletionCallback)error_callback {
+ if ((self = [super init])) {
+ socket_ = socket;
+ device_ = device;
+ success_callback_ = success_callback;
+ error_callback_ = error_callback;
+ }
+
+ return self;
+}
+
+- (void)sdpQueryComplete:(IOBluetoothDevice*)device status:(IOReturn)status {
+ DCHECK_EQ(device, device_);
+ socket_->OnSDPQueryComplete(
+ status, device, success_callback_, error_callback_);
+}
+
+@end
+
+// A simple helper class that forwards RFCOMM channel opened notifications to
+// its wrapped |socket_|.
+@interface BluetoothRfcommConnectionListener : NSObject {
+ @private
+ // The socket that owns |self|.
+ device::BluetoothSocketMac* socket_; // weak
+
+ // The OS mechanism used to subscribe to and unsubscribe from RFCOMM channel
+ // creation notifications.
+ IOBluetoothUserNotification* rfcommNewChannelNotification_; // weak
+}
+
+- (id)initWithSocket:(device::BluetoothSocketMac*)socket
+ channelID:(BluetoothRFCOMMChannelID)channelID;
+- (void)rfcommChannelOpened:(IOBluetoothUserNotification*)notification
+ channel:(IOBluetoothRFCOMMChannel*)rfcommChannel;
+
+@end
+
+@implementation BluetoothRfcommConnectionListener
+
+- (id)initWithSocket:(device::BluetoothSocketMac*)socket
+ channelID:(BluetoothRFCOMMChannelID)channelID {
+ if ((self = [super init])) {
+ socket_ = socket;
+
+ SEL selector = @selector(rfcommChannelOpened:channel:);
+ const auto kIncomingDirection =
+ kIOBluetoothUserNotificationChannelDirectionIncoming;
+ rfcommNewChannelNotification_ =
+ [IOBluetoothRFCOMMChannel
+ registerForChannelOpenNotifications:self
+ selector:selector
+ withChannelID:channelID
+ direction:kIncomingDirection];
+ }
+
+ return self;
+}
+
+- (void)dealloc {
+ [rfcommNewChannelNotification_ unregister];
+ [super dealloc];
+}
+
+- (void)rfcommChannelOpened:(IOBluetoothUserNotification*)notification
+ channel:(IOBluetoothRFCOMMChannel*)rfcommChannel {
+ if (notification != rfcommNewChannelNotification_) {
+ // This case is reachable if there are pre-existing RFCOMM channels open at
+ // the time that the listener is created. In that case, each existing
+ // channel calls into this method with a different notification than the one
+ // this class registered with. Ignore those; this class is only interested
+ // in channels that have opened since it registered for notifications.
+ return;
+ }
+
+ socket_->OnChannelOpened(scoped_ptr<device::BluetoothChannelMac>(
+ new device::BluetoothRfcommChannelMac(NULL, [rfcommChannel retain])));
+}
+
+@end
+
+// A simple helper class that forwards L2CAP channel opened notifications to
+// its wrapped |socket_|.
+@interface BluetoothL2capConnectionListener : NSObject {
@private
+ // The socket that owns |self|.
device::BluetoothSocketMac* socket_; // weak
+
+ // The OS mechanism used to subscribe to and unsubscribe from L2CAP channel
+ // creation notifications.
+ IOBluetoothUserNotification* l2capNewChannelNotification_; // weak
}
-- (id)initWithSocket:(device::BluetoothSocketMac*)socket;
+- (id)initWithSocket:(device::BluetoothSocketMac*)socket
+ psm:(BluetoothL2CAPPSM)psm;
+- (void)l2capChannelOpened:(IOBluetoothUserNotification*)notification
+ channel:(IOBluetoothL2CAPChannel*)l2capChannel;
@end
-@implementation BluetoothRFCOMMChannelDelegate
+@implementation BluetoothL2capConnectionListener
-- (id)initWithSocket:(device::BluetoothSocketMac*)socket {
- if ((self = [super init]))
+- (id)initWithSocket:(device::BluetoothSocketMac*)socket
+ psm:(BluetoothL2CAPPSM)psm {
+ if ((self = [super init])) {
socket_ = socket;
+ SEL selector = @selector(l2capChannelOpened:channel:);
+ const auto kIncomingDirection =
+ kIOBluetoothUserNotificationChannelDirectionIncoming;
+ l2capNewChannelNotification_ =
+ [IOBluetoothL2CAPChannel
+ registerForChannelOpenNotifications:self
+ selector:selector
+ withPSM:psm
+ direction:kIncomingDirection];
+ }
+
return self;
}
-- (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel
- data:(void*)dataPointer
- length:(size_t)dataLength {
- socket_->OnDataReceived(rfcommChannel, dataPointer, dataLength);
+- (void)dealloc {
+ [l2capNewChannelNotification_ unregister];
+ [super dealloc];
+}
+
+- (void)l2capChannelOpened:(IOBluetoothUserNotification*)notification
+ channel:(IOBluetoothL2CAPChannel*)l2capChannel {
+ if (notification != l2capNewChannelNotification_) {
+ // This case is reachable if there are pre-existing L2CAP channels open at
+ // the time that the listener is created. In that case, each existing
+ // channel calls into this method with a different notification than the one
+ // this class registered with. Ignore those; this class is only interested
+ // in channels that have opened since it registered for notifications.
+ return;
+ }
+
+ socket_->OnChannelOpened(scoped_ptr<device::BluetoothChannelMac>(
+ new device::BluetoothL2capChannelMac(NULL, [l2capChannel retain])));
}
@end
namespace device {
+namespace {
-BluetoothSocketMac::BluetoothSocketMac(IOBluetoothRFCOMMChannel* rfcomm_channel)
- : rfcomm_channel_(rfcomm_channel),
- delegate_([[BluetoothRFCOMMChannelDelegate alloc] initWithSocket:this]) {
- [rfcomm_channel_ setDelegate:delegate_];
- ResetIncomingDataBuffer();
+// It's safe to use 0 to represent an unregistered service, as implied by the
+// documentation at [ http://goo.gl/YRtCkF ].
+const BluetoothSDPServiceRecordHandle kInvalidServiceRecordHandle = 0;
+
+const char kInvalidOrUsedChannel[] = "Invalid channel or already in use";
+const char kInvalidOrUsedPsm[] = "Invalid PSM or already in use";
+const char kProfileNotFound[] = "Profile not found";
+const char kSDPQueryFailed[] = "SDP query failed";
+const char kSocketConnecting[] = "The socket is currently connecting";
+const char kSocketAlreadyConnected[] = "The socket is already connected";
+const char kSocketNotConnected[] = "The socket is not connected";
+const char kReceivePending[] = "A Receive operation is pending";
+
+template <class T>
+void empty_queue(std::queue<T>& queue) {
+ std::queue<T> empty;
+ std::swap(queue, empty);
}
-BluetoothSocketMac::~BluetoothSocketMac() {
- [rfcomm_channel_ setDelegate:nil];
- [rfcomm_channel_ closeChannel];
- [rfcomm_channel_ release];
- [delegate_ release];
+// Converts |uuid| to a IOBluetoothSDPUUID instance.
+IOBluetoothSDPUUID* GetIOBluetoothSDPUUID(const BluetoothUUID& uuid) {
+ // The canonical UUID format is XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
+ const std::string uuid_str = uuid.canonical_value();
+ DCHECK_EQ(uuid_str.size(), 36U);
+ DCHECK_EQ(uuid_str[8], '-');
+ DCHECK_EQ(uuid_str[13], '-');
+ DCHECK_EQ(uuid_str[18], '-');
+ DCHECK_EQ(uuid_str[23], '-');
+ std::string numbers_only = uuid_str;
+ numbers_only.erase(23, 1);
+ numbers_only.erase(18, 1);
+ numbers_only.erase(13, 1);
+ numbers_only.erase(8, 1);
+ std::vector<uint8> uuid_bytes_vector;
+ base::HexStringToBytes(numbers_only, &uuid_bytes_vector);
+ DCHECK_EQ(uuid_bytes_vector.size(), 16U);
+
+ return [IOBluetoothSDPUUID uuidWithBytes:&uuid_bytes_vector.front()
+ length:uuid_bytes_vector.size()];
}
-// static
-scoped_refptr<BluetoothSocket> BluetoothSocketMac::CreateBluetoothSocket(
- const BluetoothServiceRecord& service_record) {
- BluetoothSocketMac* bluetooth_socket = NULL;
- if (service_record.SupportsRfcomm()) {
- const BluetoothServiceRecordMac* service_record_mac =
- static_cast<const BluetoothServiceRecordMac*>(&service_record);
- IOBluetoothDevice* device = service_record_mac->GetIOBluetoothDevice();
- IOBluetoothRFCOMMChannel* rfcomm_channel;
- IOReturn status =
- [device openRFCOMMChannelAsync:&rfcomm_channel
- withChannelID:service_record.rfcomm_channel()
- delegate:nil];
- if (status == kIOReturnSuccess) {
- bluetooth_socket = new BluetoothSocketMac(rfcomm_channel);
- } else {
- LOG(ERROR) << "Failed to connect bluetooth socket ("
- << service_record.address() << "): (" << status << ")";
- }
+// Converts the given |integer| to a string.
+NSString* IntToNSString(int integer) {
+ return [[NSNumber numberWithInt:integer] stringValue];
+}
+
+// Returns a dictionary containing the Bluetooth service definition
+// corresponding to the provided |uuid| and |protocol_definition|.
+NSDictionary* BuildServiceDefinition(const BluetoothUUID& uuid,
+ NSArray* protocol_definition) {
+ NSMutableDictionary* service_definition = [NSMutableDictionary dictionary];
+
+ // TODO(isherman): The service's language is currently hardcoded to English.
+ // The language should ideally be specified in the chrome.bluetooth API
+ // instead.
+ // TODO(isherman): Pass in the service name to this function.
+ const int kEnglishLanguageBase = 100;
+ const int kServiceNameKey =
+ kEnglishLanguageBase + kBluetoothSDPAttributeIdentifierServiceName;
+ NSString* service_name = base::SysUTF8ToNSString(uuid.canonical_value());
+ [service_definition setObject:service_name
+ forKey:IntToNSString(kServiceNameKey)];
+
+ const int kUUIDsKey = kBluetoothSDPAttributeIdentifierServiceClassIDList;
+ NSArray* uuids = @[GetIOBluetoothSDPUUID(uuid)];
+ [service_definition setObject:uuids forKey:IntToNSString(kUUIDsKey)];
+
+ const int kProtocolDefinitionsKey =
+ kBluetoothSDPAttributeIdentifierProtocolDescriptorList;
+ [service_definition setObject:protocol_definition
+ forKey:IntToNSString(kProtocolDefinitionsKey)];
+
+ return service_definition;
+}
+
+// Returns a dictionary containing the Bluetooth RFCOMM service definition
+// corresponding to the provided |uuid| and |channel_id|.
+NSDictionary* BuildRfcommServiceDefinition(const BluetoothUUID& uuid,
+ int channel_id) {
+ NSArray* rfcomm_protocol_definition =
+ @[
+ @[
+ [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16L2CAP]
+ ],
+ @[
+ [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16RFCOMM],
+ @{
+ @"DataElementType": @1, // Unsigned integer.
+ @"DataElementSize": @1, // 1 byte.
+ @"DataElementValue": [NSNumber numberWithInt:channel_id]
+ }
+ ]
+ ];
+ return BuildServiceDefinition(uuid, rfcomm_protocol_definition);
+}
+
+// Returns a dictionary containing the Bluetooth L2CAP service definition
+// corresponding to the provided |uuid| and |psm|.
+NSDictionary* BuildL2capServiceDefinition(const BluetoothUUID& uuid,
+ int psm) {
+ NSArray* l2cap_protocol_definition =
+ @[
+ @[
+ [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16L2CAP],
+ @{
+ @"DataElementType": @1, // Unsigned integer.
+ @"DataElementSize": @2, // 2 bytes.
+ @"DataElementValue": [NSNumber numberWithInt:psm]
+ }
+ ]
+ ];
+ return BuildServiceDefinition(uuid, l2cap_protocol_definition);
+}
+
+// Registers a Bluetooth service with the specified |service_definition| in the
+// system SDP server. Returns a handle to the registered service on success. If
+// the service could not be registered, or if |verify_service_callback|
+// indicates that the to-be-registered service is not configured correctly,
+// returns |kInvalidServiceRecordHandle|.
+BluetoothSDPServiceRecordHandle RegisterService(
+ NSDictionary* service_definition,
+ const base::Callback<bool(IOBluetoothSDPServiceRecord*)>&
+ verify_service_callback) {
+ // Attempt to register the service.
+ IOBluetoothSDPServiceRecordRef service_record_ref;
+ IOReturn result =
+ IOBluetoothAddServiceDict((CFDictionaryRef)service_definition,
+ &service_record_ref);
+ if (result != kIOReturnSuccess)
+ return kInvalidServiceRecordHandle;
+ // Transfer ownership to a scoped object, to simplify memory management.
+ base::ScopedCFTypeRef<IOBluetoothSDPServiceRecordRef>
+ scoped_service_record_ref(service_record_ref);
+
+ // Extract the service record handle.
+ BluetoothSDPServiceRecordHandle service_record_handle;
+ IOBluetoothSDPServiceRecord* service_record =
+ [IOBluetoothSDPServiceRecord withSDPServiceRecordRef:service_record_ref];
+ result = [service_record getServiceRecordHandle:&service_record_handle];
+ if (result != kIOReturnSuccess)
+ return kInvalidServiceRecordHandle;
+
+ // Verify that the registered service was configured correctly. If not,
+ // withdraw the service.
+ if (!verify_service_callback.Run(service_record)) {
+ IOBluetoothRemoveServiceWithRecordHandle(service_record_handle);
+ return kInvalidServiceRecordHandle;
}
- // TODO(youngki): add support for L2CAP sockets as well.
- return scoped_refptr<BluetoothSocketMac>(bluetooth_socket);
+ return service_record_handle;
}
+// Returns true iff the |requested_channel_id| was registered in the RFCOMM
+// |service_record|. If it was, also updates |registered_channel_id| with the
+// registered value, as the requested id may have been left unspecified.
+bool VerifyRfcommService(int requested_channel_id,
+ BluetoothRFCOMMChannelID* registered_channel_id,
+ IOBluetoothSDPServiceRecord* service_record) {
+ // Test whether the requested channel id was available.
+ // TODO(isherman): The OS doesn't seem to actually pick a random channel if we
+ // pass in |kChannelAuto|.
+ BluetoothRFCOMMChannelID rfcomm_channel_id;
+ IOReturn result = [service_record getRFCOMMChannelID:&rfcomm_channel_id];
+ if (result != kIOReturnSuccess ||
+ (requested_channel_id != BluetoothAdapter::kChannelAuto &&
+ rfcomm_channel_id != requested_channel_id)) {
+ return false;
+ }
+
+ *registered_channel_id = rfcomm_channel_id;
+ return true;
+}
+
+// Registers an RFCOMM service with the specified |uuid| and |channel_id| in the
+// system SDP server. Returns a handle to the registered service and updates
+// |registered_channel_id| to the actual channel id, or returns
+// |kInvalidServiceRecordHandle| if the service could not be registered.
+BluetoothSDPServiceRecordHandle RegisterRfcommService(
+ const BluetoothUUID& uuid,
+ int channel_id,
+ BluetoothRFCOMMChannelID* registered_channel_id) {
+ return RegisterService(
+ BuildRfcommServiceDefinition(uuid, channel_id),
+ base::Bind(&VerifyRfcommService, channel_id, registered_channel_id));
+}
+
+// Returns true iff the |requested_psm| was registered in the L2CAP
+// |service_record|. If it was, also updates |registered_psm| with the
+// registered value, as the requested PSM may have been left unspecified.
+bool VerifyL2capService(int requested_psm,
+ BluetoothL2CAPPSM* registered_psm,
+ IOBluetoothSDPServiceRecord* service_record) {
+ // Test whether the requested PSM was available.
+ // TODO(isherman): The OS doesn't seem to actually pick a random PSM if we
+ // pass in |kPsmAuto|.
+ BluetoothL2CAPPSM l2cap_psm;
+ IOReturn result = [service_record getL2CAPPSM:&l2cap_psm];
+ if (result != kIOReturnSuccess ||
+ (requested_psm != BluetoothAdapter::kPsmAuto &&
+ l2cap_psm != requested_psm)) {
+ return false;
+ }
+
+ *registered_psm = l2cap_psm;
+ return true;
+}
+
+// Registers an L2CAP service with the specified |uuid| and |psm| in the system
+// SDP server. Returns a handle to the registered service and updates
+// |registered_psm| to the actual PSM, or returns |kInvalidServiceRecordHandle|
+// if the service could not be registered.
+BluetoothSDPServiceRecordHandle RegisterL2capService(
+ const BluetoothUUID& uuid,
+ int psm,
+ BluetoothL2CAPPSM* registered_psm) {
+ return RegisterService(BuildL2capServiceDefinition(uuid, psm),
+ base::Bind(&VerifyL2capService, psm, registered_psm));
+}
+
+} // namespace
+
// static
-scoped_refptr<BluetoothSocket> BluetoothSocketMac::CreateBluetoothSocket(
- IOBluetoothSDPServiceRecord* record) {
- BluetoothSocketMac* bluetooth_socket = NULL;
- uint8 rfcomm_channel_id;
- if ([record getRFCOMMChannelID:&rfcomm_channel_id] == kIOReturnSuccess) {
- IOBluetoothDevice* device = [record device];
- IOBluetoothRFCOMMChannel* rfcomm_channel;
- IOReturn status =
- [device openRFCOMMChannelAsync:&rfcomm_channel
- withChannelID:rfcomm_channel_id
- delegate:nil];
- if (status == kIOReturnSuccess) {
- bluetooth_socket = new BluetoothSocketMac(rfcomm_channel);
- } else {
- LOG(ERROR) << "Failed to connect bluetooth socket ("
- << base::SysNSStringToUTF8([device addressString]) << "): (" << status
- << ")";
+scoped_refptr<BluetoothSocketMac> BluetoothSocketMac::CreateSocket() {
+ return make_scoped_refptr(new BluetoothSocketMac());
+}
+
+void BluetoothSocketMac::Connect(
+ IOBluetoothDevice* device,
+ const BluetoothUUID& uuid,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ uuid_ = uuid;
+
+ // Perform an SDP query on the |device| to refresh the cache, in case the
+ // services that the |device| advertises have changed since the previous
+ // query.
+ DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
+ << uuid_.canonical_value() << ": Sending SDP query.";
+ SDPQueryListener* listener =
+ [[SDPQueryListener alloc] initWithSocket:this
+ device:device
+ success_callback:success_callback
+ error_callback:error_callback];
+ [device performSDPQuery:[listener autorelease]
+ uuids:@[GetIOBluetoothSDPUUID(uuid_)]];
+}
+
+void BluetoothSocketMac::ListenUsingRfcomm(
+ scoped_refptr<BluetoothAdapterMac> adapter,
+ const BluetoothUUID& uuid,
+ int channel_id,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ adapter_ = adapter;
+ uuid_ = uuid;
+
+ DVLOG(1) << uuid_.canonical_value() << ": Registering RFCOMM service.";
+ BluetoothRFCOMMChannelID registered_channel_id;
+ service_record_handle_ =
+ RegisterRfcommService(uuid, channel_id, &registered_channel_id);
+ if (service_record_handle_ == kInvalidServiceRecordHandle) {
+ error_callback.Run(kInvalidOrUsedChannel);
+ return;
+ }
+
+ rfcomm_connection_listener_.reset(
+ [[BluetoothRfcommConnectionListener alloc]
+ initWithSocket:this
+ channelID:registered_channel_id]);
+
+ success_callback.Run();
+}
+
+void BluetoothSocketMac::ListenUsingL2cap(
+ scoped_refptr<BluetoothAdapterMac> adapter,
+ const BluetoothUUID& uuid,
+ int psm,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ adapter_ = adapter;
+ uuid_ = uuid;
+
+ DVLOG(1) << uuid_.canonical_value() << ": Registering L2CAP service.";
+ BluetoothL2CAPPSM registered_psm;
+ service_record_handle_ = RegisterL2capService(uuid, psm, &registered_psm);
+ if (service_record_handle_ == kInvalidServiceRecordHandle) {
+ error_callback.Run(kInvalidOrUsedPsm);
+ return;
+ }
+
+ l2cap_connection_listener_.reset(
+ [[BluetoothL2capConnectionListener alloc] initWithSocket:this
+ psm:registered_psm]);
+
+ success_callback.Run();
+}
+
+void BluetoothSocketMac::OnSDPQueryComplete(
+ IOReturn status,
+ IOBluetoothDevice* device,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
+ << uuid_.canonical_value() << ": SDP query complete.";
+
+ if (status != kIOReturnSuccess) {
+ error_callback.Run(kSDPQueryFailed);
+ return;
+ }
+
+ IOBluetoothSDPServiceRecord* record = [device
+ getServiceRecordForUUID:GetIOBluetoothSDPUUID(uuid_)];
+ if (record == nil) {
+ error_callback.Run(kProfileNotFound);
+ return;
+ }
+
+ if (is_connecting()) {
+ error_callback.Run(kSocketConnecting);
+ return;
+ }
+
+ if (channel_) {
+ error_callback.Run(kSocketAlreadyConnected);
+ return;
+ }
+
+ // Since RFCOMM is built on top of L2CAP, a service record with both should
+ // always be treated as RFCOMM.
+ BluetoothRFCOMMChannelID rfcomm_channel_id = BluetoothAdapter::kChannelAuto;
+ BluetoothL2CAPPSM l2cap_psm = BluetoothAdapter::kPsmAuto;
+ status = [record getRFCOMMChannelID:&rfcomm_channel_id];
+ if (status != kIOReturnSuccess) {
+ status = [record getL2CAPPSM:&l2cap_psm];
+ if (status != kIOReturnSuccess) {
+ error_callback.Run(kProfileNotFound);
+ return;
}
}
- // TODO(youngki): Add support for L2CAP sockets as well.
+ if (rfcomm_channel_id != BluetoothAdapter::kChannelAuto) {
+ DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
+ << uuid_.canonical_value() << ": Opening RFCOMM channel: "
+ << rfcomm_channel_id;
+ } else {
+ DCHECK_NE(l2cap_psm, BluetoothAdapter::kPsmAuto);
+ DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
+ << uuid_.canonical_value() << ": Opening L2CAP channel: "
+ << l2cap_psm;
+ }
+
+ // Note: It's important to set the connect callbacks *prior* to opening the
+ // channel, as opening the channel can synchronously call into
+ // OnChannelOpenComplete().
+ connect_callbacks_.reset(new ConnectCallbacks());
+ connect_callbacks_->success_callback = success_callback;
+ connect_callbacks_->error_callback = error_callback;
+
+ if (rfcomm_channel_id != BluetoothAdapter::kChannelAuto) {
+ channel_ = BluetoothRfcommChannelMac::OpenAsync(
+ this, device, rfcomm_channel_id, &status);
+ } else {
+ DCHECK_NE(l2cap_psm, BluetoothAdapter::kPsmAuto);
+ channel_ =
+ BluetoothL2capChannelMac::OpenAsync(this, device, l2cap_psm, &status);
+ }
+ if (status != kIOReturnSuccess) {
+ ReleaseChannel();
+ std::stringstream error;
+ error << "Failed to connect bluetooth socket ("
+ << BluetoothDeviceMac::GetDeviceAddress(device) << "): (" << status
+ << ")";
+ error_callback.Run(error.str());
+ return;
+ }
+
+ DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
+ << uuid_.canonical_value()
+ << ": channel opening in background.";
+}
+
+void BluetoothSocketMac::OnChannelOpened(
+ scoped_ptr<BluetoothChannelMac> channel) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DVLOG(1) << uuid_.canonical_value() << ": Incoming channel pending.";
+
+ accept_queue_.push(linked_ptr<BluetoothChannelMac>(channel.release()));
+ if (accept_request_)
+ AcceptConnectionRequest();
+
+ // TODO(isherman): Test whether these TODOs are still relevant.
+ // TODO(isherman): Currently, both the profile and the socket remain alive
+ // even after the app that requested them is closed. That's not great, as a
+ // misbehaving app could saturate all of the system's RFCOMM channels, and
+ // then they would not be freed until the user restarts Chrome.
+ // http://crbug.com/367316
+ // TODO(isherman): Likewise, the socket currently remains alive even if the
+ // underlying rfcomm_channel is closed, e.g. via the client disconnecting, or
+ // the user closing the Bluetooth connection via the system menu. This
+ // functions essentially as a minor memory leak.
+ // http://crbug.com/367319
+}
+
+void BluetoothSocketMac::OnChannelOpenComplete(
+ const std::string& device_address,
+ IOReturn status) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(is_connecting());
+
+ DVLOG(1) << device_address << " " << uuid_.canonical_value()
+ << ": channel open complete.";
+
+ scoped_ptr<ConnectCallbacks> temp = connect_callbacks_.Pass();
+ if (status != kIOReturnSuccess) {
+ ReleaseChannel();
+ std::stringstream error;
+ error << "Failed to connect bluetooth socket (" << device_address << "): ("
+ << status << ")";
+ temp->error_callback.Run(error.str());
+ return;
+ }
+
+ temp->success_callback.Run();
+}
+
+void BluetoothSocketMac::Close() {
+ DCHECK(thread_checker_.CalledOnValidThread());
- return scoped_refptr<BluetoothSocketMac>(bluetooth_socket);
+ if (channel_)
+ ReleaseChannel();
+ else if (service_record_handle_ != kInvalidServiceRecordHandle)
+ ReleaseListener();
}
-bool BluetoothSocketMac::Receive(net::GrowableIOBuffer* buffer) {
- CHECK(buffer->offset() == 0);
- int length = incoming_data_buffer_->offset();
- if (length > 0) {
- buffer->SetCapacity(length);
- memcpy(buffer->data(), incoming_data_buffer_->StartOfBuffer(), length);
- buffer->set_offset(length);
+void BluetoothSocketMac::Disconnect(const base::Closure& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ Close();
+ callback.Run();
+}
- ResetIncomingDataBuffer();
+void BluetoothSocketMac::Receive(
+ int /* buffer_size */,
+ const ReceiveCompletionCallback& success_callback,
+ const ReceiveErrorCompletionCallback& error_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (is_connecting()) {
+ error_callback.Run(BluetoothSocket::kSystemError, kSocketConnecting);
+ return;
}
- return true;
+
+ if (!channel_) {
+ error_callback.Run(BluetoothSocket::kDisconnected, kSocketNotConnected);
+ return;
+ }
+
+ // Only one pending read at a time
+ if (receive_callbacks_) {
+ error_callback.Run(BluetoothSocket::kIOPending, kReceivePending);
+ return;
+ }
+
+ // If there is at least one packet, consume it and succeed right away.
+ if (!receive_queue_.empty()) {
+ scoped_refptr<net::IOBufferWithSize> buffer = receive_queue_.front();
+ receive_queue_.pop();
+ success_callback.Run(buffer->size(), buffer);
+ return;
+ }
+
+ // Set the receive callback to use when data is received.
+ receive_callbacks_.reset(new ReceiveCallbacks());
+ receive_callbacks_->success_callback = success_callback;
+ receive_callbacks_->error_callback = error_callback;
}
-bool BluetoothSocketMac::Send(net::DrainableIOBuffer* buffer) {
- int bytes_written = buffer->BytesRemaining();
- IOReturn status = [rfcomm_channel_ writeAsync:buffer->data()
- length:bytes_written
- refcon:nil];
+void BluetoothSocketMac::OnChannelDataReceived(void* data, size_t length) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!is_connecting());
+
+ int data_size = base::checked_cast<int>(length);
+ scoped_refptr<net::IOBufferWithSize> buffer(
+ new net::IOBufferWithSize(data_size));
+ memcpy(buffer->data(), data, buffer->size());
+
+ // If there is a pending read callback, call it now.
+ if (receive_callbacks_) {
+ scoped_ptr<ReceiveCallbacks> temp = receive_callbacks_.Pass();
+ temp->success_callback.Run(buffer->size(), buffer);
+ return;
+ }
+
+ // Otherwise, enqueue the buffer for later use
+ receive_queue_.push(buffer);
+}
+
+void BluetoothSocketMac::Send(scoped_refptr<net::IOBuffer> buffer,
+ int buffer_size,
+ const SendCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (is_connecting()) {
+ error_callback.Run(kSocketConnecting);
+ return;
+ }
+
+ if (!channel_) {
+ error_callback.Run(kSocketNotConnected);
+ return;
+ }
+
+ // Create and enqueue request in preparation of async writes.
+ linked_ptr<SendRequest> request(new SendRequest());
+ request->buffer_size = buffer_size;
+ request->success_callback = success_callback;
+ request->error_callback = error_callback;
+ send_queue_.push(request);
+
+ // |writeAsync| accepts buffers of max. mtu bytes per call, so we need to emit
+ // multiple write operations if buffer_size > mtu.
+ uint16_t mtu = channel_->GetOutgoingMTU();
+ scoped_refptr<net::DrainableIOBuffer> send_buffer(
+ new net::DrainableIOBuffer(buffer, buffer_size));
+ while (send_buffer->BytesRemaining() > 0) {
+ int byte_count = send_buffer->BytesRemaining();
+ if (byte_count > mtu)
+ byte_count = mtu;
+ IOReturn status =
+ channel_->WriteAsync(send_buffer->data(), byte_count, request.get());
+
+ if (status != kIOReturnSuccess) {
+ std::stringstream error;
+ error << "Failed to connect bluetooth socket ("
+ << channel_->GetDeviceAddress() << "): (" << status << ")";
+ // Remember the first error only
+ if (request->status == kIOReturnSuccess)
+ request->status = status;
+ request->error_signaled = true;
+ request->error_callback.Run(error.str());
+ // We may have failed to issue any write operation. In that case, there
+ // will be no corresponding completion callback for this particular
+ // request, so we must forget about it now.
+ if (request->active_async_writes == 0) {
+ send_queue_.pop();
+ }
+ return;
+ }
+
+ request->active_async_writes++;
+ send_buffer->DidConsume(byte_count);
+ }
+}
+
+void BluetoothSocketMac::OnChannelWriteComplete(void* refcon, IOReturn status) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Note: We use "CHECK" below to ensure we never run into unforeseen
+ // occurrences of asynchronous callbacks, which could lead to data
+ // corruption.
+ CHECK_EQ(static_cast<SendRequest*>(refcon), send_queue_.front().get());
+
+ // Keep a local linked_ptr to avoid releasing the request too early if we end
+ // up removing it from the queue.
+ linked_ptr<SendRequest> request = send_queue_.front();
+
+ // Remember the first error only
if (status != kIOReturnSuccess) {
- error_message_ = base::StringPrintf(
- "Failed to send data. IOReturn code: %u", status);
- return false;
+ if (request->status == kIOReturnSuccess)
+ request->status = status;
}
- buffer->DidConsume(bytes_written);
- return true;
+ // Figure out if we are done with this async request
+ request->active_async_writes--;
+ if (request->active_async_writes > 0)
+ return;
+
+ // If this was the last active async write for this request, remove it from
+ // the queue and call the appropriate callback associated to the request.
+ send_queue_.pop();
+ if (request->status != kIOReturnSuccess) {
+ if (!request->error_signaled) {
+ std::stringstream error;
+ error << "Failed to connect bluetooth socket ("
+ << channel_->GetDeviceAddress() << "): (" << status << ")";
+ request->error_signaled = true;
+ request->error_callback.Run(error.str());
+ }
+ } else {
+ request->success_callback.Run(request->buffer_size);
+ }
}
-std::string BluetoothSocketMac::GetLastErrorMessage() const {
- return error_message_;
+void BluetoothSocketMac::OnChannelClosed() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (receive_callbacks_) {
+ scoped_ptr<ReceiveCallbacks> temp = receive_callbacks_.Pass();
+ temp->error_callback.Run(BluetoothSocket::kDisconnected,
+ kSocketNotConnected);
+ }
+
+ ReleaseChannel();
}
-void BluetoothSocketMac::OnDataReceived(
- IOBluetoothRFCOMMChannel* rfcomm_channel, void* data, size_t length) {
- DCHECK(rfcomm_channel_ == rfcomm_channel);
- CHECK_LT(length, static_cast<size_t>(std::numeric_limits<int>::max()));
- int data_size = static_cast<int>(length);
- if (incoming_data_buffer_->RemainingCapacity() < data_size) {
- int additional_capacity =
- std::max(data_size, incoming_data_buffer_->capacity());
- CHECK_LT(
- additional_capacity,
- std::numeric_limits<int>::max() - incoming_data_buffer_->capacity());
- incoming_data_buffer_->SetCapacity(
- incoming_data_buffer_->capacity() + additional_capacity);
+void BluetoothSocketMac::Accept(
+ const AcceptCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Allow only one pending accept at a time.
+ if (accept_request_) {
+ error_callback.Run(net::ErrorToString(net::ERR_IO_PENDING));
+ return;
}
- memcpy(incoming_data_buffer_->data(), data, data_size);
- incoming_data_buffer_->set_offset(
- incoming_data_buffer_->offset() + data_size);
+
+ accept_request_.reset(new AcceptRequest);
+ accept_request_->success_callback = success_callback;
+ accept_request_->error_callback = error_callback;
+
+ if (accept_queue_.size() >= 1)
+ AcceptConnectionRequest();
}
-void BluetoothSocketMac::ResetIncomingDataBuffer() {
- incoming_data_buffer_ = new net::GrowableIOBuffer();
- incoming_data_buffer_->SetCapacity(1024);
+void BluetoothSocketMac::AcceptConnectionRequest() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DVLOG(1) << uuid_.canonical_value() << ": Accepting pending connection.";
+
+ linked_ptr<BluetoothChannelMac> channel = accept_queue_.front();
+ accept_queue_.pop();
+
+ adapter_->DeviceConnected(channel->GetDevice());
+ BluetoothDevice* device = adapter_->GetDevice(channel->GetDeviceAddress());
+ DCHECK(device);
+
+ scoped_refptr<BluetoothSocketMac> client_socket =
+ BluetoothSocketMac::CreateSocket();
+
+ client_socket->uuid_ = uuid_;
+ client_socket->channel_.reset(channel.release());
+
+ // Associating the socket can synchronously call into OnChannelOpenComplete().
+ // Make sure to first set the new socket to be connecting and hook it up to
+ // run the accept callback with the device object.
+ client_socket->connect_callbacks_.reset(new ConnectCallbacks());
+ client_socket->connect_callbacks_->success_callback =
+ base::Bind(accept_request_->success_callback, device, client_socket);
+ client_socket->connect_callbacks_->error_callback =
+ accept_request_->error_callback;
+ accept_request_.reset();
+
+ // Now it's safe to associate the socket with the channel.
+ client_socket->channel_->SetSocket(client_socket.get());
+
+ DVLOG(1) << uuid_.canonical_value() << ": Accept complete.";
+}
+
+BluetoothSocketMac::AcceptRequest::AcceptRequest() {}
+
+BluetoothSocketMac::AcceptRequest::~AcceptRequest() {}
+
+BluetoothSocketMac::SendRequest::SendRequest()
+ : status(kIOReturnSuccess), active_async_writes(0), error_signaled(false) {}
+
+BluetoothSocketMac::SendRequest::~SendRequest() {}
+
+BluetoothSocketMac::ReceiveCallbacks::ReceiveCallbacks() {}
+
+BluetoothSocketMac::ReceiveCallbacks::~ReceiveCallbacks() {}
+
+BluetoothSocketMac::ConnectCallbacks::ConnectCallbacks() {}
+
+BluetoothSocketMac::ConnectCallbacks::~ConnectCallbacks() {}
+
+BluetoothSocketMac::BluetoothSocketMac()
+ : service_record_handle_(kInvalidServiceRecordHandle) {
+}
+
+BluetoothSocketMac::~BluetoothSocketMac() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!channel_);
+ DCHECK(!rfcomm_connection_listener_);
+}
+
+void BluetoothSocketMac::ReleaseChannel() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ channel_.reset();
+
+ // Closing the channel above prevents the callback delegate from being called
+ // so it is now safe to release all callback state.
+ connect_callbacks_.reset();
+ receive_callbacks_.reset();
+ empty_queue(receive_queue_);
+ empty_queue(send_queue_);
+}
+
+void BluetoothSocketMac::ReleaseListener() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_NE(service_record_handle_, kInvalidServiceRecordHandle);
+
+ IOBluetoothRemoveServiceWithRecordHandle(service_record_handle_);
+ rfcomm_connection_listener_.reset();
+ l2cap_connection_listener_.reset();
+
+ // Destroying the listener above prevents the callback delegate from being
+ // called so it is now safe to release all callback state.
+ accept_request_.reset();
+ empty_queue(accept_queue_);
}
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_socket_net.cc b/chromium/device/bluetooth/bluetooth_socket_net.cc
new file mode 100644
index 00000000000..8007d77dca3
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_socket_net.cc
@@ -0,0 +1,312 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_socket_net.h"
+
+#include <queue>
+#include <string>
+
+#include "base/logging.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/thread_restrictions.h"
+#include "device/bluetooth/bluetooth_socket.h"
+#include "device/bluetooth/bluetooth_socket_thread.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+
+namespace {
+
+const char kSocketNotConnected[] = "Socket is not connected.";
+
+static void DeactivateSocket(
+ const scoped_refptr<device::BluetoothSocketThread>& socket_thread) {
+ socket_thread->OnSocketDeactivate();
+}
+
+} // namespace
+
+namespace device {
+
+BluetoothSocketNet::WriteRequest::WriteRequest()
+ : buffer_size(0) {}
+
+BluetoothSocketNet::WriteRequest::~WriteRequest() {}
+
+BluetoothSocketNet::BluetoothSocketNet(
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<BluetoothSocketThread> socket_thread,
+ net::NetLog* net_log,
+ const net::NetLog::Source& source)
+ : ui_task_runner_(ui_task_runner),
+ socket_thread_(socket_thread),
+ net_log_(net_log),
+ source_(source) {
+ DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
+ socket_thread_->OnSocketActivate();
+}
+
+BluetoothSocketNet::~BluetoothSocketNet() {
+ DCHECK(tcp_socket_.get() == NULL);
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&DeactivateSocket, socket_thread_));
+}
+
+void BluetoothSocketNet::Close() {
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ socket_thread_->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&BluetoothSocketNet::DoClose, this));
+}
+
+void BluetoothSocketNet::Disconnect(
+ const base::Closure& success_callback) {
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ socket_thread_->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &BluetoothSocketNet::DoDisconnect,
+ this,
+ base::Bind(&BluetoothSocketNet::PostSuccess,
+ this,
+ success_callback)));
+}
+
+void BluetoothSocketNet::Receive(
+ int buffer_size,
+ const ReceiveCompletionCallback& success_callback,
+ const ReceiveErrorCompletionCallback& error_callback) {
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ socket_thread_->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &BluetoothSocketNet::DoReceive,
+ this,
+ buffer_size,
+ base::Bind(&BluetoothSocketNet::PostReceiveCompletion,
+ this,
+ success_callback),
+ base::Bind(&BluetoothSocketNet::PostReceiveErrorCompletion,
+ this,
+ error_callback)));
+}
+
+void BluetoothSocketNet::Send(
+ scoped_refptr<net::IOBuffer> buffer,
+ int buffer_size,
+ const SendCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ socket_thread_->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &BluetoothSocketNet::DoSend,
+ this,
+ buffer,
+ buffer_size,
+ base::Bind(&BluetoothSocketNet::PostSendCompletion,
+ this,
+ success_callback),
+ base::Bind(&BluetoothSocketNet::PostErrorCompletion,
+ this,
+ error_callback)));
+}
+
+void BluetoothSocketNet::ResetData() {
+}
+
+void BluetoothSocketNet::ResetTCPSocket() {
+ tcp_socket_.reset(new net::TCPSocket(net_log_, source_));
+}
+
+void BluetoothSocketNet::SetTCPSocket(scoped_ptr<net::TCPSocket> tcp_socket) {
+ tcp_socket_ = tcp_socket.Pass();
+}
+
+void BluetoothSocketNet::PostSuccess(const base::Closure& callback) {
+ ui_task_runner_->PostTask(FROM_HERE, callback);
+}
+
+void BluetoothSocketNet::PostErrorCompletion(
+ const ErrorCompletionCallback& callback,
+ const std::string& error) {
+ ui_task_runner_->PostTask(FROM_HERE, base::Bind(callback, error));
+}
+
+void BluetoothSocketNet::DoClose() {
+ DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (tcp_socket_) {
+ tcp_socket_->Close();
+ tcp_socket_.reset(NULL);
+ }
+
+ // Note: Closing |tcp_socket_| above released all potential pending
+ // Send/Receive operations, so we can no safely release the state associated
+ // to those pending operations.
+ read_buffer_ = NULL;
+ std::queue<linked_ptr<WriteRequest> > empty;
+ std::swap(write_queue_, empty);
+
+ ResetData();
+}
+
+void BluetoothSocketNet::DoDisconnect(const base::Closure& callback) {
+ DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ DoClose();
+ callback.Run();
+}
+
+void BluetoothSocketNet::DoReceive(
+ int buffer_size,
+ const ReceiveCompletionCallback& success_callback,
+ const ReceiveErrorCompletionCallback& error_callback) {
+ DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (!tcp_socket_) {
+ error_callback.Run(BluetoothSocket::kDisconnected, kSocketNotConnected);
+ return;
+ }
+
+ // Only one pending read at a time
+ if (read_buffer_.get()) {
+ error_callback.Run(BluetoothSocket::kIOPending,
+ net::ErrorToString(net::ERR_IO_PENDING));
+ return;
+ }
+
+ scoped_refptr<net::IOBufferWithSize> buffer(
+ new net::IOBufferWithSize(buffer_size));
+ int read_result =
+ tcp_socket_->Read(buffer.get(),
+ buffer->size(),
+ base::Bind(&BluetoothSocketNet::OnSocketReadComplete,
+ this,
+ success_callback,
+ error_callback));
+
+ read_buffer_ = buffer;
+ if (read_result != net::ERR_IO_PENDING)
+ OnSocketReadComplete(success_callback, error_callback, read_result);
+}
+
+void BluetoothSocketNet::OnSocketReadComplete(
+ const ReceiveCompletionCallback& success_callback,
+ const ReceiveErrorCompletionCallback& error_callback,
+ int read_result) {
+ DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ scoped_refptr<net::IOBufferWithSize> buffer;
+ buffer.swap(read_buffer_);
+ if (read_result > 0) {
+ success_callback.Run(read_result, buffer);
+ } else if (read_result == net::OK ||
+ read_result == net::ERR_CONNECTION_CLOSED ||
+ read_result == net::ERR_CONNECTION_RESET) {
+ error_callback.Run(BluetoothSocket::kDisconnected,
+ net::ErrorToString(read_result));
+ } else {
+ error_callback.Run(BluetoothSocket::kSystemError,
+ net::ErrorToString(read_result));
+ }
+}
+
+void BluetoothSocketNet::DoSend(
+ scoped_refptr<net::IOBuffer> buffer,
+ int buffer_size,
+ const SendCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (!tcp_socket_) {
+ error_callback.Run(kSocketNotConnected);
+ return;
+ }
+
+ linked_ptr<WriteRequest> request(new WriteRequest());
+ request->buffer = buffer;
+ request->buffer_size = buffer_size;
+ request->success_callback = success_callback;
+ request->error_callback = error_callback;
+
+ write_queue_.push(request);
+ if (write_queue_.size() == 1) {
+ SendFrontWriteRequest();
+ }
+}
+
+void BluetoothSocketNet::SendFrontWriteRequest() {
+ DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (!tcp_socket_)
+ return;
+
+ if (write_queue_.size() == 0)
+ return;
+
+ linked_ptr<WriteRequest> request = write_queue_.front();
+ net::CompletionCallback callback =
+ base::Bind(&BluetoothSocketNet::OnSocketWriteComplete,
+ this,
+ request->success_callback,
+ request->error_callback);
+ int send_result =
+ tcp_socket_->Write(request->buffer, request->buffer_size, callback);
+ if (send_result != net::ERR_IO_PENDING) {
+ callback.Run(send_result);
+ }
+}
+
+void BluetoothSocketNet::OnSocketWriteComplete(
+ const SendCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback,
+ int send_result) {
+ DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ write_queue_.pop();
+
+ if (send_result >= net::OK) {
+ success_callback.Run(send_result);
+ } else {
+ error_callback.Run(net::ErrorToString(send_result));
+ }
+
+ // Don't call directly to avoid potentail large recursion.
+ socket_thread_->task_runner()->PostNonNestableTask(
+ FROM_HERE,
+ base::Bind(&BluetoothSocketNet::SendFrontWriteRequest, this));
+}
+
+void BluetoothSocketNet::PostReceiveCompletion(
+ const ReceiveCompletionCallback& callback,
+ int io_buffer_size,
+ scoped_refptr<net::IOBuffer> io_buffer) {
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(callback, io_buffer_size, io_buffer));
+}
+
+void BluetoothSocketNet::PostReceiveErrorCompletion(
+ const ReceiveErrorCompletionCallback& callback,
+ ErrorReason reason,
+ const std::string& error_message) {
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(callback, reason, error_message));
+}
+
+void BluetoothSocketNet::PostSendCompletion(
+ const SendCompletionCallback& callback,
+ int bytes_written) {
+ ui_task_runner_->PostTask(FROM_HERE, base::Bind(callback, bytes_written));
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_socket_net.h b/chromium/device/bluetooth/bluetooth_socket_net.h
new file mode 100644
index 00000000000..b16e2359cf1
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_socket_net.h
@@ -0,0 +1,131 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_NET_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_NET_H_
+
+#include <queue>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "device/bluetooth/bluetooth_socket.h"
+#include "device/bluetooth/bluetooth_socket_thread.h"
+#include "net/base/net_log.h"
+#include "net/socket/tcp_socket.h"
+
+namespace net {
+class IOBuffer;
+class IOBufferWithSize;
+} // namespace net
+
+namespace device {
+
+// This class is a base-class for implementations of BluetoothSocket that can
+// use net::TCPSocket. All public methods (including the factory method) must
+// be called on the UI thread, while underlying socket operations are
+// performed on a separate thread.
+class BluetoothSocketNet : public BluetoothSocket {
+ public:
+ // BluetoothSocket:
+ virtual void Close() OVERRIDE;
+ virtual void Disconnect(const base::Closure& callback) OVERRIDE;
+ virtual void Receive(int buffer_size,
+ const ReceiveCompletionCallback& success_callback,
+ const ReceiveErrorCompletionCallback& error_callback)
+ OVERRIDE;
+ virtual void Send(scoped_refptr<net::IOBuffer> buffer,
+ int buffer_size,
+ const SendCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) OVERRIDE;
+
+ protected:
+ BluetoothSocketNet(scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<BluetoothSocketThread> socket_thread,
+ net::NetLog* net_log,
+ const net::NetLog::Source& source);
+ virtual ~BluetoothSocketNet();
+
+ // Resets locally held data after a socket is closed. Default implementation
+ // does nothing, subclasses may override.
+ virtual void ResetData();
+
+ // Methods for subclasses to obtain the members.
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner() const {
+ return ui_task_runner_;
+ }
+
+ scoped_refptr<BluetoothSocketThread> socket_thread() const {
+ return socket_thread_;
+ }
+
+ net::NetLog* net_log() const { return net_log_; }
+ const net::NetLog::Source& source() const { return source_; }
+
+ net::TCPSocket* tcp_socket() { return tcp_socket_.get(); }
+
+ void ResetTCPSocket();
+ void SetTCPSocket(scoped_ptr<net::TCPSocket> tcp_socket);
+
+ void PostSuccess(const base::Closure& callback);
+ void PostErrorCompletion(const ErrorCompletionCallback& callback,
+ const std::string& error);
+
+ private:
+ struct WriteRequest {
+ WriteRequest();
+ ~WriteRequest();
+
+ scoped_refptr<net::IOBuffer> buffer;
+ int buffer_size;
+ SendCompletionCallback success_callback;
+ ErrorCompletionCallback error_callback;
+ };
+
+ void DoClose();
+ void DoDisconnect(const base::Closure& callback);
+ void DoReceive(int buffer_size,
+ const ReceiveCompletionCallback& success_callback,
+ const ReceiveErrorCompletionCallback& error_callback);
+ void OnSocketReadComplete(
+ const ReceiveCompletionCallback& success_callback,
+ const ReceiveErrorCompletionCallback& error_callback,
+ int read_result);
+ void DoSend(scoped_refptr<net::IOBuffer> buffer,
+ int buffer_size,
+ const SendCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback);
+ void SendFrontWriteRequest();
+ void OnSocketWriteComplete(const SendCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback,
+ int send_result);
+
+ void PostReceiveCompletion(const ReceiveCompletionCallback& callback,
+ int io_buffer_size,
+ scoped_refptr<net::IOBuffer> io_buffer);
+ void PostReceiveErrorCompletion(
+ const ReceiveErrorCompletionCallback& callback,
+ ErrorReason reason,
+ const std::string& error_message);
+ void PostSendCompletion(const SendCompletionCallback& callback,
+ int bytes_written);
+
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
+ scoped_refptr<BluetoothSocketThread> socket_thread_;
+ net::NetLog* net_log_;
+ const net::NetLog::Source source_;
+
+ scoped_ptr<net::TCPSocket> tcp_socket_;
+ scoped_refptr<net::IOBufferWithSize> read_buffer_;
+ std::queue<linked_ptr<WriteRequest> > write_queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothSocketNet);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_NET_H_
diff --git a/chromium/device/bluetooth/bluetooth_socket_thread.cc b/chromium/device/bluetooth/bluetooth_socket_thread.cc
new file mode 100644
index 00000000000..1f3b97bf98b
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_socket_thread.cc
@@ -0,0 +1,78 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_socket_thread.h"
+
+#include "base/lazy_instance.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/thread.h"
+
+namespace device {
+
+base::LazyInstance<scoped_refptr<BluetoothSocketThread> > g_instance =
+ LAZY_INSTANCE_INITIALIZER;
+
+// static
+scoped_refptr<BluetoothSocketThread> BluetoothSocketThread::Get() {
+ if (!g_instance.Get().get()) {
+ g_instance.Get() = new BluetoothSocketThread();
+ }
+ return g_instance.Get();
+}
+
+// static
+void BluetoothSocketThread::CleanupForTesting() {
+ DCHECK(g_instance.Get());
+ g_instance.Get() = NULL;
+}
+
+BluetoothSocketThread::BluetoothSocketThread()
+ : active_socket_count_(0) {}
+
+BluetoothSocketThread::~BluetoothSocketThread() {
+ if (thread_) {
+ thread_->Stop();
+ thread_.reset(NULL);
+ task_runner_ = NULL;
+ }
+}
+
+void BluetoothSocketThread::OnSocketActivate() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ active_socket_count_++;
+ EnsureStarted();
+}
+
+void BluetoothSocketThread::OnSocketDeactivate() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ active_socket_count_--;
+ if (active_socket_count_ == 0 && thread_) {
+ thread_->Stop();
+ thread_.reset(NULL);
+ task_runner_ = NULL;
+ }
+}
+
+void BluetoothSocketThread::EnsureStarted() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (thread_)
+ return;
+
+ base::Thread::Options thread_options;
+ thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
+ thread_.reset(new base::Thread("BluetoothSocketThread"));
+ thread_->StartWithOptions(thread_options);
+ task_runner_ = thread_->message_loop_proxy();
+}
+
+scoped_refptr<base::SequencedTaskRunner> BluetoothSocketThread::task_runner()
+ const {
+ DCHECK(active_socket_count_ > 0);
+ DCHECK(thread_);
+ DCHECK(task_runner_);
+
+ return task_runner_;
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_socket_thread.h b/chromium/device/bluetooth/bluetooth_socket_thread.h
new file mode 100644
index 00000000000..02de8204351
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_socket_thread.h
@@ -0,0 +1,50 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_THREAD_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_THREAD_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+
+namespace base {
+class SequencedTaskRunner;
+class Thread;
+} // namespace base
+
+namespace device {
+
+// Thread abstraction used by |BluetoothSocketChromeOS| and |BluetoothSocketWin|
+// to perform IO operations on the underlying platform sockets. An instance of
+// this class can be shared by many active sockets.
+class BluetoothSocketThread
+ : public base::RefCountedThreadSafe<BluetoothSocketThread> {
+ public:
+ static scoped_refptr<BluetoothSocketThread> Get();
+ static void CleanupForTesting();
+
+ void OnSocketActivate();
+ void OnSocketDeactivate();
+
+ scoped_refptr<base::SequencedTaskRunner> task_runner() const;
+
+ private:
+ friend class base::RefCountedThreadSafe<BluetoothSocketThread>;
+ BluetoothSocketThread();
+ virtual ~BluetoothSocketThread();
+
+ void EnsureStarted();
+
+ base::ThreadChecker thread_checker_;
+ int active_socket_count_;
+ scoped_ptr<base::Thread> thread_;
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothSocketThread);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_THREAD_H_
diff --git a/chromium/device/bluetooth/bluetooth_socket_win.cc b/chromium/device/bluetooth/bluetooth_socket_win.cc
index a684d14cffd..a46f4b5ec40 100644
--- a/chromium/device/bluetooth/bluetooth_socket_win.cc
+++ b/chromium/device/bluetooth/bluetooth_socket_win.cc
@@ -4,112 +4,396 @@
#include "device/bluetooth/bluetooth_socket_win.h"
+#include <objbase.h>
+
#include <string>
#include "base/logging.h"
#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "device/bluetooth/bluetooth_device_win.h"
#include "device/bluetooth/bluetooth_init_win.h"
#include "device/bluetooth/bluetooth_service_record_win.h"
+#include "device/bluetooth/bluetooth_socket_thread.h"
#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
#include "net/base/winsock_init.h"
namespace {
-std::string FormatErrorMessage(DWORD error_code) {
- TCHAR error_msg[1024];
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
- 0,
- error_code,
- 0,
- error_msg,
- 1024,
- NULL);
- return base::SysWideToUTF8(error_msg);
+const char kL2CAPNotSupported[] = "Bluetooth L2CAP protocal is not supported";
+const char kSocketAlreadyConnected[] = "Socket is already connected.";
+const char kInvalidRfcommPort[] = "Invalid RFCCOMM port.";
+const char kFailedToCreateSocket[] = "Failed to create socket.";
+const char kFailedToBindSocket[] = "Failed to bind socket.";
+const char kFailedToListenOnSocket[] = "Failed to listen on socket.";
+const char kFailedToGetSockNameForSocket[] = "Failed to getsockname.";
+const char kFailedToAccept[] = "Failed to accept.";
+const char kInvalidUUID[] = "Invalid UUID";
+const char kWsaSetServiceError[] = "WSASetService error.";
+
+std::string IPEndPointToBluetoothAddress(const net::IPEndPoint& end_point) {
+ if (end_point.address().size() != net::kBluetoothAddressSize)
+ return std::string();
+ // The address is copied from BTH_ADDR field of SOCKADDR_BTH, which is a
+ // 64-bit ULONGLONG that stores Bluetooth address in little-endian. Print in
+ // reverse order to preserve the correct ordering.
+ return base::StringPrintf("%02X:%02X:%02X:%02X:%02X:%02X",
+ end_point.address()[5],
+ end_point.address()[4],
+ end_point.address()[3],
+ end_point.address()[2],
+ end_point.address()[1],
+ end_point.address()[0]);
}
} // namespace
namespace device {
-BluetoothSocketWin::BluetoothSocketWin(SOCKET fd) : fd_(fd) {
+struct BluetoothSocketWin::ServiceRegData {
+ ServiceRegData() {
+ ZeroMemory(&address, sizeof(address));
+ ZeroMemory(&address_info, sizeof(address_info));
+ ZeroMemory(&uuid, sizeof(uuid));
+ ZeroMemory(&service, sizeof(service));
+ }
+
+ SOCKADDR_BTH address;
+ CSADDR_INFO address_info;
+ GUID uuid;
+ base::string16 name;
+ WSAQUERYSET service;
+};
+
+// static
+scoped_refptr<BluetoothSocketWin>
+BluetoothSocketWin::CreateBluetoothSocket(
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<device::BluetoothSocketThread> socket_thread,
+ net::NetLog* net_log,
+ const net::NetLog::Source& source) {
+ DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
+
+ return make_scoped_refptr(
+ new BluetoothSocketWin(ui_task_runner, socket_thread, net_log, source));
+}
+
+BluetoothSocketWin::BluetoothSocketWin(
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<BluetoothSocketThread> socket_thread,
+ net::NetLog* net_log,
+ const net::NetLog::Source& source)
+ : BluetoothSocketNet(ui_task_runner, socket_thread, net_log, source),
+ supports_rfcomm_(false),
+ rfcomm_channel_(-1),
+ bth_addr_(BTH_ADDR_NULL) {
}
BluetoothSocketWin::~BluetoothSocketWin() {
- closesocket(fd_);
}
-// static
-scoped_refptr<BluetoothSocket> BluetoothSocketWin::CreateBluetoothSocket(
- const BluetoothServiceRecord& service_record) {
- BluetoothSocketWin* bluetooth_socket = NULL;
- if (service_record.SupportsRfcomm()) {
- net::EnsureWinsockInit();
- SOCKET socket_fd = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
- SOCKADDR_BTH sa;
- ZeroMemory(&sa, sizeof(sa));
- sa.addressFamily = AF_BTH;
- sa.port = service_record.rfcomm_channel();
- const BluetoothServiceRecordWin* service_record_win =
- static_cast<const BluetoothServiceRecordWin*>(&service_record);
- sa.btAddr = service_record_win->bth_addr();
-
- int status = connect(socket_fd,
- reinterpret_cast<SOCKADDR *>(&sa),
- sizeof(sa));
- DWORD error_code = WSAGetLastError();
- if (status == 0 || error_code == WSAEINPROGRESS) {
- bluetooth_socket =
- new BluetoothSocketWin(socket_fd);
- } else {
- LOG(ERROR) << "Failed to connect bluetooth socket "
- << "(" << service_record.address() << "): "
- << "(" << error_code << ")" << FormatErrorMessage(error_code);
- closesocket(socket_fd);
+void BluetoothSocketWin::Connect(
+ const BluetoothDeviceWin* device,
+ const BluetoothUUID& uuid,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(device);
+
+ if (!uuid.IsValid()) {
+ error_callback.Run(kInvalidUUID);
+ return;
+ }
+
+ const BluetoothServiceRecordWin* service_record_win =
+ device->GetServiceRecord(uuid);
+ if (!service_record_win) {
+ error_callback.Run(kInvalidUUID);
+ return;
+ }
+
+ device_address_ = service_record_win->address();
+ if (service_record_win->SupportsRfcomm()) {
+ supports_rfcomm_ = true;
+ rfcomm_channel_ = service_record_win->rfcomm_channel();
+ bth_addr_ = service_record_win->bth_addr();
+ }
+
+ socket_thread()->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &BluetoothSocketWin::DoConnect,
+ this,
+ base::Bind(&BluetoothSocketWin::PostSuccess, this, success_callback),
+ base::Bind(
+ &BluetoothSocketWin::PostErrorCompletion, this, error_callback)));
+}
+
+void BluetoothSocketWin::Listen(scoped_refptr<BluetoothAdapter> adapter,
+ const BluetoothUUID& uuid,
+ int rfcomm_channel,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+ adapter_ = adapter;
+
+ socket_thread()->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&BluetoothSocketWin::DoListen,
+ this,
+ uuid,
+ rfcomm_channel,
+ success_callback,
+ error_callback));
+}
+
+void BluetoothSocketWin::ResetData() {
+ if (service_reg_data_) {
+ if (WSASetService(&service_reg_data_->service,RNRSERVICE_DELETE, 0) ==
+ SOCKET_ERROR) {
+ LOG(WARNING) << "Failed to unregister service.";
}
+ service_reg_data_.reset();
}
- // TODO(youngki) add support for L2CAP sockets as well.
+}
- return scoped_refptr<BluetoothSocketWin>(bluetooth_socket);
+void BluetoothSocketWin::Accept(
+ const AcceptCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+ socket_thread()->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&BluetoothSocketWin::DoAccept,
+ this,
+ success_callback,
+ error_callback));
}
-bool BluetoothSocketWin::Receive(net::GrowableIOBuffer* buffer) {
- buffer->SetCapacity(1024);
- int bytes_read;
- do {
- if (buffer->RemainingCapacity() == 0)
- buffer->SetCapacity(buffer->capacity() * 2);
- bytes_read = recv(fd_, buffer->data(), buffer->RemainingCapacity(), 0);
- if (bytes_read > 0)
- buffer->set_offset(buffer->offset() + bytes_read);
- } while (bytes_read > 0);
+void BluetoothSocketWin::DoConnect(
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread());
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (tcp_socket()) {
+ error_callback.Run(kSocketAlreadyConnected);
+ return;
+ }
+ if (!supports_rfcomm_) {
+ // TODO(youngki) add support for L2CAP sockets as well.
+ error_callback.Run(kL2CAPNotSupported);
+ return;
+ }
+
+ ResetTCPSocket();
+ net::EnsureWinsockInit();
+ SOCKET socket_fd = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
+ SOCKADDR_BTH sa;
+ ZeroMemory(&sa, sizeof(sa));
+ sa.addressFamily = AF_BTH;
+ sa.port = rfcomm_channel_;
+ sa.btAddr = bth_addr_;
+
+ // TODO(rpaquay): Condider making this call non-blocking.
+ int status = connect(socket_fd, reinterpret_cast<SOCKADDR*>(&sa), sizeof(sa));
DWORD error_code = WSAGetLastError();
- if (bytes_read < 0 && error_code != WSAEWOULDBLOCK) {
- error_message_ = FormatErrorMessage(error_code);
- return false;
+ if (!(status == 0 || error_code == WSAEINPROGRESS)) {
+ LOG(ERROR) << "Failed to connect bluetooth socket "
+ << "(" << device_address_ << "): "
+ << logging::SystemErrorCodeToString(error_code);
+ error_callback.Run("Error connecting to socket: " +
+ logging::SystemErrorCodeToString(error_code));
+ closesocket(socket_fd);
+ return;
+ }
+
+ // Note: We don't have a meaningful |IPEndPoint|, but that is ok since the
+ // TCPSocket implementation does not actually require one.
+ int net_result =
+ tcp_socket()->AdoptConnectedSocket(socket_fd, net::IPEndPoint());
+ if (net_result != net::OK) {
+ error_callback.Run("Error connecting to socket: " +
+ std::string(net::ErrorToString(net_result)));
+ closesocket(socket_fd);
+ return;
}
- return true;
+
+ success_callback.Run();
}
-bool BluetoothSocketWin::Send(net::DrainableIOBuffer* buffer) {
- int bytes_written;
- do {
- bytes_written = send(fd_, buffer->data(), buffer->BytesRemaining(), 0);
- if (bytes_written > 0)
- buffer->DidConsume(bytes_written);
- } while (buffer->BytesRemaining() > 0 && bytes_written > 0);
+void BluetoothSocketWin::DoListen(
+ const BluetoothUUID& uuid,
+ int rfcomm_channel,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(!tcp_socket() && !service_reg_data_);
- DWORD error_code = WSAGetLastError();
- if (bytes_written < 0 && error_code != WSAEWOULDBLOCK) {
- error_message_ = FormatErrorMessage(error_code);
- return false;
+ // The valid range is 0-30. 0 means BT_PORT_ANY and 1-30 are the
+ // valid RFCOMM port numbers of SOCKADDR_BTH.
+ if (rfcomm_channel < 0 || rfcomm_channel > 30) {
+ LOG(WARNING) << "Failed to start service: "
+ << "Invalid RFCCOMM port " << rfcomm_channel
+ << ", uuid=" << uuid.value();
+ PostErrorCompletion(error_callback, kInvalidRfcommPort);
+ return;
+ }
+
+ SOCKET socket_fd = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
+ if (socket_fd == INVALID_SOCKET) {
+ LOG(WARNING) << "Failed to start service: create socket, "
+ << "winsock err=" << WSAGetLastError();
+ PostErrorCompletion(error_callback, kFailedToCreateSocket);
+ return;
+ }
+
+ // Note that |socket_fd| belongs to a non-TCP address family (i.e. AF_BTH),
+ // TCPSocket methods that involve address could not be called. So bind()
+ // is called on |socket_fd| directly.
+ scoped_ptr<net::TCPSocket> scoped_socket(
+ new net::TCPSocket(NULL, net::NetLog::Source()));
+ scoped_socket->AdoptListenSocket(socket_fd);
+
+ SOCKADDR_BTH sa;
+ struct sockaddr* sock_addr = reinterpret_cast<struct sockaddr*>(&sa);
+ int sock_addr_len = sizeof(sa);
+ ZeroMemory(&sa, sock_addr_len);
+ sa.addressFamily = AF_BTH;
+ sa.port = rfcomm_channel ? rfcomm_channel : BT_PORT_ANY;
+ if (bind(socket_fd, sock_addr, sock_addr_len) < 0) {
+ LOG(WARNING) << "Failed to start service: create socket, "
+ << "winsock err=" << WSAGetLastError();
+ PostErrorCompletion(error_callback, kFailedToBindSocket);
+ return;
+ }
+
+ const int kListenBacklog = 5;
+ if (scoped_socket->Listen(kListenBacklog) < 0) {
+ LOG(WARNING) << "Failed to start service: Listen"
+ << "winsock err=" << WSAGetLastError();
+ PostErrorCompletion(error_callback, kFailedToListenOnSocket);
+ return;
+ }
+
+ scoped_ptr<ServiceRegData> reg_data(new ServiceRegData);
+ reg_data->name = base::UTF8ToUTF16(uuid.canonical_value());
+
+ if (getsockname(socket_fd, sock_addr, &sock_addr_len)) {
+ LOG(WARNING) << "Failed to start service: getsockname, "
+ << "winsock err=" << WSAGetLastError();
+ PostErrorCompletion(error_callback, kFailedToGetSockNameForSocket);
+ return;
+ }
+ reg_data->address = sa;
+
+ reg_data->address_info.LocalAddr.iSockaddrLength = sizeof(sa);
+ reg_data->address_info.LocalAddr.lpSockaddr =
+ reinterpret_cast<struct sockaddr*>(&reg_data->address);
+ reg_data->address_info.iSocketType = SOCK_STREAM;
+ reg_data->address_info.iProtocol = BTHPROTO_RFCOMM;
+
+ base::string16 cannonical_uuid = L"{" + base::ASCIIToUTF16(
+ uuid.canonical_value()) + L"}";
+ if (!SUCCEEDED(CLSIDFromString(cannonical_uuid.c_str(), &reg_data->uuid))) {
+ LOG(WARNING) << "Failed to start service: "
+ << ", invalid uuid=" << cannonical_uuid;
+ PostErrorCompletion(error_callback, kInvalidUUID);
+ return;
+ }
+
+ reg_data->service.dwSize = sizeof(WSAQUERYSET);
+ reg_data->service.lpszServiceInstanceName =
+ const_cast<LPWSTR>(reg_data->name.c_str());
+ reg_data->service.lpServiceClassId = &reg_data->uuid;
+ reg_data->service.dwNameSpace = NS_BTH;
+ reg_data->service.dwNumberOfCsAddrs = 1;
+ reg_data->service.lpcsaBuffer = &reg_data->address_info;
+
+ if (WSASetService(&reg_data->service,
+ RNRSERVICE_REGISTER, 0) == SOCKET_ERROR) {
+ LOG(WARNING) << "Failed to register profile: WSASetService"
+ << "winsock err=" << WSAGetLastError();
+ PostErrorCompletion(error_callback, kWsaSetServiceError);
+ return;
+ }
+
+ SetTCPSocket(scoped_socket.Pass());
+ service_reg_data_ = reg_data.Pass();
+
+ PostSuccess(success_callback);
+}
+
+void BluetoothSocketWin::DoAccept(
+ const AcceptCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread());
+ int result = tcp_socket()->Accept(
+ &accept_socket_,
+ &accept_address_,
+ base::Bind(&BluetoothSocketWin::OnAcceptOnSocketThread,
+ this,
+ success_callback,
+ error_callback));
+ if (result != net::OK && result != net::ERR_IO_PENDING) {
+ LOG(WARNING) << "Failed to accept, net err=" << result;
+ PostErrorCompletion(error_callback, kFailedToAccept);
}
- return true;
}
-std::string BluetoothSocketWin::GetLastErrorMessage() const {
- return error_message_;
+void BluetoothSocketWin::OnAcceptOnSocketThread(
+ const AcceptCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback,
+ int accept_result) {
+ DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread());
+ if (accept_result != net::OK) {
+ LOG(WARNING) << "OnAccept error, net err=" << accept_result;
+ PostErrorCompletion(error_callback, kFailedToAccept);
+ return;
+ }
+
+ ui_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&BluetoothSocketWin::OnAcceptOnUI,
+ this,
+ base::Passed(&accept_socket_),
+ accept_address_,
+ success_callback,
+ error_callback));
+}
+
+void BluetoothSocketWin::OnAcceptOnUI(
+ scoped_ptr<net::TCPSocket> accept_socket,
+ const net::IPEndPoint& peer_address,
+ const AcceptCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+ const std::string peer_device_address =
+ IPEndPointToBluetoothAddress(peer_address);
+ const BluetoothDevice* peer_device = adapter_->GetDevice(peer_device_address);
+ if (!peer_device) {
+ LOG(WARNING) << "OnAccept failed with unknown device, addr="
+ << peer_device_address;
+ error_callback.Run(kFailedToAccept);
+ return;
+ }
+
+ scoped_refptr<BluetoothSocketWin> peer_socket = CreateBluetoothSocket(
+ ui_task_runner(),
+ socket_thread(),
+ net_log(),
+ source());
+ peer_socket->SetTCPSocket(accept_socket.Pass());
+ success_callback.Run(peer_device, peer_socket);
}
} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_socket_win.h b/chromium/device/bluetooth/bluetooth_socket_win.h
index 41713e71309..22ec412a1f4 100644
--- a/chromium/device/bluetooth/bluetooth_socket_win.h
+++ b/chromium/device/bluetooth/bluetooth_socket_win.h
@@ -10,39 +10,91 @@
#include <string>
#include "base/memory/ref_counted.h"
+#include "device/bluetooth/bluetooth_service_record_win.h"
#include "device/bluetooth/bluetooth_socket.h"
-
-namespace net {
-
-class DrainableIOBuffer;
-class GrowableIOBuffer;
-
-} // namespace net
+#include "device/bluetooth/bluetooth_socket_net.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+#include "net/base/ip_endpoint.h"
+#include "net/socket/tcp_socket.h"
namespace device {
-class BluetoothServiceRecord;
+class BluetoothAdapter;
+class BluetoothDeviceWin;
-// This class is an implementation of BluetoothSocket class for Windows
-// platform.
-class BluetoothSocketWin : public BluetoothSocket {
+// The BluetoothSocketWin class implements BluetoothSocket for the Microsoft
+// Windows platform.
+class BluetoothSocketWin : public BluetoothSocketNet {
public:
- static scoped_refptr<BluetoothSocket> CreateBluetoothSocket(
- const BluetoothServiceRecord& service_record);
-
- // BluetoothSocket override
- virtual bool Receive(net::GrowableIOBuffer* buffer) OVERRIDE;
- virtual bool Send(net::DrainableIOBuffer* buffer) OVERRIDE;
- virtual std::string GetLastErrorMessage() const OVERRIDE;
+ static scoped_refptr<BluetoothSocketWin> CreateBluetoothSocket(
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<BluetoothSocketThread> socket_thread,
+ net::NetLog* net_log,
+ const net::NetLog::Source& source);
+
+ // Connect to the peer device and calls |success_callback| when the
+ // connection has been established successfully. If an error occurs, calls
+ // |error_callback| with a system error message.
+ void Connect(const BluetoothDeviceWin* device,
+ const BluetoothUUID& uuid,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback);
+
+ // Listens using this socket using an RFCOMM service published as UUID |uuid|
+ // with Channel |channel|. |success_callback| will be called if the service
+ // is successfully registered, |error_callback| on failure with a message
+ // explaining the cause.
+ void Listen(scoped_refptr<BluetoothAdapter> adapter,
+ const BluetoothUUID& uuid,
+ int rfcomm_channel,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback);
+
+ // BluetoothSocketNet:
+ void ResetData();
+
+ // BluetoothSocket:
+ virtual void Accept(const AcceptCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) OVERRIDE;
protected:
virtual ~BluetoothSocketWin();
private:
- explicit BluetoothSocketWin(SOCKET fd);
-
- const SOCKET fd_;
- std::string error_message_;
+ struct ServiceRegData;
+
+ BluetoothSocketWin(scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<BluetoothSocketThread> socket_thread,
+ net::NetLog* net_log,
+ const net::NetLog::Source& source);
+
+
+ void DoConnect(const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback);
+ void DoListen(const BluetoothUUID& uuid,
+ int rfcomm_channel,
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback);
+ void DoAccept(const AcceptCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback);
+ void OnAcceptOnSocketThread(const AcceptCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback,
+ int accept_result);
+ void OnAcceptOnUI(scoped_ptr<net::TCPSocket> accept_socket,
+ const net::IPEndPoint& peer_address,
+ const AcceptCompletionCallback& success_callback,
+ const ErrorCompletionCallback& error_callback);
+
+ std::string device_address_;
+ bool supports_rfcomm_;
+ uint8 rfcomm_channel_;
+ BTH_ADDR bth_addr_;
+
+ // Data members below are only used when listening.
+ scoped_refptr<device::BluetoothAdapter> adapter_;
+ scoped_ptr<ServiceRegData> service_reg_data_;
+ scoped_ptr<net::TCPSocket> accept_socket_;
+ net::IPEndPoint accept_address_;
DISALLOW_COPY_AND_ASSIGN(BluetoothSocketWin);
};
diff --git a/chromium/device/bluetooth/bluetooth_strings.grd b/chromium/device/bluetooth/bluetooth_strings.grd
index 7e10b6282ab..9d3eec4a66c 100644
--- a/chromium/device/bluetooth/bluetooth_strings.grd
+++ b/chromium/device/bluetooth/bluetooth_strings.grd
@@ -12,34 +12,34 @@ This file contains the strings for bluetooth.
</output>
<output filename="device_bluetooth_strings_am.pak" type="data_package" lang="am" />
<output filename="device_bluetooth_strings_ar.pak" type="data_package" lang="ar" />
- <if expr="pp_ifdef('use_third_party_translations')">
+ <if expr="use_third_party_translations">
<output filename="device_bluetooth_strings_ast.pak" type="data_package" lang="ast" />
</if>
<output filename="device_bluetooth_strings_bg.pak" type="data_package" lang="bg" />
<output filename="device_bluetooth_strings_bn.pak" type="data_package" lang="bn" />
- <if expr="pp_ifdef('use_third_party_translations')">
+ <if expr="use_third_party_translations">
<output filename="device_bluetooth_strings_bs.pak" type="data_package" lang="bs" />
</if>
<output filename="device_bluetooth_strings_ca.pak" type="data_package" lang="ca" />
- <if expr="pp_ifdef('use_third_party_translations')">
+ <if expr="use_third_party_translations">
<output filename="device_bluetooth_strings_ca@valencia.pak" type="data_package" lang="ca@valencia" />
</if>
<output filename="device_bluetooth_strings_cs.pak" type="data_package" lang="cs" />
<output filename="device_bluetooth_strings_da.pak" type="data_package" lang="da" />
<output filename="device_bluetooth_strings_de.pak" type="data_package" lang="de" />
<output filename="device_bluetooth_strings_el.pak" type="data_package" lang="el" />
- <if expr="pp_ifdef('use_third_party_translations')">
+ <if expr="use_third_party_translations">
<output filename="device_bluetooth_strings_en-AU.pak" type="data_package" lang="en-AU" />
</if>
<output filename="device_bluetooth_strings_en-GB.pak" type="data_package" lang="en-GB" />
<output filename="device_bluetooth_strings_en-US.pak" type="data_package" lang="en" />
- <if expr="pp_ifdef('use_third_party_translations')">
+ <if expr="use_third_party_translations">
<output filename="device_bluetooth_strings_eo.pak" type="data_package" lang="eo" />
</if>
<output filename="device_bluetooth_strings_es.pak" type="data_package" lang="es" />
<output filename="device_bluetooth_strings_es-419.pak" type="data_package" lang="es-419" />
<output filename="device_bluetooth_strings_et.pak" type="data_package" lang="et" />
- <if expr="pp_ifdef('use_third_party_translations')">
+ <if expr="use_third_party_translations">
<output filename="device_bluetooth_strings_eu.pak" type="data_package" lang="eu" />
</if>
<output filename="device_bluetooth_strings_fa.pak" type="data_package" lang="fa" />
@@ -47,7 +47,7 @@ This file contains the strings for bluetooth.
<output filename="device_bluetooth_strings_fi.pak" type="data_package" lang="fi" />
<output filename="device_bluetooth_strings_fil.pak" type="data_package" lang="fil" />
<output filename="device_bluetooth_strings_fr.pak" type="data_package" lang="fr" />
- <if expr="pp_ifdef('use_third_party_translations')">
+ <if expr="use_third_party_translations">
<output filename="device_bluetooth_strings_gl.pak" type="data_package" lang="gl" />
</if>
<output filename="device_bluetooth_strings_gu.pak" type="data_package" lang="gu" />
@@ -55,19 +55,19 @@ This file contains the strings for bluetooth.
<output filename="device_bluetooth_strings_hi.pak" type="data_package" lang="hi" />
<output filename="device_bluetooth_strings_hr.pak" type="data_package" lang="hr" />
<output filename="device_bluetooth_strings_hu.pak" type="data_package" lang="hu" />
- <if expr="pp_ifdef('use_third_party_translations')">
+ <if expr="use_third_party_translations">
<output filename="device_bluetooth_strings_hy.pak" type="data_package" lang="hy" />
<output filename="device_bluetooth_strings_ia.pak" type="data_package" lang="ia" />
</if>
<output filename="device_bluetooth_strings_id.pak" type="data_package" lang="id" />
<output filename="device_bluetooth_strings_it.pak" type="data_package" lang="it" />
<output filename="device_bluetooth_strings_ja.pak" type="data_package" lang="ja" />
- <if expr="pp_ifdef('use_third_party_translations')">
+ <if expr="use_third_party_translations">
<output filename="device_bluetooth_strings_ka.pak" type="data_package" lang="ka" />
</if>
<output filename="device_bluetooth_strings_kn.pak" type="data_package" lang="kn" />
<output filename="device_bluetooth_strings_ko.pak" type="data_package" lang="ko" />
- <if expr="pp_ifdef('use_third_party_translations')">
+ <if expr="use_third_party_translations">
<output filename="device_bluetooth_strings_ku.pak" type="data_package" lang="ku" />
<output filename="device_bluetooth_strings_kw.pak" type="data_package" lang="kw" />
</if>
@@ -94,7 +94,7 @@ This file contains the strings for bluetooth.
<output filename="device_bluetooth_strings_te.pak" type="data_package" lang="te" />
<output filename="device_bluetooth_strings_th.pak" type="data_package" lang="th" />
<output filename="device_bluetooth_strings_tr.pak" type="data_package" lang="tr" />
- <if expr="pp_ifdef('use_third_party_translations')">
+ <if expr="use_third_party_translations">
<output filename="device_bluetooth_strings_ug.pak" type="data_package" lang="ug" />
</if>
<output filename="device_bluetooth_strings_uk.pak" type="data_package" lang="uk" />
diff --git a/chromium/device/bluetooth/bluetooth_task_manager_win.cc b/chromium/device/bluetooth/bluetooth_task_manager_win.cc
index 3347f0f1753..7d8da089d6e 100644
--- a/chromium/device/bluetooth/bluetooth_task_manager_win.cc
+++ b/chromium/device/bluetooth/bluetooth_task_manager_win.cc
@@ -30,6 +30,8 @@ const int kMaxNumDeviceAddressChar = 127;
const int kServiceDiscoveryResultBufferSize = 5000;
const int kMaxDeviceDiscoveryTimeout = 48;
+typedef device::BluetoothTaskManagerWin::ServiceRecordState ServiceRecordState;
+
// Populates bluetooth adapter state using adapter_handle.
void GetAdapterState(HANDLE adapter_handle,
device::BluetoothTaskManagerWin::AdapterState* state) {
@@ -71,6 +73,49 @@ void GetDeviceState(const BLUETOOTH_DEVICE_INFO& device_info,
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);
+ service_record_state->address = device_address;
+ 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 {
@@ -78,6 +123,28 @@ namespace device {
// static
const int BluetoothTaskManagerWin::kPollIntervalMs = 500;
+BluetoothTaskManagerWin::AdapterState::AdapterState() : powered(false) {
+}
+
+BluetoothTaskManagerWin::AdapterState::~AdapterState() {
+}
+
+BluetoothTaskManagerWin::ServiceRecordState::ServiceRecordState() {
+}
+
+BluetoothTaskManagerWin::ServiceRecordState::~ServiceRecordState() {
+}
+
+BluetoothTaskManagerWin::DeviceState::DeviceState()
+ : bluetooth_class(0),
+ visible(false),
+ connected(false),
+ authenticated(false) {
+}
+
+BluetoothTaskManagerWin::DeviceState::~DeviceState() {
+}
+
BluetoothTaskManagerWin::BluetoothTaskManagerWin(
scoped_refptr<base::SequencedTaskRunner> ui_task_runner)
: ui_task_runner_(ui_task_runner),
@@ -375,41 +442,9 @@ void BluetoothTaskManagerWin::DiscoverServices(
const std::string device_address = (*iter)->address;
ScopedVector<ServiceRecordState>* service_record_states =
&(*iter)->service_record_states;
- WSAQUERYSET sdp_query;
- ZeroMemory(&sdp_query, sizeof(sdp_query));
- sdp_query.dwSize = sizeof(sdp_query);
- GUID protocol = L2CAP_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);
- DWORD sdp_buffer_size = sizeof(sdp_buffer);
- while (ERROR_SUCCESS == WSALookupServiceNext(sdp_handle,
- LUP_RETURN_ALL,
- &sdp_buffer_size,
- sdp_result_data)) {
- ServiceRecordState* service_record_state = new ServiceRecordState();
- service_record_state->name =
- base::SysWideToUTF8(sdp_result_data->lpszServiceInstanceName);
- service_record_state->address = device_address;
- 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);
+
+ DiscoverDeviceServices(
+ device_address, L2CAP_PROTOCOL_UUID, service_record_states);
}
}
diff --git a/chromium/device/bluetooth/bluetooth_task_manager_win.h b/chromium/device/bluetooth/bluetooth_task_manager_win.h
index 2c787d66ec4..991e95c34a9 100644
--- a/chromium/device/bluetooth/bluetooth_task_manager_win.h
+++ b/chromium/device/bluetooth/bluetooth_task_manager_win.h
@@ -35,18 +35,24 @@ class BluetoothTaskManagerWin
: public base::RefCountedThreadSafe<BluetoothTaskManagerWin> {
public:
struct AdapterState {
+ AdapterState();
+ ~AdapterState();
std::string name;
std::string address;
bool powered;
};
struct ServiceRecordState {
+ ServiceRecordState();
+ ~ServiceRecordState();
std::string name;
std::string address;
std::vector<uint8> sdp_bytes;
};
struct DeviceState {
+ DeviceState();
+ ~DeviceState();
std::string name;
std::string address;
uint32 bluetooth_class;
diff --git a/chromium/device/bluetooth/bluetooth_utils.cc b/chromium/device/bluetooth/bluetooth_utils.cc
deleted file mode 100644
index b1f9f7816eb..00000000000
--- a/chromium/device/bluetooth/bluetooth_utils.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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 "device/bluetooth/bluetooth_utils.h"
-
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-
-namespace {
-static const char* kCommonUuidPostfix = "-0000-1000-8000-00805f9b34fb";
-static const char* kCommonUuidPrefix = "0000";
-static const int kUuidSize = 36;
-} // namespace
-
-namespace device {
-namespace bluetooth_utils {
-
-std::string CanonicalUuid(std::string uuid) {
- if (uuid.empty())
- return std::string();
-
- if (uuid.size() < 11 && uuid.find("0x") == 0)
- uuid = uuid.substr(2);
-
- if (!(uuid.size() == 4 || uuid.size() == 8 || uuid.size() == 36))
- return std::string();
-
- if (uuid.size() == 4 || uuid.size() == 8) {
- for (size_t i = 0; i < uuid.size(); ++i) {
- if (!IsHexDigit(uuid[i]))
- return std::string();
- }
-
- if (uuid.size() == 4)
- return kCommonUuidPrefix + uuid + kCommonUuidPostfix;
-
- return uuid + kCommonUuidPostfix;
- }
-
- std::string uuid_result(uuid);
- for (int i = 0; i < kUuidSize; ++i) {
- if (i == 8 || i == 13 || i == 18 || i == 23) {
- if (uuid[i] != '-')
- return std::string();
- } else {
- if (!IsHexDigit(uuid[i]))
- return std::string();
- uuid_result[i] = tolower(uuid[i]);
- }
- }
- return uuid_result;
-}
-
-} // namespace bluetooth_utils
-} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_utils.h b/chromium/device/bluetooth/bluetooth_utils.h
deleted file mode 100644
index 099516013c4..00000000000
--- a/chromium/device/bluetooth/bluetooth_utils.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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 DEVICE_BLUETOOTH_BLUETOOTH_UTILS_H_
-#define DEVICE_BLUETOOTH_BLUETOOTH_UTILS_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-
-namespace device {
-namespace bluetooth_utils {
-
-// Takes a 4, 8 or 36 character UUID, validates it and returns it in 36
-// character format with all hex digits lower case. If |uuid| is invalid, the
-// empty string is returned.
-//
-// Valid inputs are:
-// XXXX
-// 0xXXXX
-// XXXXXXXX
-// 0xXXXXXXXX
-// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
-std::string CanonicalUuid(std::string uuid);
-
-} // namespace bluetooth_utils
-} // namespace device
-
-#endif // DEVICE_BLUETOOTH_BLUETOOTH_UTILS_H_
-
diff --git a/chromium/device/bluetooth/bluetooth_utils_unittest.cc b/chromium/device/bluetooth/bluetooth_utils_unittest.cc
deleted file mode 100644
index 46d5f34a848..00000000000
--- a/chromium/device/bluetooth/bluetooth_utils_unittest.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// 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 "base/basictypes.h"
-#include "device/bluetooth/bluetooth_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace device {
-
-TEST(BluetoothUtilsTest, CanonicalUuid) {
- // Does nothing for an already canonical UUID
- EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
- bluetooth_utils::CanonicalUuid("00001101-0000-1000-8000-00805f9b34fb"));
-
- // Rejects misformatted
- EXPECT_EQ("", bluetooth_utils::CanonicalUuid("1101a"));
- EXPECT_EQ("", bluetooth_utils::CanonicalUuid("Z101"));
- EXPECT_EQ("", bluetooth_utils::CanonicalUuid("0000-1101"));
- EXPECT_EQ("", bluetooth_utils::CanonicalUuid("0000Z101"));
- EXPECT_EQ("",
- bluetooth_utils::CanonicalUuid("0001101-0000-1000-8000-00805f9b34fb"));
- EXPECT_EQ("",
- bluetooth_utils::CanonicalUuid("Z0001101-0000-1000-8000-00805f9b34fb"));
- EXPECT_EQ("",
- bluetooth_utils::CanonicalUuid("00001101 0000-1000-8000-00805f9b34fb"));
- EXPECT_EQ("",
- bluetooth_utils::CanonicalUuid("00001101-0000:1000-8000-00805f9b34fb"));
- EXPECT_EQ("",
- bluetooth_utils::CanonicalUuid("00001101-0000-1000;8000-00805f9b34fb"));
- EXPECT_EQ("",
- bluetooth_utils::CanonicalUuid("00001101-0000-1000-8000000805f9b34fb"));
-
- // Lower case
- EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
- bluetooth_utils::CanonicalUuid("00001101-0000-1000-8000-00805F9B34FB"));
-
- // Short to full
- EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
- bluetooth_utils::CanonicalUuid("1101"));
- EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
- bluetooth_utils::CanonicalUuid("0x1101"));
- EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
- bluetooth_utils::CanonicalUuid("00001101"));
- EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
- bluetooth_utils::CanonicalUuid("0x00001101"));
-
- // No 0x prefix on 36 character
- EXPECT_EQ("",
- bluetooth_utils::CanonicalUuid("0x00001101-0000-1000-8000-00805f9b34fb"));
-}
-
-} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_uuid.cc b/chromium/device/bluetooth/bluetooth_uuid.cc
new file mode 100644
index 00000000000..714d1c5c340
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_uuid.cc
@@ -0,0 +1,95 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_uuid.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+
+namespace device {
+
+namespace {
+
+const char* kCommonUuidPostfix = "-0000-1000-8000-00805f9b34fb";
+const char* kCommonUuidPrefix = "0000";
+
+// Returns the canonical, 128-bit canonical, and the format of the UUID
+// in |canonical|, |canonical_128|, and |format| based on |uuid|.
+void GetCanonicalUuid(std::string uuid,
+ std::string* canonical,
+ std::string* canonical_128,
+ BluetoothUUID::Format* format) {
+ // Initialize the values for the failure case.
+ canonical->clear();
+ canonical_128->clear();
+ *format = BluetoothUUID::kFormatInvalid;
+
+ if (uuid.empty())
+ return;
+
+ if (uuid.size() < 11 && uuid.find("0x") == 0)
+ uuid = uuid.substr(2);
+
+ if (!(uuid.size() == 4 || uuid.size() == 8 || uuid.size() == 36))
+ return;
+
+ for (size_t i = 0; i < uuid.size(); ++i) {
+ if (i == 8 || i == 13 || i == 18 || i == 23) {
+ if (uuid[i] != '-')
+ return;
+ } else {
+ if (!IsHexDigit(uuid[i]))
+ return;
+ uuid[i] = base::ToLowerASCII(uuid[i]);
+ }
+ }
+
+ canonical->assign(uuid);
+ if (uuid.size() == 4) {
+ canonical_128->assign(kCommonUuidPrefix + uuid + kCommonUuidPostfix);
+ *format = BluetoothUUID::kFormat16Bit;
+ } else if (uuid.size() == 8) {
+ canonical_128->assign(uuid + kCommonUuidPostfix);
+ *format = BluetoothUUID::kFormat32Bit;
+ } else {
+ canonical_128->assign(uuid);
+ *format = BluetoothUUID::kFormat128Bit;
+ }
+}
+
+} // namespace
+
+
+BluetoothUUID::BluetoothUUID(const std::string& uuid) {
+ GetCanonicalUuid(uuid, &value_, &canonical_value_, &format_);
+}
+
+BluetoothUUID::BluetoothUUID() : format_(kFormatInvalid) {
+}
+
+BluetoothUUID::~BluetoothUUID() {
+}
+
+bool BluetoothUUID::IsValid() const {
+ return format_ != kFormatInvalid;
+}
+
+bool BluetoothUUID::operator<(const BluetoothUUID& uuid) const {
+ return canonical_value_ < uuid.canonical_value_;
+}
+
+bool BluetoothUUID::operator==(const BluetoothUUID& uuid) const {
+ return canonical_value_ == uuid.canonical_value_;
+}
+
+bool BluetoothUUID::operator!=(const BluetoothUUID& uuid) const {
+ return canonical_value_ != uuid.canonical_value_;
+}
+
+void PrintTo(const BluetoothUUID& uuid, std::ostream* out) {
+ *out << uuid.canonical_value();
+}
+
+} // namespace device
diff --git a/chromium/device/bluetooth/bluetooth_uuid.h b/chromium/device/bluetooth/bluetooth_uuid.h
new file mode 100644
index 00000000000..5c2fc8a3b9c
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_uuid.h
@@ -0,0 +1,97 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_UUID_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_UUID_H_
+
+#include <string>
+
+namespace device {
+
+// Opaque wrapper around a Bluetooth UUID. Instances of UUID represent the
+// 128-bit universally unique identifiers (UUIDs) of profiles and attributes
+// used in Bluetooth based communication, such as a peripheral's services,
+// characteristics, and characteristic descriptors. An instance are
+// constructed using a string representing 16, 32, or 128 bit UUID formats.
+class BluetoothUUID {
+ public:
+ // Possible representation formats used during construction.
+ enum Format {
+ kFormatInvalid,
+ kFormat16Bit,
+ kFormat32Bit,
+ kFormat128Bit
+ };
+
+ // Single argument constructor. |uuid| can be a 16, 32, or 128 bit UUID
+ // represented as a 4, 8, or 36 character string with the following
+ // formats:
+ // xxxx
+ // 0xxxxx
+ // xxxxxxxx
+ // 0xxxxxxxxx
+ // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ //
+ // 16 and 32 bit UUIDs will be internally converted to a 128 bit UUID using
+ // the base UUID defined in the Bluetooth specification, hence custom UUIDs
+ // should be provided in the 128-bit format. If |uuid| is in an unsupported
+ // format, the result might be invalid. Use IsValid to check for validity
+ // after construction.
+ explicit BluetoothUUID(const std::string& uuid);
+
+ // Default constructor does nothing. Since BluetoothUUID is copyable, this
+ // constructor is useful for initializing member variables and assigning a
+ // value to them later. The default constructor will initialize an invalid
+ // UUID by definition and the string accessors will return an empty string.
+ BluetoothUUID();
+ virtual ~BluetoothUUID();
+
+ // Returns true, if the UUID is in a valid canonical format.
+ bool IsValid() const;
+
+ // Returns the representation format of the UUID. This reflects the format
+ // that was provided during construction.
+ Format format() const { return format_; }
+
+ // Returns the value of the UUID as a string. The representation format is
+ // based on what was passed in during construction. For the supported sizes,
+ // this representation can have the following formats:
+ // - 16 bit: xxxx
+ // - 32 bit: xxxxxxxx
+ // - 128 bit: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ // where x is a lowercase hex digit.
+ const std::string& value() const { return value_; }
+
+ // Returns the underlying 128-bit value as a string in the following format:
+ // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ // where x is a lowercase hex digit.
+ const std::string& canonical_value() const { return canonical_value_; }
+
+ // Permit sufficient comparison to allow a UUID to be used as a key in a
+ // std::map.
+ bool operator<(const BluetoothUUID& uuid) const;
+
+ // Equality operators.
+ bool operator==(const BluetoothUUID& uuid) const;
+ bool operator!=(const BluetoothUUID& uuid) const;
+
+ private:
+ // String representation of the UUID that was used during construction. For
+ // the supported sizes, this representation can have the following formats:
+ // - 16 bit: xxxx
+ // - 32 bit: xxxxxxxx
+ // - 128 bit: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ Format format_;
+ std::string value_;
+
+ // The 128-bit string representation of the UUID.
+ std::string canonical_value_;
+};
+
+// This is required by gtest to print a readable output on test failures.
+void PrintTo(const BluetoothUUID& uuid, std::ostream* out);
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_UUID_H_
diff --git a/chromium/device/bluetooth/bluetooth_uuid_unittest.cc b/chromium/device/bluetooth/bluetooth_uuid_unittest.cc
new file mode 100644
index 00000000000..c59ab1c934e
--- /dev/null
+++ b/chromium/device/bluetooth/bluetooth_uuid_unittest.cc
@@ -0,0 +1,107 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+TEST(BluetoothUUIDTest, BluetoothUUID) {
+ const char kValid128Bit0[] = "12345678-1234-5678-9abc-def123456789";
+ const char kValid128Bit1[] = "00001101-0000-1000-8000-00805f9b34fb";
+ const char kInvalid36Char0[] = "1234567-1234-5678-9abc-def123456789";
+ const char kInvalid36Char1[] = "0x00001101-0000-1000-8000-00805f9b34fb";
+ const char kInvalid4Char[] = "Z101";
+ const char kValid16Bit[] = "0x1101";
+ const char kValid32Bit[] = "00001101";
+
+ // Valid 128-bit custom UUID.
+ BluetoothUUID uuid0(kValid128Bit0);
+ EXPECT_TRUE(uuid0.IsValid());
+ EXPECT_EQ(BluetoothUUID::kFormat128Bit, uuid0.format());
+ EXPECT_EQ(uuid0.value(), uuid0.canonical_value());
+
+ // Valid 128-bit UUID.
+ BluetoothUUID uuid1(kValid128Bit1);
+ EXPECT_TRUE(uuid1.IsValid());
+ EXPECT_EQ(BluetoothUUID::kFormat128Bit, uuid1.format());
+ EXPECT_EQ(uuid1.value(), uuid1.canonical_value());
+
+ EXPECT_NE(uuid0, uuid1);
+
+ // Invalid 128-bit UUID.
+ BluetoothUUID uuid2(kInvalid36Char0);
+ EXPECT_FALSE(uuid2.IsValid());
+ EXPECT_EQ(BluetoothUUID::kFormatInvalid, uuid2.format());
+ EXPECT_TRUE(uuid2.value().empty());
+ EXPECT_TRUE(uuid2.canonical_value().empty());
+
+ // Invalid 128-bit UUID.
+ BluetoothUUID uuid3(kInvalid36Char1);
+ EXPECT_FALSE(uuid3.IsValid());
+ EXPECT_EQ(BluetoothUUID::kFormatInvalid, uuid3.format());
+ EXPECT_TRUE(uuid3.value().empty());
+ EXPECT_TRUE(uuid3.canonical_value().empty());
+
+ // Invalid 16-bit UUID.
+ BluetoothUUID uuid4(kInvalid4Char);
+ EXPECT_FALSE(uuid4.IsValid());
+ EXPECT_EQ(BluetoothUUID::kFormatInvalid, uuid4.format());
+ EXPECT_TRUE(uuid4.value().empty());
+ EXPECT_TRUE(uuid4.canonical_value().empty());
+
+ // Valid 16-bit UUID.
+ BluetoothUUID uuid5(kValid16Bit);
+ EXPECT_TRUE(uuid5.IsValid());
+ EXPECT_EQ(BluetoothUUID::kFormat16Bit, uuid5.format());
+ EXPECT_NE(uuid5.value(), uuid5.canonical_value());
+ EXPECT_EQ("1101", uuid5.value());
+ EXPECT_EQ(kValid128Bit1, uuid5.canonical_value());
+
+ // Valid 32-bit UUID.
+ BluetoothUUID uuid6(kValid32Bit);
+ EXPECT_TRUE(uuid6.IsValid());
+ EXPECT_EQ(BluetoothUUID::kFormat32Bit, uuid6.format());
+ EXPECT_NE(uuid6.value(), uuid6.canonical_value());
+ EXPECT_EQ("00001101", uuid6.value());
+ EXPECT_EQ(kValid128Bit1, uuid6.canonical_value());
+
+ // uuid5, uuid6, and uuid1 are equivalent.
+ EXPECT_EQ(uuid5, uuid6);
+ EXPECT_EQ(uuid1, uuid5);
+ EXPECT_EQ(uuid1, uuid6);
+}
+
+// Verify that UUIDs are parsed case-insensitively
+TEST(BluetoothUUIDTest, BluetoothUUID_CaseInsensitive) {
+ const char k16Bit[] = "1abc";
+ const char k32Bit[] = "00001abc";
+ const char k128Bit[] = "00001abc-0000-1000-8000-00805f9b34fb";
+
+ struct TestCase {
+ const std::string input_uuid;
+ const std::string expected_value;
+ } test_cases[] = {
+ { "1abc", k16Bit },
+ { "1ABC", k16Bit },
+ { "1aBc", k16Bit },
+ { "00001abc", k32Bit },
+ { "00001ABC", k32Bit },
+ { "00001aBc", k32Bit },
+ { "00001abc-0000-1000-8000-00805f9b34fb", k128Bit },
+ { "00001ABC-0000-1000-8000-00805F9B34FB", k128Bit },
+ { "00001aBc-0000-1000-8000-00805F9b34fB", k128Bit },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+ SCOPED_TRACE("Input UUID: " + test_cases[i].input_uuid);
+ BluetoothUUID uuid(test_cases[i].input_uuid);
+ EXPECT_TRUE(uuid.IsValid());
+ EXPECT_EQ(test_cases[i].expected_value, uuid.value());
+ EXPECT_EQ(k128Bit, uuid.canonical_value());
+ }
+}
+
+} // namespace device
diff --git a/chromium/device/device_tests.gyp b/chromium/device/device_tests.gyp
index e0564b5260c..577b28d3731 100644
--- a/chromium/device/device_tests.gyp
+++ b/chromium/device/device_tests.gyp
@@ -19,20 +19,27 @@
'bluetooth/bluetooth.gyp:device_bluetooth_mocks',
'nfc/nfc.gyp:device_nfc',
'usb/usb.gyp:device_usb',
+ 'hid/hid.gyp:device_hid',
],
'sources': [
'bluetooth/bluetooth_adapter_mac_unittest.mm',
+ 'bluetooth/bluetooth_adapter_unittest.cc',
'bluetooth/bluetooth_adapter_win_unittest.cc',
+ 'bluetooth/bluetooth_device_unittest.cc',
'bluetooth/bluetooth_device_win_unittest.cc',
'bluetooth/bluetooth_chromeos_unittest.cc',
- 'bluetooth/bluetooth_profile_chromeos_unittest.cc',
- 'bluetooth/bluetooth_service_record_mac_unittest.mm',
+ 'bluetooth/bluetooth_gatt_chromeos_unittest.cc',
'bluetooth/bluetooth_service_record_win_unittest.cc',
+ 'bluetooth/bluetooth_socket_chromeos_unittest.cc',
'bluetooth/bluetooth_task_manager_win_unittest.cc',
- 'bluetooth/bluetooth_utils_unittest.cc',
+ 'bluetooth/bluetooth_uuid_unittest.cc',
'nfc/nfc_chromeos_unittest.cc',
'nfc/nfc_ndef_record_unittest.cc',
'usb/usb_ids_unittest.cc',
+ 'hid/hid_connection_unittest.cc',
+ 'hid/hid_report_descriptor_unittest.cc',
+ 'hid/hid_service_unittest.cc',
+ 'hid/input_service_linux_unittest.cc',
],
'conditions': [
['chromeos==1', {
@@ -52,13 +59,23 @@
}],
['os_posix == 1 and OS != "mac" and OS != "android" and OS != "ios"', {
'conditions': [
- ['linux_use_tcmalloc == 1', {
+ ['use_allocator!="none"', {
'dependencies': [
'../base/allocator/allocator.gyp:allocator',
],
}],
],
}],
+ ['OS=="linux" and use_udev==0', {
+ # Udev is the only Linux implementation. If we're compiling without
+ # Udev, disable these unittests.
+ 'dependencies!': [
+ 'hid/hid.gyp:device_hid',
+ ],
+ 'sources/': [
+ ['exclude', '^hid/'],
+ ],
+ }],
],
},
],
diff --git a/chromium/device/hid/DEPS b/chromium/device/hid/DEPS
new file mode 100644
index 00000000000..6a2f02e2180
--- /dev/null
+++ b/chromium/device/hid/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+net/base",
+]
diff --git a/chromium/device/hid/device_monitor_linux.cc b/chromium/device/hid/device_monitor_linux.cc
new file mode 100644
index 00000000000..8d112c127a1
--- /dev/null
+++ b/chromium/device/hid/device_monitor_linux.cc
@@ -0,0 +1,150 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/device_monitor_linux.h"
+
+#include <libudev.h>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace device {
+
+namespace {
+
+const char kUdevName[] = "udev";
+const char kUdevActionAdd[] = "add";
+const char kUdevActionRemove[] = "remove";
+
+// The instance will be reset when message loop destroys.
+base::LazyInstance<scoped_ptr<DeviceMonitorLinux> >::Leaky
+ g_device_monitor_linux_ptr = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+DeviceMonitorLinux::DeviceMonitorLinux() : monitor_fd_(-1) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ base::MessageLoop::current()->AddDestructionObserver(this);
+
+ udev_.reset(udev_new());
+ if (!udev_) {
+ LOG(ERROR) << "Failed to create udev.";
+ return;
+ }
+ monitor_.reset(udev_monitor_new_from_netlink(udev_.get(), kUdevName));
+ if (!monitor_) {
+ LOG(ERROR) << "Failed to create udev monitor.";
+ return;
+ }
+
+ int ret = udev_monitor_enable_receiving(monitor_.get());
+ if (ret != 0) {
+ LOG(ERROR) << "Failed to start udev monitoring.";
+ return;
+ }
+
+ monitor_fd_ = udev_monitor_get_fd(monitor_.get());
+ if (monitor_fd_ <= 0) {
+ LOG(ERROR) << "Failed to start udev monitoring.";
+ return;
+ }
+
+ if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
+ monitor_fd_,
+ true,
+ base::MessageLoopForIO::WATCH_READ,
+ &monitor_watcher_,
+ this)) {
+ return;
+ }
+}
+
+// static
+DeviceMonitorLinux* DeviceMonitorLinux::GetInstance() {
+ if (!HasInstance())
+ g_device_monitor_linux_ptr.Get().reset(new DeviceMonitorLinux());
+ return g_device_monitor_linux_ptr.Get().get();
+}
+
+// static
+bool DeviceMonitorLinux::HasInstance() {
+ return g_device_monitor_linux_ptr.Get().get();
+}
+
+void DeviceMonitorLinux::AddObserver(Observer* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (observer)
+ observers_.AddObserver(observer);
+}
+
+void DeviceMonitorLinux::RemoveObserver(Observer* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (observer)
+ observers_.RemoveObserver(observer);
+}
+
+ScopedUdevDevicePtr DeviceMonitorLinux::GetDeviceFromPath(
+ const std::string& path) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ScopedUdevDevicePtr device(
+ udev_device_new_from_syspath(udev_.get(), path.c_str()));
+ return device.Pass();
+}
+
+void DeviceMonitorLinux::Enumerate(const EnumerateCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev_.get()));
+
+ if (!enumerate) {
+ LOG(ERROR) << "Failed to enumerate devices.";
+ return;
+ }
+
+ if (udev_enumerate_scan_devices(enumerate.get()) != 0) {
+ LOG(ERROR) << "Failed to enumerate devices.";
+ return;
+ }
+
+ // This list is managed by |enumerate|.
+ udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
+ for (udev_list_entry* i = devices; i != NULL;
+ i = udev_list_entry_get_next(i)) {
+ ScopedUdevDevicePtr device(
+ udev_device_new_from_syspath(udev_.get(), udev_list_entry_get_name(i)));
+ if (device)
+ callback.Run(device.get());
+ }
+}
+
+void DeviceMonitorLinux::WillDestroyCurrentMessageLoop() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ g_device_monitor_linux_ptr.Get().reset(NULL);
+}
+
+void DeviceMonitorLinux::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(monitor_fd_, fd);
+
+ ScopedUdevDevicePtr device(udev_monitor_receive_device(monitor_.get()));
+ if (!device)
+ return;
+
+ std::string action(udev_device_get_action(device.get()));
+ if (action == kUdevActionAdd)
+ FOR_EACH_OBSERVER(Observer, observers_, OnDeviceAdded(device.get()));
+ else if (action == kUdevActionRemove)
+ FOR_EACH_OBSERVER(Observer, observers_, OnDeviceRemoved(device.get()));
+}
+
+void DeviceMonitorLinux::OnFileCanWriteWithoutBlocking(int fd) {}
+
+DeviceMonitorLinux::~DeviceMonitorLinux() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+ monitor_watcher_.StopWatchingFileDescriptor();
+ close(monitor_fd_);
+}
+
+} // namespace device
diff --git a/chromium/device/hid/device_monitor_linux.h b/chromium/device/hid/device_monitor_linux.h
new file mode 100644
index 00000000000..2f219dc9e24
--- /dev/null
+++ b/chromium/device/hid/device_monitor_linux.h
@@ -0,0 +1,75 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_DEVICE_MONITOR_LINUX_H_
+#define DEVICE_HID_DEVICE_MONITOR_LINUX_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump_libevent.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "device/udev_linux/udev.h"
+
+struct udev_device;
+
+namespace device {
+
+// This class listends for notifications from libudev about
+// connected/disconnected devices. This class is *NOT* thread-safe and
+// all methods must be accessed from the FILE thread.
+class DeviceMonitorLinux : public base::MessageLoop::DestructionObserver,
+ public base::MessagePumpLibevent::Watcher {
+ public:
+ typedef base::Callback<void(udev_device* device)> EnumerateCallback;
+
+ class Observer {
+ public:
+ virtual ~Observer() {}
+ virtual void OnDeviceAdded(udev_device* device) = 0;
+ virtual void OnDeviceRemoved(udev_device* device) = 0;
+ };
+
+ DeviceMonitorLinux();
+
+ static DeviceMonitorLinux* GetInstance();
+ static bool HasInstance();
+
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ ScopedUdevDevicePtr GetDeviceFromPath(const std::string& path);
+ void Enumerate(const EnumerateCallback& callback);
+
+ // Implements base::MessageLoop::DestructionObserver
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
+
+ // Implements base::MessagePumpLibevent::Watcher
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
+
+ private:
+ friend struct base::DefaultDeleter<DeviceMonitorLinux>;
+
+ virtual ~DeviceMonitorLinux();
+
+ ScopedUdevPtr udev_;
+ ScopedUdevMonitorPtr monitor_;
+ int monitor_fd_;
+ base::MessagePumpLibevent::FileDescriptorWatcher monitor_watcher_;
+
+ ObserverList<Observer> observers_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeviceMonitorLinux);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_DEVICE_MONITOR_LINUX_H_
diff --git a/chromium/device/hid/hid.gyp b/chromium/device/hid/hid.gyp
new file mode 100644
index 00000000000..c12845a0fa4
--- /dev/null
+++ b/chromium/device/hid/hid.gyp
@@ -0,0 +1,67 @@
+# Copyright 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'device_hid',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'device_monitor_linux.cc',
+ 'device_monitor_linux.h',
+ 'hid_connection.cc',
+ 'hid_connection.h',
+ 'hid_connection_linux.cc',
+ 'hid_connection_linux.h',
+ 'hid_connection_mac.cc',
+ 'hid_connection_mac.h',
+ 'hid_connection_win.cc',
+ 'hid_connection_win.h',
+ 'hid_device_info.cc',
+ 'hid_device_info.h',
+ 'hid_report_descriptor.cc',
+ 'hid_report_descriptor.h',
+ 'hid_report_descriptor_item.cc',
+ 'hid_report_descriptor_item.h',
+ 'hid_service.cc',
+ 'hid_service.h',
+ 'hid_service_linux.cc',
+ 'hid_service_linux.h',
+ 'hid_service_mac.cc',
+ 'hid_service_mac.h',
+ 'hid_service_win.cc',
+ 'hid_service_win.h',
+ 'hid_usage_and_page.cc',
+ 'hid_usage_and_page.h',
+ 'hid_utils_mac.cc',
+ 'hid_utils_mac.h',
+ 'input_service_linux.cc',
+ 'input_service_linux.h',
+ ],
+ 'conditions': [
+ ['use_udev==1', {
+ 'dependencies': [
+ '../udev_linux/udev.gyp:udev_linux',
+ ],
+ }, { # use_udev==0
+ # The Linux implementation is based on Udev.
+ 'sources!': [
+ 'device_monitor_linux.cc',
+ 'device_monitor_linux.h',
+ 'hid_service_linux.cc',
+ 'hid_service_linux.h',
+ 'input_service_linux.cc',
+ 'input_service_linux.h',
+ ],
+ }],
+ ],
+ },
+ ],
+}
diff --git a/chromium/device/hid/hid_connection.cc b/chromium/device/hid/hid_connection.cc
new file mode 100644
index 00000000000..c134bf2702e
--- /dev/null
+++ b/chromium/device/hid/hid_connection.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_connection.h"
+
+namespace device {
+
+PendingHidReport::PendingHidReport() {}
+
+PendingHidReport::~PendingHidReport() {}
+
+PendingHidRead::PendingHidRead() {}
+
+PendingHidRead::~PendingHidRead() {}
+
+HidConnection::HidConnection(const HidDeviceInfo& device_info)
+ : device_info_(device_info) {}
+
+HidConnection::~HidConnection() {}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_connection.h b/chromium/device/hid/hid_connection.h
new file mode 100644
index 00000000000..5c08fbc5bbd
--- /dev/null
+++ b/chromium/device/hid/hid_connection.h
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_CONNECTION_H_
+#define DEVICE_HID_HID_CONNECTION_H_
+
+#include <stdint.h>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "device/hid/hid_device_info.h"
+#include "net/base/io_buffer.h"
+
+namespace device {
+
+class HidConnection : public base::RefCountedThreadSafe<HidConnection> {
+ public:
+ typedef base::Callback<void(bool success, size_t size)> IOCallback;
+
+ virtual void Read(scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) = 0;
+ virtual void Write(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) = 0;
+ virtual void GetFeatureReport(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) = 0;
+ virtual void SendFeatureReport(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) = 0;
+
+ const HidDeviceInfo& device_info() const { return device_info_; }
+
+ protected:
+ friend class base::RefCountedThreadSafe<HidConnection>;
+ friend struct HidDeviceInfo;
+
+ explicit HidConnection(const HidDeviceInfo& device_info);
+ virtual ~HidConnection();
+
+ private:
+ const HidDeviceInfo device_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidConnection);
+};
+
+struct PendingHidReport {
+ PendingHidReport();
+ ~PendingHidReport();
+
+ scoped_refptr<net::IOBufferWithSize> buffer;
+};
+
+struct PendingHidRead {
+ PendingHidRead();
+ ~PendingHidRead();
+
+ scoped_refptr<net::IOBufferWithSize> buffer;
+ HidConnection::IOCallback callback;
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_CONNECTION_H_
diff --git a/chromium/device/hid/hid_connection_linux.cc b/chromium/device/hid/hid_connection_linux.cc
new file mode 100644
index 00000000000..425d88fa35c
--- /dev/null
+++ b/chromium/device/hid/hid_connection_linux.cc
@@ -0,0 +1,210 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_connection_linux.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libudev.h>
+#include <linux/hidraw.h>
+#include <sys/ioctl.h>
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/tuple.h"
+#include "device/hid/hid_service.h"
+
+// These are already defined in newer versions of linux/hidraw.h.
+#ifndef HIDIOCSFEATURE
+#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x06, len)
+#endif
+#ifndef HIDIOCGFEATURE
+#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x07, len)
+#endif
+
+namespace device {
+
+namespace {
+
+// Copies a buffer into a new one with a report ID byte inserted at the front.
+scoped_refptr<net::IOBufferWithSize> CopyBufferWithReportId(
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ uint8_t report_id) {
+ scoped_refptr<net::IOBufferWithSize> new_buffer(
+ new net::IOBufferWithSize(buffer->size() + 1));
+ new_buffer->data()[0] = report_id;
+ memcpy(new_buffer->data() + 1, buffer->data(), buffer->size());
+ return new_buffer;
+}
+
+} // namespace
+
+HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info,
+ std::string dev_node)
+ : HidConnection(device_info) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ int flags = base::File::FLAG_OPEN |
+ base::File::FLAG_READ |
+ base::File::FLAG_WRITE;
+
+ base::File device_file(base::FilePath(dev_node), flags);
+ if (!device_file.IsValid()) {
+ base::File::Error file_error = device_file.error_details();
+
+ if (file_error == base::File::FILE_ERROR_ACCESS_DENIED) {
+ VLOG(1) << "Access denied opening device read-write, trying read-only.";
+
+ flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
+
+ device_file = base::File(base::FilePath(dev_node), flags);
+ }
+ }
+ if (!device_file.IsValid()) {
+ LOG(ERROR) << "Failed to open '" << dev_node << "': "
+ << base::File::ErrorToString(device_file.error_details());
+ return;
+ }
+
+ if (fcntl(device_file.GetPlatformFile(), F_SETFL,
+ fcntl(device_file.GetPlatformFile(), F_GETFL) | O_NONBLOCK)) {
+ PLOG(ERROR) << "Failed to set non-blocking flag to device file.";
+ return;
+ }
+ device_file_ = device_file.Pass();
+
+ if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
+ device_file_.GetPlatformFile(),
+ true,
+ base::MessageLoopForIO::WATCH_READ_WRITE,
+ &device_file_watcher_,
+ this)) {
+ LOG(ERROR) << "Failed to start watching device file.";
+ }
+}
+
+HidConnectionLinux::~HidConnectionLinux() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ Disconnect();
+}
+
+void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(fd, device_file_.GetPlatformFile());
+
+ uint8 buffer[1024] = {0};
+ int bytes_read =
+ HANDLE_EINTR(read(device_file_.GetPlatformFile(), buffer, 1024));
+ if (bytes_read < 0) {
+ if (errno == EAGAIN) {
+ return;
+ }
+ Disconnect();
+ return;
+ }
+
+ PendingHidReport report;
+ report.buffer = new net::IOBufferWithSize(bytes_read);
+ memcpy(report.buffer->data(), buffer, bytes_read);
+ pending_reports_.push(report);
+ ProcessReadQueue();
+}
+
+void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {}
+
+void HidConnectionLinux::Disconnect() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ device_file_watcher_.StopWatchingFileDescriptor();
+ device_file_.Close();
+ while (!pending_reads_.empty()) {
+ PendingHidRead pending_read = pending_reads_.front();
+ pending_reads_.pop();
+ pending_read.callback.Run(false, 0);
+ }
+}
+
+void HidConnectionLinux::Read(scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ PendingHidRead pending_read;
+ pending_read.buffer = buffer;
+ pending_read.callback = callback;
+ pending_reads_.push(pending_read);
+ ProcessReadQueue();
+}
+
+void HidConnectionLinux::Write(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // If report ID is non-zero, insert it into a new copy of the buffer.
+ if (report_id != 0)
+ buffer = CopyBufferWithReportId(buffer, report_id);
+ int bytes_written = HANDLE_EINTR(
+ write(device_file_.GetPlatformFile(), buffer->data(), buffer->size()));
+ if (bytes_written < 0) {
+ Disconnect();
+ callback.Run(false, 0);
+ } else {
+ callback.Run(true, bytes_written);
+ }
+}
+
+void HidConnectionLinux::GetFeatureReport(
+ uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (buffer->size() == 0) {
+ callback.Run(false, 0);
+ return;
+ }
+
+ // The first byte of the destination buffer is the report ID being requested.
+ buffer->data()[0] = report_id;
+ int result = ioctl(device_file_.GetPlatformFile(),
+ HIDIOCGFEATURE(buffer->size()),
+ buffer->data());
+ if (result < 0)
+ callback.Run(false, 0);
+ else
+ callback.Run(true, result);
+}
+
+void HidConnectionLinux::SendFeatureReport(
+ uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (report_id != 0)
+ buffer = CopyBufferWithReportId(buffer, report_id);
+ int result = ioctl(device_file_.GetPlatformFile(),
+ HIDIOCSFEATURE(buffer->size()),
+ buffer->data());
+ if (result < 0)
+ callback.Run(false, 0);
+ else
+ callback.Run(true, result);
+}
+
+void HidConnectionLinux::ProcessReadQueue() {
+ while (pending_reads_.size() && pending_reports_.size()) {
+ PendingHidRead read = pending_reads_.front();
+ pending_reads_.pop();
+ PendingHidReport report = pending_reports_.front();
+ if (report.buffer->size() > read.buffer->size()) {
+ read.callback.Run(false, report.buffer->size());
+ } else {
+ memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
+ pending_reports_.pop();
+ read.callback.Run(true, report.buffer->size());
+ }
+ }
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_connection_linux.h b/chromium/device/hid/hid_connection_linux.h
new file mode 100644
index 00000000000..108ad881a95
--- /dev/null
+++ b/chromium/device/hid/hid_connection_linux.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_CONNECTION_LINUX_H_
+#define DEVICE_HID_HID_CONNECTION_LINUX_H_
+
+#include <queue>
+
+#include "base/files/file.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_pump_libevent.h"
+#include "device/hid/hid_connection.h"
+#include "device/hid/hid_device_info.h"
+
+namespace device {
+
+class HidConnectionLinux : public HidConnection,
+ public base::MessagePumpLibevent::Watcher {
+ public:
+ HidConnectionLinux(HidDeviceInfo device_info, std::string dev_node);
+
+ virtual void Read(scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) OVERRIDE;
+ virtual void Write(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) OVERRIDE;
+ virtual void GetFeatureReport(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) OVERRIDE;
+ virtual void SendFeatureReport(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) OVERRIDE;
+
+ // Implements base::MessagePumpLibevent::Watcher
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
+
+ private:
+ friend class base::RefCountedThreadSafe<HidConnectionLinux>;
+ virtual ~HidConnectionLinux();
+
+ void ProcessReadQueue();
+ void Disconnect();
+
+ base::File device_file_;
+ base::MessagePumpLibevent::FileDescriptorWatcher device_file_watcher_;
+
+ std::queue<PendingHidReport> pending_reports_;
+ std::queue<PendingHidRead> pending_reads_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidConnectionLinux);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_CONNECTION_LINUX_H_
diff --git a/chromium/device/hid/hid_connection_mac.cc b/chromium/device/hid/hid_connection_mac.cc
new file mode 100644
index 00000000000..ce17df4cc9b
--- /dev/null
+++ b/chromium/device/hid/hid_connection_mac.cc
@@ -0,0 +1,176 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_connection_mac.h"
+
+#include "base/bind.h"
+#include "base/mac/foundation_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_restrictions.h"
+#include "device/hid/hid_connection_mac.h"
+
+namespace device {
+
+HidConnectionMac::HidConnectionMac(HidDeviceInfo device_info)
+ : HidConnection(device_info),
+ device_(device_info.device_id, base::scoped_policy::RETAIN) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ message_loop_ = base::MessageLoopProxy::current();
+
+ DCHECK(device_.get());
+ inbound_buffer_.reset((uint8_t*)malloc(device_info.input_report_size));
+ IOHIDDeviceRegisterInputReportCallback(device_.get(),
+ inbound_buffer_.get(),
+ device_info.input_report_size,
+ &HidConnectionMac::InputReportCallback,
+ this);
+ IOHIDDeviceOpen(device_, kIOHIDOptionsTypeNone);
+}
+
+HidConnectionMac::~HidConnectionMac() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ while (!pending_reads_.empty()) {
+ pending_reads_.front().callback.Run(false, 0);
+ pending_reads_.pop();
+ }
+
+ IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone);
+}
+
+void HidConnectionMac::Read(scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!device_) {
+ callback.Run(false, 0);
+ return;
+ }
+ PendingHidRead read;
+ read.buffer = buffer;
+ read.callback = callback;
+ pending_reads_.push(read);
+ ProcessReadQueue();
+}
+
+void HidConnectionMac::Write(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ WriteReport(kIOHIDReportTypeOutput, report_id, buffer, callback);
+}
+
+void HidConnectionMac::GetFeatureReport(
+ uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (device_info().feature_report_size == 0) {
+ callback.Run(false, 0);
+ return;
+ }
+
+ if (buffer->size() < device_info().feature_report_size) {
+ callback.Run(false, 0);
+ return;
+ }
+
+ uint8_t* feature_report_buffer = reinterpret_cast<uint8_t*>(buffer->data());
+ CFIndex feature_report_size = device_info().feature_report_size;
+ IOReturn result = IOHIDDeviceGetReport(device_,
+ kIOHIDReportTypeFeature,
+ report_id,
+ feature_report_buffer,
+ &feature_report_size);
+ if (result == kIOReturnSuccess)
+ callback.Run(true, feature_report_size);
+ else
+ callback.Run(false, 0);
+}
+
+void HidConnectionMac::SendFeatureReport(
+ uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ WriteReport(kIOHIDReportTypeFeature, report_id, buffer, callback);
+}
+
+void HidConnectionMac::InputReportCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDReportType type,
+ uint32_t report_id,
+ uint8_t* report_bytes,
+ CFIndex report_length) {
+ HidConnectionMac* connection = static_cast<HidConnectionMac*>(context);
+ // report_id is already contained in report_bytes
+ scoped_refptr<net::IOBufferWithSize> buffer;
+ buffer = new net::IOBufferWithSize(report_length);
+ memcpy(buffer->data(), report_bytes, report_length);
+
+ connection->message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &HidConnectionMac::ProcessInputReport, connection, type, buffer));
+}
+
+void HidConnectionMac::ProcessReadQueue() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ while (pending_reads_.size() && pending_reports_.size()) {
+ PendingHidRead read = pending_reads_.front();
+ pending_reads_.pop();
+ PendingHidReport report = pending_reports_.front();
+ if (read.buffer->size() < report.buffer->size()) {
+ read.callback.Run(false, report.buffer->size());
+ } else {
+ memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
+ pending_reports_.pop();
+ read.callback.Run(true, report.buffer->size());
+ }
+ }
+}
+
+void HidConnectionMac::ProcessInputReport(
+ IOHIDReportType type,
+ scoped_refptr<net::IOBufferWithSize> buffer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ PendingHidReport report;
+ report.buffer = buffer;
+ pending_reports_.push(report);
+ ProcessReadQueue();
+}
+
+void HidConnectionMac::WriteReport(IOHIDReportType type,
+ uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!device_) {
+ callback.Run(false, 0);
+ return;
+ }
+ scoped_refptr<net::IOBufferWithSize> output_buffer;
+ if (report_id != 0) {
+ output_buffer = new net::IOBufferWithSize(buffer->size() + 1);
+ output_buffer->data()[0] = static_cast<uint8_t>(report_id);
+ memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
+ } else {
+ output_buffer = new net::IOBufferWithSize(buffer->size());
+ memcpy(output_buffer->data(), buffer->data(), buffer->size());
+ }
+ IOReturn res =
+ IOHIDDeviceSetReport(device_.get(),
+ type,
+ report_id,
+ reinterpret_cast<uint8_t*>(output_buffer->data()),
+ output_buffer->size());
+ if (res != kIOReturnSuccess) {
+ callback.Run(false, 0);
+ } else {
+ callback.Run(true, output_buffer->size());
+ }
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_connection_mac.h b/chromium/device/hid/hid_connection_mac.h
new file mode 100644
index 00000000000..c307fb6f878
--- /dev/null
+++ b/chromium/device/hid/hid_connection_mac.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_CONNECTION_MAC_H_
+#define DEVICE_HID_HID_CONNECTION_MAC_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/hid/IOHIDManager.h>
+
+#include <queue>
+
+#include "base/mac/foundation_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "device/hid/hid_connection.h"
+#include "device/hid/hid_device_info.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace net {
+class IOBuffer;
+}
+
+namespace device {
+
+class HidConnectionMac : public HidConnection {
+ public:
+ explicit HidConnectionMac(HidDeviceInfo device_info);
+
+ virtual void Read(scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) OVERRIDE;
+ virtual void Write(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) OVERRIDE;
+ virtual void GetFeatureReport(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) OVERRIDE;
+ virtual void SendFeatureReport(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) OVERRIDE;
+
+ private:
+ virtual ~HidConnectionMac();
+
+ static void InputReportCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDReportType type,
+ uint32_t report_id,
+ uint8_t* report_bytes,
+ CFIndex report_length);
+ void ProcessReadQueue();
+ void ProcessInputReport(IOHIDReportType type,
+ scoped_refptr<net::IOBufferWithSize> buffer);
+
+ void WriteReport(IOHIDReportType type,
+ uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback);
+
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+
+ base::ScopedCFTypeRef<IOHIDDeviceRef> device_;
+ scoped_ptr<uint8_t, base::FreeDeleter> inbound_buffer_;
+
+ std::queue<PendingHidReport> pending_reports_;
+ std::queue<PendingHidRead> pending_reads_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidConnectionMac);
+};
+
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_CONNECTION_MAC_H_
diff --git a/chromium/device/hid/hid_connection_unittest.cc b/chromium/device/hid/hid_connection_unittest.cc
new file mode 100644
index 00000000000..27254817715
--- /dev/null
+++ b/chromium/device/hid/hid_connection_unittest.cc
@@ -0,0 +1,128 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "device/hid/hid_connection.h"
+#include "device/hid/hid_service.h"
+#include "net/base/io_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+namespace {
+
+using net::IOBufferWithSize;
+
+const int kUSBLUFADemoVID = 0x03eb;
+const int kUSBLUFADemoPID = 0x204f;
+const uint64_t kReport = 0x0903a65d030f8ec9ULL;
+
+int g_read_times = 0;
+void Read(scoped_refptr<HidConnection> conn);
+
+void OnRead(scoped_refptr<HidConnection> conn,
+ scoped_refptr<IOBufferWithSize> buffer,
+ bool success,
+ size_t bytes) {
+ EXPECT_TRUE(success);
+ if (success) {
+ g_read_times++;
+ EXPECT_EQ(8U, bytes);
+ if (bytes == 8) {
+ uint64_t* data = reinterpret_cast<uint64_t*>(buffer->data());
+ EXPECT_EQ(kReport, *data);
+ } else {
+ base::MessageLoop::current()->Quit();
+ }
+ } else {
+ LOG(ERROR) << "~";
+ g_read_times++;
+ }
+
+ if (g_read_times < 3){
+ base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(Read, conn));
+ } else {
+ base::MessageLoop::current()->Quit();
+ }
+}
+
+void Read(scoped_refptr<HidConnection> conn) {
+ scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(8));
+ conn->Read(buffer, base::Bind(OnRead, conn, buffer));
+}
+
+void OnWriteNormal(bool success,
+ size_t bytes) {
+ ASSERT_TRUE(success);
+ base::MessageLoop::current()->Quit();
+}
+
+void WriteNormal(scoped_refptr<HidConnection> conn) {
+ scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(8));
+ *(int64_t*)buffer->data() = kReport;
+
+ conn->Write(0, buffer, base::Bind(OnWriteNormal));
+}
+
+} // namespace
+
+class HidConnectionTest : public testing::Test {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ message_loop_.reset(new base::MessageLoopForIO());
+ service_.reset(HidService::CreateInstance());
+ ASSERT_TRUE(service_);
+
+ std::vector<HidDeviceInfo> devices;
+ service_->GetDevices(&devices);
+ device_id_ = kInvalidHidDeviceId;
+ for (std::vector<HidDeviceInfo>::iterator it = devices.begin();
+ it != devices.end();
+ ++it) {
+ if (it->vendor_id == kUSBLUFADemoVID &&
+ it->product_id == kUSBLUFADemoPID) {
+ device_id_ = it->device_id;
+ return;
+ }
+ }
+ }
+
+ virtual void TearDown() OVERRIDE {
+ service_.reset(NULL);
+ message_loop_.reset(NULL);
+ }
+
+ HidDeviceId device_id_;
+ scoped_ptr<base::MessageLoopForIO> message_loop_;
+ scoped_ptr<HidService> service_;
+};
+
+TEST_F(HidConnectionTest, Create) {
+ scoped_refptr<HidConnection> connection = service_->Connect(device_id_);
+ ASSERT_TRUE(connection || device_id_ == kInvalidHidDeviceId);
+}
+
+TEST_F(HidConnectionTest, Read) {
+ scoped_refptr<HidConnection> connection = service_->Connect(device_id_);
+ if (connection) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(Read, connection));
+ message_loop_->Run();
+ }
+}
+
+TEST_F(HidConnectionTest, Write) {
+ scoped_refptr<HidConnection> connection = service_->Connect(device_id_);
+
+ if (connection) {
+ message_loop_->PostTask(FROM_HERE, base::Bind(WriteNormal, connection));
+ message_loop_->Run();
+ }
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_connection_win.cc b/chromium/device/hid/hid_connection_win.cc
new file mode 100644
index 00000000000..17448f07182
--- /dev/null
+++ b/chromium/device/hid/hid_connection_win.cc
@@ -0,0 +1,303 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_connection_win.h"
+
+#include <cstring>
+
+#include "base/files/file.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/win/object_watcher.h"
+#include "base/win/scoped_handle.h"
+#include "device/hid/hid_service.h"
+#include "device/hid/hid_service_win.h"
+
+#define INITGUID
+
+#include <windows.h>
+#include <hidclass.h>
+
+extern "C" {
+#include <hidsdi.h>
+}
+
+#include <setupapi.h>
+#include <winioctl.h>
+
+namespace device {
+
+struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>,
+ public base::win::ObjectWatcher::Delegate,
+ public base::MessageLoop::DestructionObserver {
+ PendingHidTransfer(scoped_refptr<HidConnectionWin> connection,
+ scoped_refptr<net::IOBufferWithSize> target_buffer,
+ scoped_refptr<net::IOBufferWithSize> receive_buffer,
+ HidConnection::IOCallback callback);
+
+ void TakeResultFromWindowsAPI(BOOL result);
+
+ OVERLAPPED* GetOverlapped() { return &overlapped_; }
+
+ // Implements base::win::ObjectWatcher::Delegate.
+ virtual void OnObjectSignaled(HANDLE object) OVERRIDE;
+
+ // Implements base::MessageLoop::DestructionObserver
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
+
+ scoped_refptr<HidConnectionWin> connection_;
+ scoped_refptr<net::IOBufferWithSize> target_buffer_;
+ scoped_refptr<net::IOBufferWithSize> receive_buffer_;
+ HidConnection::IOCallback callback_;
+ OVERLAPPED overlapped_;
+ base::win::ScopedHandle event_;
+ base::win::ObjectWatcher watcher_;
+
+ private:
+ friend class base::RefCounted<PendingHidTransfer>;
+
+ virtual ~PendingHidTransfer();
+
+ DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer);
+};
+
+PendingHidTransfer::PendingHidTransfer(
+ scoped_refptr<HidConnectionWin> connection,
+ scoped_refptr<net::IOBufferWithSize> target_buffer,
+ scoped_refptr<net::IOBufferWithSize> receive_buffer,
+ HidConnection::IOCallback callback)
+ : connection_(connection),
+ target_buffer_(target_buffer),
+ receive_buffer_(receive_buffer),
+ callback_(callback),
+ event_(CreateEvent(NULL, FALSE, FALSE, NULL)) {
+ memset(&overlapped_, 0, sizeof(OVERLAPPED));
+ overlapped_.hEvent = event_.Get();
+}
+
+PendingHidTransfer::~PendingHidTransfer() {
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+}
+
+void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) {
+ if (result || GetLastError() != ERROR_IO_PENDING) {
+ connection_->OnTransferFinished(this);
+ } else {
+ base::MessageLoop::current()->AddDestructionObserver(this);
+ AddRef();
+ watcher_.StartWatching(event_.Get(), this);
+ }
+}
+
+void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) {
+ connection_->OnTransferFinished(this);
+ Release();
+}
+
+void PendingHidTransfer::WillDestroyCurrentMessageLoop() {
+ watcher_.StopWatching();
+ connection_->OnTransferCanceled(this);
+}
+
+HidConnectionWin::HidConnectionWin(const HidDeviceInfo& device_info)
+ : HidConnection(device_info) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ file_.Set(CreateFileA(device_info.device_id.c_str(),
+ GENERIC_WRITE | GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ NULL));
+
+ if (!file_.IsValid() &&
+ GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) {
+ file_.Set(CreateFileA(device_info.device_id.c_str(),
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ NULL));
+ }
+}
+
+bool HidConnectionWin::available() const {
+ return file_.IsValid();
+}
+
+HidConnectionWin::~HidConnectionWin() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ CancelIo(file_.Get());
+}
+
+void HidConnectionWin::Read(scoped_refptr<net::IOBufferWithSize> buffer,
+ const HidConnection::IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (device_info().input_report_size == 0) {
+ // The device does not support input reports.
+ callback.Run(false, 0);
+ return;
+ }
+
+ // This fairly awkward logic is correct: If Windows does not expect a device
+ // to supply a report ID in its input reports, it requires the buffer to be
+ // 1 byte larger than what the device actually sends.
+ int receive_buffer_size = device_info().input_report_size;
+ int expected_buffer_size = receive_buffer_size;
+ if (!device_info().has_report_id)
+ expected_buffer_size -= 1;
+
+ if (buffer->size() < expected_buffer_size) {
+ callback.Run(false, 0);
+ return;
+ }
+
+ scoped_refptr<net::IOBufferWithSize> receive_buffer(buffer);
+ if (receive_buffer_size != expected_buffer_size)
+ receive_buffer = new net::IOBufferWithSize(receive_buffer_size);
+ scoped_refptr<PendingHidTransfer> transfer(
+ new PendingHidTransfer(this, buffer, receive_buffer, callback));
+ transfers_.insert(transfer);
+ transfer->TakeResultFromWindowsAPI(
+ ReadFile(file_.Get(),
+ receive_buffer->data(),
+ static_cast<DWORD>(receive_buffer->size()),
+ NULL,
+ transfer->GetOverlapped()));
+}
+
+void HidConnectionWin::Write(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const HidConnection::IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (device_info().output_report_size == 0) {
+ // The device does not support output reports.
+ callback.Run(false, 0);
+ return;
+ }
+
+ // The Windows API always wants either a report ID (if supported) or
+ // zero at the front of every output report.
+ scoped_refptr<net::IOBufferWithSize> output_buffer(buffer);
+ output_buffer = new net::IOBufferWithSize(buffer->size() + 1);
+ output_buffer->data()[0] = report_id;
+ memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
+
+ scoped_refptr<PendingHidTransfer> transfer(
+ new PendingHidTransfer(this, output_buffer, NULL, callback));
+ transfers_.insert(transfer);
+ transfer->TakeResultFromWindowsAPI(
+ WriteFile(file_.Get(),
+ output_buffer->data(),
+ static_cast<DWORD>(output_buffer->size()),
+ NULL,
+ transfer->GetOverlapped()));
+}
+
+void HidConnectionWin::GetFeatureReport(
+ uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (device_info().feature_report_size == 0) {
+ // The device does not support feature reports.
+ callback.Run(false, 0);
+ return;
+ }
+
+ int receive_buffer_size = device_info().feature_report_size;
+ int expected_buffer_size = receive_buffer_size;
+ if (!device_info().has_report_id)
+ expected_buffer_size -= 1;
+ if (buffer->size() < expected_buffer_size) {
+ callback.Run(false, 0);
+ return;
+ }
+
+ scoped_refptr<net::IOBufferWithSize> receive_buffer(buffer);
+ if (receive_buffer_size != expected_buffer_size)
+ receive_buffer = new net::IOBufferWithSize(receive_buffer_size);
+
+ // The first byte of the destination buffer is the report ID being requested.
+ receive_buffer->data()[0] = report_id;
+ scoped_refptr<PendingHidTransfer> transfer(
+ new PendingHidTransfer(this, buffer, receive_buffer, callback));
+ transfers_.insert(transfer);
+ transfer->TakeResultFromWindowsAPI(
+ DeviceIoControl(file_.Get(),
+ IOCTL_HID_GET_FEATURE,
+ NULL,
+ 0,
+ receive_buffer->data(),
+ static_cast<DWORD>(receive_buffer->size()),
+ NULL,
+ transfer->GetOverlapped()));
+}
+
+void HidConnectionWin::SendFeatureReport(
+ uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (device_info().feature_report_size == 0) {
+ // The device does not support feature reports.
+ callback.Run(false, 0);
+ return;
+ }
+
+ // The Windows API always wants either a report ID (if supported) or
+ // zero at the front of every output report.
+ scoped_refptr<net::IOBufferWithSize> output_buffer(buffer);
+ output_buffer = new net::IOBufferWithSize(buffer->size() + 1);
+ output_buffer->data()[0] = report_id;
+ memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
+
+ scoped_refptr<PendingHidTransfer> transfer(
+ new PendingHidTransfer(this, output_buffer, NULL, callback));
+ transfer->TakeResultFromWindowsAPI(
+ DeviceIoControl(file_.Get(),
+ IOCTL_HID_SET_FEATURE,
+ output_buffer->data(),
+ static_cast<DWORD>(output_buffer->size()),
+ NULL,
+ 0,
+ NULL,
+ transfer->GetOverlapped()));
+}
+
+void HidConnectionWin::OnTransferFinished(
+ scoped_refptr<PendingHidTransfer> transfer) {
+ DWORD bytes_transferred;
+ transfers_.erase(transfer);
+ if (GetOverlappedResult(
+ file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
+ if (bytes_transferred == 0)
+ transfer->callback_.Run(true, 0);
+ // If this is an input transfer and the receive buffer is not the same as
+ // the target buffer, we need to copy the receive buffer into the target
+ // buffer, discarding the first byte. This is because the target buffer's
+ // owner is not expecting a report ID but Windows will always provide one.
+ if (transfer->receive_buffer_ &&
+ transfer->receive_buffer_ != transfer->target_buffer_) {
+ // Move one byte forward.
+ --bytes_transferred;
+ memcpy(transfer->target_buffer_->data(),
+ transfer->receive_buffer_->data() + 1,
+ bytes_transferred);
+ }
+ transfer->callback_.Run(true, bytes_transferred);
+ } else {
+ transfer->callback_.Run(false, 0);
+ }
+}
+
+void HidConnectionWin::OnTransferCanceled(
+ scoped_refptr<PendingHidTransfer> transfer) {
+ transfers_.erase(transfer);
+ transfer->callback_.Run(false, 0);
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_connection_win.h b/chromium/device/hid/hid_connection_win.h
new file mode 100644
index 00000000000..263897a7eef
--- /dev/null
+++ b/chromium/device/hid/hid_connection_win.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_CONNECTION_WIN_H_
+#define DEVICE_HID_HID_CONNECTION_WIN_H_
+
+#include <windows.h>
+
+#include <set>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_checker.h"
+#include "device/hid/hid_connection.h"
+#include "device/hid/hid_device_info.h"
+
+namespace device {
+
+struct PendingHidTransfer;
+
+class HidConnectionWin : public HidConnection {
+ public:
+ explicit HidConnectionWin(const HidDeviceInfo& device_info);
+
+ bool available() const;
+
+ virtual void Read(scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) OVERRIDE;
+ virtual void Write(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) OVERRIDE;
+ virtual void GetFeatureReport(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) OVERRIDE;
+ virtual void SendFeatureReport(uint8_t report_id,
+ scoped_refptr<net::IOBufferWithSize> buffer,
+ const IOCallback& callback) OVERRIDE;
+
+ void OnTransferFinished(scoped_refptr<PendingHidTransfer> transfer);
+ void OnTransferCanceled(scoped_refptr<PendingHidTransfer> transfer);
+
+ private:
+ ~HidConnectionWin();
+
+ base::win::ScopedHandle file_;
+ std::set<scoped_refptr<PendingHidTransfer> > transfers_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidConnectionWin);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_CONNECTION_WIN_H_
diff --git a/chromium/device/hid/hid_device_info.cc b/chromium/device/hid/hid_device_info.cc
new file mode 100644
index 00000000000..89be442b90a
--- /dev/null
+++ b/chromium/device/hid/hid_device_info.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_device_info.h"
+
+namespace device {
+
+#if !defined(OS_MACOSX)
+const char kInvalidHidDeviceId[] = "";
+#endif
+
+HidDeviceInfo::HidDeviceInfo()
+ : device_id(kInvalidHidDeviceId),
+ bus_type(kHIDBusTypeUSB),
+ vendor_id(0),
+ product_id(0),
+ input_report_size(0),
+ output_report_size(0),
+ feature_report_size(0),
+ has_report_id(false) {}
+
+HidDeviceInfo::~HidDeviceInfo() {}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_device_info.h b/chromium/device/hid/hid_device_info.h
new file mode 100644
index 00000000000..1b143c22535
--- /dev/null
+++ b/chromium/device/hid/hid_device_info.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_DEVICE_INFO_H_
+#define DEVICE_HID_HID_DEVICE_INFO_H_
+
+#include <string>
+#include <vector>
+
+#include "build/build_config.h"
+#include "device/hid/hid_usage_and_page.h"
+
+#if defined(OS_MACOSX)
+#include <IOKit/hid/IOHIDDevice.h>
+#endif
+
+namespace device {
+
+enum HidBusType {
+ kHIDBusTypeUSB = 0,
+ kHIDBusTypeBluetooth = 1,
+};
+
+#if defined(OS_MACOSX)
+typedef IOHIDDeviceRef HidDeviceId;
+const HidDeviceId kInvalidHidDeviceId = NULL;
+#else
+typedef std::string HidDeviceId;
+extern const char kInvalidHidDeviceId[];
+#endif
+
+struct HidDeviceInfo {
+ HidDeviceInfo();
+ ~HidDeviceInfo();
+
+ HidDeviceId device_id;
+
+ HidBusType bus_type;
+ uint16_t vendor_id;
+ uint16_t product_id;
+
+ int input_report_size;
+ int output_report_size;
+ int feature_report_size;
+ std::vector<HidUsageAndPage> usages;
+ bool has_report_id;
+
+ std::string product_name;
+ std::string serial_number;
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_DEVICE_INFO_H_
diff --git a/chromium/device/hid/hid_report_descriptor.cc b/chromium/device/hid/hid_report_descriptor.cc
new file mode 100644
index 00000000000..f2cb0f4901f
--- /dev/null
+++ b/chromium/device/hid/hid_report_descriptor.cc
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_report_descriptor.h"
+
+#include "base/stl_util.h"
+
+namespace device {
+
+HidReportDescriptor::HidReportDescriptor(const uint8_t* bytes, size_t size) {
+ size_t header_index = 0;
+ HidReportDescriptorItem* item = NULL;
+ while (header_index < size) {
+ item = new HidReportDescriptorItem(&bytes[header_index], item);
+ items_.push_back(linked_ptr<HidReportDescriptorItem>(item));
+ header_index += item->GetSize();
+ }
+}
+
+HidReportDescriptor::~HidReportDescriptor() {}
+
+void HidReportDescriptor::GetTopLevelCollections(
+ std::vector<HidUsageAndPage>* topLevelCollections) {
+ DCHECK(topLevelCollections);
+ STLClearObject(topLevelCollections);
+
+ for (std::vector<linked_ptr<HidReportDescriptorItem> >::const_iterator
+ items_iter = items().begin();
+ items_iter != items().end();
+ ++items_iter) {
+ linked_ptr<HidReportDescriptorItem> item = *items_iter;
+
+ bool isTopLevelCollection =
+ item->tag() == HidReportDescriptorItem::kTagCollection &&
+ item->parent() == NULL;
+
+ if (isTopLevelCollection) {
+ uint16_t collection_usage = 0;
+ HidUsageAndPage::Page collection_usage_page =
+ HidUsageAndPage::kPageUndefined;
+
+ HidReportDescriptorItem* usage = item->previous();
+ if (usage && usage->tag() == HidReportDescriptorItem::kTagUsage) {
+ collection_usage = usage->GetShortData();
+ }
+
+ HidReportDescriptorItem* usage_page = usage->previous();
+ if (usage_page &&
+ usage_page->tag() == HidReportDescriptorItem::kTagUsagePage) {
+ collection_usage_page =
+ (HidUsageAndPage::Page)usage_page->GetShortData();
+ }
+
+ topLevelCollections->push_back(
+ HidUsageAndPage(collection_usage, collection_usage_page));
+ }
+ }
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_report_descriptor.h b/chromium/device/hid/hid_report_descriptor.h
new file mode 100644
index 00000000000..fa67fa43c29
--- /dev/null
+++ b/chromium/device/hid/hid_report_descriptor.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_REPORT_DESCRIPTOR_H_
+#define DEVICE_HID_HID_REPORT_DESCRIPTOR_H_
+
+#include <vector>
+
+#include "base/memory/linked_ptr.h"
+#include "device/hid/hid_report_descriptor_item.h"
+#include "device/hid/hid_usage_and_page.h"
+
+namespace device {
+
+// HID report descriptor.
+// See section 6.2.2 of HID specifications (v1.11).
+class HidReportDescriptor {
+
+ public:
+ HidReportDescriptor(const uint8_t* bytes, size_t size);
+ ~HidReportDescriptor();
+
+ const std::vector<linked_ptr<HidReportDescriptorItem> >& items() const {
+ return items_;
+ }
+
+ // Returns HID usages of top-level collections present in the descriptor.
+ void GetTopLevelCollections(
+ std::vector<HidUsageAndPage>* topLevelCollections);
+
+ private:
+ std::vector<linked_ptr<HidReportDescriptorItem> > items_;
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_REPORT_DESCRIPTOR_H_
diff --git a/chromium/device/hid/hid_report_descriptor_item.cc b/chromium/device/hid/hid_report_descriptor_item.cc
new file mode 100644
index 00000000000..bdd03ce0ebd
--- /dev/null
+++ b/chromium/device/hid/hid_report_descriptor_item.cc
@@ -0,0 +1,112 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_report_descriptor_item.h"
+
+#include <stdlib.h>
+
+#include "base/logging.h"
+#include "device/hid/hid_usage_and_page.h"
+
+namespace device {
+
+namespace {
+
+struct Header {
+ uint8_t size : 2;
+ uint8_t type : 2;
+ uint8_t tag : 4;
+};
+
+} // namespace
+
+HidReportDescriptorItem::HidReportDescriptorItem(
+ const uint8_t* bytes,
+ HidReportDescriptorItem* previous)
+ : previous_(previous), next_(NULL), parent_(NULL), shortData_(0) {
+ Header* header = (Header*)&bytes[0];
+ tag_ = (Tag)(header->tag << 2 | header->type);
+
+ if (IsLong()) {
+ // In a long item, payload size is the second byte.
+ payload_size_ = bytes[1];
+ } else {
+ payload_size_ = header->size;
+ DCHECK(payload_size_ <= sizeof(shortData_));
+ memcpy(&shortData_, &bytes[GetHeaderSize()], payload_size());
+ }
+
+ if (previous) {
+ DCHECK(!previous->next_);
+ previous->next_ = this;
+ switch (previous->tag()) {
+ case kTagCollection:
+ parent_ = previous;
+ break;
+ default:
+ break;
+ }
+ if (!parent_) {
+ switch (tag()) {
+ case kTagEndCollection:
+ if (previous->parent()) {
+ parent_ = previous->parent()->parent();
+ }
+ break;
+ default:
+ parent_ = previous->parent();
+ break;
+ }
+ }
+ }
+}
+
+size_t HidReportDescriptorItem::GetDepth() const {
+ HidReportDescriptorItem* parent_item = parent();
+ if (parent_item)
+ return parent_item->GetDepth() + 1;
+ return 0;
+}
+
+bool HidReportDescriptorItem::IsLong() const { return tag() == kTagLong; }
+
+size_t HidReportDescriptorItem::GetHeaderSize() const {
+ return IsLong() ? 3 : 1;
+}
+
+size_t HidReportDescriptorItem::GetSize() const {
+ return GetHeaderSize() + payload_size();
+}
+
+uint32_t HidReportDescriptorItem::GetShortData() const {
+ DCHECK(!IsLong());
+ return shortData_;
+}
+
+HidReportDescriptorItem::CollectionType
+HidReportDescriptorItem::GetCollectionTypeFromValue(uint32_t value) {
+ switch (value) {
+ case 0x00:
+ return kCollectionTypePhysical;
+ case 0x01:
+ return kCollectionTypePhysical;
+ case 0x02:
+ return kCollectionTypePhysical;
+ case 0x03:
+ return kCollectionTypePhysical;
+ case 0x04:
+ return kCollectionTypePhysical;
+ case 0x05:
+ return kCollectionTypePhysical;
+ case 0x06:
+ return kCollectionTypePhysical;
+ default:
+ break;
+ }
+ if (0x80 < value && value < 0xFF)
+ return kCollectionTypeVendor;
+ return kCollectionTypeReserved;
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_report_descriptor_item.h b/chromium/device/hid/hid_report_descriptor_item.h
new file mode 100644
index 00000000000..d3392330709
--- /dev/null
+++ b/chromium/device/hid/hid_report_descriptor_item.h
@@ -0,0 +1,183 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_REPORT_DESCRIPTOR_ITEM_H_
+#define DEVICE_HID_HID_REPORT_DESCRIPTOR_ITEM_H_
+
+#include "base/basictypes.h"
+
+namespace device {
+
+// An element of a HID report descriptor.
+class HidReportDescriptorItem {
+ private:
+ friend class HidReportDescriptor;
+
+ enum Type {
+ kTypeMain = 0,
+ kTypeGlobal = 1,
+ kTypeLocal = 2,
+ kTypeReserved = 3
+ };
+
+ enum MainTag {
+ kMainTagDefault = 0x00, // 0000
+ kMainTagInput = 0x08, // 1000
+ kMainTagOutput = 0x09, // 1001
+ kMainTagFeature = 0x0B, // 1011
+ kMainTagCollection = 0x0A, // 1010
+ kMainTagEndCollection = 0x0C // 1100
+ };
+
+ enum GlobalTag {
+ kGlobalTagUsagePage = 0x00, // 0000
+ kGlobalTagLogicalMinimum = 0x01, // 0001
+ kGlobalTagLogicalMaximum = 0x02, // 0010
+ kGlobalTagPhysicalMinimum = 0x03, // 0011
+ kGlobalTagPhysicalMaximum = 0x04, // 0100
+ kGlobalTagUnitExponent = 0x05, // 0101
+ kGlobalTagUnit = 0x06, // 0110
+ kGlobalTagReportSize = 0x07, // 0111
+ kGlobalTagReportId = 0x08, // 1000
+ kGlobalTagReportCount = 0x09, // 1001
+ kGlobalTagPush = 0x0A, // 1010
+ kGlobalTagPop = 0x0B // 1011
+ };
+
+ enum LocalTag {
+ kLocalTagUsage = 0x00, // 0000
+ kLocalTagUsageMinimum = 0x01, // 0001
+ kLocalTagUsageMaximum = 0x02, // 0010
+ kLocalTagDesignatorIndex = 0x03, // 0011
+ kLocalTagDesignatorMinimum = 0x04, // 0100
+ kLocalTagDesignatorMaximum = 0x05, // 0101
+ kLocalTagStringIndex = 0x07, // 0111
+ kLocalTagStringMinimum = 0x08, // 1000
+ kLocalTagStringMaximum = 0x09, // 1001
+ kLocalTagDelimiter = 0x0A // 1010
+ };
+
+ enum ReservedTag {
+ kReservedTagLong = 0xF // 1111
+ };
+
+ public:
+ enum Tag {
+ kTagDefault = kMainTagDefault << 2 | kTypeMain,
+ kTagInput = kMainTagInput << 2 | kTypeMain,
+ kTagOutput = kMainTagOutput << 2 | kTypeMain,
+ kTagFeature = kMainTagFeature << 2 | kTypeMain,
+ kTagCollection = kMainTagCollection << 2 | kTypeMain,
+ kTagEndCollection = kMainTagEndCollection << 2 | kTypeMain,
+ kTagUsagePage = kGlobalTagUsagePage << 2 | kTypeGlobal,
+ kTagLogicalMinimum = kGlobalTagLogicalMinimum << 2 | kTypeGlobal,
+ kTagLogicalMaximum = kGlobalTagLogicalMaximum << 2 | kTypeGlobal,
+ kTagPhysicalMinimum = kGlobalTagPhysicalMinimum << 2 | kTypeGlobal,
+ kTagPhysicalMaximum = kGlobalTagPhysicalMaximum << 2 | kTypeGlobal,
+ kTagUnitExponent = kGlobalTagUnitExponent << 2 | kTypeGlobal,
+ kTagUnit = kGlobalTagUnit << 2 | kTypeGlobal,
+ kTagReportSize = kGlobalTagReportSize << 2 | kTypeGlobal,
+ kTagReportId = kGlobalTagReportId << 2 | kTypeGlobal,
+ kTagReportCount = kGlobalTagReportCount << 2 | kTypeGlobal,
+ kTagPush = kGlobalTagPush << 2 | kTypeGlobal,
+ kTagPop = kGlobalTagPop << 2 | kTypeGlobal,
+ kTagUsage = kLocalTagUsage << 2 | kTypeLocal,
+ kTagUsageMinimum = kLocalTagUsageMinimum << 2 | kTypeLocal,
+ kTagUsageMaximum = kLocalTagUsageMaximum << 2 | kTypeLocal,
+ kTagDesignatorIndex = kLocalTagDesignatorIndex << 2 | kTypeLocal,
+ kTagDesignatorMinimum = kLocalTagDesignatorMinimum << 2 | kTypeLocal,
+ kTagDesignatorMaximum = kLocalTagDesignatorMaximum << 2 | kTypeLocal,
+ kTagStringIndex = kLocalTagStringIndex << 2 | kTypeLocal,
+ kTagStringMinimum = kLocalTagStringMinimum << 2 | kTypeLocal,
+ kTagStringMaximum = kLocalTagStringMaximum << 2 | kTypeLocal,
+ kTagDelimiter = kLocalTagDelimiter << 2 | kTypeLocal,
+ kTagLong = kReservedTagLong << 2 | kTypeReserved
+ };
+
+ // HID Input/Output/Feature report information.
+ // Can be retrieved from GetShortData()
+ // when item.tag() == HidReportDescriptorItem::kTagInput
+ // or HidReportDescriptorItem::kTagOutput
+ // or HidReportDescriptorItem::kTagFeature
+ struct ReportInfo {
+ uint8_t data_or_constant : 1;
+ uint8_t array_or_variable : 1;
+ uint8_t absolute_or_relative : 1;
+ uint8_t wrap : 1;
+ uint8_t linear : 1;
+ uint8_t preferred : 1;
+ uint8_t null : 1;
+ uint8_t reserved_1 : 1;
+ uint8_t bit_field_or_buffer : 1;
+ uint8_t reserved_2 : 1;
+ };
+
+ // HID collection type.
+ // Can be retrieved from GetShortData()
+ // when item.tag() == HidReportDescriptorItem::kTagCollection
+ enum CollectionType {
+ kCollectionTypePhysical,
+ kCollectionTypeApplication,
+ kCollectionTypeLogical,
+ kCollectionTypeReport,
+ kCollectionTypeNamedArray,
+ kCollectionTypeUsageSwitch,
+ kCollectionTypeUsageModifier,
+ kCollectionTypeReserved,
+ kCollectionTypeVendor
+ };
+
+ private:
+ HidReportDescriptorItem(const uint8_t* bytes,
+ HidReportDescriptorItem* previous);
+
+ public:
+ ~HidReportDescriptorItem() {}
+
+ // Previous element in report descriptor.
+ // Owned by descriptor instance.
+ HidReportDescriptorItem* previous() const {
+ return previous_;
+ };
+ // Next element in report descriptor.
+ // Owned by descriptor instance.
+ HidReportDescriptorItem* next() const {
+ return next_;
+ };
+ // Parent element in report descriptor.
+ // Owned by descriptor instance.
+ // Can be NULL.
+ HidReportDescriptorItem* parent() const {
+ return parent_;
+ };
+ // Level in Parent-Children relationship tree.
+ // 0 for top-level items (parent()==NULL).
+ // 1 if parent() is top-level.
+ // 2 if parent() has a top-level parent. Etc.
+ size_t GetDepth() const;
+ Tag tag() const { return tag_; }
+ // Returns true for a long item, false otherwise.
+ bool IsLong() const;
+ // Raw data of a short item.
+ // Not valid for a long item.
+ uint32_t GetShortData() const;
+
+ static CollectionType GetCollectionTypeFromValue(uint32_t value);
+
+ private:
+ size_t GetHeaderSize() const;
+ size_t payload_size() const { return payload_size_; }
+ size_t GetSize() const;
+
+ HidReportDescriptorItem* previous_;
+ HidReportDescriptorItem* next_;
+ HidReportDescriptorItem* parent_;
+ Tag tag_;
+ uint32_t shortData_;
+ size_t payload_size_;
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_REPORT_DESCRIPTOR_ITEM_H_
diff --git a/chromium/device/hid/hid_report_descriptor_unittest.cc b/chromium/device/hid/hid_report_descriptor_unittest.cc
new file mode 100644
index 00000000000..0d258891081
--- /dev/null
+++ b/chromium/device/hid/hid_report_descriptor_unittest.cc
@@ -0,0 +1,613 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sstream>
+
+#include "device/hid/hid_report_descriptor.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using namespace testing;
+
+namespace device {
+
+namespace {
+
+std::ostream& operator<<(std::ostream& os,
+ const HidUsageAndPage::Page& usage_page) {
+ switch (usage_page) {
+ case HidUsageAndPage::kPageUndefined:
+ os << "Undefined";
+ break;
+ case HidUsageAndPage::kPageGenericDesktop:
+ os << "Generic Desktop";
+ break;
+ case HidUsageAndPage::kPageSimulation:
+ os << "Simulation";
+ break;
+ case HidUsageAndPage::kPageVirtualReality:
+ os << "Virtual Reality";
+ break;
+ case HidUsageAndPage::kPageSport:
+ os << "Sport";
+ break;
+ case HidUsageAndPage::kPageGame:
+ os << "Game";
+ break;
+ case HidUsageAndPage::kPageKeyboard:
+ os << "Keyboard";
+ break;
+ case HidUsageAndPage::kPageLed:
+ os << "Led";
+ break;
+ case HidUsageAndPage::kPageButton:
+ os << "Button";
+ break;
+ case HidUsageAndPage::kPageOrdinal:
+ os << "Ordinal";
+ break;
+ case HidUsageAndPage::kPageTelephony:
+ os << "Telephony";
+ break;
+ case HidUsageAndPage::kPageConsumer:
+ os << "Consumer";
+ break;
+ case HidUsageAndPage::kPageDigitizer:
+ os << "Digitizer";
+ break;
+ case HidUsageAndPage::kPagePidPage:
+ os << "Pid Page";
+ break;
+ case HidUsageAndPage::kPageUnicode:
+ os << "Unicode";
+ break;
+ case HidUsageAndPage::kPageAlphanumericDisplay:
+ os << "Alphanumeric Display";
+ break;
+ case HidUsageAndPage::kPageMedicalInstruments:
+ os << "Medical Instruments";
+ break;
+ case HidUsageAndPage::kPageMonitor0:
+ os << "Monitor 0";
+ break;
+ case HidUsageAndPage::kPageMonitor1:
+ os << "Monitor 1";
+ break;
+ case HidUsageAndPage::kPageMonitor2:
+ os << "Monitor 2";
+ break;
+ case HidUsageAndPage::kPageMonitor3:
+ os << "Monitor 3";
+ break;
+ case HidUsageAndPage::kPagePower0:
+ os << "Power 0";
+ break;
+ case HidUsageAndPage::kPagePower1:
+ os << "Power 1";
+ break;
+ case HidUsageAndPage::kPagePower2:
+ os << "Power 2";
+ break;
+ case HidUsageAndPage::kPagePower3:
+ os << "Power 3";
+ break;
+ case HidUsageAndPage::kPageBarCodeScanner:
+ os << "Bar Code Scanner";
+ break;
+ case HidUsageAndPage::kPageScale:
+ os << "Scale";
+ break;
+ case HidUsageAndPage::kPageMagneticStripeReader:
+ os << "Magnetic Stripe Reader";
+ break;
+ case HidUsageAndPage::kPageReservedPointOfSale:
+ os << "Reserved Point Of Sale";
+ break;
+ case HidUsageAndPage::kPageCameraControl:
+ os << "Camera Control";
+ break;
+ case HidUsageAndPage::kPageArcade:
+ os << "Arcade";
+ break;
+ case HidUsageAndPage::kPageVendor:
+ os << "Vendor";
+ break;
+ case HidUsageAndPage::kPageMediaCenter:
+ os << "Media Center";
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os,
+ const HidUsageAndPage& usage_and_page) {
+ os << "Usage Page: " << usage_and_page.usage_page << ", Usage: "
+ << "0x" << std::hex << std::uppercase << usage_and_page.usage;
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os,
+ const HidReportDescriptorItem::Tag& tag) {
+ switch (tag) {
+ case HidReportDescriptorItem::kTagDefault:
+ os << "Default";
+ break;
+ case HidReportDescriptorItem::kTagInput:
+ os << "Input";
+ break;
+ case HidReportDescriptorItem::kTagOutput:
+ os << "Output";
+ break;
+ case HidReportDescriptorItem::kTagFeature:
+ os << "Feature";
+ break;
+ case HidReportDescriptorItem::kTagCollection:
+ os << "Collection";
+ break;
+ case HidReportDescriptorItem::kTagEndCollection:
+ os << "End Collection";
+ break;
+ case HidReportDescriptorItem::kTagUsagePage:
+ os << "Usage Page";
+ break;
+ case HidReportDescriptorItem::kTagLogicalMinimum:
+ os << "Logical Minimum";
+ break;
+ case HidReportDescriptorItem::kTagLogicalMaximum:
+ os << "Logical Maximum";
+ break;
+ case HidReportDescriptorItem::kTagPhysicalMinimum:
+ os << "Physical Minimum";
+ break;
+ case HidReportDescriptorItem::kTagPhysicalMaximum:
+ os << "Physical Maximum";
+ break;
+ case HidReportDescriptorItem::kTagUnitExponent:
+ os << "Unit Exponent";
+ break;
+ case HidReportDescriptorItem::kTagUnit:
+ os << "Unit";
+ break;
+ case HidReportDescriptorItem::kTagReportSize:
+ os << "Report Size";
+ break;
+ case HidReportDescriptorItem::kTagReportId:
+ os << "Report ID";
+ break;
+ case HidReportDescriptorItem::kTagReportCount:
+ os << "Report Count";
+ break;
+ case HidReportDescriptorItem::kTagPush:
+ os << "Push";
+ break;
+ case HidReportDescriptorItem::kTagPop:
+ os << "Pop";
+ break;
+ case HidReportDescriptorItem::kTagUsage:
+ os << "Usage";
+ break;
+ case HidReportDescriptorItem::kTagUsageMinimum:
+ os << "Usage Minimum";
+ break;
+ case HidReportDescriptorItem::kTagUsageMaximum:
+ os << "Usage Maximum";
+ break;
+ case HidReportDescriptorItem::kTagDesignatorIndex:
+ os << "Designator Index";
+ break;
+ case HidReportDescriptorItem::kTagDesignatorMinimum:
+ os << "Designator Minimum";
+ break;
+ case HidReportDescriptorItem::kTagDesignatorMaximum:
+ os << "Designator Maximum";
+ break;
+ case HidReportDescriptorItem::kTagStringIndex:
+ os << "String Index";
+ break;
+ case HidReportDescriptorItem::kTagStringMinimum:
+ os << "String Minimum";
+ break;
+ case HidReportDescriptorItem::kTagStringMaximum:
+ os << "String Maximum";
+ break;
+ case HidReportDescriptorItem::kTagDelimiter:
+ os << "Delimeter";
+ break;
+ case HidReportDescriptorItem::kTagLong:
+ os << "Long";
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os,
+ const HidReportDescriptorItem::ReportInfo& data) {
+ if (data.data_or_constant)
+ os << "Con";
+ else
+ os << "Dat";
+ if (data.array_or_variable)
+ os << "|Arr";
+ else
+ os << "|Var";
+ if (data.absolute_or_relative)
+ os << "|Abs";
+ else
+ os << "|Rel";
+ if (data.wrap)
+ os << "|Wrp";
+ else
+ os << "|NoWrp";
+ if (data.linear)
+ os << "|NoLin";
+ else
+ os << "|Lin";
+ if (data.preferred)
+ os << "|NoPrf";
+ else
+ os << "|Prf";
+ if (data.null)
+ os << "|Null";
+ else
+ os << "|NoNull";
+ if (data.bit_field_or_buffer)
+ os << "|Buff";
+ else
+ os << "|BitF";
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os,
+ const HidReportDescriptorItem::CollectionType& type) {
+ switch (type) {
+ case HidReportDescriptorItem::kCollectionTypePhysical:
+ os << "Physical";
+ break;
+ case HidReportDescriptorItem::kCollectionTypeApplication:
+ os << "Application";
+ break;
+ case HidReportDescriptorItem::kCollectionTypeLogical:
+ os << "Logical";
+ break;
+ case HidReportDescriptorItem::kCollectionTypeReport:
+ os << "Report";
+ break;
+ case HidReportDescriptorItem::kCollectionTypeNamedArray:
+ os << "Named Array";
+ break;
+ case HidReportDescriptorItem::kCollectionTypeUsageSwitch:
+ os << "Usage Switch";
+ break;
+ case HidReportDescriptorItem::kCollectionTypeUsageModifier:
+ os << "Usage Modifier";
+ break;
+ case HidReportDescriptorItem::kCollectionTypeReserved:
+ os << "Reserved";
+ break;
+ case HidReportDescriptorItem::kCollectionTypeVendor:
+ os << "Vendor";
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os,
+ const HidReportDescriptorItem& item) {
+ HidReportDescriptorItem::Tag item_tag = item.tag();
+ uint32_t data = item.GetShortData();
+
+ std::ostringstream sstr;
+ sstr << item_tag;
+ sstr << " (";
+
+ long pos = sstr.tellp();
+ switch (item_tag) {
+ case HidReportDescriptorItem::kTagDefault:
+ case HidReportDescriptorItem::kTagEndCollection:
+ case HidReportDescriptorItem::kTagPush:
+ case HidReportDescriptorItem::kTagPop:
+ case HidReportDescriptorItem::kTagLong:
+ break;
+
+ case HidReportDescriptorItem::kTagCollection:
+ sstr << HidReportDescriptorItem::GetCollectionTypeFromValue(data);
+ break;
+
+ case HidReportDescriptorItem::kTagInput:
+ case HidReportDescriptorItem::kTagOutput:
+ case HidReportDescriptorItem::kTagFeature:
+ sstr << (HidReportDescriptorItem::ReportInfo&)data;
+ break;
+
+ case HidReportDescriptorItem::kTagUsagePage:
+ sstr << (HidUsageAndPage::Page)data;
+ break;
+
+ case HidReportDescriptorItem::kTagUsage:
+ case HidReportDescriptorItem::kTagReportId:
+ sstr << "0x" << std::hex << std::uppercase << data;
+ break;
+
+ default:
+ sstr << data;
+ break;
+ }
+ if (pos == sstr.tellp()) {
+ std::string str = sstr.str();
+ str.erase(str.end() - 2, str.end());
+ os << str;
+ } else {
+ os << sstr.str() << ")";
+ }
+
+ return os;
+}
+
+const char kIndentStep[] = " ";
+
+std::ostream& operator<<(std::ostream& os,
+ const HidReportDescriptor& descriptor) {
+ for (std::vector<linked_ptr<HidReportDescriptorItem> >::const_iterator
+ items_iter = descriptor.items().begin();
+ items_iter != descriptor.items().end();
+ ++items_iter) {
+ linked_ptr<HidReportDescriptorItem> item = *items_iter;
+ size_t indentLevel = item->GetDepth();
+ for (size_t i = 0; i < indentLevel; i++)
+ os << kIndentStep;
+ os << *item.get() << std::endl;
+ }
+ return os;
+}
+
+// See 'E.6 Report Descriptor (Keyboard)'
+// in HID specifications (v1.11)
+const uint8_t kKeyboard[] = {
+ 0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, 0x29,
+ 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02,
+ 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x05, 0x75, 0x01, 0x05,
+ 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03,
+ 0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05,
+ 0x07, 0x19, 0x00, 0x29, 0x65, 0x81, 0x00, 0xC0};
+
+// See 'E.10 Report Descriptor (Mouse)'
+// in HID specifications (v1.11)
+const uint8_t kMouse[] = {0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1,
+ 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, 0x15, 0x00,
+ 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, 0x81, 0x02, 0x95,
+ 0x01, 0x75, 0x05, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30,
+ 0x09, 0x31, 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95,
+ 0x02, 0x81, 0x06, 0xC0, 0xC0};
+
+const uint8_t kLogitechUnifyingReceiver[] = {
+ 0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x10, 0x75, 0x08,
+ 0x95, 0x06, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x09, 0x01, 0x81, 0x00,
+ 0x09, 0x01, 0x91, 0x00, 0xC0, 0x06, 0x00, 0xFF, 0x09, 0x02, 0xA1,
+ 0x01, 0x85, 0x11, 0x75, 0x08, 0x95, 0x13, 0x15, 0x00, 0x26, 0xFF,
+ 0x00, 0x09, 0x02, 0x81, 0x00, 0x09, 0x02, 0x91, 0x00, 0xC0, 0x06,
+ 0x00, 0xFF, 0x09, 0x04, 0xA1, 0x01, 0x85, 0x20, 0x75, 0x08, 0x95,
+ 0x0E, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x09, 0x41, 0x81, 0x00, 0x09,
+ 0x41, 0x91, 0x00, 0x85, 0x21, 0x95, 0x1F, 0x15, 0x00, 0x26, 0xFF,
+ 0x00, 0x09, 0x42, 0x81, 0x00, 0x09, 0x42, 0x91, 0x00, 0xC0};
+
+} // namespace
+
+class HidReportDescriptorTest : public testing::Test {
+
+ protected:
+ virtual void SetUp() OVERRIDE { descriptor_ = NULL; }
+
+ virtual void TearDown() OVERRIDE {
+ if (descriptor_) {
+ delete descriptor_;
+ }
+ }
+
+ public:
+ void ParseDescriptor(const std::string& expected,
+ const uint8_t* bytes,
+ size_t size) {
+ descriptor_ = new HidReportDescriptor(bytes, size);
+
+ std::stringstream actual;
+ actual << *descriptor_;
+
+ std::cout << "HID report descriptor:" << std::endl;
+ std::cout << actual.str();
+
+ // TODO(jracle@logitech.com): refactor string comparison in favor of
+ // testing individual fields.
+ ASSERT_EQ(expected, actual.str());
+ }
+
+ void GetTopLevelCollections(const std::vector<HidUsageAndPage>& expected,
+ const uint8_t* bytes,
+ size_t size) {
+ descriptor_ = new HidReportDescriptor(bytes, size);
+
+ std::vector<HidUsageAndPage> actual;
+ descriptor_->GetTopLevelCollections(&actual);
+
+ std::cout << "HID top-level collections:" << std::endl;
+ for (std::vector<HidUsageAndPage>::const_iterator iter = actual.begin();
+ iter != actual.end();
+ ++iter) {
+ std::cout << *iter << std::endl;
+ }
+
+ ASSERT_THAT(actual, ContainerEq(expected));
+ }
+
+ private:
+ HidReportDescriptor* descriptor_;
+};
+
+TEST_F(HidReportDescriptorTest, ParseDescriptor_Keyboard) {
+ const char expected[] = {
+ "Usage Page (Generic Desktop)\n"
+ "Usage (0x6)\n"
+ "Collection (Physical)\n"
+ " Usage Page (Keyboard)\n"
+ " Usage Minimum (224)\n"
+ " Usage Maximum (231)\n"
+ " Logical Minimum (0)\n"
+ " Logical Maximum (1)\n"
+ " Report Size (1)\n"
+ " Report Count (8)\n"
+ " Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ " Report Count (1)\n"
+ " Report Size (8)\n"
+ " Input (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ " Report Count (5)\n"
+ " Report Size (1)\n"
+ " Usage Page (Led)\n"
+ " Usage Minimum (1)\n"
+ " Usage Maximum (5)\n"
+ " Output (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ " Report Count (1)\n"
+ " Report Size (3)\n"
+ " Output (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ " Report Count (6)\n"
+ " Report Size (8)\n"
+ " Logical Minimum (0)\n"
+ " Logical Maximum (101)\n"
+ " Usage Page (Keyboard)\n"
+ " Usage Minimum (0)\n"
+ " Usage Maximum (101)\n"
+ " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ "End Collection\n"};
+
+ ParseDescriptor(std::string(expected), kKeyboard, sizeof(kKeyboard));
+}
+
+TEST_F(HidReportDescriptorTest, TopLevelCollections_Keyboard) {
+ HidUsageAndPage expected[] = {
+ HidUsageAndPage(0x06, HidUsageAndPage::kPageGenericDesktop)};
+
+ GetTopLevelCollections(std::vector<HidUsageAndPage>(
+ expected, expected + ARRAYSIZE_UNSAFE(expected)),
+ kKeyboard,
+ sizeof(kKeyboard));
+}
+
+TEST_F(HidReportDescriptorTest, ParseDescriptor_Mouse) {
+ const char expected[] = {
+ "Usage Page (Generic Desktop)\n"
+ "Usage (0x2)\n"
+ "Collection (Physical)\n"
+ " Usage (0x1)\n"
+ " Collection (Physical)\n"
+ " Usage Page (Button)\n"
+ " Usage Minimum (1)\n"
+ " Usage Maximum (3)\n"
+ " Logical Minimum (0)\n"
+ " Logical Maximum (1)\n"
+ " Report Count (3)\n"
+ " Report Size (1)\n"
+ " Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ " Report Count (1)\n"
+ " Report Size (5)\n"
+ " Input (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ " Usage Page (Generic Desktop)\n"
+ " Usage (0x30)\n"
+ " Usage (0x31)\n"
+ " Logical Minimum (129)\n"
+ " Logical Maximum (127)\n"
+ " Report Size (8)\n"
+ " Report Count (2)\n"
+ " Input (Dat|Arr|Abs|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ " End Collection\n"
+ "End Collection\n"};
+
+ ParseDescriptor(std::string(expected), kMouse, sizeof(kMouse));
+}
+
+TEST_F(HidReportDescriptorTest, TopLevelCollections_Mouse) {
+ HidUsageAndPage expected[] = {
+ HidUsageAndPage(0x02, HidUsageAndPage::kPageGenericDesktop)};
+
+ GetTopLevelCollections(std::vector<HidUsageAndPage>(
+ expected, expected + ARRAYSIZE_UNSAFE(expected)),
+ kMouse,
+ sizeof(kMouse));
+}
+
+TEST_F(HidReportDescriptorTest, ParseDescriptor_LogitechUnifyingReceiver) {
+ const char expected[] = {
+ "Usage Page (Vendor)\n"
+ "Usage (0x1)\n"
+ "Collection (Physical)\n"
+ " Report ID (0x10)\n"
+ " Report Size (8)\n"
+ " Report Count (6)\n"
+ " Logical Minimum (0)\n"
+ " Logical Maximum (255)\n"
+ " Usage (0x1)\n"
+ " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ " Usage (0x1)\n"
+ " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ "End Collection\n"
+ "Usage Page (Vendor)\n"
+ "Usage (0x2)\n"
+ "Collection (Physical)\n"
+ " Report ID (0x11)\n"
+ " Report Size (8)\n"
+ " Report Count (19)\n"
+ " Logical Minimum (0)\n"
+ " Logical Maximum (255)\n"
+ " Usage (0x2)\n"
+ " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ " Usage (0x2)\n"
+ " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ "End Collection\n"
+ "Usage Page (Vendor)\n"
+ "Usage (0x4)\n"
+ "Collection (Physical)\n"
+ " Report ID (0x20)\n"
+ " Report Size (8)\n"
+ " Report Count (14)\n"
+ " Logical Minimum (0)\n"
+ " Logical Maximum (255)\n"
+ " Usage (0x41)\n"
+ " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ " Usage (0x41)\n"
+ " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ " Report ID (0x21)\n"
+ " Report Count (31)\n"
+ " Logical Minimum (0)\n"
+ " Logical Maximum (255)\n"
+ " Usage (0x42)\n"
+ " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ " Usage (0x42)\n"
+ " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
+ "End Collection\n"};
+
+ ParseDescriptor(std::string(expected),
+ kLogitechUnifyingReceiver,
+ sizeof(kLogitechUnifyingReceiver));
+}
+
+TEST_F(HidReportDescriptorTest, TopLevelCollections_LogitechUnifyingReceiver) {
+ HidUsageAndPage expected[] = {
+ HidUsageAndPage(0x01, HidUsageAndPage::kPageVendor),
+ HidUsageAndPage(0x02, HidUsageAndPage::kPageVendor),
+ HidUsageAndPage(0x04, HidUsageAndPage::kPageVendor), };
+
+ GetTopLevelCollections(std::vector<HidUsageAndPage>(
+ expected, expected + ARRAYSIZE_UNSAFE(expected)),
+ kLogitechUnifyingReceiver,
+ sizeof(kLogitechUnifyingReceiver));
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_service.cc b/chromium/device/hid/hid_service.cc
new file mode 100644
index 00000000000..4d24b0093f4
--- /dev/null
+++ b/chromium/device/hid/hid_service.cc
@@ -0,0 +1,99 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_service.h"
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/stl_util.h"
+#include "base/threading/thread_restrictions.h"
+
+#if defined(OS_LINUX) && defined(USE_UDEV)
+#include "device/hid/hid_service_linux.h"
+#elif defined(OS_MACOSX)
+#include "device/hid/hid_service_mac.h"
+#else
+#include "device/hid/hid_service_win.h"
+#endif
+
+namespace device {
+
+namespace {
+
+// The instance will be reset when message loop destroys.
+base::LazyInstance<scoped_ptr<HidService> >::Leaky g_hid_service_ptr =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+void HidService::GetDevices(std::vector<HidDeviceInfo>* devices) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ STLClearObject(devices);
+ for (DeviceMap::iterator it = devices_.begin();
+ it != devices_.end();
+ ++it) {
+ devices->push_back(it->second);
+ }
+}
+
+// Fills in the device info struct of the given device_id.
+bool HidService::GetDeviceInfo(const HidDeviceId& device_id,
+ HidDeviceInfo* info) const {
+ DeviceMap::const_iterator it = devices_.find(device_id);
+ if (it == devices_.end())
+ return false;
+ *info = it->second;
+ return true;
+}
+
+void HidService::WillDestroyCurrentMessageLoop() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ g_hid_service_ptr.Get().reset(NULL);
+}
+
+HidService::HidService() {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ base::MessageLoop::current()->AddDestructionObserver(this);
+}
+
+HidService::~HidService() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+}
+
+HidService* HidService::CreateInstance() {
+#if defined(OS_LINUX) && defined(USE_UDEV)
+ return new HidServiceLinux();
+#elif defined(OS_MACOSX)
+ return new HidServiceMac();
+#elif defined(OS_WIN)
+ return new HidServiceWin();
+#else
+ return NULL;
+#endif
+}
+
+void HidService::AddDevice(const HidDeviceInfo& info) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!ContainsKey(devices_, info.device_id)) {
+ devices_[info.device_id] = info;
+ }
+}
+
+void HidService::RemoveDevice(const HidDeviceId& device_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DeviceMap::iterator it = devices_.find(device_id);
+ if (it != devices_.end())
+ devices_.erase(it);
+}
+
+HidService* HidService::GetInstance() {
+ if (!g_hid_service_ptr.Get().get())
+ g_hid_service_ptr.Get().reset(CreateInstance());
+ return g_hid_service_ptr.Get().get();
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_service.h b/chromium/device/hid/hid_service.h
new file mode 100644
index 00000000000..ee0ebb7acbb
--- /dev/null
+++ b/chromium/device/hid/hid_service.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_SERVICE_H_
+#define DEVICE_HID_HID_SERVICE_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_checker.h"
+#include "device/hid/hid_device_info.h"
+
+namespace device {
+
+class HidConnection;
+
+class HidService : public base::MessageLoop::DestructionObserver {
+ public:
+ // Must be called on FILE thread.
+ static HidService* GetInstance();
+
+ // Enumerates and returns a list of device identifiers.
+ virtual void GetDevices(std::vector<HidDeviceInfo>* devices);
+
+ // Fills in a DeviceInfo struct with info for the given device_id.
+ // Returns |true| if successful or |false| if |device_id| is invalid.
+ bool GetDeviceInfo(const HidDeviceId& device_id, HidDeviceInfo* info) const;
+
+ virtual scoped_refptr<HidConnection> Connect(
+ const HidDeviceId& device_id) = 0;
+
+ // Implements base::MessageLoop::DestructionObserver
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
+
+ protected:
+ friend struct base::DefaultDeleter<HidService>;
+ friend class HidConnectionTest;
+
+ typedef std::map<HidDeviceId, HidDeviceInfo> DeviceMap;
+
+ HidService();
+ virtual ~HidService();
+
+ static HidService* CreateInstance();
+
+ void AddDevice(const HidDeviceInfo& info);
+ void RemoveDevice(const HidDeviceId& device_id);
+
+ base::ThreadChecker thread_checker_;
+
+ private:
+ DeviceMap devices_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidService);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_SERVICE_H_
diff --git a/chromium/device/hid/hid_service_linux.cc b/chromium/device/hid/hid_service_linux.cc
new file mode 100644
index 00000000000..5257dcd874a
--- /dev/null
+++ b/chromium/device/hid/hid_service_linux.cc
@@ -0,0 +1,202 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <linux/hidraw.h>
+#include <sys/ioctl.h>
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/platform_file.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "device/hid/hid_connection_linux.h"
+#include "device/hid/hid_device_info.h"
+#include "device/hid/hid_report_descriptor.h"
+#include "device/hid/hid_service_linux.h"
+#include "device/udev_linux/udev.h"
+
+namespace device {
+
+namespace {
+
+const char kHIDSubSystem[] = "hid";
+const char kHidrawSubsystem[] = "hidraw";
+const char kHIDID[] = "HID_ID";
+const char kHIDName[] = "HID_NAME";
+const char kHIDUnique[] = "HID_UNIQ";
+
+} // namespace
+
+HidServiceLinux::HidServiceLinux() {
+ DeviceMonitorLinux* monitor = DeviceMonitorLinux::GetInstance();
+ monitor->AddObserver(this);
+ monitor->Enumerate(
+ base::Bind(&HidServiceLinux::OnDeviceAdded, base::Unretained(this)));
+}
+
+scoped_refptr<HidConnection> HidServiceLinux::Connect(
+ const HidDeviceId& device_id) {
+ HidDeviceInfo device_info;
+ if (!GetDeviceInfo(device_id, &device_info))
+ return NULL;
+
+ ScopedUdevDevicePtr device =
+ DeviceMonitorLinux::GetInstance()->GetDeviceFromPath(
+ device_info.device_id);
+
+ if (device) {
+ std::string dev_node;
+ if (!FindHidrawDevNode(device.get(), &dev_node)) {
+ LOG(ERROR) << "Cannot open HID device as hidraw device.";
+ return NULL;
+ }
+ return new HidConnectionLinux(device_info, dev_node);
+ }
+
+ return NULL;
+}
+
+HidServiceLinux::~HidServiceLinux() {
+ if (DeviceMonitorLinux::HasInstance())
+ DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
+}
+
+void HidServiceLinux::OnDeviceAdded(udev_device* device) {
+ if (!device)
+ return;
+
+ const char* device_path = udev_device_get_syspath(device);
+ if (!device_path)
+ return;
+ const char* subsystem = udev_device_get_subsystem(device);
+ if (!subsystem || strcmp(subsystem, kHIDSubSystem) != 0)
+ return;
+
+ HidDeviceInfo device_info;
+ device_info.device_id = device_path;
+
+ uint32_t int_property = 0;
+ const char* str_property = NULL;
+
+ const char* hid_id = udev_device_get_property_value(device, kHIDID);
+ if (!hid_id)
+ return;
+
+ std::vector<std::string> parts;
+ base::SplitString(hid_id, ':', &parts);
+ if (parts.size() != 3) {
+ return;
+ }
+
+ if (HexStringToUInt(base::StringPiece(parts[1]), &int_property)) {
+ device_info.vendor_id = int_property;
+ }
+
+ if (HexStringToUInt(base::StringPiece(parts[2]), &int_property)) {
+ device_info.product_id = int_property;
+ }
+
+ str_property = udev_device_get_property_value(device, kHIDUnique);
+ if (str_property != NULL)
+ device_info.serial_number = str_property;
+
+ str_property = udev_device_get_property_value(device, kHIDName);
+ if (str_property != NULL)
+ device_info.product_name = str_property;
+
+ std::string dev_node;
+ if (!FindHidrawDevNode(device, &dev_node)) {
+ LOG(ERROR) << "Cannot find device node for HID device.";
+ return;
+ }
+
+ int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
+
+ base::File device_file(base::FilePath(dev_node), flags);
+ if (!device_file.IsValid()) {
+ LOG(ERROR) << "Cannot open '" << dev_node << "': "
+ << base::File::ErrorToString(device_file.error_details());
+ return;
+ }
+
+ int desc_size = 0;
+ int res = ioctl(device_file.GetPlatformFile(), HIDIOCGRDESCSIZE, &desc_size);
+ if (res < 0) {
+ LOG(ERROR) << "HIDIOCGRDESCSIZE failed.";
+ device_file.Close();
+ return;
+ }
+
+ hidraw_report_descriptor rpt_desc;
+ rpt_desc.size = desc_size;
+
+ res = ioctl(device_file.GetPlatformFile(), HIDIOCGRDESC, &rpt_desc);
+ if (res < 0) {
+ LOG(ERROR) << "HIDIOCGRDESC failed.";
+ device_file.Close();
+ return;
+ }
+
+ device_file.Close();
+
+ HidReportDescriptor report_descriptor(rpt_desc.value, rpt_desc.size);
+ report_descriptor.GetTopLevelCollections(&device_info.usages);
+
+ AddDevice(device_info);
+}
+
+void HidServiceLinux::OnDeviceRemoved(udev_device* device) {
+ const char* device_path = udev_device_get_syspath(device);;
+ if (device_path)
+ RemoveDevice(device_path);
+}
+
+bool HidServiceLinux::FindHidrawDevNode(udev_device* parent,
+ std::string* result) {
+ udev* udev = udev_device_get_udev(parent);
+ if (!udev) {
+ return false;
+ }
+ ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev));
+ if (!enumerate) {
+ return false;
+ }
+ if (udev_enumerate_add_match_subsystem(enumerate.get(), kHidrawSubsystem)) {
+ return false;
+ }
+ if (udev_enumerate_scan_devices(enumerate.get())) {
+ return false;
+ }
+ std::string parent_path(udev_device_get_devpath(parent));
+ if (parent_path.length() == 0 || *parent_path.rbegin() != '/')
+ parent_path += '/';
+ udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
+ for (udev_list_entry* i = devices; i != NULL;
+ i = udev_list_entry_get_next(i)) {
+ ScopedUdevDevicePtr hid_dev(
+ udev_device_new_from_syspath(udev, udev_list_entry_get_name(i)));
+ const char* raw_path = udev_device_get_devnode(hid_dev.get());
+ std::string device_path = udev_device_get_devpath(hid_dev.get());
+ if (raw_path &&
+ !device_path.compare(0, parent_path.length(), parent_path)) {
+ std::string sub_path = device_path.substr(parent_path.length());
+ if (sub_path.substr(0, sizeof(kHidrawSubsystem) - 1) ==
+ kHidrawSubsystem) {
+ *result = raw_path;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_service_linux.h b/chromium/device/hid/hid_service_linux.h
new file mode 100644
index 00000000000..8d5b115cb9e
--- /dev/null
+++ b/chromium/device/hid/hid_service_linux.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_SERVICE_LINUX_H_
+#define DEVICE_HID_HID_SERVICE_LINUX_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "device/hid/device_monitor_linux.h"
+#include "device/hid/hid_device_info.h"
+#include "device/hid/hid_service.h"
+
+struct udev_device;
+
+namespace device {
+
+class HidConnection;
+
+class HidServiceLinux : public HidService,
+ public DeviceMonitorLinux::Observer {
+ public:
+ HidServiceLinux();
+
+ virtual scoped_refptr<HidConnection> Connect(const HidDeviceId& device_id)
+ OVERRIDE;
+
+ // Implements DeviceMonitorLinux::Observer:
+ virtual void OnDeviceAdded(udev_device* device) OVERRIDE;
+ virtual void OnDeviceRemoved(udev_device* device) OVERRIDE;
+
+ private:
+ virtual ~HidServiceLinux();
+
+ static bool FindHidrawDevNode(udev_device* parent, std::string* result);
+
+ DISALLOW_COPY_AND_ASSIGN(HidServiceLinux);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_SERVICE_LINUX_H_
diff --git a/chromium/device/hid/hid_service_mac.cc b/chromium/device/hid/hid_service_mac.cc
new file mode 100644
index 00000000000..ed85ec2d5cc
--- /dev/null
+++ b/chromium/device/hid/hid_service_mac.cc
@@ -0,0 +1,192 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_service_mac.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/hid/IOHIDManager.h>
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "device/hid/hid_connection_mac.h"
+#include "device/hid/hid_utils_mac.h"
+
+namespace device {
+
+class HidServiceMac;
+
+namespace {
+
+typedef std::vector<IOHIDDeviceRef> HidDeviceList;
+
+HidServiceMac* HidServiceFromContext(void* context) {
+ return static_cast<HidServiceMac*>(context);
+}
+
+// Callback for CFSetApplyFunction as used by EnumerateHidDevices.
+void HidEnumerationBackInserter(const void* value, void* context) {
+ HidDeviceList* devices = static_cast<HidDeviceList*>(context);
+ const IOHIDDeviceRef device =
+ static_cast<IOHIDDeviceRef>(const_cast<void*>(value));
+ devices->push_back(device);
+}
+
+void EnumerateHidDevices(IOHIDManagerRef hid_manager,
+ HidDeviceList* device_list) {
+ DCHECK(device_list->size() == 0);
+ // Note that our ownership of each copied device is implied.
+ base::ScopedCFTypeRef<CFSetRef> devices(IOHIDManagerCopyDevices(hid_manager));
+ if (devices)
+ CFSetApplyFunction(devices, HidEnumerationBackInserter, device_list);
+}
+
+} // namespace
+
+HidServiceMac::HidServiceMac() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ message_loop_ = base::MessageLoopProxy::current();
+ DCHECK(message_loop_);
+ hid_manager_.reset(IOHIDManagerCreate(NULL, 0));
+ if (!hid_manager_) {
+ LOG(ERROR) << "Failed to initialize HidManager";
+ return;
+ }
+ DCHECK(CFGetTypeID(hid_manager_) == IOHIDManagerGetTypeID());
+ IOHIDManagerOpen(hid_manager_, kIOHIDOptionsTypeNone);
+ IOHIDManagerSetDeviceMatching(hid_manager_, NULL);
+
+ // Enumerate all the currently known devices.
+ Enumerate();
+
+ // Register for plug/unplug notifications.
+ StartWatchingDevices();
+}
+
+HidServiceMac::~HidServiceMac() {
+ StopWatchingDevices();
+}
+
+void HidServiceMac::StartWatchingDevices() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ IOHIDManagerRegisterDeviceMatchingCallback(
+ hid_manager_, &AddDeviceCallback, this);
+ IOHIDManagerRegisterDeviceRemovalCallback(
+ hid_manager_, &RemoveDeviceCallback, this);
+ IOHIDManagerScheduleWithRunLoop(
+ hid_manager_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
+}
+
+void HidServiceMac::StopWatchingDevices() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!hid_manager_)
+ return;
+ IOHIDManagerUnscheduleFromRunLoop(
+ hid_manager_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
+ IOHIDManagerClose(hid_manager_, kIOHIDOptionsTypeNone);
+}
+
+void HidServiceMac::AddDeviceCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDDeviceRef hid_device) {
+ DCHECK(CFRunLoopGetMain() == CFRunLoopGetCurrent());
+ // Claim ownership of the device.
+ CFRetain(hid_device);
+ HidServiceMac* service = HidServiceFromContext(context);
+ service->message_loop_->PostTask(FROM_HERE,
+ base::Bind(&HidServiceMac::PlatformAddDevice,
+ base::Unretained(service),
+ base::Unretained(hid_device)));
+}
+
+void HidServiceMac::RemoveDeviceCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDDeviceRef hid_device) {
+ DCHECK(CFRunLoopGetMain() == CFRunLoopGetCurrent());
+ HidServiceMac* service = HidServiceFromContext(context);
+ service->message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&HidServiceMac::PlatformRemoveDevice,
+ base::Unretained(service),
+ base::Unretained(hid_device)));
+}
+
+void HidServiceMac::Enumerate() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ HidDeviceList devices;
+ EnumerateHidDevices(hid_manager_, &devices);
+ for (HidDeviceList::const_iterator iter = devices.begin();
+ iter != devices.end();
+ ++iter) {
+ IOHIDDeviceRef hid_device = *iter;
+ PlatformAddDevice(hid_device);
+ }
+}
+
+void HidServiceMac::PlatformAddDevice(IOHIDDeviceRef hid_device) {
+ // Note that our ownership of hid_device is implied if calling this method.
+ // It is balanced in PlatformRemoveDevice.
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ HidDeviceInfo device_info;
+ device_info.device_id = hid_device;
+ device_info.vendor_id =
+ GetHidIntProperty(hid_device, CFSTR(kIOHIDVendorIDKey));
+ device_info.product_id =
+ GetHidIntProperty(hid_device, CFSTR(kIOHIDProductIDKey));
+ device_info.input_report_size =
+ GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxInputReportSizeKey));
+ device_info.output_report_size =
+ GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxOutputReportSizeKey));
+ device_info.feature_report_size =
+ GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxFeatureReportSizeKey));
+ CFTypeRef deviceUsagePairsRaw =
+ IOHIDDeviceGetProperty(hid_device, CFSTR(kIOHIDDeviceUsagePairsKey));
+ CFArrayRef deviceUsagePairs =
+ base::mac::CFCast<CFArrayRef>(deviceUsagePairsRaw);
+ CFIndex deviceUsagePairsCount = CFArrayGetCount(deviceUsagePairs);
+ for (CFIndex i = 0; i < deviceUsagePairsCount; i++) {
+ CFDictionaryRef deviceUsagePair = base::mac::CFCast<CFDictionaryRef>(
+ CFArrayGetValueAtIndex(deviceUsagePairs, i));
+ CFNumberRef usage_raw = base::mac::CFCast<CFNumberRef>(
+ CFDictionaryGetValue(deviceUsagePair, CFSTR(kIOHIDDeviceUsageKey)));
+ uint16_t usage;
+ CFNumberGetValue(usage_raw, kCFNumberSInt32Type, &usage);
+ CFNumberRef page_raw = base::mac::CFCast<CFNumberRef>(
+ CFDictionaryGetValue(deviceUsagePair, CFSTR(kIOHIDDeviceUsagePageKey)));
+ HidUsageAndPage::Page page;
+ CFNumberGetValue(page_raw, kCFNumberSInt32Type, &page);
+ device_info.usages.push_back(HidUsageAndPage(usage, page));
+ }
+ device_info.product_name =
+ GetHidStringProperty(hid_device, CFSTR(kIOHIDProductKey));
+ device_info.serial_number =
+ GetHidStringProperty(hid_device, CFSTR(kIOHIDSerialNumberKey));
+ AddDevice(device_info);
+}
+
+void HidServiceMac::PlatformRemoveDevice(IOHIDDeviceRef hid_device) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ RemoveDevice(hid_device);
+ CFRelease(hid_device);
+}
+
+scoped_refptr<HidConnection> HidServiceMac::Connect(
+ const HidDeviceId& device_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ HidDeviceInfo device_info;
+ if (!GetDeviceInfo(device_id, &device_info))
+ return NULL;
+ return scoped_refptr<HidConnection>(new HidConnectionMac(device_info));
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_service_mac.h b/chromium/device/hid/hid_service_mac.h
new file mode 100644
index 00000000000..7c4323412cc
--- /dev/null
+++ b/chromium/device/hid/hid_service_mac.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_SERVICE_MAC_H_
+#define DEVICE_HID_HID_SERVICE_MAC_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/hid/IOHIDManager.h>
+
+#include <string>
+
+#include "base/mac/foundation_util.h"
+#include "base/memory/ref_counted.h"
+#include "device/hid/hid_service.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace device {
+
+class HidConnection;
+
+class HidServiceMac : public HidService {
+ public:
+ HidServiceMac();
+
+ virtual scoped_refptr<HidConnection> Connect(const HidDeviceId& device_id)
+ OVERRIDE;
+
+ private:
+ virtual ~HidServiceMac();
+
+ void StartWatchingDevices();
+ void StopWatchingDevices();
+
+ // Device changing callbacks.
+ static void AddDeviceCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDDeviceRef hid_device);
+ static void RemoveDeviceCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDDeviceRef hid_device);
+
+ void Enumerate();
+
+ void PlatformAddDevice(IOHIDDeviceRef hid_device);
+ void PlatformRemoveDevice(IOHIDDeviceRef hid_device);
+
+ // Platform HID Manager
+ base::ScopedCFTypeRef<IOHIDManagerRef> hid_manager_;
+
+ // The message loop for the thread on which this service was created.
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidServiceMac);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_SERVICE_MAC_H_
diff --git a/chromium/device/hid/hid_service_unittest.cc b/chromium/device/hid/hid_service_unittest.cc
new file mode 100644
index 00000000000..0b5043bd864
--- /dev/null
+++ b/chromium/device/hid/hid_service_unittest.cc
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/message_loop/message_loop.h"
+#include "device/hid/hid_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+TEST(HidServiceTest, Create) {
+ base::MessageLoopForIO message_loop;
+ HidService* service = HidService::GetInstance();
+ ASSERT_TRUE(service);
+
+ std::vector<HidDeviceInfo> devices;
+ service->GetDevices(&devices);
+ for (std::vector<HidDeviceInfo>::iterator it = devices.begin();
+ it != devices.end();
+ ++it) {
+ ASSERT_TRUE(it->device_id != kInvalidHidDeviceId);
+ }
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_service_win.cc b/chromium/device/hid/hid_service_win.cc
new file mode 100644
index 00000000000..82477a52845
--- /dev/null
+++ b/chromium/device/hid/hid_service_win.cc
@@ -0,0 +1,250 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_service_win.h"
+
+#include <cstdlib>
+
+#include "base/files/file.h"
+#include "base/stl_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "device/hid/hid_connection_win.h"
+#include "device/hid/hid_device_info.h"
+#include "net/base/io_buffer.h"
+
+#if defined(OS_WIN)
+
+#define INITGUID
+
+#include <windows.h>
+#include <hidclass.h>
+
+extern "C" {
+
+#include <hidsdi.h>
+#include <hidpi.h>
+
+}
+
+#include <setupapi.h>
+#include <winioctl.h>
+#include "base/win/scoped_handle.h"
+
+#endif // defined(OS_WIN)
+
+// Setup API is required to enumerate HID devices.
+#pragma comment(lib, "setupapi.lib")
+#pragma comment(lib, "hid.lib")
+
+namespace device {
+namespace {
+
+const char kHIDClass[] = "HIDClass";
+
+} // namespace
+
+HidServiceWin::HidServiceWin() {
+ Enumerate();
+}
+
+HidServiceWin::~HidServiceWin() {}
+
+void HidServiceWin::Enumerate() {
+ BOOL res;
+ HDEVINFO device_info_set;
+ SP_DEVINFO_DATA devinfo_data;
+ SP_DEVICE_INTERFACE_DATA device_interface_data;
+
+ memset(&devinfo_data, 0, sizeof(SP_DEVINFO_DATA));
+ devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
+ device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+
+ device_info_set = SetupDiGetClassDevs(
+ &GUID_DEVINTERFACE_HID,
+ NULL,
+ NULL,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+
+ if (device_info_set == INVALID_HANDLE_VALUE)
+ return;
+
+ for (int device_index = 0;
+ SetupDiEnumDeviceInterfaces(device_info_set,
+ NULL,
+ &GUID_DEVINTERFACE_HID,
+ device_index,
+ &device_interface_data);
+ ++device_index) {
+ DWORD required_size = 0;
+
+ // Determime the required size of detail struct.
+ SetupDiGetDeviceInterfaceDetailA(device_info_set,
+ &device_interface_data,
+ NULL,
+ 0,
+ &required_size,
+ NULL);
+
+ scoped_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA_A, base::FreeDeleter>
+ device_interface_detail_data(
+ static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_A*>(
+ malloc(required_size)));
+ device_interface_detail_data->cbSize =
+ sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
+
+ // Get the detailed data for this device.
+ res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
+ &device_interface_data,
+ device_interface_detail_data.get(),
+ required_size,
+ NULL,
+ NULL);
+ if (!res)
+ continue;
+
+ // Enumerate device info. Looking for Setup Class "HIDClass".
+ for (DWORD i = 0;
+ SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data);
+ i++) {
+ char class_name[256] = {0};
+ res = SetupDiGetDeviceRegistryPropertyA(device_info_set,
+ &devinfo_data,
+ SPDRP_CLASS,
+ NULL,
+ (PBYTE) class_name,
+ sizeof(class_name) - 1,
+ NULL);
+ if (!res)
+ break;
+ if (memcmp(class_name, kHIDClass, sizeof(kHIDClass)) == 0) {
+ char driver_name[256] = {0};
+ // Get bounded driver.
+ res = SetupDiGetDeviceRegistryPropertyA(device_info_set,
+ &devinfo_data,
+ SPDRP_DRIVER,
+ NULL,
+ (PBYTE) driver_name,
+ sizeof(driver_name) - 1,
+ NULL);
+ if (res) {
+ // Found the driver.
+ break;
+ }
+ }
+ }
+
+ if (!res)
+ continue;
+
+ PlatformAddDevice(device_interface_detail_data->DevicePath);
+ }
+}
+
+void HidServiceWin::PlatformAddDevice(const std::string& device_path) {
+ HidDeviceInfo device_info;
+ device_info.device_id = device_path;
+
+ // Try to open the device.
+ base::win::ScopedHandle device_handle(
+ CreateFileA(device_path.c_str(),
+ GENERIC_WRITE | GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ 0));
+
+ if (!device_handle.IsValid() &&
+ GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) {
+ base::win::ScopedHandle device_handle(
+ CreateFileA(device_path.c_str(),
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ 0));
+
+ if (!device_handle.IsValid())
+ return;
+ }
+
+ // Get VID/PID pair.
+ HIDD_ATTRIBUTES attrib = {0};
+ attrib.Size = sizeof(HIDD_ATTRIBUTES);
+ if (!HidD_GetAttributes(device_handle.Get(), &attrib))
+ return;
+
+ device_info.vendor_id = attrib.VendorID;
+ device_info.product_id = attrib.ProductID;
+
+ for (ULONG i = 32;
+ HidD_SetNumInputBuffers(device_handle.Get(), i);
+ i <<= 1);
+
+ // Get usage and usage page (optional).
+ PHIDP_PREPARSED_DATA preparsed_data;
+ if (HidD_GetPreparsedData(device_handle.Get(), &preparsed_data) &&
+ preparsed_data) {
+ HIDP_CAPS capabilities;
+ if (HidP_GetCaps(preparsed_data, &capabilities) == HIDP_STATUS_SUCCESS) {
+ device_info.input_report_size = capabilities.InputReportByteLength;
+ device_info.output_report_size = capabilities.OutputReportByteLength;
+ device_info.feature_report_size = capabilities.FeatureReportByteLength;
+ device_info.usages.push_back(HidUsageAndPage(
+ capabilities.Usage,
+ static_cast<HidUsageAndPage::Page>(capabilities.UsagePage)));
+ }
+ // Detect if the device supports report ids.
+ if (capabilities.NumberInputValueCaps > 0) {
+ scoped_ptr<HIDP_VALUE_CAPS[]> value_caps(
+ new HIDP_VALUE_CAPS[capabilities.NumberInputValueCaps]);
+ USHORT value_caps_length = capabilities.NumberInputValueCaps;
+ if (HidP_GetValueCaps(HidP_Input, &value_caps[0], &value_caps_length,
+ preparsed_data) == HIDP_STATUS_SUCCESS) {
+ device_info.has_report_id = (value_caps[0].ReportID != 0);
+ }
+ }
+ if (!device_info.has_report_id && capabilities.NumberInputButtonCaps > 0)
+ {
+ scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps(
+ new HIDP_BUTTON_CAPS[capabilities.NumberInputButtonCaps]);
+ USHORT button_caps_length = capabilities.NumberInputButtonCaps;
+ if (HidP_GetButtonCaps(HidP_Input,
+ &button_caps[0],
+ &button_caps_length,
+ preparsed_data) == HIDP_STATUS_SUCCESS) {
+ device_info.has_report_id = (button_caps[0].ReportID != 0);
+ }
+ }
+
+ HidD_FreePreparsedData(preparsed_data);
+ }
+
+ AddDevice(device_info);
+}
+
+void HidServiceWin::PlatformRemoveDevice(const std::string& device_path) {
+ RemoveDevice(device_path);
+}
+
+void HidServiceWin::GetDevices(std::vector<HidDeviceInfo>* devices) {
+ Enumerate();
+ HidService::GetDevices(devices);
+}
+
+scoped_refptr<HidConnection> HidServiceWin::Connect(
+ const HidDeviceId& device_id) {
+ HidDeviceInfo device_info;
+ if (!GetDeviceInfo(device_id, &device_info))
+ return NULL;
+ scoped_refptr<HidConnectionWin> connection(new HidConnectionWin(device_info));
+ if (!connection->available()) {
+ PLOG(ERROR) << "Failed to open device.";
+ return NULL;
+ }
+ return connection;
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_service_win.h b/chromium/device/hid/hid_service_win.h
new file mode 100644
index 00000000000..8f187650328
--- /dev/null
+++ b/chromium/device/hid/hid_service_win.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_SERVICE_WIN_H_
+#define DEVICE_HID_HID_SERVICE_WIN_H_
+
+#include <map>
+
+#include "device/hid/hid_device_info.h"
+#include "device/hid/hid_service.h"
+
+namespace device {
+
+class HidConnection;
+
+class HidServiceWin : public HidService {
+ public:
+ HidServiceWin();
+
+ virtual void GetDevices(std::vector<HidDeviceInfo>* devices) OVERRIDE;
+
+ virtual scoped_refptr<HidConnection> Connect(const HidDeviceId& device_id)
+ OVERRIDE;
+
+ private:
+ virtual ~HidServiceWin();
+
+ void Enumerate();
+ void PlatformAddDevice(const std::string& device_path);
+ void PlatformRemoveDevice(const std::string& device_path);
+
+ DISALLOW_COPY_AND_ASSIGN(HidServiceWin);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_SERVICE_WIN_H_
diff --git a/chromium/device/hid/hid_usage_and_page.cc b/chromium/device/hid/hid_usage_and_page.cc
new file mode 100644
index 00000000000..773346b7a5b
--- /dev/null
+++ b/chromium/device/hid/hid_usage_and_page.cc
@@ -0,0 +1,13 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_usage_and_page.h"
+
+namespace device {
+
+bool HidUsageAndPage::operator==(const HidUsageAndPage& other) const {
+ return usage == other.usage && usage_page == other.usage_page;
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_usage_and_page.h b/chromium/device/hid/hid_usage_and_page.h
new file mode 100644
index 00000000000..98ac80d6b54
--- /dev/null
+++ b/chromium/device/hid/hid_usage_and_page.h
@@ -0,0 +1,134 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_USAGE_AND_PAGE_H_
+#define DEVICE_HID_HID_USAGE_AND_PAGE_H_
+
+#include "base/basictypes.h"
+
+namespace device {
+
+struct HidUsageAndPage {
+ enum Page {
+ kPageUndefined = 0x00,
+ kPageGenericDesktop = 0x01,
+ kPageSimulation = 0x02,
+ kPageVirtualReality = 0x03,
+ kPageSport = 0x04,
+ kPageGame = 0x05,
+ kPageKeyboard = 0x07,
+ kPageLed = 0x08,
+ kPageButton = 0x09,
+ kPageOrdinal = 0x0A,
+ kPageTelephony = 0x0B,
+ kPageConsumer = 0x0C,
+ kPageDigitizer = 0x0D,
+ kPagePidPage = 0x0F,
+ kPageUnicode = 0x10,
+ kPageAlphanumericDisplay = 0x14,
+ kPageMedicalInstruments = 0x40,
+ kPageMonitor0 = 0x80,
+ kPageMonitor1 = 0x81,
+ kPageMonitor2 = 0x82,
+ kPageMonitor3 = 0x83,
+ kPagePower0 = 0x84,
+ kPagePower1 = 0x85,
+ kPagePower2 = 0x86,
+ kPagePower3 = 0x87,
+ kPageBarCodeScanner = 0x8C,
+ kPageScale = 0x8D,
+ kPageMagneticStripeReader = 0x8E,
+ kPageReservedPointOfSale = 0x8F,
+ kPageCameraControl = 0x90,
+ kPageArcade = 0x91,
+ kPageVendor = 0xFF00,
+ kPageMediaCenter = 0xFFBC
+ };
+
+ // These usage enumerations are derived from the HID Usage Tables v1.11 spec.
+ enum GenericDesktopUsage {
+ kGenericDesktopUndefined = 0,
+ kGenericDesktopPointer = 1,
+ kGenericDesktopMouse = 2,
+ kGenericDesktopJoystick = 4,
+ kGenericDesktopGamePad = 5,
+ kGenericDesktopKeyboard = 6,
+ kGenericDesktopKeypad = 7,
+ kGenericDesktopMultiAxisController = 8,
+ kGenericDesktopX = 0x30,
+ kGenericDesktopY = 0x31,
+ kGenericDesktopZ = 0x32,
+ kGenericDesktopRx = 0x33,
+ kGenericDesktopRy = 0x34,
+ kGenericDesktopRz = 0x35,
+ kGenericDesktopSlider = 0x36,
+ kGenericDesktopDial = 0x37,
+ kGenericDesktopWheel = 0x38,
+ kGenericDesktopHatSwitch = 0x39,
+ kGenericDesktopCountedBuffer = 0x3a,
+ kGenericDesktopByteCount = 0x3b,
+ kGenericDesktopMotionWakeup = 0x3c,
+ kGenericDesktopStart = 0x3d,
+ kGenericDesktopSelect = 0x3e,
+ kGenericDesktopVx = 0x40,
+ kGenericDesktopVy = 0x41,
+ kGenericDesktopVz = 0x42,
+ kGenericDesktopVbrx = 0x43,
+ kGenericDesktopVbry = 0x44,
+ kGenericDesktopVbrz = 0x45,
+ kGenericDesktopVno = 0x46,
+
+ kGenericDesktopSystemControl = 0x80,
+ kGenericDesktopSystemPowerDown = 0x81,
+ kGenericDesktopSystemSleep = 0x82,
+ kGenericDesktopSystemWakeUp = 0x83,
+ kGenericDesktopSystemContextMenu = 0x84,
+ kGenericDesktopSystemMainMenu = 0x85,
+ kGenericDesktopSystemAppMenu = 0x86,
+ kGenericDesktopSystemMenuHelp = 0x87,
+ kGenericDesktopSystemMenuExit = 0x88,
+ kGenericDesktopSystemMenuSelect = 0x89,
+ kGenericDesktopSystemMenuRight = 0x8a,
+ kGenericDesktopSystemMenuLeft = 0x8b,
+ kGenericDesktopSystemMenuUp = 0x8c,
+ kGenericDesktopSystemMenuDown = 0x8d,
+ kGenericDesktopSystemColdRestart = 0x8e,
+ kGenericDesktopSystemWarmRestart = 0x8f,
+
+ kGenericDesktopDPadUp = 0x90,
+ kGenericDesktopDPadDown = 0x91,
+ kGenericDesktopDPadLeft = 0x92,
+ kGenericDesktopDPadRight = 0x93,
+
+ kGenericDesktopSystemDock = 0xa0,
+ kGenericDesktopSystemUndock = 0xa1,
+ kGenericDesktopSystemSetup = 0xa2,
+ kGenericDesktopSystemBreak = 0xa3,
+ kGenericDesktopSystemDebuggerBreak = 0xa4,
+ kGenericDesktopApplicationBreak = 0xa5,
+ kGenericDesktopApplicationDebuggerBreak = 0xa6,
+ kGenericDesktopSystemSpeakerMute = 0xa7,
+ kGenericDesktopSystemHibernate = 0xa8,
+ kGenericDesktopSystemDisplayInvert = 0xb0,
+ kGenericDesktopSystemDisplayInternal = 0xb1,
+ kGenericDesktopSystemDisplayExternal = 0xb2,
+ kGenericDesktopSystemDisplayBoth = 0xb3,
+ kGenericDesktopSystemDisplayDual = 0xb4,
+ kGenericDesktopSystemDisplayToggle = 0xb5,
+ kGenericDesktopSystemDisplaySwap = 0xb6,
+ };
+
+ HidUsageAndPage(uint16_t usage, Page usage_page)
+ : usage(usage), usage_page(usage_page) {}
+ ~HidUsageAndPage() {}
+
+ uint16_t usage;
+ Page usage_page;
+
+ bool operator==(const HidUsageAndPage& other) const;
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_USAGE_AND_PAGE_H_
diff --git a/chromium/device/hid/hid_utils_mac.cc b/chromium/device/hid/hid_utils_mac.cc
new file mode 100644
index 00000000000..46605d87bdb
--- /dev/null
+++ b/chromium/device/hid/hid_utils_mac.cc
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/hid/hid_utils_mac.h"
+
+#include "base/mac/foundation_util.h"
+#include "base/strings/sys_string_conversions.h"
+
+namespace device {
+
+int32_t GetHidIntProperty(IOHIDDeviceRef device, CFStringRef key) {
+ int32_t value;
+ if (TryGetHidIntProperty(device, key, &value))
+ return value;
+ return 0;
+}
+
+std::string GetHidStringProperty(IOHIDDeviceRef device, CFStringRef key) {
+ std::string value;
+ TryGetHidStringProperty(device, key, &value);
+ return value;
+}
+
+bool TryGetHidIntProperty(IOHIDDeviceRef device,
+ CFStringRef key,
+ int32_t* result) {
+ CFNumberRef ref = base::mac::CFCast<CFNumberRef>(
+ IOHIDDeviceGetProperty(device, key));
+ return ref && CFNumberGetValue(ref, kCFNumberSInt32Type, result);
+}
+
+bool TryGetHidStringProperty(IOHIDDeviceRef device,
+ CFStringRef key,
+ std::string* result) {
+ CFStringRef ref = base::mac::CFCast<CFStringRef>(
+ IOHIDDeviceGetProperty(device, key));
+ if (!ref) {
+ return false;
+ }
+ *result = base::SysCFStringRefToUTF8(ref);
+ return true;
+}
+
+} // namespace device
diff --git a/chromium/device/hid/hid_utils_mac.h b/chromium/device/hid/hid_utils_mac.h
new file mode 100644
index 00000000000..e9b252462a5
--- /dev/null
+++ b/chromium/device/hid/hid_utils_mac.h
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_HID_UTILS_MAC_H_
+#define DEVICE_HID_HID_UTILS_MAC_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/hid/IOHIDManager.h>
+#include <stdint.h>
+
+#include <string>
+
+namespace device {
+
+int32_t GetHidIntProperty(IOHIDDeviceRef device, CFStringRef key);
+
+std::string GetHidStringProperty(IOHIDDeviceRef device, CFStringRef key);
+
+bool TryGetHidIntProperty(IOHIDDeviceRef device,
+ CFStringRef key,
+ int32_t* result);
+
+bool TryGetHidStringProperty(IOHIDDeviceRef device,
+ CFStringRef key,
+ std::string* result);
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_UTILS_MAC_H_
diff --git a/chromium/device/hid/input_service_linux.cc b/chromium/device/hid/input_service_linux.cc
new file mode 100644
index 00000000000..90132e91af2
--- /dev/null
+++ b/chromium/device/hid/input_service_linux.cc
@@ -0,0 +1,240 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <libudev.h>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "device/hid/input_service_linux.h"
+
+namespace device {
+
+namespace {
+
+const char kSubsystemHid[] = "hid";
+const char kSubsystemInput[] = "input";
+const char kTypeBluetooth[] = "bluetooth";
+const char kTypeUsb[] = "usb";
+const char kTypeSerio[] = "serio";
+const char kIdInputAccelerometer[] = "ID_INPUT_ACCELEROMETER";
+const char kIdInputJoystick[] = "ID_INPUT_JOYSTICK";
+const char kIdInputKey[] = "ID_INPUT_KEY";
+const char kIdInputKeyboard[] = "ID_INPUT_KEYBOARD";
+const char kIdInputMouse[] = "ID_INPUT_MOUSE";
+const char kIdInputTablet[] = "ID_INPUT_TABLET";
+const char kIdInputTouchpad[] = "ID_INPUT_TOUCHPAD";
+const char kIdInputTouchscreen[] = "ID_INPUT_TOUCHSCREEN";
+
+// The instance will be reset when message loop destroys.
+base::LazyInstance<scoped_ptr<InputServiceLinux> >::Leaky
+ g_input_service_linux_ptr = LAZY_INSTANCE_INITIALIZER;
+
+bool GetBoolProperty(udev_device* device, const char* key) {
+ CHECK(device);
+ CHECK(key);
+ const char* property = udev_device_get_property_value(device, key);
+ if (!property)
+ return false;
+ int value;
+ if (!base::StringToInt(property, &value)) {
+ LOG(ERROR) << "Not an integer value for " << key << " property";
+ return false;
+ }
+ return (value != 0);
+}
+
+InputServiceLinux::InputDeviceInfo::Type GetDeviceType(udev_device* device) {
+ if (udev_device_get_parent_with_subsystem_devtype(
+ device, kTypeBluetooth, NULL)) {
+ return InputServiceLinux::InputDeviceInfo::TYPE_BLUETOOTH;
+ }
+ if (udev_device_get_parent_with_subsystem_devtype(device, kTypeUsb, NULL))
+ return InputServiceLinux::InputDeviceInfo::TYPE_USB;
+ if (udev_device_get_parent_with_subsystem_devtype(device, kTypeSerio, NULL))
+ return InputServiceLinux::InputDeviceInfo::TYPE_SERIO;
+ return InputServiceLinux::InputDeviceInfo::TYPE_UNKNOWN;
+}
+
+std::string GetParentDeviceName(udev_device* device, const char* subsystem) {
+ udev_device* parent =
+ udev_device_get_parent_with_subsystem_devtype(device, subsystem, NULL);
+ if (!parent)
+ return std::string();
+ const char* name = udev_device_get_property_value(parent, "NAME");
+ if (!name)
+ return std::string();
+ std::string result;
+ base::TrimString(name, "\"", &result);
+ return result;
+}
+
+class InputServiceLinuxImpl : public InputServiceLinux,
+ public DeviceMonitorLinux::Observer {
+ public:
+ // Implements DeviceMonitorLinux::Observer:
+ virtual void OnDeviceAdded(udev_device* device) OVERRIDE;
+ virtual void OnDeviceRemoved(udev_device* device) OVERRIDE;
+
+ private:
+ friend class InputServiceLinux;
+
+ InputServiceLinuxImpl();
+ virtual ~InputServiceLinuxImpl();
+
+ DISALLOW_COPY_AND_ASSIGN(InputServiceLinuxImpl);
+};
+
+InputServiceLinuxImpl::InputServiceLinuxImpl() {
+ DeviceMonitorLinux::GetInstance()->AddObserver(this);
+ DeviceMonitorLinux::GetInstance()->Enumerate(base::Bind(
+ &InputServiceLinuxImpl::OnDeviceAdded, base::Unretained(this)));
+}
+
+InputServiceLinuxImpl::~InputServiceLinuxImpl() {
+ if (DeviceMonitorLinux::HasInstance())
+ DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
+}
+
+void InputServiceLinuxImpl::OnDeviceAdded(udev_device* device) {
+ DCHECK(CalledOnValidThread());
+ if (!device)
+ return;
+ const char* devnode = udev_device_get_devnode(device);
+ if (!devnode)
+ return;
+
+ InputDeviceInfo info;
+ info.id = devnode;
+
+ const char* subsystem = udev_device_get_subsystem(device);
+ if (!subsystem)
+ return;
+ if (strcmp(subsystem, kSubsystemHid) == 0) {
+ info.subsystem = InputServiceLinux::InputDeviceInfo::SUBSYSTEM_HID;
+ info.name = GetParentDeviceName(device, kSubsystemHid);
+ } else if (strcmp(subsystem, kSubsystemInput) == 0) {
+ info.subsystem = InputServiceLinux::InputDeviceInfo::SUBSYSTEM_INPUT;
+ info.name = GetParentDeviceName(device, kSubsystemInput);
+ } else {
+ return;
+ }
+
+ info.type = GetDeviceType(device);
+
+ info.is_accelerometer = GetBoolProperty(device, kIdInputAccelerometer);
+ info.is_joystick = GetBoolProperty(device, kIdInputJoystick);
+ info.is_key = GetBoolProperty(device, kIdInputKey);
+ info.is_keyboard = GetBoolProperty(device, kIdInputKeyboard);
+ info.is_mouse = GetBoolProperty(device, kIdInputMouse);
+ info.is_tablet = GetBoolProperty(device, kIdInputTablet);
+ info.is_touchpad = GetBoolProperty(device, kIdInputTouchpad);
+ info.is_touchscreen = GetBoolProperty(device, kIdInputTouchscreen);
+
+ AddDevice(info);
+}
+
+void InputServiceLinuxImpl::OnDeviceRemoved(udev_device* device) {
+ DCHECK(CalledOnValidThread());
+ if (!device)
+ return;
+ const char* devnode = udev_device_get_devnode(device);
+ if (devnode)
+ RemoveDevice(devnode);
+}
+
+} // namespace
+
+InputServiceLinux::InputDeviceInfo::InputDeviceInfo()
+ : subsystem(SUBSYSTEM_UNKNOWN),
+ type(TYPE_UNKNOWN),
+ is_accelerometer(false),
+ is_joystick(false),
+ is_key(false),
+ is_keyboard(false),
+ is_mouse(false),
+ is_tablet(false),
+ is_touchpad(false),
+ is_touchscreen(false) {}
+
+InputServiceLinux::InputServiceLinux() {
+ base::ThreadRestrictions::AssertIOAllowed();
+ base::MessageLoop::current()->AddDestructionObserver(this);
+}
+
+InputServiceLinux::~InputServiceLinux() {
+ DCHECK(CalledOnValidThread());
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+}
+
+// static
+InputServiceLinux* InputServiceLinux::GetInstance() {
+ if (!HasInstance())
+ g_input_service_linux_ptr.Get().reset(new InputServiceLinuxImpl());
+ return g_input_service_linux_ptr.Get().get();
+}
+
+// static
+bool InputServiceLinux::HasInstance() {
+ return g_input_service_linux_ptr.Get().get();
+}
+
+// static
+void InputServiceLinux::SetForTesting(InputServiceLinux* service) {
+ g_input_service_linux_ptr.Get().reset(service);
+}
+
+void InputServiceLinux::AddObserver(Observer* observer) {
+ DCHECK(CalledOnValidThread());
+ if (observer)
+ observers_.AddObserver(observer);
+}
+
+void InputServiceLinux::RemoveObserver(Observer* observer) {
+ DCHECK(CalledOnValidThread());
+ if (observer)
+ observers_.RemoveObserver(observer);
+}
+
+void InputServiceLinux::GetDevices(std::vector<InputDeviceInfo>* devices) {
+ DCHECK(CalledOnValidThread());
+ for (DeviceMap::iterator it = devices_.begin(), ie = devices_.end(); it != ie;
+ ++it) {
+ devices->push_back(it->second);
+ }
+}
+
+bool InputServiceLinux::GetDeviceInfo(const std::string& id,
+ InputDeviceInfo* info) const {
+ DCHECK(CalledOnValidThread());
+ DeviceMap::const_iterator it = devices_.find(id);
+ if (it == devices_.end())
+ return false;
+ *info = it->second;
+ return true;
+}
+
+void InputServiceLinux::WillDestroyCurrentMessageLoop() {
+ DCHECK(CalledOnValidThread());
+ g_input_service_linux_ptr.Get().reset(NULL);
+}
+
+void InputServiceLinux::AddDevice(const InputDeviceInfo& info) {
+ devices_[info.id] = info;
+ FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceAdded(info));
+}
+
+void InputServiceLinux::RemoveDevice(const std::string& id) {
+ devices_.erase(id);
+ FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceRemoved(id));
+}
+
+bool InputServiceLinux::CalledOnValidThread() const {
+ return thread_checker_.CalledOnValidThread();
+}
+
+} // namespace device
diff --git a/chromium/device/hid/input_service_linux.h b/chromium/device/hid/input_service_linux.h
new file mode 100644
index 00000000000..dc43dba9bc2
--- /dev/null
+++ b/chromium/device/hid/input_service_linux.h
@@ -0,0 +1,98 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_HID_INPUT_SERVICE_LINUX_H_
+#define DEVICE_HID_INPUT_SERVICE_LINUX_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "device/hid/device_monitor_linux.h"
+
+namespace device {
+
+// This class provides information and notifications about
+// connected/disconnected input/HID devices. This class is *NOT*
+// thread-safe and all methods must be called from the FILE thread.
+class InputServiceLinux : public base::MessageLoop::DestructionObserver {
+ public:
+ struct InputDeviceInfo {
+ enum Subsystem { SUBSYSTEM_HID, SUBSYSTEM_INPUT, SUBSYSTEM_UNKNOWN };
+ enum Type { TYPE_BLUETOOTH, TYPE_USB, TYPE_SERIO, TYPE_UNKNOWN };
+
+ InputDeviceInfo();
+
+ std::string id;
+ std::string name;
+ Subsystem subsystem;
+ Type type;
+
+ bool is_accelerometer : 1;
+ bool is_joystick : 1;
+ bool is_key : 1;
+ bool is_keyboard : 1;
+ bool is_mouse : 1;
+ bool is_tablet : 1;
+ bool is_touchpad : 1;
+ bool is_touchscreen : 1;
+ };
+
+ class Observer {
+ public:
+ virtual ~Observer() {}
+ virtual void OnInputDeviceAdded(const InputDeviceInfo& info) = 0;
+ virtual void OnInputDeviceRemoved(const std::string& id) = 0;
+ };
+
+ InputServiceLinux();
+
+ static InputServiceLinux* GetInstance();
+ static bool HasInstance();
+ static void SetForTesting(InputServiceLinux* service);
+
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ // Returns list of all currently connected input/hid devices.
+ void GetDevices(std::vector<InputDeviceInfo>* devices);
+
+ // Returns an info about input device identified by |id|. When there're
+ // no input or hid device with such id, returns false and doesn't
+ // modify |info|.
+ bool GetDeviceInfo(const std::string& id, InputDeviceInfo* info) const;
+
+ // Implements base::MessageLoop::DestructionObserver
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
+
+ protected:
+ virtual ~InputServiceLinux();
+
+ void AddDevice(const InputDeviceInfo& info);
+ void RemoveDevice(const std::string& id);
+
+ bool CalledOnValidThread() const;
+
+ private:
+ friend struct base::DefaultDeleter<InputServiceLinux>;
+
+ typedef base::hash_map<std::string, InputDeviceInfo> DeviceMap;
+
+ DeviceMap devices_;
+ ObserverList<Observer> observers_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputServiceLinux);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_INPUT_SERVICE_LINUX_H_
diff --git a/chromium/device/hid/input_service_linux_unittest.cc b/chromium/device/hid/input_service_linux_unittest.cc
new file mode 100644
index 00000000000..9825c06f611
--- /dev/null
+++ b/chromium/device/hid/input_service_linux_unittest.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/message_loop/message_loop.h"
+#include "device/hid/input_service_linux.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+TEST(InputServiceLinux, Simple) {
+ base::MessageLoopForIO message_loop;
+ InputServiceLinux* service = InputServiceLinux::GetInstance();
+
+ ASSERT_TRUE(service);
+ std::vector<InputServiceLinux::InputDeviceInfo> devices;
+ service->GetDevices(&devices);
+ for (size_t i = 0; i < devices.size(); ++i)
+ ASSERT_TRUE(!devices[i].id.empty());
+}
+
+} // namespace device
diff --git a/chromium/device/media_transfer_protocol/media_transfer_protocol_daemon_client.cc b/chromium/device/media_transfer_protocol/media_transfer_protocol_daemon_client.cc
index 41ac37e0c8b..4edfc60f1e5 100644
--- a/chromium/device/media_transfer_protocol/media_transfer_protocol_daemon_client.cc
+++ b/chromium/device/media_transfer_protocol/media_transfer_protocol_daemon_client.cc
@@ -19,6 +19,7 @@ namespace device {
namespace {
const char kInvalidResponseMsg[] = "Invalid Response: ";
+uint32 kMaxChunkSize = 1024*1024; // D-Bus has message size limits.
// The MediaTransferProtocolDaemonClient implementation.
class MediaTransferProtocolDaemonClientImpl
@@ -140,6 +141,7 @@ class MediaTransferProtocolDaemonClientImpl
uint32 bytes_to_read,
const ReadFileCallback& callback,
const ErrorCallback& error_callback) OVERRIDE {
+ DCHECK_LE(bytes_to_read, kMaxChunkSize);
dbus::MethodCall method_call(mtpd::kMtpdInterface,
mtpd::kReadFileChunkByPath);
dbus::MessageWriter writer(&method_call);
@@ -162,6 +164,7 @@ class MediaTransferProtocolDaemonClientImpl
uint32 bytes_to_read,
const ReadFileCallback& callback,
const ErrorCallback& error_callback) OVERRIDE {
+ DCHECK_LE(bytes_to_read, kMaxChunkSize);
dbus::MethodCall method_call(mtpd::kMtpdInterface,
mtpd::kReadFileChunkById);
dbus::MessageWriter writer(&method_call);
@@ -349,7 +352,7 @@ class MediaTransferProtocolDaemonClientImpl
return;
}
- uint8* data_bytes = NULL;
+ const uint8* data_bytes = NULL;
size_t data_length = 0;
dbus::MessageReader reader(response);
if (!reader.PopArrayOfBytes(&data_bytes, &data_length)) {
diff --git a/chromium/device/nfc/OWNERS b/chromium/device/nfc/OWNERS
new file mode 100644
index 00000000000..7bf8537917f
--- /dev/null
+++ b/chromium/device/nfc/OWNERS
@@ -0,0 +1,2 @@
+keybuk@chromium.org
+armansito@chromium.org
diff --git a/chromium/device/nfc/nfc.gyp b/chromium/device/nfc/nfc.gyp
index d5e519bb95f..11e430852e6 100644
--- a/chromium/device/nfc/nfc.gyp
+++ b/chromium/device/nfc/nfc.gyp
@@ -12,6 +12,7 @@
'type': 'static_library',
'dependencies': [
'../../base/base.gyp:base',
+ '../../url/url.gyp:url_lib',
],
'sources': [
'nfc_adapter.cc',
@@ -22,12 +23,20 @@
'nfc_adapter_factory.h',
'nfc_ndef_record.cc',
'nfc_ndef_record.h',
+ 'nfc_ndef_record_utils_chromeos.cc',
+ 'nfc_ndef_record_utils_chromeos.h',
'nfc_peer.cc',
'nfc_peer.h',
+ 'nfc_peer_chromeos.cc',
+ 'nfc_peer_chromeos.h',
'nfc_tag.cc',
'nfc_tag.h',
+ 'nfc_tag_chromeos.cc',
+ 'nfc_tag_chromeos.h',
'nfc_tag_technology.cc',
- 'nfc_tag_technology.h'
+ 'nfc_tag_technology.h',
+ 'nfc_tag_technology_chromeos.cc',
+ 'nfc_tag_technology_chromeos.h'
],
'conditions': [
['chromeos==1', {
diff --git a/chromium/device/nfc/nfc_adapter.cc b/chromium/device/nfc/nfc_adapter.cc
index 1bef83d3ec2..520d4f31092 100644
--- a/chromium/device/nfc/nfc_adapter.cc
+++ b/chromium/device/nfc/nfc_adapter.cc
@@ -48,4 +48,50 @@ NfcTag* NfcAdapter::GetTag(const std::string& identifier) const {
return NULL;
}
+void NfcAdapter::SetTag(const std::string& identifier, NfcTag* tag) {
+ if (GetTag(identifier)) {
+ VLOG(1) << "Tag object for tag \"" << identifier << "\" already exists.";
+ return;
+ }
+ tags_[identifier] = tag;
+}
+
+void NfcAdapter::SetPeer(const std::string& identifier, NfcPeer* peer) {
+ if (GetPeer(identifier)) {
+ VLOG(1) << "Peer object for peer \"" << identifier << "\" already exists.";
+ return;
+ }
+ peers_[identifier] = peer;
+}
+
+NfcTag* NfcAdapter::RemoveTag(const std::string& identifier) {
+ TagsMap::iterator iter = tags_.find(identifier);
+ if (iter == tags_.end()) {
+ VLOG(1) << "Tag with identifier \"" << identifier << "\" not found.";
+ return NULL;
+ }
+ NfcTag* tag = iter->second;
+ tags_.erase(iter);
+ return tag;
+}
+
+NfcPeer* NfcAdapter::RemovePeer(const std::string& identifier) {
+ PeersMap::iterator iter = peers_.find(identifier);
+ if (iter == peers_.end()) {
+ VLOG(1) << "Peer object for peer \"" << identifier << "\" not found.";
+ return NULL;
+ }
+ NfcPeer* peer = iter->second;
+ peers_.erase(iter);
+ return peer;
+}
+
+void NfcAdapter::ClearTags() {
+ tags_.clear();
+}
+
+void NfcAdapter::ClearPeers() {
+ peers_.clear();
+}
+
} // namespace device
diff --git a/chromium/device/nfc/nfc_adapter.h b/chromium/device/nfc/nfc_adapter.h
index 373f4cc3683..455b14289b8 100644
--- a/chromium/device/nfc/nfc_adapter.h
+++ b/chromium/device/nfc/nfc_adapter.h
@@ -181,10 +181,27 @@ class NfcAdapter : public base::RefCounted<NfcAdapter> {
typedef std::map<const std::string, NfcPeer*> PeersMap;
typedef std::map<const std::string, NfcTag*> TagsMap;
+ // Set the given tag or peer for |identifier|. If a tag or peer for
+ // |identifier| already exists, these methods won't do anything.
+ void SetTag(const std::string& identifier, NfcTag* tag);
+ void SetPeer(const std::string& identifier, NfcPeer* peer);
+
+ // Removes the tag or peer for |identifier| and returns the removed object.
+ // Returns NULL, if no tag or peer for |identifier| was found.
+ NfcTag* RemoveTag(const std::string& identifier);
+ NfcPeer* RemovePeer(const std::string& identifier);
+
+ // Clear the peer and tag maps. These methods won't delete the tag and peer
+ // objects, however after the call to these methods, the peers and tags won't
+ // be returned via calls to GetPeers and GetTags.
+ void ClearTags();
+ void ClearPeers();
+
+ private:
+ // Peers and tags that are managed by this adapter.
PeersMap peers_;
TagsMap tags_;
- private:
DISALLOW_COPY_AND_ASSIGN(NfcAdapter);
};
diff --git a/chromium/device/nfc/nfc_adapter_chromeos.cc b/chromium/device/nfc/nfc_adapter_chromeos.cc
index 071b0808ae9..374e8ac83e9 100644
--- a/chromium/device/nfc/nfc_adapter_chromeos.cc
+++ b/chromium/device/nfc/nfc_adapter_chromeos.cc
@@ -9,19 +9,25 @@
#include "base/callback.h"
#include "base/logging.h"
#include "chromeos/dbus/dbus_thread_manager.h"
-#include "device/nfc/nfc_peer.h"
-#include "device/nfc/nfc_tag.h"
+#include "device/nfc/nfc_peer_chromeos.h"
+#include "device/nfc/nfc_tag_chromeos.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
+namespace {
+
+typedef std::vector<dbus::ObjectPath> ObjectPathVector;
+
+} // namespace
+
NfcAdapterChromeOS::NfcAdapterChromeOS()
: weak_ptr_factory_(this) {
DBusThreadManager::Get()->GetNfcAdapterClient()->AddObserver(this);
DBusThreadManager::Get()->GetNfcDeviceClient()->AddObserver(this);
DBusThreadManager::Get()->GetNfcTagClient()->AddObserver(this);
- const std::vector<dbus::ObjectPath>& object_paths =
+ const ObjectPathVector& object_paths =
DBusThreadManager::Get()->GetNfcAdapterClient()->GetAdapters();
if (!object_paths.empty()) {
VLOG(1) << object_paths.size() << " NFC adapter(s) available.";
@@ -126,9 +132,9 @@ void NfcAdapterChromeOS::AdapterRemoved(const dbus::ObjectPath& object_path) {
// There may still be other adapters present on the system. Set the next
// available adapter as the current one.
- const std::vector<dbus::ObjectPath>& object_paths =
+ const ObjectPathVector& object_paths =
DBusThreadManager::Get()->GetNfcAdapterClient()->GetAdapters();
- for (std::vector<dbus::ObjectPath>::const_iterator iter =
+ for (ObjectPathVector::const_iterator iter =
object_paths.begin();
iter != object_paths.end(); ++iter) {
// The removed object will still be available until the call to
@@ -155,35 +161,91 @@ void NfcAdapterChromeOS::AdapterPropertyChanged(
}
void NfcAdapterChromeOS::DeviceAdded(const dbus::ObjectPath& object_path) {
+ if (!IsPresent())
+ return;
+
+ if (GetPeer(object_path.value()))
+ return;
+
VLOG(1) << "NFC device found: " << object_path.value();
- // TODO(armansito): Implement device logic.
+
+ // Check to see if the device belongs to this adapter.
+ const ObjectPathVector& devices =
+ DBusThreadManager::Get()->GetNfcDeviceClient()->
+ GetDevicesForAdapter(object_path_);
+ bool device_found = false;
+ for (ObjectPathVector::const_iterator iter = devices.begin();
+ iter != devices.end(); ++iter) {
+ if (*iter == object_path) {
+ device_found = true;
+ break;
+ }
+ }
+ if (!device_found) {
+ VLOG(1) << "Found peer device does not belong to the current adapter.";
+ return;
+ }
+
+ // Create the peer object.
+ NfcPeerChromeOS* peer_chromeos = new NfcPeerChromeOS(object_path);
+ SetPeer(object_path.value(), peer_chromeos);
+ FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
+ PeerFound(this, peer_chromeos));
}
void NfcAdapterChromeOS::DeviceRemoved(const dbus::ObjectPath& object_path) {
VLOG(1) << "NFC device lost: " << object_path.value();
- // TODO(armansito): Implement device logic.
-}
-
-void NfcAdapterChromeOS::DevicePropertyChanged(
- const dbus::ObjectPath& object_path,
- const std::string& property_name) {
- // TODO(armansito): Implement device logic.
+ device::NfcPeer* peer = RemovePeer(object_path.value());
+ if (!peer) {
+ VLOG(1) << "Removed peer device does not belong to the current adapter.";
+ return;
+ }
+ FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, PeerLost(this, peer));
+ delete peer;
}
void NfcAdapterChromeOS::TagAdded(const dbus::ObjectPath& object_path) {
- VLOG(1) << "NFC tag found: " << object_path.value();
- // TODO(armansito): Implement tag logic.
-}
+ if (!IsPresent())
+ return;
+
+ if (GetTag(object_path.value()))
+ return;
-void NfcAdapterChromeOS::TagRemoved(const dbus::ObjectPath& object_path) {
VLOG(1) << "NFC tag found: " << object_path.value();
- // TODO(armansito): Implement tag logic.
+
+ // Check to see if the tag belongs to this adapter.
+ const std::vector<dbus::ObjectPath>& tags =
+ DBusThreadManager::Get()->GetNfcTagClient()->
+ GetTagsForAdapter(object_path_);
+ bool tag_found = false;
+ for (std::vector<dbus::ObjectPath>::const_iterator iter = tags.begin();
+ iter != tags.end(); ++iter) {
+ if (*iter == object_path) {
+ tag_found = true;
+ break;
+ }
+ }
+ if (!tag_found) {
+ VLOG(1) << "Found tag does not belong to the current adapter.";
+ return;
+ }
+
+ // Create the tag object.
+ NfcTagChromeOS* tag_chromeos = new NfcTagChromeOS(object_path);
+ SetTag(object_path.value(), tag_chromeos);
+ FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
+ TagFound(this, tag_chromeos));
}
-void NfcAdapterChromeOS::TagPropertyChanged(
- const dbus::ObjectPath& object_path,
- const std::string& property_name) {
- // TODO(armansito): Implement tag logic.
+void NfcAdapterChromeOS::TagRemoved(const dbus::ObjectPath& object_path) {
+ VLOG(1) << "NFC tag lost : " << object_path.value();
+ device::NfcTag* tag = RemoveTag(object_path.value());
+ if (!tag) {
+ VLOG(1) << "Removed tag does not belong to the current adapter.";
+ return;
+ }
+ FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_, TagLost(this, tag));
+ delete tag;
}
void NfcAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) {
@@ -200,9 +262,35 @@ void NfcAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) {
if (properties->polling.value())
PollingChanged(true);
- // TODO(armansito): Create device::NfcPeer and device::NfcTag instances for
- // all peers and tags that exist, once they have been implemented for
- // ChromeOS.
+ // Create peer objects for peers that were added before the adapter was set.
+ const ObjectPathVector& devices =
+ DBusThreadManager::Get()->GetNfcDeviceClient()->
+ GetDevicesForAdapter(object_path_);
+ for (ObjectPathVector::const_iterator iter = devices.begin();
+ iter != devices.end(); ++iter) {
+ const dbus::ObjectPath& object_path = *iter;
+ if (GetPeer(object_path.value()))
+ continue;
+ NfcPeerChromeOS* peer_chromeos = new NfcPeerChromeOS(object_path);
+ SetPeer(object_path.value(), peer_chromeos);
+ FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
+ PeerFound(this, peer_chromeos));
+ }
+
+ // Create tag objects for tags that were added before the adapter was set.
+ const std::vector<dbus::ObjectPath>& tags =
+ DBusThreadManager::Get()->GetNfcTagClient()->
+ GetTagsForAdapter(object_path_);
+ for (std::vector<dbus::ObjectPath>::const_iterator iter = tags.begin();
+ iter != tags.end(); ++iter) {
+ const dbus::ObjectPath& object_path = *iter;
+ if (GetTag(object_path.value()))
+ continue;
+ NfcTagChromeOS* tag_chromeos = new NfcTagChromeOS(object_path);
+ SetTag(object_path.value(), tag_chromeos);
+ FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
+ TagFound(this, tag_chromeos));
+ }
}
void NfcAdapterChromeOS::RemoveAdapter() {
@@ -219,22 +307,26 @@ void NfcAdapterChromeOS::RemoveAdapter() {
// Copy the tags and peers here and clear the original containers so that
// GetPeers and GetTags return no values during the *Removed observer calls.
- PeersMap peers = peers_;
- TagsMap tags = tags_;
- peers_.clear();
- tags_.clear();
-
- for (PeersMap::iterator iter = peers_.begin();
- iter != peers_.end(); ++iter) {
+ PeerList peers;
+ TagList tags;
+ GetPeers(&peers);
+ GetTags(&tags);
+ ClearPeers();
+ ClearTags();
+
+ for (PeerList::iterator iter = peers.begin();
+ iter != peers.end(); ++iter) {
+ device::NfcPeer* peer = *iter;
FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
- PeerLost(this, iter->second));
- delete iter->second;
+ PeerLost(this, peer));
+ delete peer;
}
- for (TagsMap::iterator iter = tags_.begin();
- iter != tags_.end(); ++iter) {
+ for (TagList::iterator iter = tags.begin();
+ iter != tags.end(); ++iter) {
+ device::NfcTag* tag = *iter;
FOR_EACH_OBSERVER(NfcAdapter::Observer, observers_,
- TagLost(this, iter->second));
- delete iter->second;
+ TagLost(this, tag));
+ delete tag;
}
object_path_ = dbus::ObjectPath("");
@@ -270,7 +362,7 @@ void NfcAdapterChromeOS::OnSetPowered(const base::Closure& callback,
}
callback.Run();
} else {
- LOG(WARNING) << "Failed to power up the NFC antenna radio.";
+ LOG(ERROR) << "Failed to power up the NFC antenna radio.";
error_callback.Run();
}
}
@@ -283,8 +375,8 @@ void NfcAdapterChromeOS::OnStartPollingError(
const ErrorCallback& error_callback,
const std::string& error_name,
const std::string& error_message) {
- LOG(WARNING) << object_path_.value() << ": Failed to start polling: "
- << error_name << ": " << error_message;
+ LOG(ERROR) << object_path_.value() << ": Failed to start polling: "
+ << error_name << ": " << error_message;
error_callback.Run();
}
@@ -296,8 +388,8 @@ void NfcAdapterChromeOS::OnStopPollingError(
const ErrorCallback& error_callback,
const std::string& error_name,
const std::string& error_message) {
- LOG(WARNING) << object_path_.value() << ": Failed to stop polling: "
- << error_name << ": " << error_message;
+ LOG(ERROR) << object_path_.value() << ": Failed to stop polling: "
+ << error_name << ": " << error_message;
error_callback.Run();
}
diff --git a/chromium/device/nfc/nfc_adapter_chromeos.h b/chromium/device/nfc/nfc_adapter_chromeos.h
index d70a98d2f79..3511e5dbc6e 100644
--- a/chromium/device/nfc/nfc_adapter_chromeos.h
+++ b/chromium/device/nfc/nfc_adapter_chromeos.h
@@ -60,14 +60,10 @@ class NfcAdapterChromeOS : public device::NfcAdapter,
// NfcDeviceClient::Observer overrides.
virtual void DeviceAdded(const dbus::ObjectPath& object_path) OVERRIDE;
virtual void DeviceRemoved(const dbus::ObjectPath& object_path) OVERRIDE;
- virtual void DevicePropertyChanged(const dbus::ObjectPath& object_path,
- const std::string& property_name) OVERRIDE;
// NfcTagClient::Observer overrides.
virtual void TagAdded(const dbus::ObjectPath& object_path) OVERRIDE;
virtual void TagRemoved(const dbus::ObjectPath& object_path) OVERRIDE;
- virtual void TagPropertyChanged(const dbus::ObjectPath& object_path,
- const std::string& property_name) OVERRIDE;
// Set the tracked adapter to the one in |object_path|, this object will
// subsequently operate on that adapter until it is removed.
diff --git a/chromium/device/nfc/nfc_chromeos_unittest.cc b/chromium/device/nfc/nfc_chromeos_unittest.cc
index dd94bebe4a5..672780f81bd 100644
--- a/chromium/device/nfc/nfc_chromeos_unittest.cc
+++ b/chromium/device/nfc/nfc_chromeos_unittest.cc
@@ -2,23 +2,57 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/values.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_nfc_adapter_client.h"
+#include "chromeos/dbus/fake_nfc_device_client.h"
+#include "chromeos/dbus/fake_nfc_record_client.h"
+#include "chromeos/dbus/fake_nfc_tag_client.h"
#include "device/nfc/nfc_adapter_chromeos.h"
+#include "device/nfc/nfc_ndef_record.h"
+#include "device/nfc/nfc_ndef_record_utils_chromeos.h"
+#include "device/nfc/nfc_peer.h"
+#include "device/nfc/nfc_tag.h"
+#include "device/nfc/nfc_tag_technology.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
using device::NfcAdapter;
+using device::NfcNdefMessage;
+using device::NfcNdefRecord;
+using device::NfcNdefTagTechnology;
+using device::NfcPeer;
+using device::NfcTag;
namespace chromeos {
namespace {
-class TestObserver : public NfcAdapter::Observer {
+// Callback passed to property structures.
+void OnPropertyChangedCallback(const std::string& property_name) {
+}
+
+// Callback passed to dbus::PropertyBase::Set.
+void OnSet(bool success) {
+}
+
+class TestObserver : public NfcAdapter::Observer,
+ public NfcPeer::Observer,
+ public NfcTag::Observer,
+ public NfcNdefTagTechnology::Observer {
public:
TestObserver(scoped_refptr<NfcAdapter> adapter)
: present_changed_count_(0),
powered_changed_count_(0),
+ polling_changed_count_(0),
+ peer_records_received_count_(0),
+ tag_records_received_count_(0),
+ peer_count_(0),
+ tag_count_(0),
adapter_(adapter) {
}
@@ -38,8 +72,68 @@ class TestObserver : public NfcAdapter::Observer {
powered_changed_count_++;
}
+ // NfcAdapter::Observer override.
+ virtual void AdapterPollingChanged(NfcAdapter* adapter,
+ bool powered) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+ polling_changed_count_++;
+ }
+
+ // NfcAdapter::Observer override.
+ virtual void PeerFound(NfcAdapter* adapter, NfcPeer* peer) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+ peer_count_++;
+ peer_identifier_ = peer->GetIdentifier();
+ }
+
+ // NfcAdapter::Observer override.
+ virtual void PeerLost(NfcAdapter* adapter, NfcPeer* peer) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+ EXPECT_EQ(peer_identifier_, peer->GetIdentifier());
+ peer_count_--;
+ peer_identifier_.clear();
+ }
+
+ // NfcAdapter::Observer override.
+ virtual void TagFound(NfcAdapter* adapter, NfcTag* tag) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+ tag_count_++;
+ tag_identifier_ = tag->GetIdentifier();
+ }
+
+ // NfcAdapter::Observer override.
+ virtual void TagLost(NfcAdapter* adapter, NfcTag* tag) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+ EXPECT_EQ(tag_identifier_, tag->GetIdentifier());
+ tag_count_--;
+ tag_identifier_.clear();
+ }
+
+ // NfcPeer::Observer override.
+ virtual void RecordReceived(
+ NfcPeer* peer, const NfcNdefRecord* record) OVERRIDE {
+ EXPECT_EQ(peer, adapter_->GetPeer(peer_identifier_));
+ EXPECT_EQ(peer_identifier_, peer->GetIdentifier());
+ peer_records_received_count_++;
+ }
+
+ // NfcNdefTagTechnology::Observer override.
+ virtual void RecordReceived(
+ NfcTag* tag, const NfcNdefRecord* record) OVERRIDE {
+ EXPECT_EQ(tag, adapter_->GetTag(tag_identifier_));
+ EXPECT_EQ(tag_identifier_, tag->GetIdentifier());
+ tag_records_received_count_++;
+ }
+
int present_changed_count_;
int powered_changed_count_;
+ int polling_changed_count_;
+ int peer_records_received_count_;
+ int tag_records_received_count_;
+ int peer_count_;
+ int tag_count_;
+ std::string peer_identifier_;
+ std::string tag_identifier_;
scoped_refptr<NfcAdapter> adapter_;
};
@@ -51,9 +145,16 @@ class NfcChromeOSTest : public testing::Test {
DBusThreadManager::InitializeWithStub();
fake_nfc_adapter_client_ = static_cast<FakeNfcAdapterClient*>(
DBusThreadManager::Get()->GetNfcAdapterClient());
- SetAdapter();
- message_loop_.RunUntilIdle();
-
+ fake_nfc_device_client_ = static_cast<FakeNfcDeviceClient*>(
+ DBusThreadManager::Get()->GetNfcDeviceClient());
+ fake_nfc_record_client_ = static_cast<FakeNfcRecordClient*>(
+ DBusThreadManager::Get()->GetNfcRecordClient());
+ fake_nfc_tag_client_ = static_cast<FakeNfcTagClient*>(
+ DBusThreadManager::Get()->GetNfcTagClient());
+
+ fake_nfc_adapter_client_->EnablePairingOnPoll(false);
+ fake_nfc_device_client_->DisableSimulationTimeout();
+ fake_nfc_tag_client_->DisableSimulationTimeout();
success_callback_count_ = 0;
error_callback_count_ = 0;
}
@@ -68,6 +169,7 @@ class NfcChromeOSTest : public testing::Test {
adapter_ = new NfcAdapterChromeOS();
ASSERT_TRUE(adapter_.get() != NULL);
ASSERT_TRUE(adapter_->IsInitialized());
+ base::RunLoop().RunUntilIdle();
}
// Generic callbacks for success and error.
@@ -79,23 +181,36 @@ class NfcChromeOSTest : public testing::Test {
error_callback_count_++;
}
+ void ErrorCallbackWithParameters(const std::string& error_name,
+ const std::string& error_message) {
+ LOG(INFO) << "Error callback called: " << error_name << ", "
+ << error_message;
+ error_callback_count_++;
+ }
+
protected:
+ // MessageLoop instance, used to simulate asynchronous behavior.
+ base::MessageLoop message_loop_;
+
// Fields for storing the number of times SuccessCallback and ErrorCallback
// have been called.
int success_callback_count_;
int error_callback_count_;
- // A message loop to emulate asynchronous behavior.
- base::MessageLoop message_loop_;
-
// The NfcAdapter instance under test.
scoped_refptr<NfcAdapter> adapter_;
// The fake D-Bus client instances used for testing.
FakeNfcAdapterClient* fake_nfc_adapter_client_;
+ FakeNfcDeviceClient* fake_nfc_device_client_;
+ FakeNfcRecordClient* fake_nfc_record_client_;
+ FakeNfcTagClient* fake_nfc_tag_client_;
};
+// Tests that the adapter updates correctly to reflect the current "default"
+// adapter, when multiple adapters appear and disappear.
TEST_F(NfcChromeOSTest, PresentChanged) {
+ SetAdapter();
EXPECT_TRUE(adapter_->IsPresent());
TestObserver observer(adapter_);
@@ -122,7 +237,9 @@ TEST_F(NfcChromeOSTest, PresentChanged) {
EXPECT_FALSE(adapter_->IsPresent());
}
+// Tests that the adapter correctly reflects the power state.
TEST_F(NfcChromeOSTest, SetPowered) {
+ SetAdapter();
TestObserver observer(adapter_);
adapter_->AddObserver(&observer);
@@ -177,7 +294,9 @@ TEST_F(NfcChromeOSTest, SetPowered) {
EXPECT_EQ(2, error_callback_count_);
}
+// Tests that the power state updates correctly when the adapter disappears.
TEST_F(NfcChromeOSTest, PresentChangedWhilePowered) {
+ SetAdapter();
TestObserver observer(adapter_);
adapter_->AddObserver(&observer);
@@ -199,4 +318,546 @@ TEST_F(NfcChromeOSTest, PresentChangedWhilePowered) {
EXPECT_FALSE(adapter_->IsPresent());
}
+// Tests that peer and record objects are created for all peers and records
+// that already exist when the adapter is created.
+TEST_F(NfcChromeOSTest, PeersInitializedWhenAdapterCreated) {
+ // Set up the adapter client.
+ NfcAdapterClient::Properties* properties =
+ fake_nfc_adapter_client_->GetProperties(
+ dbus::ObjectPath(FakeNfcAdapterClient::kAdapterPath0));
+ properties->powered.Set(true, base::Bind(&OnSet));
+
+ fake_nfc_adapter_client_->StartPollLoop(
+ dbus::ObjectPath(FakeNfcAdapterClient::kAdapterPath0),
+ nfc_adapter::kModeInitiator,
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallbackWithParameters,
+ base::Unretained(this)));
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_TRUE(properties->powered.value());
+ EXPECT_TRUE(properties->polling.value());
+
+ // Start pairing simulation, which will add a fake device and fake records.
+ fake_nfc_device_client_->BeginPairingSimulation(0, 0);
+ base::RunLoop().RunUntilIdle();
+
+ // Create the adapter.
+ SetAdapter();
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ // Observer shouldn't have received any calls, as it got created AFTER the
+ // notifications were sent.
+ EXPECT_EQ(0, observer.present_changed_count_);
+ EXPECT_EQ(0, observer.powered_changed_count_);
+ EXPECT_EQ(0, observer.polling_changed_count_);
+ EXPECT_EQ(0, observer.peer_count_);
+
+ EXPECT_TRUE(adapter_->IsPresent());
+ EXPECT_TRUE(adapter_->IsPowered());
+ EXPECT_FALSE(adapter_->IsPolling());
+
+ NfcAdapter::PeerList peers;
+ adapter_->GetPeers(&peers);
+ EXPECT_EQ(static_cast<size_t>(1), peers.size());
+
+ NfcPeer* peer = peers[0];
+ const NfcNdefMessage& message = peer->GetNdefMessage();
+ EXPECT_EQ(static_cast<size_t>(3), message.records().size());
+}
+
+// Tests that tag and record objects are created for all tags and records that
+// already exist when the adapter is created.
+TEST_F(NfcChromeOSTest, TagsInitializedWhenAdapterCreated) {
+ const char kTestURI[] = "fake://path/for/testing";
+
+ // Set up the adapter client.
+ NfcAdapterClient::Properties* properties =
+ fake_nfc_adapter_client_->GetProperties(
+ dbus::ObjectPath(FakeNfcAdapterClient::kAdapterPath0));
+ properties->powered.Set(true, base::Bind(&OnSet));
+
+ fake_nfc_adapter_client_->StartPollLoop(
+ dbus::ObjectPath(FakeNfcAdapterClient::kAdapterPath0),
+ nfc_adapter::kModeInitiator,
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallbackWithParameters,
+ base::Unretained(this)));
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_TRUE(properties->powered.value());
+ EXPECT_TRUE(properties->polling.value());
+
+ // Add the fake tag.
+ fake_nfc_tag_client_->BeginPairingSimulation(0);
+ base::RunLoop().RunUntilIdle();
+
+ // Create a fake record.
+ base::DictionaryValue test_record_data;
+ test_record_data.SetString(nfc_record::kTypeProperty, nfc_record::kTypeUri);
+ test_record_data.SetString(nfc_record::kUriProperty, kTestURI);
+ fake_nfc_tag_client_->Write(
+ dbus::ObjectPath(FakeNfcTagClient::kTagPath),
+ test_record_data,
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallbackWithParameters,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+
+ // Create the adapter.
+ SetAdapter();
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ // Observer shouldn't have received any calls, as it got created AFTER the
+ // notifications were sent.
+ EXPECT_EQ(0, observer.present_changed_count_);
+ EXPECT_EQ(0, observer.powered_changed_count_);
+ EXPECT_EQ(0, observer.polling_changed_count_);
+ EXPECT_EQ(0, observer.peer_count_);
+
+ EXPECT_TRUE(adapter_->IsPresent());
+ EXPECT_TRUE(adapter_->IsPowered());
+ EXPECT_FALSE(adapter_->IsPolling());
+
+ NfcAdapter::TagList tags;
+ adapter_->GetTags(&tags);
+ EXPECT_EQ(static_cast<size_t>(1), tags.size());
+
+ NfcTag* tag = tags[0];
+ const NfcNdefMessage& message = tag->GetNdefTagTechnology()->GetNdefMessage();
+ EXPECT_EQ(static_cast<size_t>(1), message.records().size());
+
+ const NfcNdefRecord* record = message.records()[0];
+ std::string uri;
+ EXPECT_TRUE(record->data().GetString(NfcNdefRecord::kFieldURI, &uri));
+ EXPECT_EQ(kTestURI, uri);
+}
+
+// Tests that the adapter correctly updates its state when polling is started
+// and stopped.
+TEST_F(NfcChromeOSTest, StartAndStopPolling) {
+ SetAdapter();
+ EXPECT_TRUE(adapter_->IsPresent());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ // Start polling while not powered. Should fail.
+ EXPECT_FALSE(adapter_->IsPowered());
+ adapter_->StartPolling(
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_FALSE(adapter_->IsPolling());
+
+ // Start polling while powered. Should succeed.
+ adapter_->SetPowered(
+ true,
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_TRUE(adapter_->IsPowered());
+
+ adapter_->StartPolling(
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_TRUE(adapter_->IsPolling());
+
+ // Start polling while already polling. Should fail.
+ adapter_->StartPolling(
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+ EXPECT_EQ(2, error_callback_count_);
+ EXPECT_TRUE(adapter_->IsPolling());
+
+ // Stop polling. Should succeed.
+ adapter_->StopPolling(
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(3, success_callback_count_);
+ EXPECT_EQ(2, error_callback_count_);
+ EXPECT_FALSE(adapter_->IsPolling());
+
+ // Stop polling while not polling. Should fail.
+ adapter_->StopPolling(
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(3, success_callback_count_);
+ EXPECT_EQ(3, error_callback_count_);
+ EXPECT_FALSE(adapter_->IsPolling());
+}
+
+// Tests a simple peer pairing simulation.
+TEST_F(NfcChromeOSTest, PeerTest) {
+ SetAdapter();
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ adapter_->SetPowered(
+ true,
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ adapter_->StartPolling(
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+
+ EXPECT_TRUE(adapter_->IsPowered());
+ EXPECT_TRUE(adapter_->IsPolling());
+ EXPECT_EQ(0, observer.peer_count_);
+
+ // Add the fake device.
+ fake_nfc_device_client_->BeginPairingSimulation(0, -1);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, observer.peer_count_);
+ EXPECT_EQ(FakeNfcDeviceClient::kDevicePath, observer.peer_identifier_);
+
+ NfcPeer* peer = adapter_->GetPeer(observer.peer_identifier_);
+ CHECK(peer);
+ peer->AddObserver(&observer);
+
+ // Peer should have no records on it.
+ EXPECT_TRUE(peer->GetNdefMessage().records().empty());
+ EXPECT_EQ(0, observer.peer_records_received_count_);
+
+ // Make records visible.
+ fake_nfc_record_client_->SetDeviceRecordsVisible(true);
+ EXPECT_EQ(3, observer.peer_records_received_count_);
+ EXPECT_EQ(static_cast<size_t>(3), peer->GetNdefMessage().records().size());
+
+ // End the simulation. Peer should get removed.
+ fake_nfc_device_client_->EndPairingSimulation();
+ EXPECT_EQ(0, observer.peer_count_);
+ EXPECT_TRUE(observer.peer_identifier_.empty());
+
+ peer = adapter_->GetPeer(observer.peer_identifier_);
+ EXPECT_FALSE(peer);
+
+ // No record related notifications will be sent when a peer gets removed.
+ EXPECT_EQ(3, observer.peer_records_received_count_);
+}
+
+// Tests a simple tag pairing simulation.
+TEST_F(NfcChromeOSTest, TagTest) {
+ const char kTestURI[] = "fake://path/for/testing";
+
+ SetAdapter();
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ adapter_->SetPowered(
+ true,
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ adapter_->StartPolling(
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+
+ EXPECT_TRUE(adapter_->IsPowered());
+ EXPECT_TRUE(adapter_->IsPolling());
+ EXPECT_EQ(0, observer.tag_count_);
+
+ // Add the fake tag.
+ fake_nfc_tag_client_->BeginPairingSimulation(0);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, observer.tag_count_);
+ EXPECT_EQ(FakeNfcTagClient::kTagPath, observer.tag_identifier_);
+
+ NfcTag* tag = adapter_->GetTag(observer.tag_identifier_);
+ CHECK(tag);
+ tag->AddObserver(&observer);
+ EXPECT_TRUE(tag->IsReady());
+ CHECK(tag->GetNdefTagTechnology());
+ tag->GetNdefTagTechnology()->AddObserver(&observer);
+
+ NfcNdefTagTechnology* tag_technology = tag->GetNdefTagTechnology();
+ EXPECT_TRUE(tag_technology->IsSupportedByTag());
+
+ // Tag should have no records on it.
+ EXPECT_TRUE(tag_technology->GetNdefMessage().records().empty());
+ EXPECT_EQ(0, observer.tag_records_received_count_);
+
+ // Set the tag record visible. By default the record has no content, so no
+ // NfcNdefMessage should be received.
+ fake_nfc_record_client_->SetTagRecordsVisible(true);
+ EXPECT_TRUE(tag_technology->GetNdefMessage().records().empty());
+ EXPECT_EQ(0, observer.tag_records_received_count_);
+ fake_nfc_record_client_->SetTagRecordsVisible(false);
+
+ // Write an NDEF record to the tag.
+ EXPECT_EQ(2, success_callback_count_); // 2 for SetPowered and StartPolling.
+ EXPECT_EQ(0, error_callback_count_);
+
+ base::DictionaryValue record_data;
+ record_data.SetString(NfcNdefRecord::kFieldURI, kTestURI);
+ NfcNdefRecord written_record;
+ written_record.Populate(NfcNdefRecord::kTypeURI, &record_data);
+ NfcNdefMessage written_message;
+ written_message.AddRecord(&written_record);
+
+ tag_technology->WriteNdef(
+ written_message,
+ base::Bind(&NfcChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&NfcChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(3, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ EXPECT_EQ(static_cast<size_t>(1),
+ tag_technology->GetNdefMessage().records().size());
+ EXPECT_EQ(1, observer.tag_records_received_count_);
+
+ NfcNdefRecord* received_record =
+ tag_technology->GetNdefMessage().records()[0];
+ EXPECT_EQ(NfcNdefRecord::kTypeURI, received_record->type());
+ std::string uri;
+ EXPECT_TRUE(received_record->data().GetString(
+ NfcNdefRecord::kFieldURI, &uri));
+ EXPECT_EQ(kTestURI, uri);
+
+ // End the simulation. Tag should get removed.
+ fake_nfc_tag_client_->EndPairingSimulation();
+ EXPECT_EQ(0, observer.tag_count_);
+ EXPECT_TRUE(observer.tag_identifier_.empty());
+
+ tag = adapter_->GetTag(observer.tag_identifier_);
+ EXPECT_FALSE(tag);
+
+ // No record related notifications will be sent when a tag gets removed.
+ EXPECT_EQ(1, observer.tag_records_received_count_);
+}
+
+// Unit tests for nfc_ndef_record_utils methods.
+TEST_F(NfcChromeOSTest, NfcNdefRecordToDBusAttributes) {
+ const char kText[] = "text";
+ const char kURI[] = "test://uri";
+ const char kEncoding[] = "encoding";
+ const char kLanguageCode[] = "en";
+ const char kMimeType[] = "mime-type";
+ const double kSize = 5;
+
+ // Text record.
+ base::DictionaryValue data;
+ data.SetString(NfcNdefRecord::kFieldText, kText);
+ data.SetString(NfcNdefRecord::kFieldLanguageCode, kLanguageCode);
+ data.SetString(NfcNdefRecord::kFieldEncoding, kEncoding);
+
+ scoped_ptr<NfcNdefRecord> record(new NfcNdefRecord());
+ ASSERT_TRUE(record->Populate(NfcNdefRecord::kTypeText, &data));
+
+ base::DictionaryValue result;
+ EXPECT_TRUE(nfc_ndef_record_utils::NfcNdefRecordToDBusAttributes(
+ record.get(), &result));
+
+ std::string string_value;
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kTypeProperty, &string_value));
+ EXPECT_EQ(nfc_record::kTypeText, string_value);
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kRepresentationProperty, &string_value));
+ EXPECT_EQ(kText, string_value);
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kLanguageProperty, &string_value));
+ EXPECT_EQ(kLanguageCode, string_value);
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kEncodingProperty, &string_value));
+ EXPECT_EQ(kEncoding, string_value);
+
+ // URI record.
+ data.Clear();
+ data.SetString(NfcNdefRecord::kFieldURI, kURI);
+ data.SetString(NfcNdefRecord::kFieldMimeType, kMimeType);
+ data.SetDouble(NfcNdefRecord::kFieldTargetSize, kSize);
+
+ record.reset(new NfcNdefRecord());
+ ASSERT_TRUE(record->Populate(NfcNdefRecord::kTypeURI, &data));
+
+ result.Clear();
+ EXPECT_TRUE(nfc_ndef_record_utils::NfcNdefRecordToDBusAttributes(
+ record.get(), &result));
+
+ EXPECT_TRUE(result.GetString(nfc_record::kTypeProperty, &string_value));
+ EXPECT_EQ(nfc_record::kTypeUri, string_value);
+ EXPECT_TRUE(result.GetString(nfc_record::kUriProperty, &string_value));
+ EXPECT_EQ(kURI, string_value);
+ EXPECT_TRUE(result.GetString(nfc_record::kMimeTypeProperty, &string_value));
+ EXPECT_EQ(kMimeType, string_value);
+ double double_value;
+ EXPECT_TRUE(result.GetDouble(nfc_record::kSizeProperty, &double_value));
+ EXPECT_EQ(kSize, double_value);
+
+ // SmartPoster record.
+ base::DictionaryValue* title = new base::DictionaryValue();
+ title->SetString(NfcNdefRecord::kFieldText, kText);
+ title->SetString(NfcNdefRecord::kFieldLanguageCode, kLanguageCode);
+ title->SetString(NfcNdefRecord::kFieldEncoding, kEncoding);
+
+ base::ListValue* titles = new base::ListValue();
+ titles->Append(title);
+ data.Set(NfcNdefRecord::kFieldTitles, titles);
+
+ record.reset(new NfcNdefRecord());
+ ASSERT_TRUE(record->Populate(NfcNdefRecord::kTypeSmartPoster, &data));
+
+ result.Clear();
+ EXPECT_TRUE(nfc_ndef_record_utils::NfcNdefRecordToDBusAttributes(
+ record.get(), &result));
+
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kTypeProperty, &string_value));
+ EXPECT_EQ(nfc_record::kTypeSmartPoster, string_value);
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kRepresentationProperty, &string_value));
+ EXPECT_EQ(kText, string_value);
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kLanguageProperty, &string_value));
+ EXPECT_EQ(kLanguageCode, string_value);
+ EXPECT_TRUE(result.GetString(
+ nfc_record::kEncodingProperty, &string_value));
+ EXPECT_EQ(kEncoding, string_value);
+ EXPECT_TRUE(result.GetString(nfc_record::kUriProperty, &string_value));
+ EXPECT_EQ(kURI, string_value);
+ EXPECT_TRUE(result.GetString(nfc_record::kMimeTypeProperty, &string_value));
+ EXPECT_EQ(kMimeType, string_value);
+ EXPECT_TRUE(result.GetDouble(nfc_record::kSizeProperty, &double_value));
+ EXPECT_EQ(kSize, double_value);
+}
+
+TEST_F(NfcChromeOSTest, RecordPropertiesToNfcNdefRecord) {
+ const char kText[] = "text";
+ const char kURI[] = "test://uri";
+ const char kEncoding[] = "encoding";
+ const char kLanguageCode[] = "en";
+ const char kMimeType[] = "mime-type";
+ const uint32 kSize = 5;
+
+ FakeNfcRecordClient::Properties record_properties(
+ base::Bind(&OnPropertyChangedCallback));
+
+ // Text record.
+ record_properties.type.ReplaceValue(nfc_record::kTypeText);
+ record_properties.representation.ReplaceValue(kText);
+ record_properties.language.ReplaceValue(kLanguageCode);
+ record_properties.encoding.ReplaceValue(kEncoding);
+
+ scoped_ptr<NfcNdefRecord> record(new NfcNdefRecord());
+ EXPECT_TRUE(nfc_ndef_record_utils::RecordPropertiesToNfcNdefRecord(
+ &record_properties, record.get()));
+ EXPECT_TRUE(record->IsPopulated());
+
+ std::string string_value;
+ EXPECT_EQ(NfcNdefRecord::kTypeText, record->type());
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldText, &string_value));
+ EXPECT_EQ(kText, string_value);
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldLanguageCode, &string_value));
+ EXPECT_EQ(kLanguageCode, string_value);
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldEncoding, &string_value));
+ EXPECT_EQ(kEncoding, string_value);
+
+ // URI record.
+ record_properties.representation.ReplaceValue("");
+ record_properties.language.ReplaceValue("");
+ record_properties.encoding.ReplaceValue("");
+
+ record_properties.type.ReplaceValue(nfc_record::kTypeUri);
+ record_properties.uri.ReplaceValue(kURI);
+ record_properties.mime_type.ReplaceValue(kMimeType);
+ record_properties.size.ReplaceValue(kSize);
+
+ record.reset(new NfcNdefRecord());
+ EXPECT_TRUE(nfc_ndef_record_utils::RecordPropertiesToNfcNdefRecord(
+ &record_properties, record.get()));
+ EXPECT_TRUE(record->IsPopulated());
+
+ EXPECT_EQ(NfcNdefRecord::kTypeURI, record->type());
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldURI, &string_value));
+ EXPECT_EQ(kURI, string_value);
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldMimeType, &string_value));
+ EXPECT_EQ(kMimeType, string_value);
+ double double_value;
+ EXPECT_TRUE(record->data().GetDouble(
+ NfcNdefRecord::kFieldTargetSize, &double_value));
+ EXPECT_EQ(kSize, double_value);
+
+ // Contents not matching type.
+ record_properties.representation.ReplaceValue(kText);
+ record_properties.language.ReplaceValue(kLanguageCode);
+ record_properties.encoding.ReplaceValue(kEncoding);
+
+ record.reset(new NfcNdefRecord());
+ EXPECT_FALSE(nfc_ndef_record_utils::RecordPropertiesToNfcNdefRecord(
+ &record_properties, record.get()));
+ EXPECT_FALSE(record->IsPopulated());
+
+ // SmartPoster record.
+ record_properties.type.ReplaceValue(nfc_record::kTypeSmartPoster);
+ EXPECT_TRUE(nfc_ndef_record_utils::RecordPropertiesToNfcNdefRecord(
+ &record_properties, record.get()));
+ EXPECT_TRUE(record->IsPopulated());
+
+ EXPECT_EQ(NfcNdefRecord::kTypeSmartPoster, record->type());
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldURI, &string_value));
+ EXPECT_EQ(kURI, string_value);
+ EXPECT_TRUE(record->data().GetString(
+ NfcNdefRecord::kFieldMimeType, &string_value));
+ EXPECT_EQ(kMimeType, string_value);
+ EXPECT_TRUE(record->data().GetDouble(
+ NfcNdefRecord::kFieldTargetSize, &double_value));
+ EXPECT_EQ(kSize, double_value);
+
+ const base::ListValue* titles = NULL;
+ EXPECT_TRUE(record->data().GetList(NfcNdefRecord::kFieldTitles, &titles));
+ EXPECT_EQ(static_cast<size_t>(1), titles->GetSize());
+ ASSERT_TRUE(titles);
+ const base::DictionaryValue* title = NULL;
+ EXPECT_TRUE(titles->GetDictionary(0, &title));
+ CHECK(title);
+
+ EXPECT_TRUE(title->GetString(NfcNdefRecord::kFieldText, &string_value));
+ EXPECT_EQ(kText, string_value);
+ EXPECT_TRUE(title->GetString(
+ NfcNdefRecord::kFieldLanguageCode, &string_value));
+ EXPECT_EQ(kLanguageCode, string_value);
+ EXPECT_TRUE(title->GetString(NfcNdefRecord::kFieldEncoding, &string_value));
+ EXPECT_EQ(kEncoding, string_value);
+}
+
} // namespace chromeos
diff --git a/chromium/device/nfc/nfc_ndef_record.cc b/chromium/device/nfc/nfc_ndef_record.cc
index ec13c2f3587..5b8bb994a81 100644
--- a/chromium/device/nfc/nfc_ndef_record.cc
+++ b/chromium/device/nfc/nfc_ndef_record.cc
@@ -7,6 +7,7 @@
#include <map>
#include "base/logging.h"
+#include "url/gurl.h"
using base::DictionaryValue;
using base::ListValue;
@@ -17,6 +18,23 @@ namespace {
typedef std::map<std::string, base::Value::Type> FieldValueMap;
+bool ValidateURI(const DictionaryValue* data) {
+ std::string uri;
+ if (!data->GetString(NfcNdefRecord::kFieldURI, &uri)) {
+ VLOG(1) << "No URI entry in data.";
+ return false;
+ }
+ DCHECK(!uri.empty());
+
+ // Use GURL to check validity.
+ GURL url(uri);
+ if (!url.is_valid()) {
+ LOG(ERROR) << "Invalid URI given: " << uri;
+ return false;
+ }
+ return true;
+}
+
bool CheckFieldsAreValid(
const FieldValueMap& required_fields,
const FieldValueMap& optional_fields,
@@ -47,6 +65,12 @@ bool CheckFieldsAreValid(
<< field_iter->second;
return false;
}
+ // Make sure that the value is non-empty, if the value is a string.
+ std::string string_value;
+ if (iter.value().GetAsString(&string_value) && string_value.empty()) {
+ VLOG(1) << "Empty value given for field of type string: " << iter.key();
+ return false;
+ }
}
// Check for required fields.
if (required_count != required_fields.size()) {
@@ -63,13 +87,23 @@ bool HandleTypeText(const DictionaryValue* data) {
VLOG(1) << "Populating record with type \"Text\".";
FieldValueMap required_fields;
required_fields[NfcNdefRecord::kFieldText] = base::Value::TYPE_STRING;
+ required_fields[NfcNdefRecord::kFieldEncoding] = base::Value::TYPE_STRING;
+ required_fields[NfcNdefRecord::kFieldLanguageCode] = base::Value::TYPE_STRING;
FieldValueMap optional_fields;
- optional_fields[NfcNdefRecord::kFieldEncoding] = base::Value::TYPE_STRING;
- optional_fields[NfcNdefRecord::kFieldLanguageCode] = base::Value::TYPE_STRING;
if (!CheckFieldsAreValid(required_fields, optional_fields, data)) {
VLOG(1) << "Failed to populate record.";
return false;
}
+
+ // Verify that the "Encoding" property has valid values.
+ std::string encoding;
+ if (!data->GetString(NfcNdefRecord::kFieldEncoding, &encoding)) {
+ if (encoding != NfcNdefRecord::kEncodingUtf8 ||
+ encoding != NfcNdefRecord::kEncodingUtf16) {
+ VLOG(1) << "Invalid \"Encoding\" value:" << encoding;
+ return false;
+ }
+ }
return true;
}
@@ -113,7 +147,7 @@ bool HandleTypeSmartPoster(const DictionaryValue* data) {
}
}
}
- return true;
+ return ValidateURI(data);
}
// Verifies that the contents of |data| conform to the fields of NDEF type
@@ -125,11 +159,13 @@ bool HandleTypeUri(const DictionaryValue* data) {
FieldValueMap optional_fields;
optional_fields[NfcNdefRecord::kFieldMimeType] = base::Value::TYPE_STRING;
optional_fields[NfcNdefRecord::kFieldTargetSize] = base::Value::TYPE_DOUBLE;
+
+ // Allow passing TargetSize as an integer, but convert it to a double.
if (!CheckFieldsAreValid(required_fields, optional_fields, data)) {
VLOG(1) << "Failed to populate record.";
return false;
}
- return true;
+ return ValidateURI(data);
}
} // namespace
@@ -210,4 +246,15 @@ void NfcNdefMessage::AddRecord(NfcNdefRecord* record) {
records_.push_back(record);
}
+bool NfcNdefMessage::RemoveRecord(NfcNdefRecord* record) {
+ for (RecordList::iterator iter = records_.begin();
+ iter != records_.end(); ++iter) {
+ if (*iter == record) {
+ records_.erase(iter);
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace device
diff --git a/chromium/device/nfc/nfc_ndef_record.h b/chromium/device/nfc/nfc_ndef_record.h
index 2be66b175a3..24cbc12090c 100644
--- a/chromium/device/nfc/nfc_ndef_record.h
+++ b/chromium/device/nfc/nfc_ndef_record.h
@@ -146,9 +146,15 @@ class NfcNdefMessage {
const RecordList& records() const { return records_; }
// Adds the NDEF record |record| to the sequence of records that this
- // NdefMessage contains.
+ // NfcNdefMessage contains. This method simply adds the record to this message
+ // and does NOT take ownership of it.
void AddRecord(NfcNdefRecord* record);
+ // Removes the NDEF record |record| from this message. Returns true, if the
+ // record was removed, otherwise returns false if |record| was not contained
+ // in this message.
+ bool RemoveRecord(NfcNdefRecord* record);
+
private:
// The NDEF records that are contained by this message.
RecordList records_;
diff --git a/chromium/device/nfc/nfc_ndef_record_unittest.cc b/chromium/device/nfc/nfc_ndef_record_unittest.cc
index 00c2d2f5133..2b6ef48b2bb 100644
--- a/chromium/device/nfc/nfc_ndef_record_unittest.cc
+++ b/chromium/device/nfc/nfc_ndef_record_unittest.cc
@@ -21,7 +21,7 @@ const char kTestLanguageCode[] = "test-language-code";
const char kTestMimeType[] = "test-mime-type";
const uint32 kTestTargetSize = 0;
const char kTestText[] = "test-text";
-const char kTestURI[] = "test-uri";
+const char kTestURI[] = "test://uri";
} // namespace
@@ -33,35 +33,32 @@ TEST(NfcNdefRecordTest, PopulateTextRecord) {
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeText, &data));
EXPECT_FALSE(record->IsPopulated());
- // Text field with incorrect entry. Should fail.
+ // Text field with incorrect type. Should fail.
data.SetInteger(NfcNdefRecord::kFieldText, 0);
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeText, &data));
EXPECT_FALSE(record->IsPopulated());
- // Text field with correct entry. Should succeed.
+ // Text field with correct type but missing encoding and language.
+ // Should fail.
data.SetString(NfcNdefRecord::kFieldText, kTestText);
- EXPECT_TRUE(record->Populate(NfcNdefRecord::kTypeText, &data));
- EXPECT_TRUE(record->IsPopulated());
- EXPECT_EQ(NfcNdefRecord::kTypeText, record->type());
+ EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeText, &data));
+ EXPECT_FALSE(record->IsPopulated());
// Populating a successfully populated record should fail.
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeText, &data));
- // Recycle the record.
- record.reset(new NfcNdefRecord());
- EXPECT_FALSE(record->IsPopulated());
-
- // Incorrect optional fields. Should fail.
+ // Incorrect type for language code.
data.SetInteger(NfcNdefRecord::kFieldLanguageCode, 0);
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeText, &data));
EXPECT_FALSE(record->IsPopulated());
+ // Correct type for language code, invalid encoding.
data.SetString(NfcNdefRecord::kFieldLanguageCode, kTestLanguageCode);
data.SetInteger(NfcNdefRecord::kFieldEncoding, 0);
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeText, &data));
EXPECT_FALSE(record->IsPopulated());
- // Optional fields are correct. Should succeed.
+ // All entries valid. Should succeed.
data.SetString(NfcNdefRecord::kFieldEncoding, kTestEncoding);
EXPECT_TRUE(record->Populate(NfcNdefRecord::kTypeText, &data));
EXPECT_TRUE(record->IsPopulated());
@@ -88,12 +85,16 @@ TEST(NfcNdefRecordTest, PopulateUriRecord) {
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeURI, &data));
EXPECT_FALSE(record->IsPopulated());
- // URI field with incorrect entry. Should fail.
+ // URI field with incorrect type. Should fail.
data.SetInteger(NfcNdefRecord::kFieldURI, 0);
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeURI, &data));
EXPECT_FALSE(record->IsPopulated());
- // URI field with correct entry. Should succeed.
+ // URI field with correct type but invalid format.
+ data.SetString(NfcNdefRecord::kFieldURI, "test.uri");
+ EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeURI, &data));
+ EXPECT_FALSE(record->IsPopulated());
+
data.SetString(NfcNdefRecord::kFieldURI, kTestURI);
EXPECT_TRUE(record->Populate(NfcNdefRecord::kTypeURI, &data));
EXPECT_TRUE(record->IsPopulated());
@@ -203,8 +204,10 @@ TEST(NfcNdefRecordTest, PopulateSmartPoster) {
EXPECT_FALSE(record->Populate(NfcNdefRecord::kTypeSmartPoster, &data));
EXPECT_FALSE(record->IsPopulated());
- // Title value with valid "text" field.
+ // Title value with valid entries.
title_value->SetString(NfcNdefRecord::kFieldText, kTestText);
+ title_value->SetString(NfcNdefRecord::kFieldLanguageCode, kTestLanguageCode);
+ title_value->SetString(NfcNdefRecord::kFieldEncoding, kTestEncoding);
EXPECT_TRUE(record->Populate(NfcNdefRecord::kTypeSmartPoster, &data));
EXPECT_TRUE(record->IsPopulated());
@@ -233,6 +236,12 @@ TEST(NfcNdefRecordTest, PopulateSmartPoster) {
EXPECT_TRUE(const_dictionary_value->GetString(
NfcNdefRecord::kFieldText, &string_value));
EXPECT_EQ(kTestText, string_value);
+ EXPECT_TRUE(const_dictionary_value->GetString(
+ NfcNdefRecord::kFieldLanguageCode, &string_value));
+ EXPECT_EQ(kTestLanguageCode, string_value);
+ EXPECT_TRUE(const_dictionary_value->GetString(
+ NfcNdefRecord::kFieldEncoding, &string_value));
+ EXPECT_EQ(kTestEncoding, string_value);
}
} // namespace device
diff --git a/chromium/device/nfc/nfc_ndef_record_utils_chromeos.cc b/chromium/device/nfc/nfc_ndef_record_utils_chromeos.cc
new file mode 100644
index 00000000000..466cf38b971
--- /dev/null
+++ b/chromium/device/nfc/nfc_ndef_record_utils_chromeos.cc
@@ -0,0 +1,238 @@
+// Copyright 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/nfc/nfc_ndef_record_utils_chromeos.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "device/nfc/nfc_ndef_record.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+using device::NfcNdefRecord;
+
+namespace chromeos {
+namespace nfc_ndef_record_utils {
+
+namespace {
+
+// Maps the NDEF type |type| as returned by neard to the corresponding
+// device::NfcNdefRecord::Type value.
+NfcNdefRecord::Type DBusRecordTypeValueToNfcNdefRecordType(
+ const std::string& type) {
+ if (type == nfc_record::kTypeSmartPoster)
+ return NfcNdefRecord::kTypeSmartPoster;
+ if (type == nfc_record::kTypeText)
+ return NfcNdefRecord::kTypeText;
+ if (type == nfc_record::kTypeUri)
+ return NfcNdefRecord::kTypeURI;
+ if (type == nfc_record::kTypeHandoverRequest)
+ return NfcNdefRecord::kTypeHandoverRequest;
+ if (type == nfc_record::kTypeHandoverSelect)
+ return NfcNdefRecord::kTypeHandoverSelect;
+ if (type == nfc_record::kTypeHandoverCarrier)
+ return NfcNdefRecord::kTypeHandoverCarrier;
+ return NfcNdefRecord::kTypeUnknown;
+}
+
+// Maps the NDEF type |type| given as a NFC C++ API enumeration to the
+// corresponding string value defined by neard.
+std::string NfcRecordTypeEnumToPropertyValue(NfcNdefRecord::Type type) {
+ switch (type) {
+ case NfcNdefRecord::kTypeSmartPoster:
+ return nfc_record::kTypeSmartPoster;
+ case NfcNdefRecord::kTypeText:
+ return nfc_record::kTypeText;
+ case NfcNdefRecord::kTypeURI:
+ return nfc_record::kTypeUri;
+ case NfcNdefRecord::kTypeHandoverRequest:
+ return nfc_record::kTypeHandoverRequest;
+ case NfcNdefRecord::kTypeHandoverSelect:
+ return nfc_record::kTypeHandoverSelect;
+ case NfcNdefRecord::kTypeHandoverCarrier:
+ return nfc_record::kTypeHandoverCarrier;
+ default:
+ return "";
+ }
+}
+
+// Maps the field name |field_name| given as defined in NfcNdefRecord to the
+// Neard Record D-Bus property name. This handles all fields except for
+// NfcNdefRecord::kFieldTitles and NfcNdefRecord::kFieldAction, which need
+// special handling.
+std::string NdefRecordFieldToDBusProperty(const std::string& field_name) {
+ if (field_name == NfcNdefRecord::kFieldEncoding)
+ return nfc_record::kEncodingProperty;
+ if (field_name == NfcNdefRecord::kFieldLanguageCode)
+ return nfc_record::kLanguageProperty;
+ if (field_name == NfcNdefRecord::kFieldText)
+ return nfc_record::kRepresentationProperty;
+ if (field_name == NfcNdefRecord::kFieldURI)
+ return nfc_record::kUriProperty;
+ if (field_name == NfcNdefRecord::kFieldMimeType)
+ return nfc_record::kMimeTypeProperty;
+ if (field_name == NfcNdefRecord::kFieldTargetSize)
+ return nfc_record::kSizeProperty;
+ return "";
+}
+
+std::string NfcNdefRecordActionValueToDBusActionValue(
+ const std::string& action) {
+ // TODO(armansito): Add bindings for values returned by neard to
+ // service_constants.h.
+ if (action == device::NfcNdefRecord::kSmartPosterActionDo)
+ return "Do";
+ if (action == device::NfcNdefRecord::kSmartPosterActionSave)
+ return "Save";
+ if (action == device::NfcNdefRecord::kSmartPosterActionOpen)
+ return "Edit";
+ return "";
+}
+
+std::string DBusActionValueToNfcNdefRecordActionValue(
+ const std::string& action) {
+ // TODO(armansito): Add bindings for values returned by neard to
+ // service_constants.h.
+ if (action == "Do")
+ return device::NfcNdefRecord::kSmartPosterActionDo;
+ if (action == "Save")
+ return device::NfcNdefRecord::kSmartPosterActionSave;
+ if (action == "Edit")
+ return device::NfcNdefRecord::kSmartPosterActionOpen;
+ return "";
+}
+
+
+// Translates the given dictionary of NDEF fields by recursively converting
+// each key in |record_data| to its corresponding Record property name defined
+// by the Neard D-Bus API. The output is stored in |out|. Returns false if an
+// error occurs.
+bool ConvertNdefFieldsToDBusAttributes(
+ const base::DictionaryValue& fields,
+ base::DictionaryValue* out) {
+ DCHECK(out);
+ for (base::DictionaryValue::Iterator iter(fields);
+ !iter.IsAtEnd(); iter.Advance()) {
+ // Special case the "titles" and "action" fields.
+ if (iter.key() == NfcNdefRecord::kFieldTitles) {
+ const base::ListValue* titles = NULL;
+ bool value_result = iter.value().GetAsList(&titles);
+ DCHECK(value_result);
+ DCHECK(titles->GetSize() != 0);
+ // TODO(armansito): For now, pick the first title in the list and write
+ // its contents directly to the top level of the field. This is due to an
+ // error in the Neard D-Bus API design. This code will need to be updated
+ // if the neard API changes to correct this.
+ const base::DictionaryValue* first_title = NULL;
+ value_result = titles->GetDictionary(0, &first_title);
+ DCHECK(value_result);
+ if (!ConvertNdefFieldsToDBusAttributes(*first_title, out)) {
+ LOG(ERROR) << "Invalid title field.";
+ return false;
+ }
+ } else if (iter.key() == NfcNdefRecord::kFieldAction) {
+ // The value of the action field needs to be translated.
+ std::string action_value;
+ bool value_result = iter.value().GetAsString(&action_value);
+ DCHECK(value_result);
+ std::string action =
+ NfcNdefRecordActionValueToDBusActionValue(action_value);
+ if (action.empty()) {
+ VLOG(1) << "Invalid action value: \"" << action_value << "\"";
+ return false;
+ }
+ out->SetString(nfc_record::kActionProperty, action);
+ } else {
+ std::string dbus_property = NdefRecordFieldToDBusProperty(iter.key());
+ if (dbus_property.empty()) {
+ LOG(ERROR) << "Invalid field: " << iter.key();
+ return false;
+ }
+ out->Set(dbus_property, iter.value().DeepCopy());
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+bool NfcNdefRecordToDBusAttributes(
+ const NfcNdefRecord* record,
+ base::DictionaryValue* out) {
+ DCHECK(record);
+ DCHECK(out);
+ if (!record->IsPopulated()) {
+ LOG(ERROR) << "Record is not populated.";
+ return false;
+ }
+ out->SetString(nfc_record::kTypeProperty,
+ NfcRecordTypeEnumToPropertyValue(record->type()));
+ return ConvertNdefFieldsToDBusAttributes(record->data(), out);
+}
+
+bool RecordPropertiesToNfcNdefRecord(
+ const NfcRecordClient::Properties* properties,
+ device::NfcNdefRecord* out) {
+ if (out->IsPopulated()) {
+ LOG(ERROR) << "Record is already populated!";
+ return false;
+ }
+ NfcNdefRecord::Type type =
+ DBusRecordTypeValueToNfcNdefRecordType(properties->type.value());
+ if (type == NfcNdefRecord::kTypeUnknown) {
+ LOG(ERROR) << "Record type is unknown.";
+ return false;
+ }
+
+ // Extract each property.
+ base::DictionaryValue attributes;
+ if (!properties->uri.value().empty())
+ attributes.SetString(NfcNdefRecord::kFieldURI, properties->uri.value());
+ if (!properties->mime_type.value().empty()) {
+ attributes.SetString(NfcNdefRecord::kFieldMimeType,
+ properties->mime_type.value());
+ }
+ if (properties->size.value() != 0) {
+ attributes.SetDouble(NfcNdefRecord::kFieldTargetSize,
+ static_cast<double>(properties->size.value()));
+ }
+ std::string action_value =
+ DBusActionValueToNfcNdefRecordActionValue(properties->action.value());
+ if (!action_value.empty())
+ attributes.SetString(NfcNdefRecord::kFieldAction, action_value);
+
+ // The "representation", "encoding", and "language" properties will be stored
+ // differently, depending on whether the record type is "SmartPoster" or
+ // "Text".
+ {
+ scoped_ptr<base::DictionaryValue> text_attributes(
+ new base::DictionaryValue());
+ if (!properties->representation.value().empty()) {
+ text_attributes->SetString(NfcNdefRecord::kFieldText,
+ properties->representation.value());
+ }
+ if (!properties->encoding.value().empty()) {
+ text_attributes->SetString(NfcNdefRecord::kFieldEncoding,
+ properties->encoding.value());
+ }
+ if (!properties->language.value().empty()) {
+ text_attributes->SetString(NfcNdefRecord::kFieldLanguageCode,
+ properties->language.value());
+ }
+ if (!text_attributes->empty()) {
+ if (type == NfcNdefRecord::kTypeSmartPoster) {
+ base::ListValue* titles = new base::ListValue();
+ titles->Append(text_attributes.release());
+ attributes.Set(NfcNdefRecord::kFieldTitles, titles);
+ } else {
+ attributes.MergeDictionary(text_attributes.get());
+ }
+ }
+ }
+
+ // Populate the given record.
+ return out->Populate(type, &attributes);
+}
+
+} // namespace nfc_ndef_record_utils
+} // namespace chromeos
diff --git a/chromium/device/nfc/nfc_ndef_record_utils_chromeos.h b/chromium/device/nfc/nfc_ndef_record_utils_chromeos.h
new file mode 100644
index 00000000000..2d96b97a323
--- /dev/null
+++ b/chromium/device/nfc/nfc_ndef_record_utils_chromeos.h
@@ -0,0 +1,36 @@
+// Copyright 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/values.h"
+#include "chromeos/dbus/nfc_record_client.h"
+
+#ifndef DEVICE_NFC_CHROMEOS_NDEF_RECORD_UTILS_CHROMEOS_H_
+#define DEVICE_NFC_CHROMEOS_NDEF_RECORD_UTILS_CHROMEOS_H_
+
+namespace device {
+class NfcNdefRecord;
+} // namespace device;
+
+namespace chromeos {
+namespace nfc_ndef_record_utils {
+
+// Converts the NfcNdefRecord |record| to a dictionary that can be passed to
+// NfcDeviceClient::Push and NfcTagClient::Write and stores it in |out|.
+// Returns false, if an error occurs during conversion.
+bool NfcNdefRecordToDBusAttributes(
+ const device::NfcNdefRecord* record,
+ base::DictionaryValue* out);
+
+// Converts an NDEF record D-Bus properties structure to an NfcNdefRecord
+// instance by populating the instance passed in |out|. |out| must not be NULL
+// and must not be already populated. Returns false, if an error occurs during
+// conversion.
+bool RecordPropertiesToNfcNdefRecord(
+ const NfcRecordClient::Properties* properties,
+ device::NfcNdefRecord* out);
+
+} // namespace nfc_ndef_record_utils
+} // namespace chromeos
+
+#endif // DEVICE_NFC_CHROMEOS_NDEF_RECORD_UTILS_CHROMEOS_H_
diff --git a/chromium/device/nfc/nfc_peer.h b/chromium/device/nfc/nfc_peer.h
index 157873872ee..a38192f166a 100644
--- a/chromium/device/nfc/nfc_peer.h
+++ b/chromium/device/nfc/nfc_peer.h
@@ -37,12 +37,12 @@ class NfcPeer {
public:
virtual ~Observer() {}
- // This method will be called when an NDEF message |message| from the peer
+ // This method will be called when an NDEF record |record| from the peer
// device |peer| is received. Users can use this method to be notified of
// new records on the device and when the initial set of records are
- // received from it, if any.
- virtual void RecordsReceived(NfcPeer* peer,
- const NfcNdefMessage& message) {}
+ // received from it, if any. All records received from |peer| can be
+ // accessed by calling |peer->GetNdefMessage()|.
+ virtual void RecordReceived(NfcPeer* peer, const NfcNdefRecord* record) {}
};
// The ErrorCallback is used by methods to asynchronously report errors.
@@ -64,12 +64,12 @@ class NfcPeer {
// this only means that no records have yet been received from the device.
// Users should use this method in conjunction with the Observer methods
// to be notified when the records are ready.
- virtual NfcNdefMessage GetNdefMessage() const = 0;
+ virtual const NfcNdefMessage& GetNdefMessage() const = 0;
// Sends the NDEF records contained in |message| to the peer device. On
// success, |callback| will be invoked. On failure, |error_callback| will be
// invoked.
- virtual void PushNdef(NfcNdefMessage* message,
+ virtual void PushNdef(const NfcNdefMessage& message,
const base::Closure& callback,
const ErrorCallback& error_callback) = 0;
diff --git a/chromium/device/nfc/nfc_peer_chromeos.cc b/chromium/device/nfc/nfc_peer_chromeos.cc
new file mode 100644
index 00000000000..4ef1804b857
--- /dev/null
+++ b/chromium/device/nfc/nfc_peer_chromeos.cc
@@ -0,0 +1,194 @@
+// Copyright 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/nfc/nfc_peer_chromeos.h"
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/nfc_device_client.h"
+#include "device/nfc/nfc_ndef_record_utils_chromeos.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+using device::NfcNdefMessage;
+using device::NfcNdefRecord;
+
+namespace chromeos {
+
+namespace {
+
+typedef std::vector<dbus::ObjectPath> ObjectPathVector;
+
+} // namespace
+
+NfcPeerChromeOS::NfcPeerChromeOS(const dbus::ObjectPath& object_path)
+ : object_path_(object_path),
+ weak_ptr_factory_(this) {
+ // Create record objects for all records that were received before.
+ const ObjectPathVector& records =
+ DBusThreadManager::Get()->GetNfcRecordClient()->
+ GetRecordsForDevice(object_path_);
+ for (ObjectPathVector::const_iterator iter = records.begin();
+ iter != records.end(); ++iter) {
+ AddRecord(*iter);
+ }
+ DBusThreadManager::Get()->GetNfcRecordClient()->AddObserver(this);
+}
+
+NfcPeerChromeOS::~NfcPeerChromeOS() {
+ DBusThreadManager::Get()->GetNfcRecordClient()->RemoveObserver(this);
+ STLDeleteValues(&records_);
+}
+
+void NfcPeerChromeOS::AddObserver(device::NfcPeer::Observer* observer) {
+ DCHECK(observer);
+ observers_.AddObserver(observer);
+}
+
+void NfcPeerChromeOS::RemoveObserver(device::NfcPeer::Observer* observer) {
+ DCHECK(observer);
+ observers_.RemoveObserver(observer);
+}
+
+std::string NfcPeerChromeOS::GetIdentifier() const {
+ return object_path_.value();
+}
+
+const NfcNdefMessage& NfcPeerChromeOS::GetNdefMessage() const {
+ return message_;
+}
+
+void NfcPeerChromeOS::PushNdef(const NfcNdefMessage& message,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (message.records().empty()) {
+ LOG(ERROR) << "Given NDEF message is empty. Cannot push it.";
+ error_callback.Run();
+ return;
+ }
+ // TODO(armansito): neard currently supports pushing only one NDEF record
+ // to a remote device and won't support multiple records until 0.15. Until
+ // then, report failure if |message| contains more than one record.
+ if (message.records().size() > 1) {
+ LOG(ERROR) << "Currently, pushing only 1 NDEF record is supported.";
+ error_callback.Run();
+ return;
+ }
+ const NfcNdefRecord* record = message.records()[0];
+ base::DictionaryValue attributes;
+ if (!nfc_ndef_record_utils::NfcNdefRecordToDBusAttributes(
+ record, &attributes)) {
+ LOG(ERROR) << "Failed to extract NDEF record fields for NDEF push.";
+ error_callback.Run();
+ return;
+ }
+ DBusThreadManager::Get()->GetNfcDeviceClient()->Push(
+ object_path_,
+ attributes,
+ base::Bind(&NfcPeerChromeOS::OnPushNdef,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&NfcPeerChromeOS::OnPushNdefError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+}
+
+void NfcPeerChromeOS::StartHandover(HandoverType handover_type,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ // TODO(armansito): Initiating handover with a peer is currently not
+ // supported. For now, return an error immediately.
+ LOG(ERROR) << "NFC Handover currently not supported.";
+ error_callback.Run();
+}
+
+void NfcPeerChromeOS::RecordAdded(const dbus::ObjectPath& object_path) {
+ // Don't create the record object yet. Instead, wait until all record
+ // properties have been received and contruct the object and notify observers
+ // then.
+ VLOG(1) << "Record added: " << object_path.value() << ". Waiting until "
+ << "all properties have been fetched to create record object.";
+}
+
+void NfcPeerChromeOS::RecordRemoved(const dbus::ObjectPath& object_path) {
+ NdefRecordMap::iterator iter = records_.find(object_path);
+ if (iter == records_.end())
+ return;
+ VLOG(1) << "Lost remote NDEF record object: " << object_path.value()
+ << ", removing record.";
+ NfcNdefRecord* record = iter->second;
+ message_.RemoveRecord(record);
+ delete record;
+ records_.erase(iter);
+}
+
+void NfcPeerChromeOS::RecordPropertiesReceived(
+ const dbus::ObjectPath& object_path) {
+ VLOG(1) << "Record properties received for: " << object_path.value();
+
+ // Check if the found record belongs to this device.
+ bool record_found = false;
+ const ObjectPathVector& records =
+ DBusThreadManager::Get()->GetNfcRecordClient()->
+ GetRecordsForDevice(object_path_);
+ for (ObjectPathVector::const_iterator iter = records.begin();
+ iter != records.end(); ++iter) {
+ if (*iter == object_path) {
+ record_found = true;
+ break;
+ }
+ }
+ if (!record_found) {
+ VLOG(1) << "Record \"" << object_path.value() << "\" doesn't belong to this"
+ << " device. Ignoring.";
+ return;
+ }
+
+ AddRecord(object_path);
+}
+
+void NfcPeerChromeOS::OnPushNdef(const base::Closure& callback) {
+ callback.Run();
+}
+
+void NfcPeerChromeOS::OnPushNdefError(const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(ERROR) << object_path_.value() << ": Failed to Push NDEF message: "
+ << error_name << ": " << error_message;
+ error_callback.Run();
+}
+
+void NfcPeerChromeOS::AddRecord(const dbus::ObjectPath& object_path) {
+ // Ignore this call if an entry for this record already exists.
+ if (records_.find(object_path) != records_.end()) {
+ VLOG(1) << "Record object for remote \"" << object_path.value()
+ << "\" already exists.";
+ return;
+ }
+
+ NfcRecordClient::Properties* record_properties =
+ DBusThreadManager::Get()->GetNfcRecordClient()->
+ GetProperties(object_path);
+ DCHECK(record_properties);
+
+ NfcNdefRecord* record = new NfcNdefRecord();
+ if (!nfc_ndef_record_utils::RecordPropertiesToNfcNdefRecord(
+ record_properties, record)) {
+ LOG(ERROR) << "Failed to create record object for record with object "
+ << "path \"" << object_path.value() << "\"";
+ delete record;
+ return;
+ }
+
+ message_.AddRecord(record);
+ records_[object_path] = record;
+ FOR_EACH_OBSERVER(NfcPeer::Observer, observers_,
+ RecordReceived(this, record));
+}
+
+} // namespace chromeos
diff --git a/chromium/device/nfc/nfc_peer_chromeos.h b/chromium/device/nfc/nfc_peer_chromeos.h
new file mode 100644
index 00000000000..eec8e4bce2a
--- /dev/null
+++ b/chromium/device/nfc/nfc_peer_chromeos.h
@@ -0,0 +1,81 @@
+// Copyright 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_NFC_NFC_PEER_CHROMEOS_H_
+#define DEVICE_NFC_NFC_PEER_CHROMEOS_H_
+
+#include <map>
+
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "chromeos/dbus/nfc_record_client.h"
+#include "dbus/object_path.h"
+#include "device/nfc/nfc_ndef_record.h"
+#include "device/nfc/nfc_peer.h"
+
+namespace chromeos {
+
+// The NfcPeerChromeOS class implements NfcPeer for the Chrome OS platform.
+class NfcPeerChromeOS : public device::NfcPeer,
+ public NfcRecordClient::Observer {
+ public:
+ // NfcPeer overrides.
+ virtual void AddObserver(device::NfcPeer::Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(device::NfcPeer::Observer* observer) OVERRIDE;
+ virtual std::string GetIdentifier() const OVERRIDE;
+ virtual const device::NfcNdefMessage& GetNdefMessage() const OVERRIDE;
+ virtual void PushNdef(const device::NfcNdefMessage& message,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual void StartHandover(HandoverType handover_type,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+
+ private:
+ friend class NfcAdapterChromeOS;
+
+ // Mapping from D-Bus object paths to NfcNdefRecord objects.
+ typedef std::map<dbus::ObjectPath, device::NfcNdefRecord*> NdefRecordMap;
+
+ explicit NfcPeerChromeOS(const dbus::ObjectPath& object_path);
+ virtual ~NfcPeerChromeOS();
+
+ // NfcRecordClient::Observer overrides.
+ virtual void RecordAdded(const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void RecordRemoved(const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void RecordPropertiesReceived(
+ const dbus::ObjectPath& object_path) OVERRIDE;
+
+ // Called by dbus:: on completion of the D-Bus method call to push an NDEF.
+ void OnPushNdef(const base::Closure& callback);
+ void OnPushNdefError(const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Creates a record object for the record with object path |object_path| and
+ // notifies the observers, if a record object did not already exist for it.
+ void AddRecord(const dbus::ObjectPath& object_path);
+
+ // Object path of the peer that we are currently tracking.
+ dbus::ObjectPath object_path_;
+
+ // A map containing the NDEF records that were received from the peer.
+ NdefRecordMap records_;
+
+ // Message instance that contains pointers to all created records.
+ device::NfcNdefMessage message_;
+
+ // List of observers interested in event notifications from us.
+ ObserverList<device::NfcPeer::Observer> observers_;
+
+ // 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<NfcPeerChromeOS> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(NfcPeerChromeOS);
+};
+
+} // namespace chromeos
+
+#endif // DEVICE_NFC_NFC_PEER_CHROMEOS_H_
diff --git a/chromium/device/nfc/nfc_tag.h b/chromium/device/nfc/nfc_tag.h
index 650f37b0599..039fc78133d 100644
--- a/chromium/device/nfc/nfc_tag.h
+++ b/chromium/device/nfc/nfc_tag.h
@@ -31,7 +31,8 @@ class NfcTag {
kTagType1,
kTagType2,
kTagType3,
- kTagType4
+ kTagType4,
+ kTagTypeUnknown,
};
// NFC protocols that a tag can support. A tag will usually support only one
@@ -41,7 +42,8 @@ class NfcTag {
kProtocolIsoDep,
kProtocolJewel,
kProtocolMifare,
- kProtocolNfcDep
+ kProtocolNfcDep,
+ kProtocolUnknown
};
// Interface for observing changes from NFC tags.
@@ -49,12 +51,18 @@ class NfcTag {
public:
virtual ~Observer() {}
- // This method will be called when an NDEF message |message|, stored on the
- // NFC tag |tag| has been read. Although NDEF is the most common record
- // storage format for NFC tags, not all tags support it. This method won't
- // be called if there are no records on an NDEF compliant tag or if the tag
- // doesn't support NDEF.
- virtual void RecordsReceived(NfcTag* tag, const NfcNdefMessage& message) {}
+ // Called when the tag type has been determined.
+ virtual void TagTypeChanged(NfcTag* tag, TagType type) {}
+
+ // Called when the write access to the tag has been determined or changed.
+ virtual void TagWritePermissionChanged(NfcTag* tag, bool read_only) {}
+
+ // Called when the underlying NFC protocol has been determined.
+ virtual void TagSupportedProtocolChanged(NfcTag* tag, Protocol protocol) {}
+
+ // Called when all initial values of the tag properties have been received
+ // from the remote tag and |tag| is ready to use.
+ virtual void TagReady(NfcTag* tag) {}
};
virtual ~NfcTag();
@@ -81,6 +89,17 @@ class NfcTag {
virtual NfcTagTechnology::TechnologyTypeMask
GetSupportedTechnologies() const = 0;
+ // Returns true, if all tag properties have been received from the remote tag
+ // and this object is ready to use.
+ virtual bool IsReady() const = 0;
+
+ // Returns a pointer to the NDEF technology object that allows I/O on NDEF
+ // records. If NDEF is not supported by this tag, operations that are
+ // performed on the returned instance may not succeed. Users can determine
+ // support by calling NfcTagTechnology::IsSupportedByTag. The returned
+ // instance is owned by this tag.
+ virtual NfcNdefTagTechnology* GetNdefTagTechnology() = 0;
+
protected:
NfcTag();
diff --git a/chromium/device/nfc/nfc_tag_chromeos.cc b/chromium/device/nfc/nfc_tag_chromeos.cc
new file mode 100644
index 00000000000..e2390fa04fb
--- /dev/null
+++ b/chromium/device/nfc/nfc_tag_chromeos.cc
@@ -0,0 +1,164 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/nfc/nfc_tag_chromeos.h"
+
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "device/nfc/nfc_tag_technology_chromeos.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+using device::NfcTag;
+using device::NfcTagTechnology;
+using device::NfcNdefTagTechnology;
+
+namespace chromeos {
+
+namespace {
+
+// Converts an NFC tag type value returned by neard to a NfcTag::TagType enum
+// value.
+NfcTag::TagType DBusTypePropertyToTagType(const std::string& type) {
+ if (type == nfc_tag::kTagType1)
+ return NfcTag::kTagType1;
+ if (type == nfc_tag::kTagType2)
+ return NfcTag::kTagType2;
+ if (type == nfc_tag::kTagType3)
+ return NfcTag::kTagType3;
+ if (type == nfc_tag::kTagType4)
+ return NfcTag::kTagType4;
+ return NfcTag::kTagTypeUnknown;
+}
+
+// Converts an NFC tag protocol value returned by neard to a NfcTag::Protocol
+// enum value.
+NfcTag::Protocol DBusProtocolPropertyToTagProtocol(
+ const std::string& protocol) {
+ if (protocol == nfc_common::kProtocolFelica)
+ return NfcTag::kProtocolFelica;
+ if (protocol == nfc_common::kProtocolIsoDep)
+ return NfcTag::kProtocolIsoDep;
+ if (protocol == nfc_common::kProtocolJewel)
+ return NfcTag::kProtocolJewel;
+ if (protocol == nfc_common::kProtocolMifare)
+ return NfcTag::kProtocolMifare;
+ if (protocol == nfc_common::kProtocolNfcDep)
+ return NfcTag::kProtocolNfcDep;
+ return NfcTag::kProtocolUnknown;
+}
+
+} // namespace
+
+NfcTagChromeOS::NfcTagChromeOS(const dbus::ObjectPath& object_path)
+ : object_path_(object_path),
+ is_ready_(false),
+ ndef_technology_(new NfcNdefTagTechnologyChromeOS(this)) {
+ DBusThreadManager::Get()->GetNfcTagClient()->AddObserver(this);
+}
+
+NfcTagChromeOS::~NfcTagChromeOS() {
+ DBusThreadManager::Get()->GetNfcTagClient()->RemoveObserver(this);
+}
+
+void NfcTagChromeOS::AddObserver(NfcTag::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void NfcTagChromeOS::RemoveObserver(NfcTag::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+std::string NfcTagChromeOS::GetIdentifier() const {
+ return object_path_.value();
+}
+
+NfcTag::TagType NfcTagChromeOS::GetType() const {
+ DCHECK(object_path_.IsValid());
+ return DBusTypePropertyToTagType(
+ DBusThreadManager::Get()->GetNfcTagClient()->
+ GetProperties(object_path_)->type.value());
+}
+
+bool NfcTagChromeOS::IsReadOnly() const {
+ DCHECK(object_path_.IsValid());
+ return DBusThreadManager::Get()->GetNfcTagClient()->
+ GetProperties(object_path_)->read_only.value();
+}
+
+NfcTag::Protocol NfcTagChromeOS::GetSupportedProtocol() const {
+ DCHECK(object_path_.IsValid());
+ return DBusProtocolPropertyToTagProtocol(
+ DBusThreadManager::Get()->GetNfcTagClient()->
+ GetProperties(object_path_)->protocol.value());
+}
+
+NfcTagTechnology::TechnologyTypeMask
+NfcTagChromeOS::GetSupportedTechnologies() const {
+ // Determine supported technologies based on the tag's protocol and
+ // type.
+ NfcTag::TagType type = GetType();
+ NfcTag::Protocol protocol = GetSupportedProtocol();
+ if (type == NfcTag::kTagTypeUnknown || protocol == kProtocolUnknown) {
+ VLOG(1) << "Tag type and protocol unknown.";
+ return 0;
+ }
+
+ NfcTagTechnology::TechnologyTypeMask technologies = 0;
+ technologies |= NfcTagTechnology::kTechnologyTypeNdef;
+ if (type == NfcTag::kTagType3) {
+ DCHECK(protocol == NfcTag::kProtocolFelica);
+ return technologies | NfcTagTechnology::kTechnologyTypeNfcF;
+ }
+
+ if (protocol == NfcTag::kProtocolIsoDep) {
+ DCHECK(type == NfcTag::kTagType4);
+ technologies |= NfcTagTechnology::kTechnologyTypeIsoDep;
+ // TODO(armansito): Neard doesn't provide enough information to determine
+ // if the underlying wave-form is type A or type B. For now, report
+ // neither.
+ return technologies;
+ }
+
+ return technologies | NfcTagTechnology::kTechnologyTypeNfcA;
+}
+
+bool NfcTagChromeOS::IsReady() const {
+ return is_ready_;
+}
+
+NfcNdefTagTechnology* NfcTagChromeOS::GetNdefTagTechnology() {
+ return ndef_technology_.get();
+}
+
+void NfcTagChromeOS::TagPropertyChanged(const dbus::ObjectPath& object_path,
+ const std::string& property_name) {
+ if (object_path != object_path_)
+ return;
+
+ NfcTagClient::Properties* properties =
+ DBusThreadManager::Get()->GetNfcTagClient()->GetProperties(object_path_);
+ DCHECK(properties);
+
+ if (property_name == properties->type.name()) {
+ FOR_EACH_OBSERVER(NfcTag::Observer, observers_,
+ TagTypeChanged(this, GetType()));
+ } else if (property_name == properties->read_only.name()) {
+ FOR_EACH_OBSERVER(NfcTag::Observer, observers_,
+ TagWritePermissionChanged(this, IsReadOnly()));
+ } else if (property_name == properties->protocol.name()) {
+ FOR_EACH_OBSERVER(
+ NfcTag::Observer, observers_,
+ TagSupportedProtocolChanged(this, GetSupportedProtocol()));
+ }
+}
+
+void NfcTagChromeOS::TagPropertiesReceived(
+ const dbus::ObjectPath& object_path) {
+ if (is_ready_ || object_path != object_path_)
+ return;
+
+ is_ready_ = true;
+ FOR_EACH_OBSERVER(NfcTag::Observer, observers_, TagReady(this));
+}
+
+} // namespace chromeos
diff --git a/chromium/device/nfc/nfc_tag_chromeos.h b/chromium/device/nfc/nfc_tag_chromeos.h
new file mode 100644
index 00000000000..5e4a723eeda
--- /dev/null
+++ b/chromium/device/nfc/nfc_tag_chromeos.h
@@ -0,0 +1,70 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_NFC_NFC_TAG_CHROMEOS_H_
+#define DEVICE_NFC_NFC_TAG_CHROMEOS_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "chromeos/dbus/nfc_tag_client.h"
+#include "dbus/object_path.h"
+#include "device/nfc/nfc_tag.h"
+
+namespace chromeos {
+
+class NfcNdefTagTechnologyChromeOS;
+
+// The NfcTagChromeOS class implements device::NfcTag for the Chrome OS
+// platform.
+class NfcTagChromeOS : public device::NfcTag,
+ public NfcTagClient::Observer {
+ public:
+ // device::NfcTag overrides.
+ virtual void AddObserver(device::NfcTag::Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(device::NfcTag::Observer* observer) OVERRIDE;
+ virtual std::string GetIdentifier() const OVERRIDE;
+ virtual TagType GetType() const OVERRIDE;
+ virtual bool IsReadOnly() const OVERRIDE;
+ virtual device::NfcTag::Protocol GetSupportedProtocol() const OVERRIDE;
+ virtual device::NfcTagTechnology::TechnologyTypeMask
+ GetSupportedTechnologies() const OVERRIDE;
+ virtual bool IsReady() const OVERRIDE;
+ virtual device::NfcNdefTagTechnology* GetNdefTagTechnology() OVERRIDE;
+
+ // NfcTagClient::Observer overrides.
+ virtual void TagPropertyChanged(const dbus::ObjectPath& object_path,
+ const std::string& property_name) OVERRIDE;
+ virtual void TagPropertiesReceived(
+ const dbus::ObjectPath& object_path) OVERRIDE;
+
+ // Object path representing the remote tag object.
+ const dbus::ObjectPath& object_path() const { return object_path_; }
+
+ private:
+ friend class NfcAdapterChromeOS;
+
+ explicit NfcTagChromeOS(const dbus::ObjectPath& object_path);
+ virtual ~NfcTagChromeOS();
+
+ // Object path of the tag that we are currently tracking.
+ dbus::ObjectPath object_path_;
+
+ // Stores whether or not the initial set of properties have been received.
+ bool is_ready_;
+
+ // The NfcNdefTagTechnology instance that allows users to perform NDEF
+ // read and write on this tag.
+ scoped_ptr<NfcNdefTagTechnologyChromeOS> ndef_technology_;
+
+ // List of observers interested in event notifications from us.
+ ObserverList<device::NfcTag::Observer> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(NfcTagChromeOS);
+};
+
+} // namespace chromeos
+
+#endif // DEVICE_NFC_NFC_TAG_CHROMEOS_H_
diff --git a/chromium/device/nfc/nfc_tag_technology.cc b/chromium/device/nfc/nfc_tag_technology.cc
index fa2b66a2baf..b07fb5263f1 100644
--- a/chromium/device/nfc/nfc_tag_technology.cc
+++ b/chromium/device/nfc/nfc_tag_technology.cc
@@ -29,11 +29,4 @@ NfcNdefTagTechnology::NfcNdefTagTechnology(NfcTag* tag)
NfcNdefTagTechnology::~NfcNdefTagTechnology() {
}
-// static
-NfcNdefTagTechnology* NfcNdefTagTechnology::Create(NfcTag* tag) {
- // TODO(armansito): Create and return platform-specific implementation
- // instances here.
- return NULL;
-}
-
} // namespace device
diff --git a/chromium/device/nfc/nfc_tag_technology.h b/chromium/device/nfc/nfc_tag_technology.h
index c5d1c19af37..35def12d8ef 100644
--- a/chromium/device/nfc/nfc_tag_technology.h
+++ b/chromium/device/nfc/nfc_tag_technology.h
@@ -15,7 +15,8 @@ class NfcTag;
// NfcTagTechnology represents an NFC technology that allows a certain type of
// I/O operation on an NFC tag. NFC tags can support a wide array of protocols.
// The NfcTagTechnology hierarchy allows both raw and high-level I/O operations
-// on NFC tags.
+// on NFC tags. Do not create instances of these objects directly. Instead,
+// obtain a handle directly from an NfcTag object.
class NfcTagTechnology {
public:
// The various I/O technologies that an NFC tag can support.
@@ -56,7 +57,15 @@ class NfcTagTechnology {
};
// NfcNdefTagTechnology allows reading and writing NDEF messages to a tag. This
-// is the most commonly used data exchange format in NFC.
+// is the most commonly used data exchange format in NFC. NDEF is a data
+// exchange format and is the top most layer of the protocol stack. NDEF itself
+// is not a protocol; data is typically formatted in a way that is defined by
+// the NDEF format and then transmitted via one of the underlying protocols.
+// Hence all tags are capable of NDEF data exchange, however, all tags don't
+// necessarily use NDEF to operate (e.g. a tag may contain a smart chip that
+// does data processing on ISO-DEP based APDUs and ignores NDEF). This is why,
+// even if a tag inherently supports NDEF, operations done via this class may
+// not necessarily succeed.
class NfcNdefTagTechnology : public NfcTagTechnology {
public:
// The ErrorCallback is used by methods to asynchronously report errors.
@@ -64,6 +73,25 @@ class NfcNdefTagTechnology : public NfcTagTechnology {
virtual ~NfcNdefTagTechnology();
+ // Interface for observing changes from NFC tags related to NDEF records.
+ class Observer {
+ public:
+ virtual ~Observer() {}
+
+ // This method will be called when an NDEF record |record|, stored on the
+ // NFC tag |tag| has been read. This method will be called multiple times
+ // as records are read from the tag or when the tag's records change (e.g.
+ // when the tag has been rewritten). All received records can be accessed by
+ // calling GetNdefMessage().
+ virtual void RecordReceived(NfcTag* tag, const NfcNdefRecord* record) {}
+ };
+
+ // Adds and removes observers for events on this NFC tag. If monitoring
+ // multiple tags, check the |tag| parameter of observer methods to determine
+ // which tag is issuing the event.
+ virtual void AddObserver(Observer* observer) = 0;
+ virtual void RemoveObserver(Observer* observer) = 0;
+
// NfcTagTechnology override.
virtual bool IsSupportedByTag() const OVERRIDE;
@@ -72,22 +100,17 @@ class NfcNdefTagTechnology : public NfcTagTechnology {
// means that no records have yet been received from the tag. Users should
// use this method in conjunction with the NfcTag::Observer::RecordsReceived
// method to be notified when the records are ready.
- virtual NfcNdefMessage GetNdefMessage() const = 0;
+ virtual const NfcNdefMessage& GetNdefMessage() const = 0;
// Writes the given NDEF message to the underlying tag, overwriting any
// existing NDEF message on it. On success, |callback| will be invoked. On
// failure, |error_callback| will be invoked. This method can fail, if the
// underlying tag does not support NDEF as a technology.
- virtual void WriteNdefMessage(const NfcNdefMessage& message,
- const base::Closure& callback,
- const ErrorCallback& error_callback) = 0;
+ virtual void WriteNdef(const NfcNdefMessage& message,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
- // Static factory method for constructing an instance. The ownership of the
- // returned instance belongs to the caller. Returns NULL, if NFC is not
- // supported on the current platform.
- static NfcNdefTagTechnology* Create(NfcTag* tag);
-
- private:
+ protected:
// Constructs a technology instance, where |tag| is the NFC tag that this
// instance will operate on.
explicit NfcNdefTagTechnology(NfcTag* tag);
diff --git a/chromium/device/nfc/nfc_tag_technology_chromeos.cc b/chromium/device/nfc/nfc_tag_technology_chromeos.cc
new file mode 100644
index 00000000000..25d1b240dae
--- /dev/null
+++ b/chromium/device/nfc/nfc_tag_technology_chromeos.cc
@@ -0,0 +1,186 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/nfc/nfc_tag_technology_chromeos.h"
+
+#include "base/stl_util.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "device/nfc/nfc_ndef_record_utils_chromeos.h"
+#include "device/nfc/nfc_tag_chromeos.h"
+
+using device::NfcNdefMessage;
+using device::NfcNdefRecord;
+
+namespace chromeos {
+
+namespace {
+
+typedef std::vector<dbus::ObjectPath> ObjectPathVector;
+
+} // namespace
+
+NfcNdefTagTechnologyChromeOS::NfcNdefTagTechnologyChromeOS(NfcTagChromeOS* tag)
+ : NfcNdefTagTechnology(tag),
+ object_path_(tag->object_path()),
+ weak_ptr_factory_(this) {
+ DCHECK(tag);
+ // Create record objects for all records that were received before.
+ const ObjectPathVector& records =
+ DBusThreadManager::Get()->GetNfcRecordClient()->
+ GetRecordsForTag(object_path_);
+ for (ObjectPathVector::const_iterator iter = records.begin();
+ iter != records.end(); ++iter) {
+ AddRecord(*iter);
+ }
+ DBusThreadManager::Get()->GetNfcRecordClient()->AddObserver(this);
+}
+
+NfcNdefTagTechnologyChromeOS::~NfcNdefTagTechnologyChromeOS() {
+ DBusThreadManager::Get()->GetNfcRecordClient()->RemoveObserver(this);
+ STLDeleteValues(&records_);
+}
+
+void NfcNdefTagTechnologyChromeOS::AddObserver(
+ NfcNdefTagTechnology::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void NfcNdefTagTechnologyChromeOS::RemoveObserver(
+ NfcNdefTagTechnology::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+const NfcNdefMessage& NfcNdefTagTechnologyChromeOS::GetNdefMessage() const {
+ return message_;
+}
+
+void NfcNdefTagTechnologyChromeOS::WriteNdef(
+ const device::NfcNdefMessage& message,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (message.records().empty()) {
+ LOG(ERROR) << "Given NDEF message is empty. Cannot write it.";
+ error_callback.Run();
+ return;
+ }
+ if (!tag()->IsReady()) {
+ LOG(ERROR) << "The tag is not ready yet: " << tag()->GetIdentifier();
+ error_callback.Run();
+ return;
+ }
+ // TODO(armansito): neard currently supports writing only one NDEF record
+ // to a tag and won't support multiple records until 0.15. Until then, report
+ // failure if |message| contains more than one record.
+ if (message.records().size() > 1) {
+ LOG(ERROR) << "Currently, writing only 1 NDEF record is supported.";
+ error_callback.Run();
+ return;
+ }
+ const NfcNdefRecord* record = message.records()[0];
+ base::DictionaryValue attributes;
+ if (!nfc_ndef_record_utils::NfcNdefRecordToDBusAttributes(
+ record, &attributes)) {
+ LOG(ERROR) << "Failed to extract NDEF record fields for NDEF push.";
+ error_callback.Run();
+ return;
+ }
+ DBusThreadManager::Get()->GetNfcTagClient()->Write(
+ object_path_,
+ attributes,
+ base::Bind(&NfcNdefTagTechnologyChromeOS::OnWriteNdefMessage,
+ weak_ptr_factory_.GetWeakPtr(), callback),
+ base::Bind(&NfcNdefTagTechnologyChromeOS::OnWriteNdefMessageError,
+ weak_ptr_factory_.GetWeakPtr(), error_callback));
+}
+
+void NfcNdefTagTechnologyChromeOS::RecordAdded(
+ const dbus::ObjectPath& object_path) {
+ // Don't create the record object yet. Instead, wait until all record
+ // properties have been received and construct the object and notify observers
+ // then.
+ VLOG(1) << "Record added: " << object_path.value() << ". Waiting until "
+ "all properties have been fetched to create record object.";
+}
+
+void NfcNdefTagTechnologyChromeOS::RecordRemoved(
+ const dbus::ObjectPath& object_path) {
+ NdefRecordMap::iterator iter = records_.find(object_path);
+ if (iter == records_.end())
+ return;
+ VLOG(1) << "Lost remote NDEF record object: " << object_path.value()
+ << ", removing record.";
+ NfcNdefRecord* record = iter->second;
+ message_.RemoveRecord(record);
+ delete record;
+ records_.erase(iter);
+}
+
+void NfcNdefTagTechnologyChromeOS::RecordPropertiesReceived(
+ const dbus::ObjectPath& object_path) {
+ VLOG(1) << "Record properties received for: " << object_path.value();
+
+ // Check if the found record belongs to this tag.
+ bool record_found = false;
+ const ObjectPathVector& records =
+ DBusThreadManager::Get()->GetNfcRecordClient()->
+ GetRecordsForTag(object_path_);
+ for (ObjectPathVector::const_iterator iter = records.begin();
+ iter != records.end(); ++iter) {
+ if (*iter == object_path) {
+ record_found = true;
+ break;
+ }
+ }
+ if (!record_found) {
+ VLOG(1) << "Record \"" << object_path.value() << "\" doesn't belong to this"
+ << " tag. Ignoring.";
+ return;
+ }
+ AddRecord(object_path);
+}
+
+void NfcNdefTagTechnologyChromeOS::OnWriteNdefMessage(
+ const base::Closure& callback) {
+ callback.Run();
+}
+
+void NfcNdefTagTechnologyChromeOS::OnWriteNdefMessageError(
+ const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(ERROR) << object_path_.value() << ": Failed to Push NDEF message: "
+ << error_name << ": " << error_message;
+ error_callback.Run();
+}
+
+void NfcNdefTagTechnologyChromeOS::AddRecord(
+ const dbus::ObjectPath& object_path) {
+ // Ignore this call if an entry for this record already exists.
+ if (records_.find(object_path) != records_.end()) {
+ VLOG(1) << "Record object for remote \"" << object_path.value()
+ << "\" already exists.";
+ return;
+ }
+
+ NfcRecordClient::Properties* record_properties =
+ DBusThreadManager::Get()->GetNfcRecordClient()->
+ GetProperties(object_path);
+ DCHECK(record_properties);
+
+ NfcNdefRecord* record = new NfcNdefRecord();
+ if (!nfc_ndef_record_utils::RecordPropertiesToNfcNdefRecord(
+ record_properties, record)) {
+ LOG(ERROR) << "Failed to create record object for record with object "
+ << "path \"" << object_path.value() << "\"";
+ delete record;
+ return;
+ }
+
+ message_.AddRecord(record);
+ records_[object_path] = record;
+ FOR_EACH_OBSERVER(NfcNdefTagTechnology::Observer, observers_,
+ RecordReceived(tag(), record));
+}
+
+} // namespace chromeos
diff --git a/chromium/device/nfc/nfc_tag_technology_chromeos.h b/chromium/device/nfc/nfc_tag_technology_chromeos.h
new file mode 100644
index 00000000000..71b070ef74f
--- /dev/null
+++ b/chromium/device/nfc/nfc_tag_technology_chromeos.h
@@ -0,0 +1,87 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_NFC_NFC_TAG_TECHNOLOGY_CHROMEOS_H_
+#define DEVICE_NFC_NFC_TAG_TECHNOLOGY_CHROMEOS_H_
+
+#include <map>
+
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "chromeos/dbus/nfc_record_client.h"
+#include "device/nfc/nfc_tag_technology.h"
+
+namespace chromeos {
+
+class NfcTagChromeOS;
+
+// The NfcNdefTagTechnologyChromeOS class implements
+// device::NfcNdefTagTechnology for the Chrome OS platform. The lifetime of an
+// instance of this class must be tied to an instance of NfcTagChromeOS.
+// Instances of this class must never outlast the owning NfcTagChromeOS
+// instance.
+class NfcNdefTagTechnologyChromeOS : public device::NfcNdefTagTechnology,
+ public NfcRecordClient::Observer {
+ public:
+ virtual ~NfcNdefTagTechnologyChromeOS();
+
+ // device::NfcNdefTagTechnology overrides.
+ virtual void AddObserver(device::NfcNdefTagTechnology::Observer* observer)
+ OVERRIDE;
+ virtual void RemoveObserver(device::NfcNdefTagTechnology::Observer* observer)
+ OVERRIDE;
+ virtual const device::NfcNdefMessage& GetNdefMessage() const OVERRIDE;
+ virtual void WriteNdef(const device::NfcNdefMessage& message,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+
+ // NfcRecordClient::Observer overrides.
+ virtual void RecordAdded(const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void RecordRemoved(const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void RecordPropertiesReceived(
+ const dbus::ObjectPath& object_path) OVERRIDE;
+
+ private:
+ friend class NfcTagChromeOS;
+
+ // Mapping from D-Bus object paths to NfcNdefRecord objects.
+ typedef std::map<dbus::ObjectPath, device::NfcNdefRecord*> NdefRecordMap;
+
+ explicit NfcNdefTagTechnologyChromeOS(NfcTagChromeOS* tag);
+
+ // Called by dbus:: on completion of the D-Bus method call to write an NDEF.
+ void OnWriteNdefMessage(const base::Closure& callback);
+ void OnWriteNdefMessageError(const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Creates a record object for the record with object path |object_path| and
+ // notifies the observers, if a record object did not already exist for it.
+ void AddRecord(const dbus::ObjectPath& object_path);
+
+ // A map containing the NDEF records that were received from the tag.
+ NdefRecordMap records_;
+
+ // Message instance that contains pointers to all created records that are
+ // in |records_|. This is mainly used as the cached return value for
+ // GetNdefMessage().
+ device::NfcNdefMessage message_;
+
+ // List of observers interested in event notifications from us.
+ ObserverList<device::NfcNdefTagTechnology::Observer> observers_;
+
+ // D-Bus object path of the remote tag or device that this object operates
+ // on.
+ dbus::ObjectPath object_path_;
+
+ // 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<NfcNdefTagTechnologyChromeOS> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(NfcNdefTagTechnologyChromeOS);
+};
+
+} // namespace chromeos
+
+#endif // DEVICE_NFC_NFC_TAG_TECHNOLOGY_CHROMEOS_H_
diff --git a/chromium/device/serial/BUILD.gn b/chromium/device/serial/BUILD.gn
new file mode 100644
index 00000000000..0b38e93cbae
--- /dev/null
+++ b/chromium/device/serial/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+# GYP version: device/serial/serial.gyp:device_serial
+static_library("serial") {
+ output_name = "device_serial"
+
+ sources = [
+ "serial_device_enumerator.cc",
+ "serial_device_enumerator.h",
+ "serial_device_enumerator_linux.cc",
+ "serial_device_enumerator_linux.h",
+ "serial_device_enumerator_mac.cc",
+ "serial_device_enumerator_mac.h",
+ "serial_device_enumerator_win.cc",
+ "serial_device_enumerator_win.h",
+ ]
+
+ if (is_linux) {
+ configs += [ "//build/config/linux:udev" ]
+ }
+
+ deps = [
+ ":serial_mojo",
+ ]
+}
+
+mojom("serial_mojo") {
+ visibility = ":serial"
+
+ sources = [
+ "serial.mojom",
+ ]
+}
diff --git a/chromium/device/serial/serial.gyp b/chromium/device/serial/serial.gyp
new file mode 100644
index 00000000000..0df7005af83
--- /dev/null
+++ b/chromium/device/serial/serial.gyp
@@ -0,0 +1,43 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ # GN version: //device/serial
+ 'target_name': 'device_serial',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '../..',
+ ],
+ 'conditions': [
+ ['OS=="linux"', {
+ 'dependencies': [
+ '../../build/linux/system.gyp:udev',
+ ],
+ }],
+ ],
+ 'variables': {
+ 'mojom_base_output_dir': 'device/serial',
+ },
+ 'includes': [
+ '../../mojo/public/tools/bindings/mojom_bindings_generator.gypi',
+ ],
+ 'sources': [
+ 'serial.mojom',
+ 'serial_device_enumerator.cc',
+ 'serial_device_enumerator.h',
+ 'serial_device_enumerator_linux.cc',
+ 'serial_device_enumerator_linux.h',
+ 'serial_device_enumerator_mac.cc',
+ 'serial_device_enumerator_mac.h',
+ 'serial_device_enumerator_win.cc',
+ 'serial_device_enumerator_win.h',
+ ],
+ },
+ ],
+}
diff --git a/chromium/device/serial/serial.mojom b/chromium/device/serial/serial.mojom
new file mode 100644
index 00000000000..f925dff33cd
--- /dev/null
+++ b/chromium/device/serial/serial.mojom
@@ -0,0 +1,16 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module device {
+
+struct SerialDeviceInfo {
+ string path;
+ uint16 vendor_id;
+ bool has_vendor_id = false;
+ uint16 product_id;
+ bool has_product_id = false;
+ string display_name;
+};
+
+}
diff --git a/chromium/device/serial/serial_device_enumerator.cc b/chromium/device/serial/serial_device_enumerator.cc
new file mode 100644
index 00000000000..586d33d8446
--- /dev/null
+++ b/chromium/device/serial/serial_device_enumerator.cc
@@ -0,0 +1,13 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/serial/serial_device_enumerator.h"
+
+namespace device {
+
+SerialDeviceEnumerator::SerialDeviceEnumerator() {}
+
+SerialDeviceEnumerator::~SerialDeviceEnumerator() {}
+
+} // namespace device
diff --git a/chromium/device/serial/serial_device_enumerator.h b/chromium/device/serial/serial_device_enumerator.h
new file mode 100644
index 00000000000..c1245c73d6b
--- /dev/null
+++ b/chromium/device/serial/serial_device_enumerator.h
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_H_
+#define DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "device/serial/serial.mojom.h"
+#include "mojo/public/cpp/bindings/array.h"
+
+namespace device {
+
+// Discovers and enumerates serial devices available to the host.
+class SerialDeviceEnumerator {
+ public:
+ static scoped_ptr<SerialDeviceEnumerator> Create();
+
+ SerialDeviceEnumerator();
+ virtual ~SerialDeviceEnumerator();
+
+ virtual mojo::Array<SerialDeviceInfoPtr> GetDevices() = 0;
+};
+
+} // namespace device
+
+#endif // DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_H_
diff --git a/chromium/device/serial/serial_device_enumerator_linux.cc b/chromium/device/serial/serial_device_enumerator_linux.cc
new file mode 100644
index 00000000000..269c7ef9b28
--- /dev/null
+++ b/chromium/device/serial/serial_device_enumerator_linux.cc
@@ -0,0 +1,107 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/serial/serial_device_enumerator_linux.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+
+namespace device {
+
+namespace {
+
+const char kSerialSubsystem[] = "tty";
+
+const char kHostPathKey[] = "DEVNAME";
+const char kHostBusKey[] = "ID_BUS";
+const char kVendorIDKey[] = "ID_VENDOR_ID";
+const char kProductIDKey[] = "ID_MODEL_ID";
+const char kProductNameKey[] = "ID_MODEL";
+
+struct UdevEnumerateDeleter {
+ void operator()(udev_enumerate* enumerate) {
+ udev_enumerate_unref(enumerate);
+ }
+};
+
+struct UdevDeviceDeleter {
+ void operator()(udev_device* device) { udev_device_unref(device); }
+};
+
+typedef scoped_ptr<udev_enumerate, UdevEnumerateDeleter> ScopedUdevEnumeratePtr;
+typedef scoped_ptr<udev_device, UdevDeviceDeleter> ScopedUdevDevicePtr;
+}
+
+// static
+scoped_ptr<SerialDeviceEnumerator> SerialDeviceEnumerator::Create() {
+ return scoped_ptr<SerialDeviceEnumerator>(new SerialDeviceEnumeratorLinux());
+}
+
+SerialDeviceEnumeratorLinux::SerialDeviceEnumeratorLinux() {
+ udev_.reset(udev_new());
+}
+
+SerialDeviceEnumeratorLinux::~SerialDeviceEnumeratorLinux() {}
+
+mojo::Array<SerialDeviceInfoPtr> SerialDeviceEnumeratorLinux::GetDevices() {
+ mojo::Array<SerialDeviceInfoPtr> devices;
+ ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev_.get()));
+ if (!enumerate) {
+ LOG(ERROR) << "Serial device enumeration failed.";
+ return devices.Pass();
+ }
+ if (udev_enumerate_add_match_subsystem(enumerate.get(), kSerialSubsystem)) {
+ LOG(ERROR) << "Serial device enumeration failed.";
+ return devices.Pass();
+ }
+ if (udev_enumerate_scan_devices(enumerate.get())) {
+ LOG(ERROR) << "Serial device enumeration failed.";
+ return devices.Pass();
+ }
+
+ udev_list_entry* entry = udev_enumerate_get_list_entry(enumerate.get());
+ for (; entry != NULL; entry = udev_list_entry_get_next(entry)) {
+ ScopedUdevDevicePtr device(udev_device_new_from_syspath(
+ udev_.get(), udev_list_entry_get_name(entry)));
+ // TODO(rockot): There may be a better way to filter serial devices here,
+ // but it's not clear what that would be. Udev will list lots of virtual
+ // devices with no real endpoint to back them anywhere. The presence of
+ // a bus identifier (e.g., "pci" or "usb") seems to be a good heuristic
+ // for detecting actual devices.
+ const char* path =
+ udev_device_get_property_value(device.get(), kHostPathKey);
+ const char* bus = udev_device_get_property_value(device.get(), kHostBusKey);
+ if (path != NULL && bus != NULL) {
+ SerialDeviceInfoPtr info(SerialDeviceInfo::New());
+ info->path = path;
+
+ const char* vendor_id =
+ udev_device_get_property_value(device.get(), kVendorIDKey);
+ const char* product_id =
+ udev_device_get_property_value(device.get(), kProductIDKey);
+ const char* product_name =
+ udev_device_get_property_value(device.get(), kProductNameKey);
+
+ uint32 int_value;
+ if (vendor_id && base::HexStringToUInt(vendor_id, &int_value)) {
+ info->vendor_id = int_value;
+ info->has_vendor_id = true;
+ }
+ if (product_id && base::HexStringToUInt(product_id, &int_value)) {
+ info->product_id = int_value;
+ info->has_product_id = true;
+ }
+ if (product_name)
+ info->display_name = product_name;
+ devices.push_back(info.Pass());
+ }
+ }
+ return devices.Pass();
+}
+
+void SerialDeviceEnumeratorLinux::UdevDeleter::operator()(udev* handle) {
+ udev_unref(handle);
+}
+
+} // namespace device
diff --git a/chromium/device/serial/serial_device_enumerator_linux.h b/chromium/device/serial/serial_device_enumerator_linux.h
new file mode 100644
index 00000000000..0b34fe841de
--- /dev/null
+++ b/chromium/device/serial/serial_device_enumerator_linux.h
@@ -0,0 +1,36 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_LINUX_H_
+#define DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_LINUX_H_
+
+#include <libudev.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "device/serial/serial_device_enumerator.h"
+
+namespace device {
+
+// Discovers and enumerates serial devices available to the host.
+class SerialDeviceEnumeratorLinux : public SerialDeviceEnumerator {
+ public:
+ SerialDeviceEnumeratorLinux();
+ virtual ~SerialDeviceEnumeratorLinux();
+
+ // Implementation for SerialDeviceEnumerator.
+ virtual mojo::Array<SerialDeviceInfoPtr> GetDevices() OVERRIDE;
+
+ private:
+ struct UdevDeleter {
+ void operator()(udev* handle);
+ };
+
+ scoped_ptr<udev, UdevDeleter> udev_;
+
+ DISALLOW_COPY_AND_ASSIGN(SerialDeviceEnumeratorLinux);
+};
+
+} // namespace device
+
+#endif // DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_LINUX_H_
diff --git a/chromium/device/serial/serial_device_enumerator_mac.cc b/chromium/device/serial/serial_device_enumerator_mac.cc
new file mode 100644
index 00000000000..b29352f425e
--- /dev/null
+++ b/chromium/device/serial/serial_device_enumerator_mac.cc
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/serial/serial_device_enumerator_mac.h"
+
+#include "base/file_util.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+
+namespace device {
+
+// static
+scoped_ptr<SerialDeviceEnumerator> SerialDeviceEnumerator::Create() {
+ return scoped_ptr<SerialDeviceEnumerator>(new SerialDeviceEnumeratorMac());
+}
+
+SerialDeviceEnumeratorMac::SerialDeviceEnumeratorMac() {}
+
+SerialDeviceEnumeratorMac::~SerialDeviceEnumeratorMac() {}
+
+// TODO(rockot): Use IOKit to enumerate serial interfaces.
+mojo::Array<SerialDeviceInfoPtr> SerialDeviceEnumeratorMac::GetDevices() {
+ const base::FilePath kDevRoot("/dev");
+ const int kFilesAndSymLinks =
+ base::FileEnumerator::FILES | base::FileEnumerator::SHOW_SYM_LINKS;
+
+ std::set<std::string> valid_patterns;
+ valid_patterns.insert("/dev/*Bluetooth*");
+ valid_patterns.insert("/dev/*Modem*");
+ valid_patterns.insert("/dev/*bluetooth*");
+ valid_patterns.insert("/dev/*modem*");
+ valid_patterns.insert("/dev/*serial*");
+ valid_patterns.insert("/dev/tty.*");
+ valid_patterns.insert("/dev/cu.*");
+
+ mojo::Array<SerialDeviceInfoPtr> devices;
+ base::FileEnumerator enumerator(kDevRoot, false, kFilesAndSymLinks);
+ do {
+ const base::FilePath next_device_path(enumerator.Next());
+ const std::string next_device = next_device_path.value();
+ if (next_device.empty())
+ break;
+
+ std::set<std::string>::const_iterator i = valid_patterns.begin();
+ for (; i != valid_patterns.end(); ++i) {
+ if (MatchPattern(next_device, *i)) {
+ SerialDeviceInfoPtr info(SerialDeviceInfo::New());
+ info->path = next_device;
+ devices.push_back(info.Pass());
+ break;
+ }
+ }
+ } while (true);
+ return devices.Pass();
+}
+
+} // namespace device
diff --git a/chromium/device/serial/serial_device_enumerator_mac.h b/chromium/device/serial/serial_device_enumerator_mac.h
new file mode 100644
index 00000000000..d1c6db378df
--- /dev/null
+++ b/chromium/device/serial/serial_device_enumerator_mac.h
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_MAC_H_
+#define DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_MAC_H_
+
+#include "device/serial/serial_device_enumerator.h"
+
+namespace device {
+
+// Discovers and enumerates serial devices available to the host.
+class SerialDeviceEnumeratorMac : public SerialDeviceEnumerator {
+ public:
+ SerialDeviceEnumeratorMac();
+ virtual ~SerialDeviceEnumeratorMac();
+
+ // Implementation for SerialDeviceEnumerator.
+ virtual mojo::Array<SerialDeviceInfoPtr> GetDevices() OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SerialDeviceEnumeratorMac);
+};
+
+} // namespace device
+
+#endif // DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_MAC_H_
diff --git a/chromium/device/serial/serial_device_enumerator_win.cc b/chromium/device/serial/serial_device_enumerator_win.cc
new file mode 100644
index 00000000000..c00d4d4c20f
--- /dev/null
+++ b/chromium/device/serial/serial_device_enumerator_win.cc
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/serial/serial_device_enumerator_win.h"
+
+#include <windows.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/registry.h"
+
+namespace device {
+
+// static
+scoped_ptr<SerialDeviceEnumerator> SerialDeviceEnumerator::Create() {
+ return scoped_ptr<SerialDeviceEnumerator>(new SerialDeviceEnumeratorWin());
+}
+
+SerialDeviceEnumeratorWin::SerialDeviceEnumeratorWin() {}
+
+SerialDeviceEnumeratorWin::~SerialDeviceEnumeratorWin() {}
+
+// TODO(rockot): Query the system for more information than just device paths.
+// This may or may not require using a different strategy than scanning the
+// registry location below.
+mojo::Array<SerialDeviceInfoPtr> SerialDeviceEnumeratorWin::GetDevices() {
+ base::win::RegistryValueIterator iter_key(
+ HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM\\");
+ mojo::Array<SerialDeviceInfoPtr> devices;
+ for (; iter_key.Valid(); ++iter_key) {
+ SerialDeviceInfoPtr info(SerialDeviceInfo::New());
+ info->path = base::UTF16ToASCII(iter_key.Value());
+ devices.push_back(info.Pass());
+ }
+ return devices.Pass();
+}
+
+} // namespace device
diff --git a/chromium/device/serial/serial_device_enumerator_win.h b/chromium/device/serial/serial_device_enumerator_win.h
new file mode 100644
index 00000000000..082b8f92b73
--- /dev/null
+++ b/chromium/device/serial/serial_device_enumerator_win.h
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_WIN_H_
+#define DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_WIN_H_
+
+#include "device/serial/serial_device_enumerator.h"
+
+namespace device {
+
+// Discovers and enumerates serial devices available to the host.
+class SerialDeviceEnumeratorWin : public SerialDeviceEnumerator {
+ public:
+ SerialDeviceEnumeratorWin();
+ virtual ~SerialDeviceEnumeratorWin();
+
+ // Implementation for SerialDeviceEnumerator.
+ virtual mojo::Array<SerialDeviceInfoPtr> GetDevices() OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SerialDeviceEnumeratorWin);
+};
+
+} // namespace device
+
+#endif // DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_WIN_H_
diff --git a/chromium/device/udev_linux/BUILD.gn b/chromium/device/udev_linux/BUILD.gn
new file mode 100644
index 00000000000..917a7e593d4
--- /dev/null
+++ b/chromium/device/udev_linux/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+
+if (use_udev) {
+ source_set("udev_linux") {
+ sources = [
+ "udev.cc",
+ "udev.h",
+ ]
+
+ deps = [
+ "//base",
+ ]
+
+ configs += [
+ "//build/config/linux:udev",
+ ]
+ }
+}
diff --git a/chromium/device/udev_linux/udev.cc b/chromium/device/udev_linux/udev.cc
new file mode 100644
index 00000000000..e10074b5aa7
--- /dev/null
+++ b/chromium/device/udev_linux/udev.cc
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <libudev.h>
+
+#include "device/udev_linux/udev.h"
+
+namespace device {
+
+void UdevDeleter::operator()(udev* dev) const {
+ udev_unref(dev);
+}
+
+void UdevEnumerateDeleter::operator()(udev_enumerate* enumerate) const {
+ udev_enumerate_unref(enumerate);
+}
+
+void UdevDeviceDeleter::operator()(udev_device* device) const {
+ udev_device_unref(device);
+}
+
+void UdevMonitorDeleter::operator()(udev_monitor* monitor) const {
+ udev_monitor_unref(monitor);
+}
+
+} // namespace device
diff --git a/chromium/device/udev_linux/udev.gyp b/chromium/device/udev_linux/udev.gyp
new file mode 100644
index 00000000000..6d142ac8766
--- /dev/null
+++ b/chromium/device/udev_linux/udev.gyp
@@ -0,0 +1,30 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'conditions': [
+ ['use_udev==1', {
+ 'targets': [
+ {
+ 'target_name': 'udev_linux',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../build/linux/system.gyp:udev',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'udev.cc',
+ 'udev.h',
+ ],
+ },
+ ],
+ }],
+ ]
+}
diff --git a/chromium/device/udev_linux/udev.h b/chromium/device/udev_linux/udev.h
new file mode 100644
index 00000000000..cffb6723cc4
--- /dev/null
+++ b/chromium/device/udev_linux/udev.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_UDEV_LINUX_UDEV_H_
+#define DEVICE_UDEV_LINUX_UDEV_H_
+
+#include <libudev.h>
+
+#include "base/memory/scoped_ptr.h"
+
+#if !defined(USE_UDEV)
+#error "USE_UDEV not defined"
+#endif
+
+namespace device {
+
+struct UdevDeleter {
+ void operator()(udev* dev) const;
+};
+struct UdevEnumerateDeleter {
+ void operator()(udev_enumerate* enumerate) const;
+};
+struct UdevDeviceDeleter {
+ void operator()(udev_device* device) const;
+};
+struct UdevMonitorDeleter {
+ void operator()(udev_monitor* monitor) const;
+};
+
+typedef scoped_ptr<udev, UdevDeleter> ScopedUdevPtr;
+typedef scoped_ptr<udev_enumerate, UdevEnumerateDeleter> ScopedUdevEnumeratePtr;
+typedef scoped_ptr<udev_device, UdevDeviceDeleter> ScopedUdevDevicePtr;
+typedef scoped_ptr<udev_monitor, UdevMonitorDeleter> ScopedUdevMonitorPtr;
+
+} // namespace device
+
+#endif // DEVICE_UDEV_LINUX_UDEV_H_
diff --git a/chromium/device/usb/BUILD.gn b/chromium/device/usb/BUILD.gn
new file mode 100644
index 00000000000..f024965e72e
--- /dev/null
+++ b/chromium/device/usb/BUILD.gn
@@ -0,0 +1,31 @@
+# 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.
+
+source_ids = "//third_party/usb_ids/usb.ids"
+generated_ids = "$target_gen_dir/usb_ids_gen.cc"
+
+source_set("usb") {
+ sources = [
+ "usb_ids.cc",
+ "usb_ids.h",
+ generated_ids,
+ ]
+ deps = [
+ ":usb_device_ids",
+ "//base",
+ ]
+}
+
+action("usb_device_ids") {
+ script = "//device/usb/tools/usb_ids.py"
+ source_prereqs = [ source_ids ]
+ outputs = [ generated_ids ]
+ args = [
+ "-i", rebase_path(source_ids, root_build_dir),
+ "-o", rebase_path(generated_ids, root_build_dir),
+ ]
+
+ # Only the device_usb target can depend on us.
+ visibility = [ ":usb" ]
+}
diff --git a/chromium/device/usb/tools/usb_ids.py b/chromium/device/usb/tools/usb_ids.py
new file mode 100644
index 00000000000..e07bd4ca6f7
--- /dev/null
+++ b/chromium/device/usb/tools/usb_ids.py
@@ -0,0 +1,110 @@
+# 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.
+
+import itertools
+import optparse
+import re
+
+VENDOR_PATTERN = re.compile("^(?P<id>[0-9a-fA-F]{4})\s+(?P<name>.+)$")
+PRODUCT_PATTERN = re.compile("^\t(?P<id>[0-9a-fA-F]{4})\s+(?P<name>.+)$")
+
+def EscapeName(name):
+ name = name.replace("\\", "\\\\")
+ name = name.replace("\"", "\\\"")
+ name = name.replace("?", "\?")
+ return name
+
+def ParseTable(input_path):
+ input_file = open(input_path, "r")
+ input = input_file.read().split("\n")
+ input_file.close()
+
+ table = {}
+ vendor = None
+
+ for line in input:
+ vendor_match = VENDOR_PATTERN.match(line)
+ if vendor_match:
+ if vendor:
+ table[vendor["id"]] = vendor
+ vendor = {}
+ vendor["id"] = int(vendor_match.group("id"), 16)
+ vendor["name"] = vendor_match.group("name")
+ vendor["products"] = []
+ continue
+
+ product_match = PRODUCT_PATTERN.match(line)
+ if product_match:
+ if not vendor:
+ raise Exception("Product seems to appear before vendor.")
+ product = {}
+ product["id"] = int(product_match.group("id"), 16)
+ product["name"] = product_match.group("name")
+ vendor["products"].append(product)
+
+ return table
+
+def GenerateDeviceDefinitions(table):
+ output = ""
+
+ for vendor_id in sorted(table.keys()):
+ vendor = table[vendor_id]
+ if len(vendor["products"]) == 0:
+ continue
+
+ output += "static const UsbProduct vendor_%.4x_products[] = {\n" % \
+ vendor["id"]
+ for product in vendor["products"]:
+ output += " {0x%.4x, \"%s\"},\n" % (product["id"],
+ EscapeName(product["name"]))
+ output += "};\n"
+
+ return output
+
+def GenerateVendorDefinitions(table):
+ output = "const size_t UsbIds::vendor_size_ = %d;\n" % len(table.keys())
+ output += "const UsbVendor UsbIds::vendors_[] = {\n"
+
+ for vendor_id in sorted(table.keys()):
+ vendor = table[vendor_id]
+
+ product_table = "NULL"
+ if len(vendor["products"]) != 0:
+ product_table = "vendor_%.4x_products" % (vendor["id"])
+ output += " {0x%.4x, \"%s\", %d, %s},\n" % (vendor["id"],
+ EscapeName(vendor["name"]), len(vendor["products"]), product_table)
+
+ output += "};\n"
+ return output
+
+if __name__ == "__main__":
+ parser = optparse.OptionParser(
+ description="Generates a C++ USB ID lookup table.")
+ parser.add_option("-i", "--input", help="Path to usb.ids")
+ parser.add_option("-o", "--output", help="Output file path")
+
+ (opts, args) = parser.parse_args()
+ table = ParseTable(opts.input)
+
+ output = """// Generated from %s
+#ifndef GENERATED_USB_IDS_H_
+#define GENERATED_USB_IDS_H_
+
+#include "device/usb/usb_ids.h"
+
+namespace device {
+
+""" % (opts.input)
+ output += GenerateDeviceDefinitions(table)
+ output += GenerateVendorDefinitions(table)
+ output += """
+
+} // namespace device
+
+#endif // GENERATED_USB_IDS_H_
+"""
+
+ output_file = open(opts.output, "w+")
+ output_file.write(output)
+ output_file.close()
diff --git a/chromium/device/usb/usb.gyp b/chromium/device/usb/usb.gyp
index b7cae895b0c..47cf824593f 100644
--- a/chromium/device/usb/usb.gyp
+++ b/chromium/device/usb/usb.gyp
@@ -21,8 +21,8 @@
{
'action_name': 'generate_usb_ids',
'variables': {
- 'usb_ids_path%': '<(DEPTH)/third_party/usb_ids/usb.ids',
- 'usb_ids_py_path': '<(DEPTH)/tools/usb_ids/usb_ids.py',
+ 'usb_ids_path%': '../../third_party/usb_ids/usb.ids',
+ 'usb_ids_py_path': 'tools/usb_ids.py',
},
'inputs': [
'<(usb_ids_path)',