Merge from Chromium at DEPS revision 251904
This commit was generated by merge_to_master.py.
Change-Id: I1f9543259d7d2a57d81aa41a1b84f85837439d21
diff --git a/device/OWNERS b/device/OWNERS
index a83889f..f6716f4 100644
--- a/device/OWNERS
+++ b/device/OWNERS
@@ -1,3 +1,5 @@
keybuk@chromium.org
gdk@chromium.org
miket@chromium.org
+rockot@chromium.org
+rpaquay@chromium.org
diff --git a/device/bluetooth/bluetooth.gyp b/device/bluetooth/bluetooth.gyp
index c5d8cad..c4012bd 100644
--- a/device/bluetooth/bluetooth.gyp
+++ b/device/bluetooth/bluetooth.gyp
@@ -16,6 +16,7 @@
'../../net/net.gyp:net',
'../../third_party/libxml/libxml.gyp:libxml',
'../../ui/gfx/gfx.gyp:gfx',
+ '../../ui/gfx/gfx.gyp:gfx_geometry',
'../../ui/ui.gyp:ui',
'bluetooth_strings.gyp:device_bluetooth_strings',
],
@@ -38,6 +39,12 @@
'bluetooth_device_mac.mm',
'bluetooth_device_win.cc',
'bluetooth_device_win.h',
+ 'bluetooth_gatt_characteristic.cc',
+ 'bluetooth_gatt_characteristic.h',
+ 'bluetooth_gatt_descriptor.cc',
+ 'bluetooth_gatt_descriptor.h',
+ 'bluetooth_gatt_service.cc',
+ 'bluetooth_gatt_service.h',
'bluetooth_init_win.cc',
'bluetooth_init_win.h',
'bluetooth_out_of_band_pairing_data.h',
diff --git a/device/bluetooth/bluetooth_adapter.h b/device/bluetooth/bluetooth_adapter.h
index ed61586..24e9dcd 100644
--- a/device/bluetooth/bluetooth_adapter.h
+++ b/device/bluetooth/bluetooth_adapter.h
@@ -43,6 +43,12 @@
virtual void AdapterPoweredChanged(BluetoothAdapter* adapter,
bool powered) {}
+ // 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.
@@ -91,6 +97,12 @@
// 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;
@@ -109,6 +121,17 @@
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;
diff --git a/device/bluetooth/bluetooth_adapter_chromeos.cc b/device/bluetooth/bluetooth_adapter_chromeos.cc
index 319a6e9..0d962b9 100644
--- a/device/bluetooth/bluetooth_adapter_chromeos.cc
+++ b/device/bluetooth/bluetooth_adapter_chromeos.cc
@@ -79,6 +79,18 @@
return properties->alias.value();
}
+void BluetoothAdapterChromeOS::SetName(const std::string& name,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ 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;
}
@@ -105,7 +117,31 @@
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::IsDiscoverable() const {
+ if (!IsPresent())
+ return false;
+
+ BluetoothAdapterClient::Properties* properties =
+ DBusThreadManager::Get()->GetBluetoothAdapterClient()->
+ GetProperties(object_path_);
+
+ return properties->discoverable.value();
+}
+
+void BluetoothAdapterChromeOS::SetDiscoverable(
+ bool discoverable,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ DBusThreadManager::Get()->GetBluetoothAdapterClient()->
+ GetProperties(object_path_)->discoverable.Set(
+ discoverable,
+ base::Bind(&BluetoothAdapterChromeOS::OnSetDiscoverable,
weak_ptr_factory_.GetWeakPtr(),
callback,
error_callback));
@@ -185,6 +221,8 @@
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());
}
@@ -297,7 +335,7 @@
VLOG(1) << object_path_.value() << ": using adapter.";
- SetAdapterName();
+ SetDefaultAdapterName();
BluetoothAdapterClient::Properties* properties =
DBusThreadManager::Get()->GetBluetoothAdapterClient()->
@@ -307,6 +345,8 @@
if (properties->powered.value())
PoweredChanged(true);
+ if (properties->discoverable.value())
+ DiscoverableChanged(true);
if (properties->discovering.value())
DiscoveringChanged(true);
@@ -326,7 +366,7 @@
}
}
-void BluetoothAdapterChromeOS::SetAdapterName() {
+void BluetoothAdapterChromeOS::SetDefaultAdapterName() {
std::string board = base::SysInfo::GetLsbReleaseBoard();
std::string alias;
if (board.substr(0, 6) == "stumpy") {
@@ -337,16 +377,7 @@
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 +392,8 @@
if (properties->powered.value())
PoweredChanged(false);
+ if (properties->discoverable.value())
+ DiscoverableChanged(false);
if (properties->discovering.value())
DiscoveringChanged(false);
@@ -384,6 +417,11 @@
AdapterPoweredChanged(this, powered));
}
+void BluetoothAdapterChromeOS::DiscoverableChanged(bool discoverable) {
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ AdapterDiscoverableChanged(this, discoverable));
+}
+
void BluetoothAdapterChromeOS::DiscoveringChanged(
bool discovering) {
FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
@@ -403,9 +441,25 @@
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
diff --git a/device/bluetooth/bluetooth_adapter_chromeos.h b/device/bluetooth/bluetooth_adapter_chromeos.h
index 74beb35..b400ed3 100644
--- a/device/bluetooth/bluetooth_adapter_chromeos.h
+++ b/device/bluetooth/bluetooth_adapter_chromeos.h
@@ -40,6 +40,9 @@
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,6 +50,11 @@
bool powered,
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
+ virtual bool IsDiscoverable() const OVERRIDE;
+ virtual void SetDiscoverable(
+ bool discoverable,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
virtual bool IsDiscovering() const OVERRIDE;
virtual void StartDiscovering(
const base::Closure& callback,
@@ -95,10 +103,8 @@
// 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,6 +112,7 @@
// 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);
@@ -113,10 +120,15 @@
// its D-Bus properties.
void NotifyDeviceChanged(BluetoothDeviceChromeOS* device);
- // 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 the discoverable property change.
+ void OnSetDiscoverable(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);
// Called by dbus:: on completion of the D-Bus method call to start discovery.
void OnStartDiscovery(const base::Closure& callback);
diff --git a/device/bluetooth/bluetooth_adapter_mac.h b/device/bluetooth/bluetooth_adapter_mac.h
index 7054acf..6639db0 100644
--- a/device/bluetooth/bluetooth_adapter_mac.h
+++ b/device/bluetooth/bluetooth_adapter_mac.h
@@ -47,6 +47,9 @@
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,6 +57,11 @@
bool powered,
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
+ virtual bool IsDiscoverable() const OVERRIDE;
+ virtual void SetDiscoverable(
+ bool discoverable,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
virtual bool IsDiscovering() const OVERRIDE;
virtual void StartDiscovering(
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm
index e15b894..502ed81 100644
--- a/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -127,6 +127,12 @@
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,6 +148,19 @@
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 {
diff --git a/device/bluetooth/bluetooth_adapter_win.cc b/device/bluetooth/bluetooth_adapter_win.cc
index f6ca1d6..521afae 100644
--- a/device/bluetooth/bluetooth_adapter_win.cc
+++ b/device/bluetooth/bluetooth_adapter_win.cc
@@ -53,6 +53,12 @@
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,6 +80,18 @@
task_manager_->PostSetPoweredBluetoothTask(powered, callback, error_callback);
}
+bool BluetoothAdapterWin::IsDiscoverable() const {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void BluetoothAdapterWin::SetDiscoverable(
+ bool discoverable,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ NOTIMPLEMENTED();
+}
+
bool BluetoothAdapterWin::IsDiscovering() const {
return discovery_status_ == DISCOVERING ||
discovery_status_ == DISCOVERY_STOPPING;
diff --git a/device/bluetooth/bluetooth_adapter_win.h b/device/bluetooth/bluetooth_adapter_win.h
index a017e29..773c6f3 100644
--- a/device/bluetooth/bluetooth_adapter_win.h
+++ b/device/bluetooth/bluetooth_adapter_win.h
@@ -39,11 +39,19 @@
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,
+ bool discoverable,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual bool IsDiscoverable() const OVERRIDE;
+ virtual void SetDiscoverable(
+ bool discoverable,
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
virtual bool IsDiscovering() const OVERRIDE;
diff --git a/device/bluetooth/bluetooth_chromeos_unittest.cc b/device/bluetooth/bluetooth_chromeos_unittest.cc
index dea1322..a1cc67d 100644
--- a/device/bluetooth/bluetooth_chromeos_unittest.cc
+++ b/device/bluetooth/bluetooth_chromeos_unittest.cc
@@ -28,6 +28,7 @@
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),
@@ -56,6 +57,13 @@
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 +107,7 @@
int present_changed_count_;
int powered_changed_count_;
+ int discoverable_changed_count_;
int discovering_changed_count_;
bool last_present_;
bool last_powered_;
@@ -264,7 +273,7 @@
ASSERT_TRUE(adapter_.get() != NULL);
if (base::MessageLoop::current() == NULL) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
DiscoverDevices();
return;
}
@@ -507,8 +516,81 @@
EXPECT_FALSE(adapter_->IsPowered());
}
+TEST_F(BluetoothChromeOSTest, ChangeAdapterName) {
+ GetAdapter();
+
+ 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(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ EXPECT_EQ(new_name, adapter_->GetName());
+}
+
+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_->SetDiscoverable(
+ 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_EQ(1, observer.discoverable_changed_count_);
+
+ EXPECT_TRUE(adapter_->IsDiscoverable());
+}
+
+TEST_F(BluetoothChromeOSTest, BecomeNotDiscoverable) {
+ GetAdapter();
+ adapter_->SetDiscoverable(
+ 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_);
+ callback_count_ = 0;
+
+ ASSERT_TRUE(adapter_->IsDiscoverable());
+
+ // Install an observer; expect the AdapterDiscoverableChanged to be called
+ // with false, and IsDiscoverable() to return false.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ 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_);
+
+ EXPECT_EQ(1, observer.discoverable_changed_count_);
+
+ EXPECT_FALSE(adapter_->IsDiscoverable());
+}
+
TEST_F(BluetoothChromeOSTest, StopDiscovery) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
GetAdapter();
@@ -551,7 +633,7 @@
}
TEST_F(BluetoothChromeOSTest, StopDiscoveryAfterTwoStarts) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
GetAdapter();
@@ -623,7 +705,7 @@
TEST_F(BluetoothChromeOSTest, Discovery) {
// Test a simulated discovery session.
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -670,7 +752,7 @@
}
TEST_F(BluetoothChromeOSTest, PoweredAndDiscovering) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
GetAdapter();
adapter_->SetPowered(
@@ -749,7 +831,7 @@
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());
@@ -805,7 +887,7 @@
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
@@ -823,7 +905,7 @@
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) {
@@ -1169,7 +1251,7 @@
}
TEST_F(BluetoothChromeOSTest, PairAppleMouse) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1230,7 +1312,7 @@
}
TEST_F(BluetoothChromeOSTest, PairAppleKeyboard) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1292,7 +1374,7 @@
}
TEST_F(BluetoothChromeOSTest, PairMotorolaKeyboard) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1375,7 +1457,7 @@
}
TEST_F(BluetoothChromeOSTest, PairSonyHeadphones) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1438,7 +1520,7 @@
}
TEST_F(BluetoothChromeOSTest, PairPhone) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1498,7 +1580,7 @@
}
TEST_F(BluetoothChromeOSTest, PairWeirdDevice) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1559,7 +1641,7 @@
}
TEST_F(BluetoothChromeOSTest, PairUnpairableDeviceFails) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1602,7 +1684,7 @@
}
TEST_F(BluetoothChromeOSTest, PairingFails) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1646,7 +1728,7 @@
}
TEST_F(BluetoothChromeOSTest, PairingFailsAtConnection) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1703,7 +1785,7 @@
}
TEST_F(BluetoothChromeOSTest, PairingRejectedAtPinCode) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1750,7 +1832,7 @@
}
TEST_F(BluetoothChromeOSTest, PairingCancelledAtPinCode) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1797,7 +1879,7 @@
}
TEST_F(BluetoothChromeOSTest, PairingRejectedAtPasskey) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1844,7 +1926,7 @@
}
TEST_F(BluetoothChromeOSTest, PairingCancelledAtPasskey) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1891,7 +1973,7 @@
}
TEST_F(BluetoothChromeOSTest, PairingRejectedAtConfirmation) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1938,7 +2020,7 @@
}
TEST_F(BluetoothChromeOSTest, PairingCancelledAtConfirmation) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
@@ -1985,7 +2067,7 @@
}
TEST_F(BluetoothChromeOSTest, PairingCancelledInFlight) {
- base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop message_loop;
fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
GetAdapter();
diff --git a/device/bluetooth/bluetooth_device.cc b/device/bluetooth/bluetooth_device.cc
index 7b6ee0d..7c100ec 100644
--- a/device/bluetooth/bluetooth_device.cc
+++ b/device/bluetooth/bluetooth_device.cc
@@ -24,17 +24,17 @@
BluetoothDevice::~BluetoothDevice() {
}
-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:
diff --git a/device/bluetooth/bluetooth_device.h b/device/bluetooth/bluetooth_device.h
index aaf4e93..95fe683 100644
--- a/device/bluetooth/bluetooth_device.h
+++ b/device/bluetooth/bluetooth_device.h
@@ -188,7 +188,7 @@
// 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
@@ -369,7 +369,7 @@
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/device/bluetooth/bluetooth_gatt_characteristic.cc b/device/bluetooth/bluetooth_gatt_characteristic.cc
new file mode 100644
index 0000000..49fd3b3
--- /dev/null
+++ b/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 bluetooth_utils::UUID& 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/device/bluetooth/bluetooth_gatt_characteristic.h b/device/bluetooth/bluetooth_gatt_characteristic.h
new file mode 100644
index 0000000..00f7a88
--- /dev/null
+++ b/device/bluetooth/bluetooth_gatt_characteristic.h
@@ -0,0 +1,195 @@
+// 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_GATT_CHARACTERISTIC_H_
+#define DEVICE_BLUETOOTH_GATT_CHARACTERISTIC_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "device/bluetooth/bluetooth_utils.h"
+
+namespace device {
+
+class BluetoothGattDescriptor;
+class BluetoothGattService;
+
+// 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.
+ 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
+ };
+ 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;
+
+ // Interface for observing changes from a BluetoothGattCharacteristic.
+ // Properties of remote characteristics are received asynchonously. The
+ // Observer interface can be used to be notified when the initial values of a
+ // characteristic are received as well as when successive changes occur during
+ // its life cycle.
+ class Observer {
+ public:
+ // Called when the UUID of |characteristic| has changed.
+ virtual void UuidChanged(
+ BluetoothGattCharacteristic* characteristic,
+ const bluetooth_utils::UUID& uuid) {}
+
+ // Called when the current value of |characteristic| has changed.
+ virtual void ValueChanged(
+ BluetoothGattCharacteristic* characteristic,
+ const std::vector<uint8>& value) {}
+
+ // Called when the descriptors that are associated with |characteristic|
+ // have changed.
+ virtual void DescriptorsChanged(
+ BluetoothGattCharacteristic* characteristic,
+ const std::vector<BluetoothGattDescriptor*>& descriptors) {}
+ };
+
+ // The ErrorCallback is used by methods to asynchronously report errors.
+ typedef base::Callback<void(const std::string&)> 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;
+
+ // Adds and removes observers for events on this GATT characteristic. If
+ // monitoring multiple characteristics, check the |characteristic| parameter
+ // of observer methods to determine which characteristic is issuing the event.
+ virtual void AddObserver(Observer* observer) = 0;
+ virtual void RemoveObserver(Observer* observer) = 0;
+
+ // 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 bluetooth_utils::UUID& uuid,
+ const std::vector<uint8>& value,
+ Properties properties,
+ Permissions permissions);
+
+ // The Bluetooth-specific UUID of the characteristic.
+ virtual const bluetooth_utils::UUID& 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 a pointer to the GATT service this characteristic belongs to.
+ virtual const BluetoothGattService* GetService() const = 0;
+
+ // Returns the list of GATT characteristic descriptors that provide more
+ // information about this characteristic.
+ virtual const std::vector<BluetoothGattDescriptor*>
+ GetDescriptors() 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;
+
+ // 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 starting at offset |offset| 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(
+ int offset,
+ 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_GATT_CHARACTERISTIC_H_
diff --git a/device/bluetooth/bluetooth_gatt_descriptor.cc b/device/bluetooth/bluetooth_gatt_descriptor.cc
new file mode 100644
index 0000000..4746cad
--- /dev/null
+++ b/device/bluetooth/bluetooth_gatt_descriptor.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/bluetooth/bluetooth_gatt_descriptor.h"
+
+#include "base/logging.h"
+
+namespace device {
+
+using bluetooth_utils::UUID;
+
+const UUID BluetoothGattDescriptor::
+ kCharacteristicExtendedPropertiesUuid("0x2900");
+const UUID BluetoothGattDescriptor::
+ kCharacteristicUserDescriptionUuid("0x2901");
+const UUID BluetoothGattDescriptor::
+ kClientCharacteristicConfigurationUuid("0x2902");
+const UUID BluetoothGattDescriptor::
+ kServerCharacteristicConfigurationUuid("0x2903");
+const UUID BluetoothGattDescriptor::
+ kCharacteristicPresentationFormatUuid("0x2904");
+const UUID BluetoothGattDescriptor::
+ kCharacteristicAggregateFormatUuid("0x2905");
+
+BluetoothGattDescriptor::BluetoothGattDescriptor() {
+}
+
+BluetoothGattDescriptor::~BluetoothGattDescriptor() {
+}
+
+// static
+BluetoothGattDescriptor* BluetoothGattDescriptor::Create(
+ const bluetooth_utils::UUID& uuid,
+ const std::vector<uint8>& value) {
+ LOG(ERROR) << "Creating local GATT characteristic descriptors currently not "
+ << "supported.";
+ return NULL;
+}
+
+} // namespace device
diff --git a/device/bluetooth/bluetooth_gatt_descriptor.h b/device/bluetooth/bluetooth_gatt_descriptor.h
new file mode 100644
index 0000000..9c0a1d2
--- /dev/null
+++ b/device/bluetooth/bluetooth_gatt_descriptor.h
@@ -0,0 +1,197 @@
+// 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_GATT_DESCRIPTOR_H_
+#define DEVICE_BLUETOOTH_GATT_DESCRIPTOR_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "device/bluetooth/bluetooth_utils.h"
+
+namespace device {
+
+class BluetoothGattCharacteristic;
+
+// 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 bluetooth_utils::UUID kCharacteristicExtendedPropertiesUuid;
+
+ // 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 bluetooth_utils::UUID kCharacteristicUserDescriptionUuid;
+
+ // 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 bluetooth_utils::UUID kClientCharacteristicConfigurationUuid;
+
+ // 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 bluetooth_utils::UUID kServerCharacteristicConfigurationUuid;
+
+ // 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 bluetooth_utils::UUID kCharacteristicPresentationFormatUuid;
+
+ // 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 bluetooth_utils::UUID kCharacteristicAggregateFormatUuid;
+
+ // Interface for observing changes from a BluetoothGattDescriptor.
+ // Properties of remote characteristic desciptors are received asynchonously.
+ // The Observer interface can be used to be notified when the initial values
+ // of a characteristic descriptor are received as well as when successive
+ // changes occur during its life cycle.
+ class Observer {
+ public:
+ // Called when the UUID of |descriptor| has changed.
+ virtual void UuidChanged(
+ BluetoothGattDescriptor* descriptor,
+ const bluetooth_utils::UUID& uuid) {}
+
+ // Called when the current value of |descriptor| has changed.
+ virtual void ValueChanged(
+ BluetoothGattDescriptor* descriptor,
+ const std::vector<uint8>& value) {}
+ };
+
+ // The ErrorCallback is used by methods to asynchronously report errors.
+ typedef base::Callback<void(const std::string&)> 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;
+
+ // Adds and removes observers for events on this GATT characteristic
+ // descriptor. If monitoring multiple descriptors, check the |descriptor|
+ // parameter of observer methods to determine which characteristic is issuing
+ // the event.
+ virtual void AddObserver(Observer* observer) = 0;
+ virtual void RemoveObserver(Observer* observer) = 0;
+
+ // 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 bluetooth_utils::UUID& uuid,
+ const std::vector<uint8>& value);
+
+ // The Bluetooth-specific UUID of the characteristic descriptor.
+ virtual const bluetooth_utils::UUID& 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 a pointer to the GATT characteristic that this characteristic
+ // descriptor belongs to.
+ virtual const BluetoothGattCharacteristic* GetCharacteristic() 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 starting at offset |offset| 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(
+ int offset,
+ 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_GATT_DESCRIPTOR_H_
diff --git a/device/bluetooth/bluetooth_gatt_service.cc b/device/bluetooth/bluetooth_gatt_service.cc
new file mode 100644
index 0000000..dccb06a
--- /dev/null
+++ b/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 bluetooth_utils::UUID& uuid,
+ bool is_primary,
+ Delegate* delegate) {
+ LOG(ERROR) << "Creating local GATT services currently not supported.";
+ return NULL;
+}
+
+} // namespace device
diff --git a/device/bluetooth/bluetooth_gatt_service.h b/device/bluetooth/bluetooth_gatt_service.h
new file mode 100644
index 0000000..963fdd5
--- /dev/null
+++ b/device/bluetooth/bluetooth_gatt_service.h
@@ -0,0 +1,226 @@
+// 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_GATT_SERVICE_H_
+#define DEVICE_BLUETOOTH_GATT_SERVICE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "device/bluetooth/bluetooth_utils.h"
+
+namespace device {
+
+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 the UUID of |service| have changed.
+ virtual void UuidChanged(
+ BluetoothGattService* service,
+ const bluetooth_utils::UUID& uuid) {}
+
+ // Called when the services included by |service| have changed.
+ virtual void IncludedServicesChanged(
+ BluetoothGattService* service,
+ const std::vector<BluetoothGattService*>& included_services) {}
+
+ // Called when the characteristics that belong to |service| have changed.
+ virtual void CharacteristicsChanged(
+ BluetoothGattService* service,
+ const std::vector<BluetoothGattCharacteristic*>& characteristics) {}
+ };
+
+ // The ErrorCallback is used by methods to asynchronously report errors.
+ typedef base::Callback<void(const std::string&)> ErrorCallback;
+
+ // 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 bluetooth_utils::UUID& uuid,
+ bool is_primary,
+ Delegate* delegate);
+
+ // The Bluetooth-specific UUID of the service.
+ virtual const bluetooth_utils::UUID& 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;
+
+ // List of characteristics that belong to this service.
+ virtual const std::vector<BluetoothGattCharacteristic*>&
+ GetCharacteristics() const = 0;
+
+ // List of GATT services that are included by this service.
+ virtual const std::vector<BluetoothGattService*>&
+ GetIncludedServices() const = 0;
+
+ // 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;
+
+ // 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 AddService(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();
+ virtual ~BluetoothGattService();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BluetoothGattService);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_GATT_SERVICE_H_
diff --git a/device/bluetooth/bluetooth_profile_chromeos_unittest.cc b/device/bluetooth/bluetooth_profile_chromeos_unittest.cc
index 1792cda..dd7c945 100644
--- a/device/bluetooth/bluetooth_profile_chromeos_unittest.cc
+++ b/device/bluetooth/bluetooth_profile_chromeos_unittest.cc
@@ -31,8 +31,7 @@
class BluetoothProfileChromeOSTest : public testing::Test {
public:
BluetoothProfileChromeOSTest()
- : message_loop_(base::MessageLoop::TYPE_IO),
- callback_count_(0),
+ : callback_count_(0),
error_callback_count_(0),
profile_callback_count_(0),
connection_callback_count_(0),
@@ -102,7 +101,7 @@
}
protected:
- base::MessageLoop message_loop_;
+ base::MessageLoopForIO message_loop_;
FakeBluetoothProfileManagerClient* fake_bluetooth_profile_manager_client_;
scoped_refptr<BluetoothAdapter> adapter_;
diff --git a/device/bluetooth/bluetooth_utils.cc b/device/bluetooth/bluetooth_utils.cc
index b1f9f78..df0c875 100644
--- a/device/bluetooth/bluetooth_utils.cc
+++ b/device/bluetooth/bluetooth_utils.cc
@@ -10,49 +10,103 @@
#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) {
+namespace {
+
+const char* kCommonUuidPostfix = "-0000-1000-8000-00805f9b34fb";
+const char* kCommonUuidPrefix = "0000";
+const int kUuidSize = 36;
+
+// 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,
+ UUID::Format* format) {
+ // Initialize the values for the failure case.
+ canonical->clear();
+ canonical_128->clear();
+ *format = UUID::kFormatInvalid;
+
if (uuid.empty())
- return std::string();
+ return;
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();
+ return;
if (uuid.size() == 4 || uuid.size() == 8) {
for (size_t i = 0; i < uuid.size(); ++i) {
if (!IsHexDigit(uuid[i]))
- return std::string();
+ return;
}
-
- if (uuid.size() == 4)
- return kCommonUuidPrefix + uuid + kCommonUuidPostfix;
-
- return uuid + kCommonUuidPostfix;
+ if (uuid.size() == 4) {
+ canonical->assign(uuid);
+ canonical_128->assign(kCommonUuidPrefix + uuid + kCommonUuidPostfix);
+ *format = UUID::kFormat16Bit;
+ return;
+ }
+ canonical->assign(uuid);
+ canonical_128->assign(uuid + kCommonUuidPostfix);
+ *format = UUID::kFormat32Bit;
+ return;
}
- 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();
+ return;
} else {
if (!IsHexDigit(uuid[i]))
- return std::string();
- uuid_result[i] = tolower(uuid[i]);
+ return;
+ uuid[i] = tolower(uuid[i]);
}
}
- return uuid_result;
+
+ canonical->assign(uuid);
+ canonical_128->assign(uuid);
+ *format = UUID::kFormat128Bit;
+}
+
+} // namespace
+
+
+UUID::UUID(const std::string& uuid) {
+ GetCanonicalUuid(uuid, &value_, &canonical_value_, &format_);
+}
+
+UUID::UUID() : format_(kFormatInvalid) {
+}
+
+UUID::~UUID() {
+}
+
+bool UUID::IsValid() const {
+ return format_ != kFormatInvalid;
+}
+
+bool UUID::operator<(const UUID& uuid) const {
+ return canonical_value_ < uuid.canonical_value_;
+}
+
+bool UUID::operator==(const UUID& uuid) const {
+ return canonical_value_ == uuid.canonical_value_;
+}
+
+bool UUID::operator!=(const UUID& uuid) const {
+ return canonical_value_ != uuid.canonical_value_;
+}
+
+std::string CanonicalUuid(std::string uuid) {
+ std::string value;
+ std::string canonical_value;
+ UUID::Format format;
+ GetCanonicalUuid(uuid, &value, &canonical_value, &format);
+ return canonical_value;
}
} // namespace bluetooth_utils
diff --git a/device/bluetooth/bluetooth_utils.h b/device/bluetooth/bluetooth_utils.h
index 0995160..44b52b1 100644
--- a/device/bluetooth/bluetooth_utils.h
+++ b/device/bluetooth/bluetooth_utils.h
@@ -12,6 +12,84 @@
namespace device {
namespace bluetooth_utils {
+// 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 UUID {
+ 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 UUID(const std::string& uuid);
+ ~UUID();
+
+ // 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 UUID& uuid) const;
+
+ // Equality operators.
+ bool operator==(const UUID& uuid) const;
+ bool operator!=(const UUID& uuid) const;
+
+ private:
+ UUID();
+
+ // 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_;
+};
+
+// DEPRECATED. Use bluetooth_utils::UUID instead.
+//
// 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.
diff --git a/device/bluetooth/bluetooth_utils_unittest.cc b/device/bluetooth/bluetooth_utils_unittest.cc
index 46d5f34..07c63b5 100644
--- a/device/bluetooth/bluetooth_utils_unittest.cc
+++ b/device/bluetooth/bluetooth_utils_unittest.cc
@@ -7,47 +7,100 @@
#include "testing/gtest/include/gtest/gtest.h"
namespace device {
+namespace bluetooth_utils {
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"));
+ 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"));
+ EXPECT_EQ("", CanonicalUuid("1101a"));
+ EXPECT_EQ("", CanonicalUuid("Z101"));
+ EXPECT_EQ("", CanonicalUuid("0000-1101"));
+ EXPECT_EQ("", CanonicalUuid("0000Z101"));
+ EXPECT_EQ("", CanonicalUuid("0001101-0000-1000-8000-00805f9b34fb"));
+ EXPECT_EQ("", CanonicalUuid("Z0001101-0000-1000-8000-00805f9b34fb"));
+ EXPECT_EQ("", CanonicalUuid("00001101 0000-1000-8000-00805f9b34fb"));
+ EXPECT_EQ("", CanonicalUuid("00001101-0000:1000-8000-00805f9b34fb"));
+ EXPECT_EQ("", CanonicalUuid("00001101-0000-1000;8000-00805f9b34fb"));
+ EXPECT_EQ("", CanonicalUuid("00001101-0000-1000-8000000805f9b34fb"));
// Lower case
EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
- bluetooth_utils::CanonicalUuid("00001101-0000-1000-8000-00805F9B34FB"));
+ CanonicalUuid("00001101-0000-1000-8000-00805F9B34FB"));
// Short to full
EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
- bluetooth_utils::CanonicalUuid("1101"));
+ CanonicalUuid("1101"));
EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
- bluetooth_utils::CanonicalUuid("0x1101"));
+ CanonicalUuid("0x1101"));
EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
- bluetooth_utils::CanonicalUuid("00001101"));
+ CanonicalUuid("00001101"));
EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
- bluetooth_utils::CanonicalUuid("0x00001101"));
+ CanonicalUuid("0x00001101"));
// No 0x prefix on 36 character
- EXPECT_EQ("",
- bluetooth_utils::CanonicalUuid("0x00001101-0000-1000-8000-00805f9b34fb"));
+ EXPECT_EQ("", CanonicalUuid("0x00001101-0000-1000-8000-00805f9b34fb"));
}
+TEST(BluetoothUtilsTest, UUID) {
+ const char kValid128Bit0[] = "12345678-1234-5678-9abc-def123456789";
+ const char kValid128Bit1[] = "00001101-0000-1000-8000-00805f9b34fb";
+ const char kInvalid36Char[] = "1234567-1234-5678-9abc-def123456789";
+ const char kInvalid4Char[] = "Z101";
+ const char kValid16Bit[] = "0x1101";
+ const char kValid32Bit[] = "00001101";
+
+ // Valid 128-bit custom UUID.
+ UUID uuid0(kValid128Bit0);
+ EXPECT_TRUE(uuid0.IsValid());
+ EXPECT_EQ(UUID::kFormat128Bit, uuid0.format());
+ EXPECT_EQ(uuid0.value(), uuid0.canonical_value());
+
+ // Valid 128-bit UUID.
+ UUID uuid1(kValid128Bit1);
+ EXPECT_TRUE(uuid1.IsValid());
+ EXPECT_EQ(UUID::kFormat128Bit, uuid1.format());
+ EXPECT_EQ(uuid1.value(), uuid1.canonical_value());
+
+ EXPECT_NE(uuid0, uuid1);
+
+ // Invalid 128-bit UUID.
+ UUID uuid2(kInvalid36Char);
+ EXPECT_FALSE(uuid2.IsValid());
+ EXPECT_EQ(UUID::kFormatInvalid, uuid2.format());
+ EXPECT_TRUE(uuid2.value().empty());
+ EXPECT_TRUE(uuid2.canonical_value().empty());
+
+ // Invalid 16-bit UUID.
+ UUID uuid3(kInvalid4Char);
+ EXPECT_FALSE(uuid3.IsValid());
+ EXPECT_EQ(UUID::kFormatInvalid, uuid3.format());
+ EXPECT_TRUE(uuid3.value().empty());
+ EXPECT_TRUE(uuid3.canonical_value().empty());
+
+ // Valid 16-bit UUID.
+ UUID uuid4(kValid16Bit);
+ EXPECT_TRUE(uuid4.IsValid());
+ EXPECT_EQ(UUID::kFormat16Bit, uuid4.format());
+ EXPECT_NE(uuid4.value(), uuid4.canonical_value());
+ EXPECT_EQ("1101", uuid4.value());
+ EXPECT_EQ(kValid128Bit1, uuid4.canonical_value());
+
+ // Valid 32-bit UUID.
+ UUID uuid5(kValid32Bit);
+ EXPECT_TRUE(uuid5.IsValid());
+ EXPECT_EQ(UUID::kFormat32Bit, uuid5.format());
+ EXPECT_NE(uuid5.value(), uuid5.canonical_value());
+ EXPECT_EQ("00001101", uuid5.value());
+ EXPECT_EQ(kValid128Bit1, uuid5.canonical_value());
+
+ // uuid4, uuid5, and uuid1 are equivalent.
+ EXPECT_EQ(uuid4, uuid5);
+ EXPECT_EQ(uuid1, uuid4);
+ EXPECT_EQ(uuid1, uuid5);
+}
+
+} // namespace bluetooth_utils
} // namespace device
diff --git a/device/bluetooth/test/mock_bluetooth_adapter.h b/device/bluetooth/test/mock_bluetooth_adapter.h
index d2c9878..0266f03 100644
--- a/device/bluetooth/test/mock_bluetooth_adapter.h
+++ b/device/bluetooth/test/mock_bluetooth_adapter.h
@@ -35,11 +35,20 @@
MOCK_METHOD1(RemoveObserver, void(BluetoothAdapter::Observer*));
MOCK_CONST_METHOD0(GetAddress, std::string());
MOCK_CONST_METHOD0(GetName, std::string());
+ MOCK_METHOD3(SetName,
+ void(const std::string& name,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback));
MOCK_CONST_METHOD0(IsInitialized, bool());
MOCK_CONST_METHOD0(IsPresent, bool());
MOCK_CONST_METHOD0(IsPowered, bool());
MOCK_METHOD3(SetPowered,
- void(bool discovering,
+ void(bool powered,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback));
+ MOCK_CONST_METHOD0(IsDiscoverable, bool());
+ MOCK_METHOD3(SetDiscoverable,
+ void(bool discoverable,
const base::Closure& callback,
const ErrorCallback& error_callback));
MOCK_CONST_METHOD0(IsDiscovering, bool());
diff --git a/device/bluetooth/test/mock_bluetooth_device.cc b/device/bluetooth/test/mock_bluetooth_device.cc
index b4c6cfe..c1fe56f 100644
--- a/device/bluetooth/test/mock_bluetooth_device.cc
+++ b/device/bluetooth/test/mock_bluetooth_device.cc
@@ -33,7 +33,7 @@
ON_CALL(*this, IsConnecting())
.WillByDefault(testing::Return(false));
ON_CALL(*this, GetName())
- .WillByDefault(testing::Return(UTF8ToUTF16(name_)));
+ .WillByDefault(testing::Return(base::UTF8ToUTF16(name_)));
ON_CALL(*this, ExpectingPinCode())
.WillByDefault(testing::Return(false));
ON_CALL(*this, ExpectingPasskey())
diff --git a/device/bluetooth/test/mock_bluetooth_device.h b/device/bluetooth/test/mock_bluetooth_device.h
index 6020579..de2907d 100644
--- a/device/bluetooth/test/mock_bluetooth_device.h
+++ b/device/bluetooth/test/mock_bluetooth_device.h
@@ -32,7 +32,7 @@
MOCK_CONST_METHOD0(GetVendorID, uint16());
MOCK_CONST_METHOD0(GetProductID, uint16());
MOCK_CONST_METHOD0(GetDeviceID, uint16());
- MOCK_CONST_METHOD0(GetName, string16());
+ MOCK_CONST_METHOD0(GetName, base::string16());
MOCK_CONST_METHOD0(GetDeviceType, BluetoothDevice::DeviceType());
MOCK_CONST_METHOD0(IsPaired, bool());
MOCK_CONST_METHOD0(IsConnected, bool());
diff --git a/device/device_tests.gyp b/device/device_tests.gyp
index e0564b5..268b270 100644
--- a/device/device_tests.gyp
+++ b/device/device_tests.gyp
@@ -19,6 +19,7 @@
'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',
@@ -33,6 +34,8 @@
'nfc/nfc_chromeos_unittest.cc',
'nfc/nfc_ndef_record_unittest.cc',
'usb/usb_ids_unittest.cc',
+ 'hid/hid_connection_unittest.cc',
+ 'hid/hid_service_unittest.cc',
],
'conditions': [
['chromeos==1', {
diff --git a/device/hid/DEPS b/device/hid/DEPS
new file mode 100644
index 0000000..6a2f02e
--- /dev/null
+++ b/device/hid/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+net/base",
+]
diff --git a/device/hid/hid.gyp b/device/hid/hid.gyp
new file mode 100644
index 0000000..c5c3684
--- /dev/null
+++ b/device/hid/hid.gyp
@@ -0,0 +1,47 @@
+# 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': [
+ '../..',
+ ],
+ 'conditions': [
+ ['OS=="linux"', {
+ 'dependencies': [
+ '../../build/linux/system.gyp:udev',
+ ],
+ }],
+ ],
+ 'sources': [
+ '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_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_utils_mac.cc',
+ 'hid_utils_mac.h',
+ ],
+ },
+ ],
+}
diff --git a/device/hid/hid_connection.cc b/device/hid/hid_connection.cc
new file mode 100644
index 0000000..5678407
--- /dev/null
+++ b/device/hid/hid_connection.cc
@@ -0,0 +1,18 @@
+// 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 {
+
+HidConnection::HidConnection(HidDeviceInfo device_info)
+ : device_info_(device_info) {}
+
+HidConnection::~HidConnection() {}
+
+const HidDeviceInfo& HidConnection::device_info() const {
+ return device_info_;
+}
+
+} // namespace device
diff --git a/device/hid/hid_connection.h b/device/hid/hid_connection.h
new file mode 100644
index 0000000..27792c4
--- /dev/null
+++ b/device/hid/hid_connection.h
@@ -0,0 +1,54 @@
+// 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_H_
+#define DEVICE_HID_HID_CONNECTION_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "device/hid/hid_device_info.h"
+
+namespace net {
+class IOBuffer;
+}
+
+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::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) = 0;
+ virtual void Write(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) = 0;
+ virtual void GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) = 0;
+ virtual void SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) = 0;
+
+ const HidDeviceInfo& device_info() const;
+
+ protected:
+ friend class base::RefCountedThreadSafe<HidConnection>;
+ friend struct HidDeviceInfo;
+
+ HidConnection(HidDeviceInfo device_info);
+ virtual ~HidConnection();
+
+ const HidDeviceInfo device_info_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidConnection);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_CONNECTION_H_
diff --git a/device/hid/hid_connection_linux.cc b/device/hid/hid_connection_linux.cc
new file mode 100644
index 0000000..0722fab
--- /dev/null
+++ b/device/hid/hid_connection_linux.cc
@@ -0,0 +1,231 @@
+// 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 <string>
+
+#include "base/threading/thread_restrictions.h"
+#include "base/tuple.h"
+#include "device/hid/hid_service.h"
+#include "device/hid/hid_service_linux.h"
+
+
+namespace device {
+
+namespace {
+
+const char kHidrawSubsystem[] = "hidraw";
+
+} // namespace
+
+HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info,
+ ScopedUdevDevicePtr udev_raw_device)
+ : HidConnection(device_info),
+ initialized_(false) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ udev_device* dev = udev_raw_device.get();
+ std::string dev_node;
+ if (!FindHidrawDevNode(dev, &dev_node)) {
+ LOG(ERROR) << "Cannot open HID device as hidraw device.";
+ return;
+ }
+
+ base::PlatformFileError error;
+
+ int flags = base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_WRITE |
+ base::PLATFORM_FILE_EXCLUSIVE_READ |
+ base::PLATFORM_FILE_EXCLUSIVE_WRITE;
+
+ base::PlatformFile device_file = base::CreatePlatformFile(
+ base::FilePath(dev_node),
+ flags,
+ NULL,
+ &error);
+ if (error || device_file <= 0) {
+ LOG(ERROR) << error;
+ if (device_file)
+ base::ClosePlatformFile(device_file);
+ return;
+ }
+ if (fcntl(device_file, F_SETFL, fcntl(device_file, F_GETFL) | O_NONBLOCK)) {
+ PLOG(ERROR) << "Failed to set non-blocking flag to device file.";
+ return;
+ }
+ device_file_ = device_file;
+
+ if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
+ device_file_,
+ true,
+ base::MessageLoopForIO::WATCH_READ_WRITE,
+ &device_file_watcher_,
+ this)) {
+ LOG(ERROR) << "Cannot start watching file descriptor.";
+ return;
+ }
+
+ initialized_ = true;
+}
+
+HidConnectionLinux::~HidConnectionLinux() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ Disconnect();
+}
+
+void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(fd, device_file_);
+ DCHECK(initialized_);
+
+ uint8 buffer[1024] = {0};
+ int bytes = read(device_file_, buffer, 1024);
+ if (bytes < 0) {
+ if (errno == EAGAIN) {
+ return;
+ }
+ Disconnect();
+ return;
+ }
+ scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(bytes));
+ memcpy(io_buffer->data(), buffer, bytes);
+ input_reports_.push(std::make_pair(io_buffer, bytes));
+
+ ProcessReadQueue();
+}
+
+void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {}
+
+void HidConnectionLinux::Disconnect() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!initialized_)
+ return;
+
+ initialized_ = false;
+ device_file_watcher_.StopWatchingFileDescriptor();
+ close(device_file_);
+ while (!read_queue_.empty()) {
+ PendingRequest callback = read_queue_.front();
+ read_queue_.pop();
+ callback.c.Run(false, 0);
+ }
+}
+
+void HidConnectionLinux::Read(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!initialized_) {
+ DCHECK(read_queue_.empty());
+ // There might be unread reports.
+ if (!input_reports_.empty()){
+ read_queue_.push(MakeTuple(buffer, size, callback));
+ ProcessReadQueue();
+ }
+ callback.Run(false, 0);
+ return;
+ } else {
+ read_queue_.push(MakeTuple(buffer, size, callback));
+ ProcessReadQueue();
+ }
+}
+
+void HidConnectionLinux::Write(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!initialized_) {
+ callback.Run(false, 0);
+ return;
+ } else {
+ int bytes = write(device_file_, buffer->data(), size);
+ if (bytes < 0) {
+ Disconnect();
+ callback.Run(false, 0);
+ } else {
+ callback.Run(true, bytes);
+ }
+ }
+}
+
+void HidConnectionLinux::GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!initialized_) {
+ callback.Run(false, 0);
+ return;
+ }
+ NOTIMPLEMENTED();
+}
+
+void HidConnectionLinux::SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!initialized_) {
+ callback.Run(false, 0);
+ return;
+ }
+ NOTIMPLEMENTED();
+}
+
+void HidConnectionLinux::ProcessReadQueue() {
+ while(read_queue_.size() && input_reports_.size()) {
+ PendingRequest request = read_queue_.front();
+ read_queue_.pop();
+ PendingReport report = input_reports_.front();
+ if (report.second > request.b) {
+ request.c.Run(false, report.second);
+ } else {
+ memcpy(request.a->data(), report.first->data(), report.second);
+ input_reports_.pop();
+ request.c.Run(true, report.second);
+ }
+ }
+}
+
+bool HidConnectionLinux::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;
+ }
+
+ const char* parent_path = udev_device_get_devpath(parent);
+ 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());
+ if (strncmp(parent_path,
+ udev_device_get_devpath(hid_dev.get()),
+ strlen(parent_path)) == 0 &&
+ raw_path) {
+ *result = raw_path;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace device
diff --git a/device/hid/hid_connection_linux.h b/device/hid/hid_connection_linux.h
new file mode 100644
index 0000000..37d6cb0
--- /dev/null
+++ b/device/hid/hid_connection_linux.h
@@ -0,0 +1,69 @@
+// 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 "base/memory/ref_counted.h"
+#include "base/platform_file.h"
+#include "base/tuple.h"
+#include "device/hid/hid_connection.h"
+#include "device/hid/hid_device_info.h"
+#include "device/hid/hid_service_linux.h"
+#include "net/base/io_buffer.h"
+
+namespace device {
+
+class HidConnectionLinux : public HidConnection,
+ public base::MessagePumpLibevent::Watcher {
+ public:
+ HidConnectionLinux(HidDeviceInfo device_info,
+ ScopedUdevDevicePtr udev_raw_device);
+
+ virtual void Read(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) OVERRIDE;
+ virtual void Write(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) OVERRIDE;
+ virtual void GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) OVERRIDE;
+ virtual void SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) OVERRIDE;
+
+ bool initialized() const { return initialized_; }
+
+ // Implements base::MessagePumpLibevent::Watcher
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
+
+ private:
+ friend class base::RefCountedThreadSafe<HidConnectionLinux>;
+ virtual ~HidConnectionLinux();
+
+ static bool FindHidrawDevNode(udev_device* parent, std::string* result);
+
+ void ProcessReadQueue();
+ void Disconnect();
+
+ base::PlatformFile device_file_;
+ base::MessagePumpLibevent::FileDescriptorWatcher device_file_watcher_;
+
+ typedef std::pair<scoped_refptr<net::IOBuffer>, size_t> PendingReport;
+ typedef Tuple3<scoped_refptr<net::IOBuffer>, size_t, IOCallback>
+ PendingRequest;
+
+ std::queue<PendingReport> input_reports_;
+ std::queue<PendingRequest> read_queue_;
+
+ bool initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidConnectionLinux);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_CONNECTION_LINUX__
diff --git a/device/hid/hid_connection_mac.cc b/device/hid/hid_connection_mac.cc
new file mode 100644
index 0000000..bce7113
--- /dev/null
+++ b/device/hid/hid_connection_mac.cc
@@ -0,0 +1,188 @@
+// 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/callback.h"
+#include "base/mac/foundation_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/tuple.h"
+#include "device/hid/hid_service.h"
+#include "device/hid/hid_service_mac.h"
+#include "net/base/io_buffer.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/hid/IOHIDManager.h>
+
+namespace device {
+
+HidConnectionMac::HidConnectionMac(HidServiceMac* service,
+ HidDeviceInfo device_info,
+ IOHIDDeviceRef device)
+ : HidConnection(device_info),
+ service_(service),
+ device_(device),
+ disconnected_(false) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ message_loop_ = base::MessageLoopProxy::current();
+
+ CFRetain(device);
+ inbound_buffer_.reset((uint8_t*) malloc(device_info.input_report_size + 1));
+ IOHIDDeviceRegisterInputReportCallback(
+ device_.get(),
+ inbound_buffer_.get(),
+ device_info.input_report_size + 1,
+ &HidConnectionMac::InputReportCallback,
+ this);
+ IOHIDDeviceOpen(device_, kIOHIDOptionsTypeNone);
+}
+HidConnectionMac::~HidConnectionMac() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ while (read_queue_.size()) {
+ read_queue_.front().c.Run(false, 0);
+ read_queue_.pop();
+ }
+
+ IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone);
+}
+
+void HidConnectionMac::InputReportCallback(void * context,
+ IOReturn result,
+ void * sender,
+ IOHIDReportType type,
+ uint32_t reportID,
+ uint8_t * report,
+ CFIndex reportLength) {
+ HidConnectionMac* connection = reinterpret_cast<HidConnectionMac*>(context);
+ size_t length = reportLength + (reportID != 0);
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(length));
+ if (reportID) {
+ buffer->data()[0] = reportID;
+ memcpy(buffer->data() + 1, report, reportLength);
+ } else {
+ memcpy(buffer->data(), report, reportLength);
+ }
+ connection->message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&HidConnectionMac::ProcessInputReport,
+ connection,
+ type,
+ buffer,
+ length));
+}
+
+void HidConnectionMac::ProcessReadQueue() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ while(read_queue_.size() && input_reports_.size()) {
+ PendingRead read = read_queue_.front();
+ read_queue_.pop();
+ PendingReport report = input_reports_.front();
+
+ if (read.b < report.second) {
+ read.c.Run(false, report.second);
+ } else {
+ memcpy(read.a->data(), report.first->data(), report.second);
+ input_reports_.pop();
+ read.c.Run(true, report.second);
+ }
+ }
+}
+
+void HidConnectionMac::ProcessInputReport(IOHIDReportType type,
+ scoped_refptr<net::IOBuffer> report,
+ CFIndex reportLength) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ input_reports_.push(std::make_pair(report, reportLength));
+ ProcessReadQueue();
+}
+
+void HidConnectionMac::WriteReport(IOHIDReportType type,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (disconnected_ || !device_) {
+ callback.Run(false, 0);
+ return;
+ }
+ const unsigned char* data_to_send =
+ reinterpret_cast<const unsigned char*>(buffer->data());
+ size_t length_to_send = size;
+ if (data_to_send[0] == 0x0) {
+ /* Not using numbered Reports.
+ Don't send the report number. */
+ ++data_to_send;
+ --length_to_send;
+ }
+ IOReturn res = IOHIDDeviceSetReport(device_.get(),
+ type,
+ buffer->data()[0], /* Report ID*/
+ data_to_send,
+ length_to_send);
+ if (res != kIOReturnSuccess) {
+ callback.Run(false, 0);
+ } else {
+ callback.Run(true, size);
+ }
+}
+
+void HidConnectionMac::Read(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (disconnected_ || !device_) {
+ callback.Run(false, 0);
+ return;
+ }
+ read_queue_.push(MakeTuple(buffer, size, callback));
+ ProcessReadQueue();
+}
+
+void HidConnectionMac::Write(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ WriteReport(kIOHIDReportTypeOutput, buffer, size, callback);
+}
+
+void HidConnectionMac::SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ WriteReport(kIOHIDReportTypeFeature, buffer, size, callback);
+}
+
+void HidConnectionMac::GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (disconnected_ || !device_ || device_info_.feature_report_size == 0) {
+ callback.Run(false, 0);
+ return;
+ }
+
+ if (device_info_.feature_report_size != 0 &&
+ device_info_.feature_report_size != size) {
+ callback.Run(false, 0);
+ return;
+ }
+
+ CFIndex len = device_info_.feature_report_size;
+ IOReturn res = IOHIDDeviceGetReport(device_,
+ kIOHIDReportTypeFeature,
+ 0,
+ (uint8_t*) buffer->data(),
+ &len);
+ if (res == kIOReturnSuccess)
+ callback.Run(true, len);
+ else
+ callback.Run(false, 0);
+}
+
+} // namespace device
diff --git a/device/hid/hid_connection_mac.h b/device/hid/hid_connection_mac.h
new file mode 100644
index 0000000..730eeae
--- /dev/null
+++ b/device/hid/hid_connection_mac.h
@@ -0,0 +1,77 @@
+// 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 "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/tuple.h"
+#include "device/hid/hid_connection.h"
+#include "device/hid/hid_device_info.h"
+#include "device/hid/hid_service_mac.h"
+
+namespace net {
+class IOBuffer;
+}
+
+namespace device {
+
+class HidConnectionMac : public HidConnection {
+ public:
+ HidConnectionMac(HidServiceMac* service,
+ HidDeviceInfo device_info,
+ IOHIDDeviceRef device);
+
+ virtual void Read(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) OVERRIDE;
+ virtual void Write(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) OVERRIDE;
+ virtual void GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) OVERRIDE;
+ virtual void SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) OVERRIDE;
+
+ private:
+ virtual ~HidConnectionMac();
+
+ static void InputReportCallback(void * context,
+ IOReturn result,
+ void * sender,
+ IOHIDReportType type,
+ uint32_t reportID,
+ uint8_t * report,
+ CFIndex reportLength);
+ void ProcessReadQueue();
+ void ProcessInputReport(IOHIDReportType type,
+ scoped_refptr<net::IOBuffer> report,
+ CFIndex reportLength);
+
+ void WriteReport(IOHIDReportType type,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback);
+
+ HidServiceMac* service_;
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+ base::ScopedCFTypeRef<IOHIDDeviceRef> device_;
+ scoped_ptr_malloc<uint8_t> inbound_buffer_;
+ bool disconnected_;
+
+ typedef std::pair<scoped_refptr<net::IOBuffer>, size_t> PendingReport;
+ std::queue<PendingReport> input_reports_;
+ typedef Tuple3<scoped_refptr<net::IOBuffer>, size_t, IOCallback> PendingRead;
+ std::queue<PendingRead> read_queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidConnectionMac);
+};
+
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_CONNECTION_MAC_H_
diff --git a/device/hid/hid_connection_unittest.cc b/device/hid/hid_connection_unittest.cc
new file mode 100644
index 0000000..14a2ef9
--- /dev/null
+++ b/device/hid/hid_connection_unittest.cc
@@ -0,0 +1,130 @@
+// 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::IOBuffer;
+
+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<net::IOBuffer> 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<IOBuffer> buffer(new IOBuffer(8));
+ conn->Read(buffer, 8, 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<IOBuffer> buffer(new IOBuffer(8));
+ *(int64_t*)buffer->data() = kReport;
+
+ conn->Write(buffer, 8, 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);
+ 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);
+ }
+
+ std::string 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_.empty());
+}
+
+TEST_F(HidConnectionTest, Read) {
+ scoped_refptr<HidConnection> connection = service_->Connect(device_id_);
+
+ if (!device_id_.empty()) {
+ ASSERT_TRUE(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 (!device_id_.empty()) {
+ ASSERT_TRUE(connection);
+ message_loop_->PostTask(FROM_HERE, base::Bind(WriteNormal, connection));
+ message_loop_->Run();
+ }
+}
+
+} // namespace device
diff --git a/device/hid/hid_connection_win.cc b/device/hid/hid_connection_win.cc
new file mode 100644
index 0000000..bbb158f
--- /dev/null
+++ b/device/hid/hid_connection_win.cc
@@ -0,0 +1,279 @@
+// 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/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "device/hid/hid_service.h"
+#include "device/hid/hid_service_win.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 <setupapi.h>
+#include <winioctl.h>
+#include "base/win/scoped_handle.h"
+
+#endif // defined(OS_WIN)
+
+namespace device {
+
+HidConnectionWin::PendingTransfer::PendingTransfer(
+ scoped_refptr<HidConnectionWin> conn,
+ scoped_refptr<net::IOBuffer> target,
+ scoped_refptr<net::IOBuffer> receiving,
+ bool is_input,
+ IOCallback callback)
+ : conn_(conn),
+ is_input_(is_input),
+ target_(target),
+ receiving_(receiving),
+ callback_(callback),
+ event_(CreateEvent(NULL, FALSE, FALSE, NULL)) {
+ memset(&overlapped_, 0, sizeof(OVERLAPPED));
+ overlapped_.hEvent = event_.Get();
+}
+HidConnectionWin::PendingTransfer::~PendingTransfer() {
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+}
+
+void HidConnectionWin::PendingTransfer::TakeResultFromWindowsAPI(BOOL result) {
+ if (result || GetLastError() != ERROR_IO_PENDING) {
+ conn_->OnTransferFinished(this);
+ } else {
+ base::MessageLoop::current()->AddDestructionObserver(this);
+ AddRef();
+ watcher_.StartWatching(event_.Get(), this);
+ }
+}
+
+void HidConnectionWin::PendingTransfer::OnObjectSignaled(HANDLE event_handle) {
+ conn_->OnTransferFinished(this);
+ Release();
+}
+
+void HidConnectionWin::PendingTransfer::WillDestroyCurrentMessageLoop() {
+ watcher_.StopWatching();
+ conn_->OnTransferCanceled(this);
+}
+
+void HidConnectionWin::OnTransferFinished(
+ scoped_refptr<PendingTransfer> transfer) {
+ DWORD bytes_transfered;
+ transfers_.erase(transfer);
+ if (GetOverlappedResult(file_,
+ transfer->GetOverlapped(),
+ &bytes_transfered,
+ FALSE)) {
+ if (transfer->is_input_ && !device_info_.has_report_id) {
+ // Move one byte forward.
+ --bytes_transfered;
+ memcpy(transfer->target_->data(),
+ transfer->receiving_->data() + 1,
+ bytes_transfered);
+ }
+ transfer->callback_.Run(true, bytes_transfered);
+ } else {
+ transfer->callback_.Run(false, 0);
+ }
+}
+
+void HidConnectionWin::OnTransferCanceled(
+ scoped_refptr<PendingTransfer> transfer) {
+ transfers_.erase(transfer);
+ transfer->callback_.Run(false, 0);
+}
+
+HidConnectionWin::HidConnectionWin(HidDeviceInfo device_info)
+ : HidConnection(device_info),
+ available_(false) {
+ 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));
+ available_ = file_.IsValid();
+}
+
+HidConnectionWin::~HidConnectionWin() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ CancelIo(file_.Get());
+}
+
+void HidConnectionWin::Read(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const HidConnection::IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ size_t report_size = device_info_.input_report_size;
+ if (report_size == 0) {
+ // The device does not supoort input reports.
+ callback.Run(false, 0);
+ return;
+ }
+
+ if (size + !device_info_.has_report_id < report_size) {
+ // Buffer too short.
+ callback.Run(false, 0);
+ return;
+ }
+
+ scoped_refptr<net::IOBuffer> expanded_buffer;
+ if (!device_info_.has_report_id) {
+ ++size;
+ expanded_buffer = new net::IOBuffer(static_cast<int>(size));
+ }
+
+ scoped_refptr<PendingTransfer> transfer(
+ new PendingTransfer(this, buffer, expanded_buffer, true, callback));
+ transfers_.insert(transfer);
+ transfer->TakeResultFromWindowsAPI(ReadFile(file_.Get(),
+ device_info_.has_report_id ?
+ buffer->data() :
+ expanded_buffer->data(),
+ static_cast<DWORD>(size),
+ NULL,
+ transfer->GetOverlapped()));
+}
+
+void HidConnectionWin::Write(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const HidConnection::IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ size_t report_size = device_info_.output_report_size;
+ if (report_size == 0) {
+ // The device does not supoort output reports.
+ callback.Run(false, 0);
+ return;
+ }
+
+ if (size + !device_info_.has_report_id > report_size) {
+ // Size of report too long.
+ callback.Run(false, 0);
+ return;
+ }
+
+ scoped_refptr<net::IOBuffer> expanded_buffer;
+ if (!device_info_.has_report_id) {
+ expanded_buffer = new net::IOBuffer(
+ static_cast<int>(device_info_.output_report_size));
+ memset(expanded_buffer->data(), 0, device_info_.output_report_size);
+ memcpy(expanded_buffer->data() + 1,
+ buffer->data(),
+ size);
+ size++;
+ }
+
+ scoped_refptr<PendingTransfer> transfer(
+ new PendingTransfer(this, buffer, expanded_buffer, false, callback));
+ transfers_.insert(transfer);
+ transfer->TakeResultFromWindowsAPI(
+ WriteFile(file_.Get(),
+ device_info_.has_report_id ?
+ buffer->data() : expanded_buffer->data(),
+ static_cast<DWORD>(device_info_.output_report_size),
+ NULL,
+ transfer->GetOverlapped()));
+}
+
+void HidConnectionWin::GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ size_t report_size = device_info_.feature_report_size;
+ if (report_size == 0) {
+ // The device does not supoort input reports.
+ callback.Run(false, 0);
+ return;
+ }
+
+ if (size + !device_info_.has_report_id < report_size) {
+ // Buffer too short.
+ callback.Run(false, 0);
+ return;
+ }
+
+ scoped_refptr<net::IOBuffer> expanded_buffer;
+ if (!device_info_.has_report_id) {
+ ++size;
+ expanded_buffer = new net::IOBuffer(static_cast<int>(size));
+ }
+
+ scoped_refptr<PendingTransfer> transfer(
+ new PendingTransfer(this, buffer, expanded_buffer, true, callback));
+ transfers_.insert(transfer);
+ transfer->TakeResultFromWindowsAPI(
+ DeviceIoControl(file_.Get(),
+ IOCTL_HID_GET_FEATURE,
+ NULL,
+ 0,
+ device_info_.has_report_id ?
+ buffer->data() :
+ expanded_buffer->data(),
+ static_cast<DWORD>(size),
+ NULL,
+ transfer->GetOverlapped()));
+}
+
+void HidConnectionWin::SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ size_t report_size = device_info_.feature_report_size;
+ if (report_size == 0) {
+ // The device does not supoort output reports.
+ callback.Run(false, 0);
+ return;
+ }
+
+ if (size + !device_info_.has_report_id > report_size) {
+ // Size of report too long.
+ callback.Run(false, 0);
+ return;
+ }
+
+ scoped_refptr<net::IOBuffer> expanded_buffer;
+ if (!device_info_.has_report_id) {
+ expanded_buffer = new net::IOBuffer(
+ static_cast<int>(device_info_.feature_report_size));
+ memset(expanded_buffer->data(), 0, device_info_.feature_report_size);
+ memcpy(expanded_buffer->data() + 1,
+ buffer->data(),
+ size);
+ size++;
+ }
+
+ scoped_refptr<PendingTransfer> transfer(
+ new PendingTransfer(this, buffer, expanded_buffer, false, callback));
+ transfer->TakeResultFromWindowsAPI(
+ DeviceIoControl(file_.Get(),
+ IOCTL_HID_SET_FEATURE,
+ device_info_.has_report_id ?
+ buffer->data() :
+ expanded_buffer->data(),
+ static_cast<DWORD>(device_info_.output_report_size),
+ NULL,
+ 0,
+ NULL,
+ transfer->GetOverlapped()));
+}
+
+} // namespace device
diff --git a/device/hid/hid_connection_win.h b/device/hid/hid_connection_win.h
new file mode 100644
index 0000000..6cbb9e9
--- /dev/null
+++ b/device/hid/hid_connection_win.h
@@ -0,0 +1,96 @@
+// 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 <set>
+#include <windows.h>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_checker.h"
+#include "base/win/object_watcher.h"
+#include "device/hid/hid_connection.h"
+#include "device/hid/hid_device_info.h"
+#include "net/base/io_buffer.h"
+
+namespace device {
+
+class HidConnectionWin : public HidConnection {
+ public:
+ struct PendingTransfer : public base::RefCounted<PendingTransfer>,
+ public base::win::ObjectWatcher::Delegate,
+ public base::MessageLoop::DestructionObserver {
+ public:
+ PendingTransfer(scoped_refptr<HidConnectionWin> conn,
+ scoped_refptr<net::IOBuffer> target,
+ scoped_refptr<net::IOBuffer> receiving,
+ bool is_input,
+ 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;
+
+
+ private:
+ friend class base::RefCounted<PendingTransfer>;
+ friend class HidConnectionWin;
+
+ virtual ~PendingTransfer();
+
+ scoped_refptr<HidConnectionWin> conn_;
+ bool is_input_;
+ scoped_refptr<net::IOBuffer> target_;
+ scoped_refptr<net::IOBuffer> receiving_;
+ IOCallback callback_;
+ OVERLAPPED overlapped_;
+ base::win::ScopedHandle event_;
+ base::win::ObjectWatcher watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(PendingTransfer);
+ };
+
+ HidConnectionWin(HidDeviceInfo device_info);
+
+ virtual void Read(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) OVERRIDE;
+ virtual void Write(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) OVERRIDE;
+ virtual void GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) OVERRIDE;
+ virtual void SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const IOCallback& callback) OVERRIDE;
+
+ void OnTransferFinished(scoped_refptr<PendingTransfer> transfer);
+ void OnTransferCanceled(scoped_refptr<PendingTransfer> transfer);
+
+ bool available() const { return available_; }
+
+ private:
+ ~HidConnectionWin();
+
+ base::win::ScopedHandle file_;
+ std::set<scoped_refptr<PendingTransfer> > transfers_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidConnectionWin);
+
+ bool available_;
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_CONNECTION_WIN_H_
diff --git a/device/hid/hid_device_info.cc b/device/hid/hid_device_info.cc
new file mode 100644
index 0000000..34ea686
--- /dev/null
+++ b/device/hid/hid_device_info.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_device_info.h"
+
+namespace device {
+
+HidDeviceInfo::HidDeviceInfo()
+ : bus_type(kHIDBusTypeUSB),
+ vendor_id(0),
+ product_id(0),
+ input_report_size(0),
+ output_report_size(0),
+ feature_report_size(0),
+ usage_page(0),
+ usage(0),
+ has_report_id(false) {}
+
+HidDeviceInfo::~HidDeviceInfo() {}
+
+} // namespace device
diff --git a/device/hid/hid_device_info.h b/device/hid/hid_device_info.h
new file mode 100644
index 0000000..b665944
--- /dev/null
+++ b/device/hid/hid_device_info.h
@@ -0,0 +1,43 @@
+// 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_DEVICE_INFO_H_
+#define DEVICE_HID_HID_DEVICE_INFO_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace device {
+
+enum HidBusType {
+ kHIDBusTypeUSB = 0,
+ kHIDBusTypeBluetooth = 1,
+};
+
+struct HidDeviceInfo {
+ HidDeviceInfo();
+ ~HidDeviceInfo();
+
+ std::string device_id;
+
+ HidBusType bus_type;
+ uint16 vendor_id;
+ uint16 product_id;
+
+ size_t input_report_size;
+ size_t output_report_size;
+ size_t feature_report_size;
+
+ uint16 usage_page;
+ uint16 usage;
+ bool has_report_id;
+
+ std::string product_name;
+ std::string serial_number;
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_DEVICE_INFO_H_
diff --git a/device/hid/hid_service.cc b/device/hid/hid_service.cc
new file mode 100644
index 0000000..973e901
--- /dev/null
+++ b/device/hid/hid_service.cc
@@ -0,0 +1,106 @@
+// 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_service.h"
+
+#include <vector>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
+#include "device/hid/hid_device_info.h"
+
+#if defined(OS_LINUX)
+#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
+
+HidService::HidService() : initialized_(false) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ base::MessageLoop::current()->AddDestructionObserver(this);
+}
+
+HidService::~HidService() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+}
+
+void HidService::WillDestroyCurrentMessageLoop() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ g_hid_service_ptr.Get().reset(NULL);
+}
+
+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::GetInfo(std::string 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::AddDevice(HidDeviceInfo info) {
+ if (!ContainsKey(devices_, info.device_id)) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ devices_[info.device_id] = info;
+ }
+}
+
+void HidService::RemoveDevice(std::string device_id) {
+ if (ContainsKey(devices_, device_id)) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ devices_.erase(device_id);
+ }
+}
+
+HidService* HidService::CreateInstance() {
+#if defined(OS_LINUX)
+ return new HidServiceLinux();
+#elif defined(OS_MACOSX)
+ return new HidServiceMac();
+#elif defined(OS_WIN)
+ return new HidServiceWin();
+#else
+ return NULL;
+#endif
+}
+
+HidService* HidService::GetInstance() {
+ if (!g_hid_service_ptr.Get().get()){
+ scoped_ptr<HidService> service(CreateInstance());
+
+ if (service && service->initialized())
+ g_hid_service_ptr.Get().reset(service.release());
+ }
+ return g_hid_service_ptr.Get().get();
+}
+
+} // namespace device
diff --git a/device/hid/hid_service.h b/device/hid/hid_service.h
new file mode 100644
index 0000000..2589220
--- /dev/null
+++ b/device/hid/hid_service.h
@@ -0,0 +1,79 @@
+// 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_SERVICE_H_
+#define DEVICE_HID_HID_SERVICE_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string16.h"
+#include "base/threading/thread_checker.h"
+#include "build/build_config.h"
+#include "device/hid/hid_device_info.h"
+
+namespace device {
+
+namespace {
+
+class HidServiceContainer;
+
+} // namespace
+
+class HidConnection;
+class HidService;
+
+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 the device info struct of the given device_id.
+ // Returns true if succeed.
+ // Returns false if the device_id is invalid, with info untouched.
+ bool GetInfo(std::string device_id, HidDeviceInfo* info) const;
+
+ virtual scoped_refptr<HidConnection> Connect(
+ std::string platform_device_id) = 0;
+
+ // Implements base::MessageLoop::DestructionObserver
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
+
+ // Gets whether the HidService have been successfully initialized.
+ bool initialized() const { return initialized_; }
+
+ protected:
+ friend class HidServiceContainer;
+ friend struct base::DefaultDeleter<HidService>;
+ friend class HidConnectionTest;
+
+ HidService();
+ virtual ~HidService();
+
+ static HidService* CreateInstance();
+
+ virtual void AddDevice(HidDeviceInfo info);
+ virtual void RemoveDevice(std::string platform_device_id);
+
+ typedef std::map<std::string, HidDeviceInfo> DeviceMap;
+ DeviceMap devices_;
+
+ bool initialized_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidService);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_SERVICE_H_
diff --git a/device/hid/hid_service_linux.cc b/device/hid/hid_service_linux.cc
new file mode 100644
index 0000000..f02a550
--- /dev/null
+++ b/device/hid/hid_service_linux.cc
@@ -0,0 +1,211 @@
+// 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 <libudev.h>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/platform_file.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/threading/thread_restrictions.h"
+#include "device/hid/hid_connection.h"
+#include "device/hid/hid_connection_linux.h"
+#include "device/hid/hid_device_info.h"
+#include "device/hid/hid_service_linux.h"
+
+namespace device {
+
+namespace {
+
+const char kUdevName[] = "udev";
+const char kUdevActionAdd[] = "add";
+const char kUdevActionRemove[] = "remove";
+const char kHIDSubSystem[] = "hid";
+
+const char kHIDID[] = "HID_ID";
+const char kHIDName[] = "HID_NAME";
+const char kHIDUnique[] = "HID_UNIQ";
+
+} // namespace
+
+HidServiceLinux::HidServiceLinux() {
+ 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_filter_add_match_subsystem_devtype(
+ monitor_.get(),
+ kHIDSubSystem,
+ NULL);
+ if (ret != 0) {
+ LOG(ERROR) << "Failed to add udev monitor filter.";
+ return;
+ }
+
+ 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;
+
+ Enumerate();
+}
+
+HidServiceLinux::~HidServiceLinux() {
+ monitor_watcher_.StopWatchingFileDescriptor();
+ close(monitor_fd_);
+}
+
+void HidServiceLinux::Enumerate() {
+ scoped_ptr<udev_enumerate, UdevEnumerateDeleter> enumerate(
+ udev_enumerate_new(udev_.get()));
+
+ if (!enumerate) {
+ LOG(ERROR) << "Failed to enumerate devices.";
+ return;
+ }
+
+ if (udev_enumerate_add_match_subsystem(enumerate.get(), kHIDSubSystem)) {
+ 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 hid_dev(
+ udev_device_new_from_syspath(udev_.get(), udev_list_entry_get_name(i)));
+ if (hid_dev) {
+ PlatformDeviceAdd(hid_dev.get());
+ }
+ }
+
+ initialized_ = true;
+}
+
+void HidServiceLinux::PlatformDeviceAdd(udev_device* device) {
+ if (!device)
+ return;
+
+ const char* device_id = udev_device_get_syspath(device);
+ if (!device_id)
+ return;
+
+
+ HidDeviceInfo device_info;
+ device_info.device_id = device_id;
+
+ uint32 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;
+
+ AddDevice(device_info);
+}
+
+void HidServiceLinux::PlatformDeviceRemove(udev_device* raw_dev) {
+ // The returned the device is not referenced.
+ udev_device* hid_dev =
+ udev_device_get_parent_with_subsystem_devtype(raw_dev, "hid", NULL);
+
+ if (!hid_dev)
+ return;
+
+ const char* device_id = NULL;
+ device_id = udev_device_get_syspath(hid_dev);
+ if (device_id == NULL)
+ return;
+
+ RemoveDevice(device_id);
+}
+
+scoped_refptr<HidConnection> HidServiceLinux::Connect(std::string device_id) {
+ if (!ContainsKey(devices_, device_id))
+ return NULL;
+ ScopedUdevDevicePtr hid_device(
+ udev_device_new_from_syspath(udev_.get(), device_id.c_str()));
+ if (hid_device) {
+ scoped_refptr<HidConnectionLinux> connection =
+ new HidConnectionLinux(devices_[device_id], hid_device.Pass());
+ if (connection->initialized())
+ return connection;
+ }
+ return NULL;
+}
+
+void HidServiceLinux::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK_EQ(monitor_fd_, fd);
+
+ ScopedUdevDevicePtr dev(udev_monitor_receive_device(monitor_.get()));
+ if (!dev)
+ return;
+
+ std::string action(udev_device_get_action(dev.get()));
+ if (action == kUdevActionAdd) {
+ PlatformDeviceAdd(dev.get());
+ } else if (action == kUdevActionRemove) {
+ PlatformDeviceRemove(dev.get());
+ }
+}
+
+void HidServiceLinux::OnFileCanWriteWithoutBlocking(int fd) {}
+
+} // namespace dev
diff --git a/device/hid/hid_service_linux.h b/device/hid/hid_service_linux.h
new file mode 100644
index 0000000..ba58fad
--- /dev/null
+++ b/device/hid/hid_service_linux.h
@@ -0,0 +1,76 @@
+// 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_SERVICE_LINUX_H_
+#define DEVICE_HID_HID_SERVICE_LINUX_H_
+
+#include <libudev.h>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_pump_libevent.h"
+#include "device/hid/hid_device_info.h"
+#include "device/hid/hid_service.h"
+
+namespace device {
+
+class HidConnection;
+
+template<typename T, void func(T*)>
+void DiscardReturnType(T* arg) { func(arg); }
+template<typename T, T* func(T*)>
+void DiscardReturnType(T* arg) { func(arg); }
+
+template<typename T, void func(T*)>
+struct Deleter {
+ void operator()(T* enumerate) const {
+ if (enumerate != NULL)
+ func(enumerate);
+ }
+};
+
+typedef Deleter<
+ udev_enumerate,
+ DiscardReturnType<udev_enumerate, udev_enumerate_unref> >
+ UdevEnumerateDeleter;
+typedef Deleter<
+ udev_device,
+ DiscardReturnType<udev_device, udev_device_unref> > UdevDeviceDeleter;
+typedef Deleter<udev, DiscardReturnType<udev, udev_unref> > UdevDeleter;
+typedef Deleter<
+ udev_monitor,
+ DiscardReturnType<udev_monitor, udev_monitor_unref> > UdevMonitorDeleter;
+
+typedef scoped_ptr<udev_device, UdevDeviceDeleter> ScopedUdevDevicePtr;
+typedef scoped_ptr<udev_enumerate, UdevEnumerateDeleter> ScopedUdevEnumeratePtr;
+
+class HidServiceLinux : public HidService,
+ public base::MessagePumpLibevent::Watcher {
+ public:
+ HidServiceLinux();
+
+ virtual scoped_refptr<HidConnection> Connect(std::string device_id) OVERRIDE;
+
+ // Implements base::MessagePumpLibevent::Watcher
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
+
+ private:
+ virtual ~HidServiceLinux();
+
+ void Enumerate();
+ void PlatformDeviceAdd(udev_device* device);
+ void PlatformDeviceRemove(udev_device* raw_dev);
+
+ scoped_ptr<udev, UdevDeleter> udev_;
+ scoped_ptr<udev_monitor, UdevMonitorDeleter> monitor_;
+ int monitor_fd_;
+ base::MessagePumpLibevent::FileDescriptorWatcher monitor_watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidServiceLinux);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_SERVICE_LINUX_H_
diff --git a/device/hid/hid_service_mac.cc b/device/hid/hid_service_mac.cc
new file mode 100644
index 0000000..12da7cc
--- /dev/null
+++ b/device/hid/hid_service_mac.cc
@@ -0,0 +1,250 @@
+// 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_service_mac.h"
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "device/hid/hid_connection.h"
+#include "device/hid/hid_connection_mac.h"
+#include "device/hid/hid_utils_mac.h"
+#include "net/base/io_buffer.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/hid/IOHIDManager.h>
+
+namespace device {
+
+class HidServiceMac;
+
+HidServiceMac::HidServiceMac() : enumeration_runloop_init_(true, false) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ hid_manager_ref_.reset(IOHIDManagerCreate(kCFAllocatorDefault,
+ kIOHIDOptionsTypeNone));
+ if (!hid_manager_ref_ ||
+ CFGetTypeID(hid_manager_ref_) != IOHIDManagerGetTypeID()) {
+ LOG(ERROR) << "Failed to initialize HidManager";
+ return;
+ }
+ CFRetain(hid_manager_ref_);
+
+ // Register for plug/unplug notifications.
+ IOHIDManagerSetDeviceMatching(hid_manager_ref_.get(), NULL);
+ IOHIDManagerRegisterDeviceMatchingCallback(
+ hid_manager_ref_.get(),
+ &HidServiceMac::AddDeviceCallback,
+ this);
+ IOHIDManagerRegisterDeviceRemovalCallback(
+ hid_manager_ref_.get(),
+ &HidServiceMac::RemoveDeviceCallback,
+ this);
+
+ // Blocking operation to enumerate all the pre-existing devices.
+ Enumerate();
+
+ // Save the owner message loop.
+ message_loop_ = base::MessageLoopProxy::current();
+
+ // Start a thread to monitor HID device changes.
+ // The reason to create a new thread is that by default the only thread in the
+ // browser process having a CFRunLoop is the UI thread. We do not want to
+ // run potentially blocking routines on it.
+ enumeration_runloop_thread_.reset(
+ new base::Thread("HidService Device Enumeration Thread"));
+
+ base::Thread::Options options;
+ options.message_loop_type = base::MessageLoop::TYPE_UI;
+ enumeration_runloop_thread_->StartWithOptions(options);
+ enumeration_runloop_thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&HidServiceMac::ScheduleRunLoop, base::Unretained(this)));
+
+ enumeration_runloop_init_.Wait();
+ initialized_ = true;
+}
+
+HidServiceMac::~HidServiceMac() {
+ enumeration_runloop_thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&HidServiceMac::UnscheduleRunLoop, base::Unretained(this)));
+
+ enumeration_runloop_thread_->Stop();
+}
+
+void HidServiceMac::ScheduleRunLoop() {
+ enumeration_runloop_ = CFRunLoopGetCurrent();
+
+ IOHIDManagerScheduleWithRunLoop(
+ hid_manager_ref_,
+ enumeration_runloop_,
+ kCFRunLoopDefaultMode);
+
+ IOHIDManagerOpen(hid_manager_ref_, kIOHIDOptionsTypeNone);
+
+ enumeration_runloop_init_.Signal();
+}
+
+void HidServiceMac::UnscheduleRunLoop() {
+ IOHIDManagerUnscheduleFromRunLoop(
+ hid_manager_ref_,
+ enumeration_runloop_,
+ kCFRunLoopDefaultMode);
+
+ IOHIDManagerClose(hid_manager_ref_, kIOHIDOptionsTypeNone);
+
+}
+
+HidServiceMac* HidServiceMac::InstanceFromContext(void* context) {
+ return reinterpret_cast<HidServiceMac*>(context);
+}
+
+void HidServiceMac::AddDeviceCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDDeviceRef ref) {
+ HidServiceMac* service = InstanceFromContext(context);
+
+ // Takes ownership of ref. Will be released inside PlatformDeviceAdd.
+ CFRetain(ref);
+ service->message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&HidServiceMac::PlatformAddDevice,
+ base::Unretained(service),
+ base::Unretained(ref)));
+}
+
+void HidServiceMac::RemoveDeviceCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDDeviceRef ref) {
+ HidServiceMac* service = InstanceFromContext(context);
+
+ // Takes ownership of ref. Will be released inside PlatformDeviceRemove.
+ CFRetain(ref);
+ service->message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&HidServiceMac::PlatformRemoveDevice,
+ base::Unretained(service),
+ base::Unretained(ref)));
+}
+
+IOHIDDeviceRef HidServiceMac::FindDevice(std::string id) {
+ base::ScopedCFTypeRef<CFSetRef> devices(
+ IOHIDManagerCopyDevices(hid_manager_ref_));
+ CFIndex count = CFSetGetCount(devices);
+ scoped_ptr<IOHIDDeviceRef[]> device_refs(new IOHIDDeviceRef[count]);
+ CFSetGetValues(devices, (const void **)(device_refs.get()));
+
+ for (CFIndex i = 0; i < count; i++) {
+ int32_t int_property = 0;
+ if (GetHidIntProperty(device_refs[i], CFSTR(kIOHIDLocationIDKey),
+ &int_property)) {
+ if (id == base::HexEncode(&int_property, sizeof(int_property))) {
+ return device_refs[i];
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void HidServiceMac::Enumerate() {
+ base::ScopedCFTypeRef<CFSetRef> devices(
+ IOHIDManagerCopyDevices(hid_manager_ref_));
+ CFIndex count = CFSetGetCount(devices);
+ scoped_ptr<IOHIDDeviceRef[]> device_refs(new IOHIDDeviceRef[count]);
+ CFSetGetValues(devices, (const void **)(device_refs.get()));
+
+ for (CFIndex i = 0; i < count; i++) {
+ // Takes ownership. Will be released inside PlatformDeviceAdd.
+ CFRetain(device_refs[i]);
+ PlatformAddDevice(device_refs[i]);
+ }
+}
+
+void HidServiceMac::PlatformAddDevice(IOHIDDeviceRef raw_ref) {
+ HidDeviceInfo device;
+ int32_t int_property = 0;
+ std::string str_property;
+
+ // Auto-release.
+ base::ScopedCFTypeRef<IOHIDDeviceRef> ref(raw_ref);
+
+ // Unique identifier for HID device.
+ if (GetHidIntProperty(ref, CFSTR(kIOHIDLocationIDKey), &int_property)) {
+ device.device_id = base::HexEncode(&int_property, sizeof(int_property));
+ } else {
+ // Not an available device.
+ return;
+ }
+
+ if (GetHidIntProperty(ref, CFSTR(kIOHIDVendorIDKey), &int_property)) {
+ device.vendor_id = int_property;
+ }
+ if (GetHidIntProperty(ref, CFSTR(kIOHIDProductIDKey), &int_property)) {
+ device.product_id = int_property;
+ }
+ if (GetHidIntProperty(ref, CFSTR(kIOHIDPrimaryUsageKey), &int_property)) {
+ device.usage = int_property;
+ }
+ if (GetHidIntProperty(ref, CFSTR(kIOHIDPrimaryUsagePageKey), &int_property)) {
+ device.usage_page = int_property;
+ }
+ if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxInputReportSizeKey),
+ &int_property)) {
+ device.input_report_size = int_property;
+ }
+ if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxOutputReportSizeKey),
+ &int_property)) {
+ device.output_report_size = int_property;
+ }
+ if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxFeatureReportSizeKey),
+ &int_property)) {
+ device.feature_report_size = int_property;
+ }
+ if (GetHidStringProperty(ref, CFSTR(kIOHIDProductKey), &str_property)) {
+ device.product_name = str_property;
+ }
+ if (GetHidStringProperty(ref, CFSTR(kIOHIDSerialNumberKey), &str_property)) {
+ device.serial_number = str_property;
+ }
+ HidService::AddDevice(device);
+}
+
+void HidServiceMac::PlatformRemoveDevice(IOHIDDeviceRef raw_ref) {
+ std::string device_id;
+ int32_t int_property = 0;
+ // Auto-release.
+ base::ScopedCFTypeRef<IOHIDDeviceRef> ref(raw_ref);
+ if (!GetHidIntProperty(ref, CFSTR(kIOHIDLocationIDKey), &int_property)) {
+ return;
+ }
+ device_id = base::HexEncode(&int_property, sizeof(int_property));
+ HidService::RemoveDevice(device_id);
+}
+
+scoped_refptr<HidConnection>
+HidServiceMac::Connect(std::string device_id) {
+ if (!ContainsKey(devices_, device_id))
+ return NULL;
+
+ IOHIDDeviceRef ref = FindDevice(device_id);
+ if (ref == NULL)
+ return NULL;
+ return scoped_refptr<HidConnection>(
+ new HidConnectionMac(this, devices_[device_id], ref));
+}
+
+} // namespace device
diff --git a/device/hid/hid_service_mac.h b/device/hid/hid_service_mac.h
new file mode 100644
index 0000000..9351d1e
--- /dev/null
+++ b/device/hid/hid_service_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_SERVICE_MAC_H_
+#define DEVICE_HID_HID_SERVICE_MAC_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/mac/foundation_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string16.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_checker.h"
+#include "build/build_config.h"
+#include "device/hid/hid_device_info.h"
+#include "device/hid/hid_service.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/hid/IOHIDManager.h>
+
+namespace device {
+
+class HidConnection;
+class HidService;
+
+class HidServiceMac : public HidService {
+ public:
+ HidServiceMac();
+
+ virtual scoped_refptr<HidConnection> Connect(std::string device_id) OVERRIDE;
+
+ private:
+ virtual ~HidServiceMac();
+
+ void ScheduleRunLoop();
+ void UnscheduleRunLoop();
+
+ // Device changing callbacks.
+ static void AddDeviceCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDDeviceRef ref);
+ static void RemoveDeviceCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDDeviceRef ref);
+ static HidServiceMac* InstanceFromContext(void* context);
+
+ IOHIDDeviceRef FindDevice(std::string id);
+
+ void Enumerate();
+
+ void PlatformAddDevice(IOHIDDeviceRef ref);
+ void PlatformRemoveDevice(IOHIDDeviceRef ref);
+
+ // The message loop this object belongs to.
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+
+ // Platform HID Manager
+ base::ScopedCFTypeRef<IOHIDManagerRef> hid_manager_ref_;
+
+ // Enumeration thread.
+ scoped_ptr<base::Thread> enumeration_runloop_thread_;
+ CFRunLoopRef enumeration_runloop_;
+ base::WaitableEvent enumeration_runloop_init_;
+
+ bool available_;
+
+ DISALLOW_COPY_AND_ASSIGN(HidServiceMac);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_SERVICE_MAC_H_
diff --git a/device/hid/hid_service_unittest.cc b/device/hid/hid_service_unittest.cc
new file mode 100644
index 0000000..1fa1c0a
--- /dev/null
+++ b/device/hid/hid_service_unittest.cc
@@ -0,0 +1,33 @@
+// 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 {
+
+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.empty());
+ }
+}
+
+} // namespace device
diff --git a/device/hid/hid_service_win.cc b/device/hid/hid_service_win.cc
new file mode 100644
index 0000000..cf9db15
--- /dev/null
+++ b/device/hid/hid_service_win.cc
@@ -0,0 +1,240 @@
+// 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_service_win.h"
+
+#include <cstdlib>
+#include <string>
+
+#include "base/callback_helpers.h"
+#include "base/lazy_instance.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "device/hid/hid_connection.h"
+#include "device/hid/hid_connection_win.h"
+#include "device/hid/hid_service.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() {
+ initialized_ = Enumerate();
+}
+HidServiceWin::~HidServiceWin() {}
+
+bool 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 false;
+
+ 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_malloc<SP_DEVICE_INTERFACE_DETAIL_DATA_A>
+ device_interface_detail_data(
+ reinterpret_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 drive.
+ break;
+ }
+ }
+ }
+
+ if (!res)
+ continue;
+
+ PlatformAddDevice(device_interface_detail_data->DevicePath);
+ }
+
+ return true;
+}
+
+void HidServiceWin::PlatformAddDevice(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(),
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ 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.usage = capabilities.Usage;
+ device_info.usage_page = capabilities.UsagePage;
+ device_info.input_report_size = capabilities.InputReportByteLength;
+ device_info.output_report_size = capabilities.OutputReportByteLength;
+ device_info.feature_report_size = capabilities.FeatureReportByteLength;
+ }
+ // 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);
+ }
+ }
+ HidD_FreePreparsedData(preparsed_data);
+ }
+
+ // Get the serial number
+ wchar_t str_property[512] = { 0 };
+ if (HidD_GetSerialNumberString(device_handle.Get(),
+ str_property,
+ sizeof(str_property))) {
+ device_info.serial_number = base::SysWideToUTF8(str_property);
+ }
+
+ if (HidD_GetProductString(device_handle.Get(),
+ str_property,
+ sizeof(str_property))) {
+ device_info.product_name = base::SysWideToUTF8(str_property);
+ }
+
+ HidService::AddDevice(device_info);
+}
+
+void HidServiceWin::PlatformRemoveDevice(std::string device_path) {
+ HidService::RemoveDevice(device_path);
+}
+
+void HidServiceWin::GetDevices(std::vector<HidDeviceInfo>* devices) {
+ Enumerate();
+ HidService::GetDevices(devices);
+}
+
+scoped_refptr<HidConnection> HidServiceWin::Connect(std::string device_id) {
+ if (!ContainsKey(devices_, device_id)) return NULL;
+ scoped_refptr<HidConnectionWin> connection(
+ new HidConnectionWin(devices_[device_id]));
+ if (!connection->available()) {
+ LOG_GETLASTERROR(ERROR) << "Failed to open device.";
+ return NULL;
+ }
+ return connection;
+}
+
+} // namespace device
diff --git a/device/hid/hid_service_win.h b/device/hid/hid_service_win.h
new file mode 100644
index 0000000..428420e
--- /dev/null
+++ b/device/hid/hid_service_win.h
@@ -0,0 +1,42 @@
+// 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_SERVICE_WIN_H_
+#define DEVICE_HID_HID_SERVICE_WIN_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "device/hid/hid_device_info.h"
+#include "device/hid/hid_service.h"
+
+namespace device {
+
+class HidConnection;
+class HidService;
+
+class HidServiceWin : public HidService {
+ public:
+ HidServiceWin();
+
+ virtual void GetDevices(std::vector<HidDeviceInfo>* devices) OVERRIDE;
+
+ virtual scoped_refptr<HidConnection> Connect(std::string device_id) OVERRIDE;
+
+ private:
+ virtual ~HidServiceWin();
+
+ bool Enumerate();
+ void PlatformAddDevice(std::string device_path);
+ void PlatformRemoveDevice(std::string device_path);
+
+ DISALLOW_COPY_AND_ASSIGN(HidServiceWin);
+};
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_SERVICE_WIN_H_
diff --git a/device/hid/hid_utils_mac.cc b/device/hid/hid_utils_mac.cc
new file mode 100644
index 0000000..588041a
--- /dev/null
+++ b/device/hid/hid_utils_mac.cc
@@ -0,0 +1,39 @@
+// 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_utils_mac.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/strings/sys_string_conversions.h"
+
+#if defined(OS_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/hid/IOHIDManager.h>
+#endif
+
+namespace device {
+
+bool GetHidIntProperty(IOHIDDeviceRef device,
+ CFStringRef key,
+ int32_t* result) {
+ CFNumberRef ref = base::mac::CFCast<CFNumberRef>(
+ IOHIDDeviceGetProperty(device, key));
+ return ref && CFNumberGetValue(ref, kCFNumberSInt32Type, result);
+}
+
+bool GetHidStringProperty(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/device/hid/hid_utils_mac.h b/device/hid/hid_utils_mac.h
new file mode 100644
index 0000000..f1349ae
--- /dev/null
+++ b/device/hid/hid_utils_mac.h
@@ -0,0 +1,23 @@
+// 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_UTILS_MAC_H_
+#define DEVICE_HID_HID_UTILS_MAC_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+
+#include <IOKit/hid/IOHIDManager.h>
+
+namespace device {
+
+bool GetHidIntProperty(IOHIDDeviceRef device, CFStringRef key, int32_t* result);
+
+bool GetHidStringProperty(IOHIDDeviceRef device,
+ CFStringRef key,
+ std::string* result);
+
+} // namespace device
+
+#endif // DEVICE_HID_HID_UTILS_MAC_H_
diff --git a/device/nfc/nfc.gyp b/device/nfc/nfc.gyp
index d5e519b..11e4308 100644
--- a/device/nfc/nfc.gyp
+++ b/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/device/nfc/nfc_adapter.cc b/device/nfc/nfc_adapter.cc
index 1bef83d..520d4f3 100644
--- a/device/nfc/nfc_adapter.cc
+++ b/device/nfc/nfc_adapter.cc
@@ -48,4 +48,50 @@
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/device/nfc/nfc_adapter.h b/device/nfc/nfc_adapter.h
index 373f4cc..455b142 100644
--- a/device/nfc/nfc_adapter.h
+++ b/device/nfc/nfc_adapter.h
@@ -181,10 +181,27 @@
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/device/nfc/nfc_adapter_chromeos.cc b/device/nfc/nfc_adapter_chromeos.cc
index 071b080..374e8ac 100644
--- a/device/nfc/nfc_adapter_chromeos.cc
+++ b/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 @@
// 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::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) {
+ if (!IsPresent())
+ return;
+
+ if (GetTag(object_path.value()))
+ return;
+
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::TagRemoved(const dbus::ObjectPath& object_path) {
- VLOG(1) << "NFC tag found: " << object_path.value();
- // TODO(armansito): Implement tag logic.
-}
-
-void NfcAdapterChromeOS::TagPropertyChanged(
- const dbus::ObjectPath& object_path,
- const std::string& property_name) {
- // TODO(armansito): Implement tag logic.
+ 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 @@
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 @@
// 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();
+ PeerList peers;
+ TagList tags;
+ GetPeers(&peers);
+ GetTags(&tags);
+ ClearPeers();
+ ClearTags();
- for (PeersMap::iterator iter = peers_.begin();
- iter != peers_.end(); ++iter) {
+ 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 @@
}
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 @@
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 @@
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/device/nfc/nfc_adapter_chromeos.h b/device/nfc/nfc_adapter_chromeos.h
index d70a98d..3511e5d 100644
--- a/device/nfc/nfc_adapter_chromeos.h
+++ b/device/nfc/nfc_adapter_chromeos.h
@@ -60,14 +60,10 @@
// 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/device/nfc/nfc_chromeos_unittest.cc b/device/nfc/nfc_chromeos_unittest.cc
index dd94beb..672780f 100644
--- a/device/nfc/nfc_chromeos_unittest.cc
+++ b/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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/device/nfc/nfc_ndef_record.cc b/device/nfc/nfc_ndef_record.cc
index ec13c2f..5b8bb99 100644
--- a/device/nfc/nfc_ndef_record.cc
+++ b/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 @@
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 @@
<< 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 @@
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 @@
}
}
}
- return true;
+ return ValidateURI(data);
}
// Verifies that the contents of |data| conform to the fields of NDEF type
@@ -125,11 +159,13 @@
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 @@
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/device/nfc/nfc_ndef_record.h b/device/nfc/nfc_ndef_record.h
index 2be66b1..24cbc12 100644
--- a/device/nfc/nfc_ndef_record.h
+++ b/device/nfc/nfc_ndef_record.h
@@ -146,9 +146,15 @@
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/device/nfc/nfc_ndef_record_unittest.cc b/device/nfc/nfc_ndef_record_unittest.cc
index 00c2d2f..2b6ef48 100644
--- a/device/nfc/nfc_ndef_record_unittest.cc
+++ b/device/nfc/nfc_ndef_record_unittest.cc
@@ -21,7 +21,7 @@
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 @@
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 @@
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 @@
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 @@
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/device/nfc/nfc_ndef_record_utils_chromeos.cc b/device/nfc/nfc_ndef_record_utils_chromeos.cc
new file mode 100644
index 0000000..466cf38
--- /dev/null
+++ b/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/device/nfc/nfc_ndef_record_utils_chromeos.h b/device/nfc/nfc_ndef_record_utils_chromeos.h
new file mode 100644
index 0000000..2d96b97
--- /dev/null
+++ b/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/device/nfc/nfc_peer.h b/device/nfc/nfc_peer.h
index 1578738..a38192f 100644
--- a/device/nfc/nfc_peer.h
+++ b/device/nfc/nfc_peer.h
@@ -37,12 +37,12 @@
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 @@
// 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/device/nfc/nfc_peer_chromeos.cc b/device/nfc/nfc_peer_chromeos.cc
new file mode 100644
index 0000000..4ef1804
--- /dev/null
+++ b/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/device/nfc/nfc_peer_chromeos.h b/device/nfc/nfc_peer_chromeos.h
new file mode 100644
index 0000000..eec8e4b
--- /dev/null
+++ b/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/device/nfc/nfc_tag.h b/device/nfc/nfc_tag.h
index 650f37b..039fc78 100644
--- a/device/nfc/nfc_tag.h
+++ b/device/nfc/nfc_tag.h
@@ -31,7 +31,8 @@
kTagType1,
kTagType2,
kTagType3,
- kTagType4
+ kTagType4,
+ kTagTypeUnknown,
};
// NFC protocols that a tag can support. A tag will usually support only one
@@ -41,7 +42,8 @@
kProtocolIsoDep,
kProtocolJewel,
kProtocolMifare,
- kProtocolNfcDep
+ kProtocolNfcDep,
+ kProtocolUnknown
};
// Interface for observing changes from NFC tags.
@@ -49,12 +51,18 @@
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 @@
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/device/nfc/nfc_tag_chromeos.cc b/device/nfc/nfc_tag_chromeos.cc
new file mode 100644
index 0000000..e2390fa
--- /dev/null
+++ b/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/device/nfc/nfc_tag_chromeos.h b/device/nfc/nfc_tag_chromeos.h
new file mode 100644
index 0000000..5e4a723
--- /dev/null
+++ b/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/device/nfc/nfc_tag_technology.cc b/device/nfc/nfc_tag_technology.cc
index fa2b66a..b07fb52 100644
--- a/device/nfc/nfc_tag_technology.cc
+++ b/device/nfc/nfc_tag_technology.cc
@@ -29,11 +29,4 @@
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/device/nfc/nfc_tag_technology.h b/device/nfc/nfc_tag_technology.h
index c5d1c19..35def12 100644
--- a/device/nfc/nfc_tag_technology.h
+++ b/device/nfc/nfc_tag_technology.h
@@ -15,7 +15,8 @@
// 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 @@
};
// 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 @@
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 @@
// 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/device/nfc/nfc_tag_technology_chromeos.cc b/device/nfc/nfc_tag_technology_chromeos.cc
new file mode 100644
index 0000000..25d1b24
--- /dev/null
+++ b/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/device/nfc/nfc_tag_technology_chromeos.h b/device/nfc/nfc_tag_technology_chromeos.h
new file mode 100644
index 0000000..71b070e
--- /dev/null
+++ b/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/device/serial/serial.gyp b/device/serial/serial.gyp
new file mode 100644
index 0000000..1f200b9
--- /dev/null
+++ b/device/serial/serial.gyp
@@ -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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'device_serial',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '../..',
+ ],
+ 'conditions': [
+ ['OS=="linux"', {
+ 'dependencies': [
+ '../../build/linux/system.gyp:udev',
+ ],
+ }],
+ ],
+ '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',
+ 'serial_device_info.cc',
+ 'serial_device_info.h',
+ ],
+ },
+ ],
+}
diff --git a/device/serial/serial_device_enumerator.cc b/device/serial/serial_device_enumerator.cc
new file mode 100644
index 0000000..586d33d
--- /dev/null
+++ b/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/device/serial/serial_device_enumerator.h b/device/serial/serial_device_enumerator.h
new file mode 100644
index 0000000..33c8878
--- /dev/null
+++ b/device/serial/serial_device_enumerator.h
@@ -0,0 +1,28 @@
+// 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 <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "device/serial/serial_device_info.h"
+
+namespace device {
+
+// Discovers and enumerates serial devices available to the host.
+class SerialDeviceEnumerator {
+ public:
+ static scoped_ptr<SerialDeviceEnumerator> Create();
+
+ SerialDeviceEnumerator();
+ virtual ~SerialDeviceEnumerator();
+
+ virtual void GetDevices(SerialDeviceInfoList* devices) = 0;
+};
+
+} // namespace device
+
+#endif // DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_H_
diff --git a/device/serial/serial_device_enumerator_linux.cc b/device/serial/serial_device_enumerator_linux.cc
new file mode 100644
index 0000000..48f1441e
--- /dev/null
+++ b/device/serial/serial_device_enumerator_linux.cc
@@ -0,0 +1,105 @@
+// 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/memory/linked_ptr.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() {}
+
+void SerialDeviceEnumeratorLinux::GetDevices(SerialDeviceInfoList* devices) {
+ devices->clear();
+
+ ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev_.get()));
+ if (!enumerate) {
+ LOG(ERROR) << "Serial device enumeration failed.";
+ return;
+ }
+ if (udev_enumerate_add_match_subsystem(enumerate.get(), kSerialSubsystem)) {
+ LOG(ERROR) << "Serial device enumeration failed.";
+ return;
+ }
+ if (udev_enumerate_scan_devices(enumerate.get())) {
+ LOG(ERROR) << "Serial device enumeration failed.";
+ return;
+ }
+
+ 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) {
+ linked_ptr<SerialDeviceInfo> info(new SerialDeviceInfo());
+ info->path = std::string(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.reset(new uint16(int_value));
+ if (product_id && base::HexStringToUInt(product_id, &int_value))
+ info->product_id.reset(new uint16(int_value));
+ if (product_name)
+ info->display_name.reset(new std::string(product_name));
+
+ devices->push_back(info);
+ }
+ }
+}
+
+void SerialDeviceEnumeratorLinux::UdevDeleter::operator()(udev* handle) {
+ udev_unref(handle);
+}
+
+} // namespace device
diff --git a/device/serial/serial_device_enumerator_linux.h b/device/serial/serial_device_enumerator_linux.h
new file mode 100644
index 0000000..3a8d8a9
--- /dev/null
+++ b/device/serial/serial_device_enumerator_linux.h
@@ -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.
+
+#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"
+#include "device/serial/serial_device_info.h"
+
+namespace device {
+
+// Discovers and enumerates serial devices available to the host.
+class SerialDeviceEnumeratorLinux : public SerialDeviceEnumerator {
+ public:
+ SerialDeviceEnumeratorLinux();
+ virtual ~SerialDeviceEnumeratorLinux();
+
+ // Implementation for SerialDeviceEnumerator.
+ virtual void GetDevices(SerialDeviceInfoList* devices) 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_H_
diff --git a/device/serial/serial_device_enumerator_mac.cc b/device/serial/serial_device_enumerator_mac.cc
new file mode 100644
index 0000000..1719275
--- /dev/null
+++ b/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/linked_ptr.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.
+void SerialDeviceEnumeratorMac::GetDevices(SerialDeviceInfoList* devices) {
+ 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.*");
+
+ devices->clear();
+ 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)) {
+ linked_ptr<SerialDeviceInfo> info(new SerialDeviceInfo);
+ info->path = next_device;
+ devices->push_back(info);
+ break;
+ }
+ }
+ } while (true);
+}
+
+} // namespace device
diff --git a/device/serial/serial_device_enumerator_mac.h b/device/serial/serial_device_enumerator_mac.h
new file mode 100644
index 0000000..d76b7bc
--- /dev/null
+++ b/device/serial/serial_device_enumerator_mac.h
@@ -0,0 +1,28 @@
+// 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"
+#include "device/serial/serial_device_info.h"
+
+namespace device {
+
+// Discovers and enumerates serial devices available to the host.
+class SerialDeviceEnumeratorMac : public SerialDeviceEnumerator {
+ public:
+ SerialDeviceEnumeratorMac();
+ virtual ~SerialDeviceEnumeratorMac();
+
+ // Implementation for SerialDeviceEnumerator.
+ virtual void GetDevices(SerialDeviceInfoList* devices) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SerialDeviceEnumeratorMac);
+};
+
+} // namespace device
+
+#endif // DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_MAC_H_
diff --git a/device/serial/serial_device_enumerator_win.cc b/device/serial/serial_device_enumerator_win.cc
new file mode 100644
index 0000000..e3bde29
--- /dev/null
+++ b/device/serial/serial_device_enumerator_win.cc
@@ -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.
+
+#include "device/serial/serial_device_enumerator_win.h"
+
+#include <windows.h>
+
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.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.
+void SerialDeviceEnumeratorWin::GetDevices(SerialDeviceInfoList* devices) {
+ devices->clear();
+
+ base::win::RegistryValueIterator iter_key(
+ HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM\\");
+ for (; iter_key.Valid(); ++iter_key) {
+ base::string16 value(iter_key.Value());
+ linked_ptr<SerialDeviceInfo> info(new SerialDeviceInfo);
+ info->path = WideToASCII(value);
+ devices->push_back(info);
+ }
+}
+
+} // namespace device
diff --git a/device/serial/serial_device_enumerator_win.h b/device/serial/serial_device_enumerator_win.h
new file mode 100644
index 0000000..4d36c40
--- /dev/null
+++ b/device/serial/serial_device_enumerator_win.h
@@ -0,0 +1,28 @@
+// 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"
+#include "device/serial/serial_device_info.h"
+
+namespace device {
+
+// Discovers and enumerates serial devices available to the host.
+class SerialDeviceEnumeratorWin : public SerialDeviceEnumerator {
+ public:
+ SerialDeviceEnumeratorWin();
+ virtual ~SerialDeviceEnumeratorWin();
+
+ // Implementation for SerialDeviceEnumerator.
+ virtual void GetDevices(SerialDeviceInfoList* devices) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SerialDeviceEnumeratorWin);
+};
+
+} // namespace device
+
+#endif // DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_WIN_H_
diff --git a/device/serial/serial_device_info.cc b/device/serial/serial_device_info.cc
new file mode 100644
index 0000000..9c7284d
--- /dev/null
+++ b/device/serial/serial_device_info.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_info.h"
+
+namespace device {
+
+SerialDeviceInfo::SerialDeviceInfo() {}
+
+SerialDeviceInfo::~SerialDeviceInfo() {}
+
+} // namespace device
diff --git a/device/serial/serial_device_info.h b/device/serial/serial_device_info.h
new file mode 100644
index 0000000..277bb07
--- /dev/null
+++ b/device/serial/serial_device_info.h
@@ -0,0 +1,34 @@
+// 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_INFO_H_
+#define DEVICE_SERIAL_SERIAL_DEVICE_INFO_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace device {
+
+struct SerialDeviceInfo {
+ SerialDeviceInfo();
+ ~SerialDeviceInfo();
+
+ std::string path;
+ scoped_ptr<uint16> vendor_id;
+ scoped_ptr<uint16> product_id;
+ scoped_ptr<std::string> display_name;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SerialDeviceInfo);
+};
+
+typedef std::vector<linked_ptr<SerialDeviceInfo> > SerialDeviceInfoList;
+
+} // namespace device
+
+#endif // DEVICE_SERIAL_SERIAL_DEVICE_INFO_H_