summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorarmansito@chromium.org <armansito@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-27 22:26:37 +0000
committerarmansito@chromium.org <armansito@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-27 22:26:37 +0000
commitc3c2abb6c117830b7ed277f229b53e91b19ae2a4 (patch)
tree24150f1907a54384888c0734b73d3dde55958ba9
parent2b6c37214d2c8fe24032c6da6b4c46fb33379415 (diff)
downloadchromium_src-c3c2abb6c117830b7ed277f229b53e91b19ae2a4.zip
chromium_src-c3c2abb6c117830b7ed277f229b53e91b19ae2a4.tar.gz
chromium_src-c3c2abb6c117830b7ed277f229b53e91b19ae2a4.tar.bz2
device/bluetooth: Add device::BluetoothGattNotifySession.
This CL introduces device::BluetoothGattNotifySession, which represents a single instance of a request to a remote characteristic to start sending value updates if it supports notifications or indications. Instances are obtained by calling BluetoothGattCharacteristic::StartNotifySession and sessions are stopped by calling BluetoothGattNotifySession::Stop on the received instance, which is owned by the caller. The characteristic will keep sending value updates as long as there is at least one active session. This CL implements this functionality for Chrome OS by adding the necessary bindings for the new D-Bus API calls "StartNotify" and "StopNotify" on org.bluez.GattCharacteristic1. A reference count is kept for all created sessions inside BluetoothRemoteGattCharacteristicChromeOS. BUG=387989 TEST=device_unittests Review URL: https://codereview.chromium.org/352063002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@280433 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chromeos/dbus/bluetooth_gatt_characteristic_client.cc62
-rw-r--r--chromeos/dbus/bluetooth_gatt_characteristic_client.h18
-rw-r--r--chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc68
-rw-r--r--chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h6
-rw-r--r--device/bluetooth/bluetooth.gyp6
-rw-r--r--device/bluetooth/bluetooth_device_chromeos.cc3
-rw-r--r--device/bluetooth/bluetooth_gatt_characteristic.h18
-rw-r--r--device/bluetooth/bluetooth_gatt_chromeos_unittest.cc343
-rw-r--r--device/bluetooth/bluetooth_gatt_notify_session.cc15
-rw-r--r--device/bluetooth/bluetooth_gatt_notify_session.h44
-rw-r--r--device/bluetooth/bluetooth_gatt_notify_session_chromeos.cc132
-rw-r--r--device/bluetooth/bluetooth_gatt_notify_session_chromeos.h78
-rw-r--r--device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc193
-rw-r--r--device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h46
-rw-r--r--device/bluetooth/bluetooth_remote_gatt_service_chromeos.cc10
-rw-r--r--device/bluetooth/bluetooth_remote_gatt_service_chromeos.h16
-rw-r--r--device/bluetooth/test/mock_bluetooth_gatt_characteristic.cc1
-rw-r--r--device/bluetooth/test/mock_bluetooth_gatt_characteristic.h3
-rw-r--r--device/bluetooth/test/mock_bluetooth_gatt_notify_session.cc21
-rw-r--r--device/bluetooth/test/mock_bluetooth_gatt_notify_session.h32
20 files changed, 1060 insertions, 55 deletions
diff --git a/chromeos/dbus/bluetooth_gatt_characteristic_client.cc b/chromeos/dbus/bluetooth_gatt_characteristic_client.cc
index a510b91..823a101 100644
--- a/chromeos/dbus/bluetooth_gatt_characteristic_client.cc
+++ b/chromeos/dbus/bluetooth_gatt_characteristic_client.cc
@@ -13,6 +13,15 @@
namespace chromeos {
+namespace {
+
+// TODO(armansito): Move these to service_constants.h later.
+const char kNotifyingProperty[] = "Notifying";
+const char kStartNotify[] = "StartNotify";
+const char kStopNotify[] = "StopNotify";
+
+} // namespace
+
// static
const char BluetoothGattCharacteristicClient::kNoResponseError[] =
"org.chromium.Error.NoResponse";
@@ -27,6 +36,7 @@ BluetoothGattCharacteristicClient::Properties::Properties(
: dbus::PropertySet(object_proxy, interface_name, callback) {
RegisterProperty(bluetooth_gatt_characteristic::kUUIDProperty, &uuid);
RegisterProperty(bluetooth_gatt_characteristic::kServiceProperty, &service);
+ RegisterProperty(kNotifyingProperty, &notifying);
RegisterProperty(bluetooth_gatt_characteristic::kFlagsProperty, &flags);
}
@@ -135,6 +145,58 @@ class BluetoothGattCharacteristicClientImpl
error_callback));
}
+ // BluetoothGattCharacteristicClient override.
+ virtual void StartNotify(const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ dbus::ObjectProxy* object_proxy =
+ object_manager_->GetObjectProxy(object_path);
+ if (!object_proxy) {
+ error_callback.Run(kUnknownCharacteristicError, "");
+ return;
+ }
+
+ dbus::MethodCall method_call(
+ bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
+ kStartNotify);
+
+ object_proxy->CallMethodWithErrorCallback(
+ &method_call,
+ dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&BluetoothGattCharacteristicClientImpl::OnSuccess,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&BluetoothGattCharacteristicClientImpl::OnError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+ }
+
+ // BluetoothGattCharacteristicClient override.
+ virtual void StopNotify(const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ dbus::ObjectProxy* object_proxy =
+ object_manager_->GetObjectProxy(object_path);
+ if (!object_proxy) {
+ error_callback.Run(kUnknownCharacteristicError, "");
+ return;
+ }
+
+ dbus::MethodCall method_call(
+ bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
+ kStopNotify);
+
+ object_proxy->CallMethodWithErrorCallback(
+ &method_call,
+ dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&BluetoothGattCharacteristicClientImpl::OnSuccess,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&BluetoothGattCharacteristicClientImpl::OnError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+ }
+
// dbus::ObjectManager::Interface override.
virtual dbus::PropertySet* CreateProperties(
dbus::ObjectProxy *object_proxy,
diff --git a/chromeos/dbus/bluetooth_gatt_characteristic_client.h b/chromeos/dbus/bluetooth_gatt_characteristic_client.h
index e5d48ad..29b339a 100644
--- a/chromeos/dbus/bluetooth_gatt_characteristic_client.h
+++ b/chromeos/dbus/bluetooth_gatt_characteristic_client.h
@@ -29,6 +29,10 @@ class CHROMEOS_EXPORT BluetoothGattCharacteristicClient : public DBusClient {
// [read-only]
dbus::Property<dbus::ObjectPath> service;
+ // Whether or not this characteristic is currently sending ValueUpdated
+ // signals.
+ dbus::Property<bool> notifying;
+
// List of flags representing the GATT "Characteristic Properties bit field"
// and properties read from the GATT "Characteristic Extended Properties"
// descriptor bit field. [read-only, optional]
@@ -103,6 +107,20 @@ class CHROMEOS_EXPORT BluetoothGattCharacteristicClient : public DBusClient {
const base::Closure& callback,
const ErrorCallback& error_callback) = 0;
+ // Starts a notification session from this characteristic with object path
+ // |object_path| if it supports value notifications or indications. Invokes
+ // |callback| on success and |error_callback| on failure.
+ virtual void StartNotify(const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+
+ // Cancels any previous StartNotify transaction for characteristic with
+ // object path |object_path|. Invokes |callback| on success and
+ // |error_callback| on failure.
+ virtual void StopNotify(const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) = 0;
+
// Creates the instance.
static BluetoothGattCharacteristicClient* Create();
diff --git a/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc
index 0010ace..04745e6 100644
--- a/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc
+++ b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc
@@ -16,6 +16,7 @@ namespace chromeos {
namespace {
+const int kStartNotifyResponseIntervalMs = 200;
const int kHeartRateMeasurementNotificationIntervalMs = 2000;
} // namespace
@@ -170,6 +171,62 @@ void FakeBluetoothGattCharacteristicClient::WriteValue(
callback.Run();
}
+void FakeBluetoothGattCharacteristicClient::StartNotify(
+ const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (!IsHeartRateVisible()) {
+ error_callback.Run(kUnknownCharacteristicError, "");
+ return;
+ }
+
+ if (object_path.value() != heart_rate_measurement_path_) {
+ error_callback.Run("org.bluez.Error.NotSupported",
+ "This characteristic does not support notifications");
+ return;
+ }
+
+ if (heart_rate_measurement_properties_->notifying.value()) {
+ error_callback.Run("org.bluez.Error.Busy",
+ "Characteristic already notifying");
+ return;
+ }
+
+ heart_rate_measurement_properties_->notifying.ReplaceValue(true);
+ ScheduleHeartRateMeasurementValueChange();
+
+ // Respond asynchronously.
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ callback,
+ base::TimeDelta::FromMilliseconds(kStartNotifyResponseIntervalMs));
+}
+
+void FakeBluetoothGattCharacteristicClient::StopNotify(
+ const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (!IsHeartRateVisible()) {
+ error_callback.Run(kUnknownCharacteristicError, "");
+ return;
+ }
+
+ if (object_path.value() != heart_rate_measurement_path_) {
+ error_callback.Run("org.bluez.Error.NotSupported",
+ "This characteristic does not support notifications");
+ return;
+ }
+
+ if (!heart_rate_measurement_properties_->notifying.value()) {
+ error_callback.Run("org.bluez.Error.Failed", "Not notifying");
+ return;
+ }
+
+ heart_rate_measurement_properties_->notifying.ReplaceValue(false);
+
+ callback.Run();
+}
+
void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics(
const dbus::ObjectPath& service_path) {
if (IsHeartRateVisible()) {
@@ -227,12 +284,6 @@ void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics(
NotifyCharacteristicAdded(dbus::ObjectPath(body_sensor_location_path_));
NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_control_point_path_));
- // Set up notifications for heart rate measurement.
- // TODO(armansito): Do this based on the value of the "client characteristic
- // configuration" descriptor. Since it's still unclear how descriptors will
- // be handled by BlueZ, automatically set up notifications for now.
- ScheduleHeartRateMeasurementValueChange();
-
// Expose CCC descriptor for Heart Rate Measurement characteristic.
FakeBluetoothGattDescriptorClient* descriptor_client =
static_cast<FakeBluetoothGattDescriptorClient*>(
@@ -315,6 +366,11 @@ void FakeBluetoothGattCharacteristicClient::
ScheduleHeartRateMeasurementValueChange() {
if (!IsHeartRateVisible())
return;
+
+ // Don't send updates if the characteristic is not notifying.
+ if (!heart_rate_measurement_properties_->notifying.value())
+ return;
+
VLOG(2) << "Updating heart rate value.";
std::vector<uint8> measurement = GetHeartRateMeasurementValue();
diff --git a/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h
index 2f23586..12be69d 100644
--- a/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h
+++ b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h
@@ -56,6 +56,12 @@ class CHROMEOS_EXPORT FakeBluetoothGattCharacteristicClient
const std::vector<uint8>& value,
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
+ virtual void StartNotify(const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual void StopNotify(const dbus::ObjectPath& object_path,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
// Makes the group of characteristics belonging to a particular GATT based
// profile available under the GATT service with object path |service_path|.
diff --git a/device/bluetooth/bluetooth.gyp b/device/bluetooth/bluetooth.gyp
index e0f1134..4d206ec 100644
--- a/device/bluetooth/bluetooth.gyp
+++ b/device/bluetooth/bluetooth.gyp
@@ -53,6 +53,10 @@
'bluetooth_gatt_connection_chromeos.h',
'bluetooth_gatt_descriptor.cc',
'bluetooth_gatt_descriptor.h',
+ 'bluetooth_gatt_notify_session.cc',
+ 'bluetooth_gatt_notify_session.h',
+ 'bluetooth_gatt_notify_session_chromeos.cc',
+ 'bluetooth_gatt_notify_session_chromeos.h',
'bluetooth_gatt_service.cc',
'bluetooth_gatt_service.h',
'bluetooth_init_win.cc',
@@ -141,6 +145,8 @@
'test/mock_bluetooth_gatt_connection.h',
'test/mock_bluetooth_gatt_descriptor.cc',
'test/mock_bluetooth_gatt_descriptor.h',
+ 'test/mock_bluetooth_gatt_notify_session.cc',
+ 'test/mock_bluetooth_gatt_notify_session.h',
'test/mock_bluetooth_gatt_service.cc',
'test/mock_bluetooth_gatt_service.h',
'test/mock_bluetooth_socket.cc',
diff --git a/device/bluetooth/bluetooth_device_chromeos.cc b/device/bluetooth/bluetooth_device_chromeos.cc
index c808569..2045a1e 100644
--- a/device/bluetooth/bluetooth_device_chromeos.cc
+++ b/device/bluetooth/bluetooth_device_chromeos.cc
@@ -509,7 +509,8 @@ void BluetoothDeviceChromeOS::GattServiceAdded(
VLOG(1) << "Adding new remote GATT service for device: " << GetAddress();
BluetoothRemoteGattServiceChromeOS* service =
- new BluetoothRemoteGattServiceChromeOS(this, object_path);
+ new BluetoothRemoteGattServiceChromeOS(adapter_, this, object_path);
+
gatt_services_[service->GetIdentifier()] = service;
DCHECK(service->object_path() == object_path);
DCHECK(service->GetUUID().IsValid());
diff --git a/device/bluetooth/bluetooth_gatt_characteristic.h b/device/bluetooth/bluetooth_gatt_characteristic.h
index e71d2fd..7b952e0 100644
--- a/device/bluetooth/bluetooth_gatt_characteristic.h
+++ b/device/bluetooth/bluetooth_gatt_characteristic.h
@@ -10,12 +10,14 @@
#include "base/basictypes.h"
#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
#include "device/bluetooth/bluetooth_uuid.h"
namespace device {
class BluetoothGattDescriptor;
class BluetoothGattService;
+class BluetoothGattNotifySession;
// BluetoothGattCharacteristic represents a local or remote GATT characteristic.
// A GATT characteristic is a basic data element used to construct a GATT
@@ -82,6 +84,11 @@ class BluetoothGattCharacteristic {
// upon a read request.
typedef base::Callback<void(const std::vector<uint8>&)> ValueCallback;
+ // The NotifySessionCallback is used to return sessions after they have
+ // been successfully started.
+ typedef base::Callback<void(scoped_ptr<BluetoothGattNotifySession>)>
+ NotifySessionCallback;
+
// Constructs a BluetoothGattCharacteristic that can be associated with a
// local GATT service when the adapter is in the peripheral role. To
// associate the returned characteristic with a service, add it to a local
@@ -133,6 +140,10 @@ class BluetoothGattCharacteristic {
// Returns the bitmask of characteristic attribute permissions.
virtual Permissions GetPermissions() const = 0;
+ // Returns whether or not this characteristic is currently sending value
+ // updates in the form of a notification or indication.
+ virtual bool IsNotifying() const = 0;
+
// Returns the list of GATT characteristic descriptors that provide more
// information about this characteristic.
virtual std::vector<BluetoothGattDescriptor*>
@@ -162,6 +173,13 @@ class BluetoothGattCharacteristic {
// returns false if this instance represents a remote characteristic.
virtual bool UpdateValue(const std::vector<uint8>& value) = 0;
+ // Starts a notify session for the remote characteristic, if it supports
+ // notifications/indications. On success, the characteristic starts sending
+ // value notifications and |callback| is called with a session object whose
+ // ownership belongs to the caller. |error_callback| is called on errors.
+ virtual void StartNotifySession(const NotifySessionCallback& callback,
+ const ErrorCallback& error_callback) = 0;
+
// Sends a read request to a remote characteristic to read its value.
// |callback| is called to return the read value on success and
// |error_callback| is called for failures.
diff --git a/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc b/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc
index 0454399..673ed5f 100644
--- a/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc
+++ b/device/bluetooth/bluetooth_gatt_chromeos_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "chromeos/dbus/fake_bluetooth_adapter_client.h"
@@ -19,6 +20,7 @@
#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_gatt_connection.h"
#include "device/bluetooth/bluetooth_gatt_descriptor.h"
+#include "device/bluetooth/bluetooth_gatt_notify_session.h"
#include "device/bluetooth/bluetooth_gatt_service.h"
#include "device/bluetooth/bluetooth_uuid.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -29,6 +31,7 @@ using device::BluetoothGattCharacteristic;
using device::BluetoothGattConnection;
using device::BluetoothGattDescriptor;
using device::BluetoothGattService;
+using device::BluetoothGattNotifySession;
using device::BluetoothUUID;
namespace chromeos {
@@ -343,6 +346,7 @@ class BluetoothGattChromeOSTest : public testing::Test {
virtual void TearDown() {
adapter_ = NULL;
+ update_sessions_.clear();
gatt_conn_.reset();
DBusThreadManager::Shutdown();
}
@@ -374,15 +378,32 @@ class BluetoothGattChromeOSTest : public testing::Test {
gatt_conn_ = conn.Pass();
}
+ void NotifySessionCallback(scoped_ptr<BluetoothGattNotifySession> session) {
+ ++success_callback_count_;
+ update_sessions_.push_back(session.release());
+ QuitMessageLoop();
+ }
+
void ErrorCallback() {
++error_callback_count_;
}
+ void DBusErrorCallback(const std::string& error_name,
+ const std::string& error_message) {
+ ++error_callback_count_;
+ }
+
void ConnectErrorCallback(BluetoothDevice::ConnectErrorCode error) {
++error_callback_count_;
}
protected:
+ void QuitMessageLoop() {
+ if (base::MessageLoop::current() &&
+ base::MessageLoop::current()->is_running())
+ base::MessageLoop::current()->Quit();
+ }
+
base::MessageLoop message_loop_;
FakeBluetoothDeviceClient* fake_bluetooth_device_client_;
@@ -391,6 +412,7 @@ class BluetoothGattChromeOSTest : public testing::Test {
fake_bluetooth_gatt_characteristic_client_;
FakeBluetoothGattDescriptorClient* fake_bluetooth_gatt_descriptor_client_;
scoped_ptr<device::BluetoothGattConnection> gatt_conn_;
+ ScopedVector<BluetoothGattNotifySession> update_sessions_;
scoped_refptr<BluetoothAdapter> adapter_;
int success_callback_count_;
@@ -600,7 +622,7 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicAddedAndRemoved) {
EXPECT_EQ(4, service_observer.gatt_service_changed_count_);
EXPECT_EQ(3, service_observer.gatt_characteristic_added_count_);
EXPECT_EQ(0, service_observer.gatt_characteristic_removed_count_);
- EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
EXPECT_EQ(3U, service->GetCharacteristics().size());
// Hide the characteristics. 3 removed signals should be received.
@@ -608,7 +630,7 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicAddedAndRemoved) {
EXPECT_EQ(8, service_observer.gatt_service_changed_count_);
EXPECT_EQ(3, service_observer.gatt_characteristic_added_count_);
EXPECT_EQ(3, service_observer.gatt_characteristic_removed_count_);
- EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
EXPECT_TRUE(service->GetCharacteristics().empty());
// Re-expose the heart rate characteristics.
@@ -617,7 +639,7 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicAddedAndRemoved) {
EXPECT_EQ(12, service_observer.gatt_service_changed_count_);
EXPECT_EQ(6, service_observer.gatt_characteristic_added_count_);
EXPECT_EQ(3, service_observer.gatt_characteristic_removed_count_);
- EXPECT_EQ(2, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
EXPECT_EQ(3U, service->GetCharacteristics().size());
// Hide the service. All characteristics should disappear.
@@ -625,7 +647,7 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicAddedAndRemoved) {
EXPECT_EQ(16, service_observer.gatt_service_changed_count_);
EXPECT_EQ(6, service_observer.gatt_characteristic_added_count_);
EXPECT_EQ(6, service_observer.gatt_characteristic_removed_count_);
- EXPECT_EQ(2, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
}
TEST_F(BluetoothGattChromeOSTest, GattDescriptorAddedAndRemoved) {
@@ -726,7 +748,7 @@ TEST_F(BluetoothGattChromeOSTest, AdapterAddedAfterGattService) {
// This unit test tests that all remote GATT objects are created for D-Bus
// objects that were already exposed.
adapter_ = NULL;
- EXPECT_FALSE(device::BluetoothAdapterFactory::HasSharedInstanceForTesting());
+ ASSERT_FALSE(device::BluetoothAdapterFactory::HasSharedInstanceForTesting());
// Create the fake D-Bus objects.
fake_bluetooth_device_client_->CreateDevice(
@@ -823,31 +845,6 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicValue) {
// Run the message loop so that the characteristics appear.
base::MessageLoop::current()->Run();
- // We should get an initial value changed signal from the Heart Rate
- // Measurement characteristic when it getsadded.
- EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
-
- // The Heart Rate Measurement characteristic should send regular
- // notifications.
- base::MessageLoop::current()->Run();
- EXPECT_EQ(2, service_observer.gatt_characteristic_value_changed_count_);
- EXPECT_EQ(kHeartRateMeasurementUUID,
- service_observer.last_gatt_characteristic_uuid_);
- EXPECT_EQ(fake_bluetooth_gatt_characteristic_client_->
- GetHeartRateMeasurementPath().value(),
- service_observer.last_gatt_characteristic_id_);
-
- // Receive another notification.
- service_observer.last_gatt_characteristic_id_.clear();
- service_observer.last_gatt_characteristic_uuid_ = BluetoothUUID();
- base::MessageLoop::current()->Run();
- EXPECT_EQ(3, service_observer.gatt_characteristic_value_changed_count_);
- EXPECT_EQ(kHeartRateMeasurementUUID,
- service_observer.last_gatt_characteristic_uuid_);
- EXPECT_EQ(fake_bluetooth_gatt_characteristic_client_->
- GetHeartRateMeasurementPath().value(),
- service_observer.last_gatt_characteristic_id_);
-
// Issue write request to non-writeable characteristics.
service_observer.last_gatt_characteristic_id_.clear();
service_observer.last_gatt_characteristic_uuid_ = BluetoothUUID();
@@ -858,6 +855,7 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicValue) {
service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_->
GetHeartRateMeasurementPath().value());
ASSERT_TRUE(characteristic);
+ EXPECT_FALSE(characteristic->IsNotifying());
EXPECT_EQ(fake_bluetooth_gatt_characteristic_client_->
GetHeartRateMeasurementPath().value(),
characteristic->GetIdentifier());
@@ -872,7 +870,7 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicValue) {
EXPECT_FALSE(service_observer.last_gatt_characteristic_uuid_.IsValid());
EXPECT_EQ(0, success_callback_count_);
EXPECT_EQ(1, error_callback_count_);
- EXPECT_EQ(3, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
characteristic = service->GetCharacteristic(
fake_bluetooth_gatt_characteristic_client_->
@@ -892,7 +890,7 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicValue) {
EXPECT_FALSE(service_observer.last_gatt_characteristic_uuid_.IsValid());
EXPECT_EQ(0, success_callback_count_);
EXPECT_EQ(2, error_callback_count_);
- EXPECT_EQ(3, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
// Issue write request to writeable characteristic. The "Body Sensor Location"
// characteristic does not send notifications and WriteValue does not result
@@ -916,7 +914,7 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicValue) {
EXPECT_FALSE(service_observer.last_gatt_characteristic_uuid_.IsValid());
EXPECT_EQ(1, success_callback_count_);
EXPECT_EQ(2, error_callback_count_);
- EXPECT_EQ(3, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
// Issue a read request. A successful read results in a
// CharacteristicValueChanged notification.
@@ -935,17 +933,8 @@ TEST_F(BluetoothGattChromeOSTest, GattCharacteristicValue) {
base::Unretained(this)));
EXPECT_EQ(2, success_callback_count_);
EXPECT_EQ(2, error_callback_count_);
- EXPECT_EQ(4, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
EXPECT_TRUE(ValuesEqual(characteristic->GetValue(), last_read_value_));
-
- // One last value changed notification.
- base::MessageLoop::current()->Run();
- EXPECT_EQ(5, service_observer.gatt_characteristic_value_changed_count_);
- EXPECT_EQ(kHeartRateMeasurementUUID,
- service_observer.last_gatt_characteristic_uuid_);
- EXPECT_EQ(fake_bluetooth_gatt_characteristic_client_->
- GetHeartRateMeasurementPath().value(),
- service_observer.last_gatt_characteristic_id_);
}
TEST_F(BluetoothGattChromeOSTest, GattCharacteristicProperties) {
@@ -1086,4 +1075,272 @@ TEST_F(BluetoothGattChromeOSTest, GattDescriptorValue) {
EXPECT_EQ(2, service_observer.gatt_descriptor_value_changed_count_);
}
+TEST_F(BluetoothGattChromeOSTest, NotifySessions) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device =
+ adapter_->GetDevice(FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+
+ // Run the message loop so that the characteristics appear.
+ base::MessageLoop::current()->Run();
+
+ BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath()
+ .value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_FALSE(characteristic->IsNotifying());
+ EXPECT_TRUE(update_sessions_.empty());
+
+ // Request to start notifications.
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ // The operation still hasn't completed but we should have received the first
+ // notification.
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(update_sessions_.empty());
+
+ // Send a two more requests, which should get queued.
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(update_sessions_.empty());
+ EXPECT_TRUE(characteristic->IsNotifying());
+
+ // Run the main loop. The initial call should complete. The queued call should
+ // succeed immediately.
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(3, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(3U, update_sessions_.size());
+
+ // Notifications should be getting sent regularly now.
+ base::MessageLoop::current()->Run();
+ EXPECT_GT(service_observer.gatt_characteristic_value_changed_count_, 1);
+
+ // Stop one of the sessions. The session should become inactive but the
+ // characteristic should still be notifying.
+ BluetoothGattNotifySession* session = update_sessions_[0];
+ EXPECT_TRUE(session->IsActive());
+ session->Stop(base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(4, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_FALSE(session->IsActive());
+ EXPECT_EQ(characteristic->GetIdentifier(),
+ session->GetCharacteristicIdentifier());
+ EXPECT_TRUE(characteristic->IsNotifying());
+
+ // Delete another session. Characteristic should still be notifying.
+ update_sessions_.pop_back();
+ EXPECT_EQ(2U, update_sessions_.size());
+ EXPECT_TRUE(characteristic->IsNotifying());
+ EXPECT_FALSE(update_sessions_[0]->IsActive());
+ EXPECT_TRUE(update_sessions_[1]->IsActive());
+
+ // Clear the last session.
+ update_sessions_.clear();
+ EXPECT_TRUE(update_sessions_.empty());
+ EXPECT_FALSE(characteristic->IsNotifying());
+
+ success_callback_count_ = 0;
+ service_observer.gatt_characteristic_value_changed_count_ = 0;
+
+ // Enable notifications again.
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(update_sessions_.empty());
+ EXPECT_TRUE(characteristic->IsNotifying());
+
+ // Run the message loop. Notifications should begin.
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_EQ(1U, update_sessions_.size());
+ EXPECT_TRUE(update_sessions_[0]->IsActive());
+ EXPECT_TRUE(characteristic->IsNotifying());
+
+ // Check that notifications are happening.
+ base::MessageLoop::current()->Run();
+ EXPECT_GT(service_observer.gatt_characteristic_value_changed_count_, 1);
+
+ // Request another session. This should return immediately.
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(2U, update_sessions_.size());
+ EXPECT_TRUE(update_sessions_[0]->IsActive());
+ EXPECT_TRUE(update_sessions_[1]->IsActive());
+ EXPECT_TRUE(characteristic->IsNotifying());
+
+ // Hide the characteristic. The sessions should become inactive.
+ fake_bluetooth_gatt_characteristic_client_->HideHeartRateCharacteristics();
+ EXPECT_EQ(2U, update_sessions_.size());
+ EXPECT_FALSE(update_sessions_[0]->IsActive());
+ EXPECT_FALSE(update_sessions_[1]->IsActive());
+}
+
+TEST_F(BluetoothGattChromeOSTest, NotifySessionsMadeInactive) {
+ fake_bluetooth_device_client_->CreateDevice(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ BluetoothDevice* device =
+ adapter_->GetDevice(FakeBluetoothDeviceClient::kLowEnergyAddress);
+ ASSERT_TRUE(device);
+
+ TestDeviceObserver observer(adapter_, device);
+
+ // Expose the fake Heart Rate service. This will asynchronously expose
+ // characteristics.
+ fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
+ ASSERT_EQ(1, observer.gatt_service_added_count_);
+
+ BluetoothGattService* service =
+ device->GetGattService(observer.last_gatt_service_id_);
+
+ TestGattServiceObserver service_observer(adapter_, device, service);
+ EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
+
+ // Run the message loop so that the characteristics appear.
+ base::MessageLoop::current()->Run();
+
+ BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
+ fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath()
+ .value());
+ ASSERT_TRUE(characteristic);
+ EXPECT_FALSE(characteristic->IsNotifying());
+ EXPECT_TRUE(update_sessions_.empty());
+
+ // Send several requests to start notifications.
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ // The operation still hasn't completed but we should have received the first
+ // notification.
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(characteristic->IsNotifying());
+ EXPECT_TRUE(update_sessions_.empty());
+
+ // Run the main loop. The initial call should complete. The queued calls
+ // should succeed immediately.
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(4, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(characteristic->IsNotifying());
+ EXPECT_EQ(4U, update_sessions_.size());
+
+ for (int i = 0; i < 4; i++)
+ EXPECT_TRUE(update_sessions_[0]->IsActive());
+
+ // Stop notifications directly through the client. The sessions should get
+ // marked as inactive.
+ fake_bluetooth_gatt_characteristic_client_->StopNotify(
+ fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath(),
+ base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::DBusErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(5, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_FALSE(characteristic->IsNotifying());
+ EXPECT_EQ(4U, update_sessions_.size());
+
+ for (int i = 0; i < 4; i++)
+ EXPECT_FALSE(update_sessions_[0]->IsActive());
+
+ // It should be possible to restart notifications and the call should reset
+ // the session count and make a request through the client.
+ update_sessions_.clear();
+ success_callback_count_ = 0;
+ service_observer.gatt_characteristic_value_changed_count_ = 0;
+ characteristic->StartNotifySession(
+ base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(0, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(characteristic->IsNotifying());
+ EXPECT_TRUE(update_sessions_.empty());
+
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(1, success_callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
+ EXPECT_TRUE(characteristic->IsNotifying());
+ EXPECT_EQ(1U, update_sessions_.size());
+ EXPECT_TRUE(update_sessions_[0]->IsActive());
+}
+
} // namespace chromeos
diff --git a/device/bluetooth/bluetooth_gatt_notify_session.cc b/device/bluetooth/bluetooth_gatt_notify_session.cc
new file mode 100644
index 0000000..3c9a2423
--- /dev/null
+++ b/device/bluetooth/bluetooth_gatt_notify_session.cc
@@ -0,0 +1,15 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_gatt_notify_session.h"
+
+namespace device {
+
+BluetoothGattNotifySession::BluetoothGattNotifySession() {
+}
+
+BluetoothGattNotifySession::~BluetoothGattNotifySession() {
+}
+
+} // namespace device
diff --git a/device/bluetooth/bluetooth_gatt_notify_session.h b/device/bluetooth/bluetooth_gatt_notify_session.h
new file mode 100644
index 0000000..9837e3a
--- /dev/null
+++ b/device/bluetooth/bluetooth_gatt_notify_session.h
@@ -0,0 +1,44 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_GATT_NOTIFY_SESSION_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_GATT_NOTIFY_SESSION_H_
+
+#include <string>
+
+#include "base/callback.h"
+
+namespace device {
+
+// A BluetoothGattNotifySession represents an active session for listening
+// to value updates from GATT characteristics that support notifications and/or
+// indications. Instances are obtained by calling
+// BluetoothGattCharacteristic::StartNotifySession.
+class BluetoothGattNotifySession {
+ public:
+ // Destructor autmatically stops this session.
+ virtual ~BluetoothGattNotifySession();
+
+ // Returns the identifier of the associated characteristic.
+ virtual std::string GetCharacteristicIdentifier() const = 0;
+
+ // Returns true if this session is active.
+ virtual bool IsActive() = 0;
+
+ // Stops this session and calls |callback| upon completion. This won't
+ // necessarily stop value updates from the characteristic -- since updates
+ // are shared among BluetoothGattNotifySession instances -- but it will
+ // terminate this session.
+ virtual void Stop(const base::Closure& callback) = 0;
+
+ protected:
+ BluetoothGattNotifySession();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BluetoothGattNotifySession);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_GATT_NOTIFY_SESSION_H_
diff --git a/device/bluetooth/bluetooth_gatt_notify_session_chromeos.cc b/device/bluetooth/bluetooth_gatt_notify_session_chromeos.cc
new file mode 100644
index 0000000..ba7b843
--- /dev/null
+++ b/device/bluetooth/bluetooth_gatt_notify_session_chromeos.cc
@@ -0,0 +1,132 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_gatt_notify_session_chromeos.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_gatt_service.h"
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h"
+
+namespace chromeos {
+
+BluetoothGattNotifySessionChromeOS::BluetoothGattNotifySessionChromeOS(
+ scoped_refptr<device::BluetoothAdapter> adapter,
+ const std::string& device_address,
+ const std::string& service_identifier,
+ const std::string& characteristic_identifier,
+ const dbus::ObjectPath& characteristic_path)
+ : active_(true),
+ adapter_(adapter),
+ device_address_(device_address),
+ service_id_(service_identifier),
+ characteristic_id_(characteristic_identifier),
+ object_path_(characteristic_path) {
+ DCHECK(adapter_.get());
+ DCHECK(!device_address_.empty());
+ DCHECK(!service_id_.empty());
+ DCHECK(!characteristic_id_.empty());
+ DCHECK(object_path_.IsValid());
+
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->AddObserver(
+ this);
+}
+
+BluetoothGattNotifySessionChromeOS::~BluetoothGattNotifySessionChromeOS() {
+ DBusThreadManager::Get()
+ ->GetBluetoothGattCharacteristicClient()
+ ->RemoveObserver(this);
+ Stop(base::Bind(&base::DoNothing));
+}
+
+std::string BluetoothGattNotifySessionChromeOS::GetCharacteristicIdentifier()
+ const {
+ return characteristic_id_;
+}
+
+bool BluetoothGattNotifySessionChromeOS::IsActive() {
+ // Determine if the session is active. If |active_| is false, then it's
+ // been explicitly marked, so return false.
+ if (!active_)
+ return false;
+
+ // The fact that |active_| is true doesn't mean that the session is
+ // actually active, since the characteristic might have stopped sending
+ // notifications yet this method was called before we processed the
+ // observer event (e.g. because somebody else called this method in their
+ // BluetoothGattCharacteristicClient::Observer implementation, which was
+ // called before ours). Check the client to see if notifications are still
+ // being sent.
+ BluetoothGattCharacteristicClient::Properties* properties =
+ DBusThreadManager::Get()
+ ->GetBluetoothGattCharacteristicClient()
+ ->GetProperties(object_path_);
+ if (!properties || !properties->notifying.value())
+ active_ = false;
+
+ return active_;
+}
+
+void BluetoothGattNotifySessionChromeOS::Stop(const base::Closure& callback) {
+ if (!active_) {
+ VLOG(1) << "Notify session already inactive.";
+ callback.Run();
+ return;
+ }
+
+ // Mark this session as inactive no matter what.
+ active_ = false;
+
+ device::BluetoothDevice* device = adapter_->GetDevice(device_address_);
+ if (!device)
+ return;
+
+ device::BluetoothGattService* service = device->GetGattService(service_id_);
+ if (!service)
+ return;
+
+ BluetoothRemoteGattCharacteristicChromeOS* chrc =
+ static_cast<BluetoothRemoteGattCharacteristicChromeOS*>(
+ service->GetCharacteristic(characteristic_id_));
+ if (!chrc)
+ return;
+
+ chrc->RemoveNotifySession(callback);
+}
+
+void BluetoothGattNotifySessionChromeOS::GattCharacteristicRemoved(
+ const dbus::ObjectPath& object_path) {
+ if (object_path != object_path_)
+ return;
+
+ active_ = false;
+}
+
+void BluetoothGattNotifySessionChromeOS::GattCharacteristicPropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) {
+ if (object_path != object_path_)
+ return;
+
+ if (!active_)
+ return;
+
+ BluetoothGattCharacteristicClient::Properties* properties =
+ DBusThreadManager::Get()
+ ->GetBluetoothGattCharacteristicClient()
+ ->GetProperties(object_path_);
+ if (!properties) {
+ active_ = false;
+ return;
+ }
+
+ if (property_name == properties->notifying.name() &&
+ !properties->notifying.value())
+ active_ = false;
+}
+
+} // namespace chromeos
diff --git a/device/bluetooth/bluetooth_gatt_notify_session_chromeos.h b/device/bluetooth/bluetooth_gatt_notify_session_chromeos.h
new file mode 100644
index 0000000..1202fd8
--- /dev/null
+++ b/device/bluetooth/bluetooth_gatt_notify_session_chromeos.h
@@ -0,0 +1,78 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_GATT_NOTIFY_SESSION_CHROMEOS_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_GATT_NOTIFY_SESSION_CHROMEOS_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "chromeos/dbus/bluetooth_gatt_characteristic_client.h"
+#include "device/bluetooth/bluetooth_gatt_notify_session.h"
+
+namespace device {
+
+class BluetoothAdapter;
+
+} // namespace device
+
+namespace chromeos {
+
+class BluetoothRemoteGattCharacteristicChromeOS;
+
+// BluetoothGattNotifySessionChromeOS implements
+// BluetoothGattNotifySession for the Chrome OS platform.
+class BluetoothGattNotifySessionChromeOS
+ : public device::BluetoothGattNotifySession,
+ public BluetoothGattCharacteristicClient::Observer {
+ public:
+ virtual ~BluetoothGattNotifySessionChromeOS();
+
+ // BluetoothGattNotifySession overrides.
+ virtual std::string GetCharacteristicIdentifier() const OVERRIDE;
+ virtual bool IsActive() OVERRIDE;
+ virtual void Stop(const base::Closure& callback) OVERRIDE;
+
+ private:
+ friend class BluetoothRemoteGattCharacteristicChromeOS;
+
+ explicit BluetoothGattNotifySessionChromeOS(
+ scoped_refptr<device::BluetoothAdapter> adapter,
+ const std::string& device_address,
+ const std::string& service_identifier,
+ const std::string& characteristic_identifier,
+ const dbus::ObjectPath& characteristic_path);
+
+ // BluetoothGattCharacteristicClient::Observer overrides.
+ virtual void GattCharacteristicRemoved(
+ const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void GattCharacteristicPropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) OVERRIDE;
+
+ // True, if this session is currently active.
+ bool active_;
+
+ // The Bluetooth adapter that this session is associated with.
+ scoped_refptr<device::BluetoothAdapter> adapter_;
+
+ // The Bluetooth address of the device hosting the characteristic.
+ std::string device_address_;
+
+ // The GATT service that the characteristic belongs to.
+ std::string service_id_;
+
+ // Identifier of the associated characteristic.
+ std::string characteristic_id_;
+
+ // D-Bus object path of the associated characteristic. This is used to filter
+ // observer events.
+ dbus::ObjectPath object_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothGattNotifySessionChromeOS);
+};
+
+} // namespace chromeos
+
+#endif // DEVICE_BLUETOOTH_BLUETOOTH_GATT_NOTIFY_SESSION_CHROMEOS_H_
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc
index e3de615..83231b9 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.cc
@@ -4,9 +4,14 @@
#include "device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h"
+#include <limits>
+
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "chromeos/dbus/dbus_thread_manager.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_gatt_notify_session_chromeos.h"
#include "device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.h"
#include "device/bluetooth/bluetooth_remote_gatt_service_chromeos.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
@@ -31,9 +36,11 @@ BluetoothRemoteGattCharacteristicChromeOS::
BluetoothRemoteGattCharacteristicChromeOS(
BluetoothRemoteGattServiceChromeOS* service,
const dbus::ObjectPath& object_path)
- : object_path_(object_path),
- service_(service),
- weak_ptr_factory_(this) {
+ : object_path_(object_path),
+ service_(service),
+ num_notify_sessions_(0),
+ notify_call_pending_(false),
+ weak_ptr_factory_(this) {
VLOG(1) << "Creating remote GATT characteristic with identifier: "
<< GetIdentifier() << ", UUID: " << GetUUID().canonical_value();
DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
@@ -62,6 +69,13 @@ BluetoothRemoteGattCharacteristicChromeOS::
for (DescriptorMap::iterator iter = descriptors_.begin();
iter != descriptors_.end(); ++iter)
delete iter->second;
+
+ // Report an error for all pending calls to StartNotifySession.
+ while (!pending_start_notify_calls_.empty()) {
+ PendingStartNotifyCall callbacks = pending_start_notify_calls_.front();
+ pending_start_notify_calls_.pop();
+ callbacks.second.Run();
+ }
}
std::string BluetoothRemoteGattCharacteristicChromeOS::GetIdentifier() const {
@@ -135,6 +149,16 @@ BluetoothRemoteGattCharacteristicChromeOS::GetPermissions() const {
return kPermissionNone;
}
+bool BluetoothRemoteGattCharacteristicChromeOS::IsNotifying() const {
+ BluetoothGattCharacteristicClient::Properties* properties =
+ DBusThreadManager::Get()
+ ->GetBluetoothGattCharacteristicClient()
+ ->GetProperties(object_path_);
+ DCHECK(properties);
+
+ return properties->notifying.value();
+}
+
std::vector<device::BluetoothGattDescriptor*>
BluetoothRemoteGattCharacteristicChromeOS::GetDescriptors() const {
std::vector<device::BluetoothGattDescriptor*> descriptors;
@@ -200,6 +224,96 @@ void BluetoothRemoteGattCharacteristicChromeOS::WriteRemoteCharacteristic(
error_callback));
}
+void BluetoothRemoteGattCharacteristicChromeOS::StartNotifySession(
+ const NotifySessionCallback& callback,
+ const ErrorCallback& error_callback) {
+ VLOG(1) << __func__;
+
+ if (num_notify_sessions_ > 0) {
+ // The characteristic might have stopped notifying even though the session
+ // count is nonzero. This means that notifications stopped outside of our
+ // control and we should reset the count. If the characteristic is still
+ // notifying, then return success. Otherwise, reset the count and treat
+ // this call as if the count were 0.
+ if (IsNotifying()) {
+ // Check for overflows, though unlikely.
+ if (num_notify_sessions_ == std::numeric_limits<size_t>::max()) {
+ error_callback.Run();
+ return;
+ }
+
+ ++num_notify_sessions_;
+ DCHECK(service_);
+ DCHECK(service_->GetDevice());
+ scoped_ptr<device::BluetoothGattNotifySession> session(
+ new BluetoothGattNotifySessionChromeOS(
+ service_->GetAdapter(),
+ service_->GetDevice()->GetAddress(),
+ service_->GetIdentifier(),
+ GetIdentifier(),
+ object_path_));
+ callback.Run(session.Pass());
+ return;
+ }
+
+ num_notify_sessions_ = 0;
+ }
+
+ // Queue the callbacks if there is a pending call to bluetoothd.
+ if (notify_call_pending_) {
+ pending_start_notify_calls_.push(std::make_pair(callback, error_callback));
+ return;
+ }
+
+ notify_call_pending_ = true;
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->StartNotify(
+ object_path_,
+ base::Bind(
+ &BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifySuccess,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifyError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::RemoveNotifySession(
+ const base::Closure& callback) {
+ VLOG(1) << __func__;
+
+ if (num_notify_sessions_ > 1) {
+ DCHECK(!notify_call_pending_);
+ --num_notify_sessions_;
+ callback.Run();
+ return;
+ }
+
+ // Notifications may have stopped outside our control. If the characteristic
+ // is no longer notifying, return success.
+ if (!IsNotifying()) {
+ num_notify_sessions_ = 0;
+ callback.Run();
+ return;
+ }
+
+ if (notify_call_pending_ || num_notify_sessions_ == 0) {
+ callback.Run();
+ return;
+ }
+
+ DCHECK(num_notify_sessions_ == 1);
+ notify_call_pending_ = true;
+ DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->StopNotify(
+ object_path_,
+ base::Bind(
+ &BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifySuccess,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifyError,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback));
+}
+
void BluetoothRemoteGattCharacteristicChromeOS::GattCharacteristicValueUpdated(
const dbus::ObjectPath& object_path,
const std::vector<uint8>& value) {
@@ -289,4 +403,77 @@ void BluetoothRemoteGattCharacteristicChromeOS::OnError(
error_callback.Run();
}
+void BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifySuccess(
+ const NotifySessionCallback& callback) {
+ VLOG(1) << "Started notifications from characteristic: "
+ << object_path_.value();
+ DCHECK(num_notify_sessions_ == 0);
+ DCHECK(notify_call_pending_);
+
+ ++num_notify_sessions_;
+ notify_call_pending_ = false;
+
+ // Invoke the queued callbacks for this operation.
+ DCHECK(service_);
+ DCHECK(service_->GetDevice());
+ scoped_ptr<device::BluetoothGattNotifySession> session(
+ new BluetoothGattNotifySessionChromeOS(
+ service_->GetAdapter(),
+ service_->GetDevice()->GetAddress(),
+ service_->GetIdentifier(),
+ GetIdentifier(),
+ object_path_));
+ callback.Run(session.Pass());
+
+ ProcessStartNotifyQueue();
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::OnStartNotifyError(
+ const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ VLOG(1) << "Failed to start notifications from characteristic: "
+ << object_path_.value() << ": " << error_name << ", "
+ << error_message;
+ DCHECK(num_notify_sessions_ == 0);
+ DCHECK(notify_call_pending_);
+
+ notify_call_pending_ = false;
+ error_callback.Run();
+
+ ProcessStartNotifyQueue();
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifySuccess(
+ const base::Closure& callback) {
+ DCHECK(notify_call_pending_);
+ DCHECK(num_notify_sessions_ == 1);
+
+ notify_call_pending_ = false;
+ --num_notify_sessions_;
+ callback.Run();
+
+ ProcessStartNotifyQueue();
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::OnStopNotifyError(
+ const base::Closure& callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ VLOG(1) << "Call to stop notifications failed for characteristic: "
+ << object_path_.value() << ": " << error_name << ", "
+ << error_message;
+
+ // Since this is a best effort operation, treat this as success.
+ OnStopNotifySuccess(callback);
+}
+
+void BluetoothRemoteGattCharacteristicChromeOS::ProcessStartNotifyQueue() {
+ while (!pending_start_notify_calls_.empty()) {
+ PendingStartNotifyCall callbacks = pending_start_notify_calls_.front();
+ pending_start_notify_calls_.pop();
+ StartNotifySession(callbacks.first, callbacks.second);
+ }
+}
+
} // namespace chromeos
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h b/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h
index 35a0884..26fda0b 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h
@@ -6,7 +6,9 @@
#define DEVICE_BLUETOOTH_BLUETOOTH_REMOTE_GATT_CHARACTERISTIC_CHROMEOS_H_
#include <map>
+#include <queue>
#include <string>
+#include <utility>
#include <vector>
#include "base/memory/weak_ptr.h"
@@ -44,6 +46,7 @@ class BluetoothRemoteGattCharacteristicChromeOS
virtual device::BluetoothGattService* GetService() const OVERRIDE;
virtual Properties GetProperties() const OVERRIDE;
virtual Permissions GetPermissions() const OVERRIDE;
+ virtual bool IsNotifying() const OVERRIDE;
virtual std::vector<device::BluetoothGattDescriptor*>
GetDescriptors() const OVERRIDE;
virtual device::BluetoothGattDescriptor* GetDescriptor(
@@ -58,6 +61,14 @@ class BluetoothRemoteGattCharacteristicChromeOS
const std::vector<uint8>& new_value,
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
+ virtual void StartNotifySession(const NotifySessionCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+
+ // Removes one value update session and invokes |callback| on completion. This
+ // decrements the session reference count by 1 and if the number reaches 0,
+ // makes a call to the subsystem to stop notifications from this
+ // characteristic.
+ void RemoveNotifySession(const base::Closure& callback);
// Object path of the underlying D-Bus characteristic.
const dbus::ObjectPath& object_path() const { return object_path_; }
@@ -92,6 +103,29 @@ class BluetoothRemoteGattCharacteristicChromeOS
const std::string& error_name,
const std::string& error_message);
+ // Called by dbus:: on successful completion of a request to start
+ // notifications.
+ void OnStartNotifySuccess(const NotifySessionCallback& callback);
+
+ // Called by dbus:: on unsuccessful completion of a request to start
+ // notifications.
+ void OnStartNotifyError(const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Called by dbus:: on successful completion of a request to stop
+ // notifications.
+ void OnStopNotifySuccess(const base::Closure& callback);
+
+ // Called by dbus:: on unsuccessful completion of a request to stop
+ // notifications.
+ void OnStopNotifyError(const base::Closure& callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Calls StartNotifySession for each queued request.
+ void ProcessStartNotifyQueue();
+
// Object path of the D-Bus characteristic object.
dbus::ObjectPath object_path_;
@@ -102,6 +136,18 @@ class BluetoothRemoteGattCharacteristicChromeOS
// notification.
std::vector<uint8> cached_value_;
+ // The total number of currently active value update sessions.
+ size_t num_notify_sessions_;
+
+ // Calls to StartNotifySession that are pending. This can happen during the
+ // first remote call to start notifications.
+ typedef std::pair<NotifySessionCallback, ErrorCallback>
+ PendingStartNotifyCall;
+ std::queue<PendingStartNotifyCall> pending_start_notify_calls_;
+
+ // True, if a Start or Stop notify call to bluetoothd is currently pending.
+ bool notify_call_pending_;
+
// Mapping from GATT descriptor object paths to descriptor objects owned by
// this characteristic. Since the Chrome OS implementation uses object paths
// as unique identifiers, we also use this mapping to return descriptors by
diff --git a/device/bluetooth/bluetooth_remote_gatt_service_chromeos.cc b/device/bluetooth/bluetooth_remote_gatt_service_chromeos.cc
index 4f88edf..7038270 100644
--- a/device/bluetooth/bluetooth_remote_gatt_service_chromeos.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_service_chromeos.cc
@@ -8,6 +8,7 @@
#include "base/strings/stringprintf.h"
#include "chromeos/dbus/bluetooth_gatt_service_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
+#include "device/bluetooth/bluetooth_adapter_chromeos.h"
#include "device/bluetooth/bluetooth_device_chromeos.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h"
#include "device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.h"
@@ -15,13 +16,17 @@
namespace chromeos {
BluetoothRemoteGattServiceChromeOS::BluetoothRemoteGattServiceChromeOS(
+ BluetoothAdapterChromeOS* adapter,
BluetoothDeviceChromeOS* device,
const dbus::ObjectPath& object_path)
: object_path_(object_path),
+ adapter_(adapter),
device_(device),
weak_ptr_factory_(this) {
VLOG(1) << "Creating remote GATT service with identifier: "
<< object_path.value() << ", UUID: " << GetUUID().canonical_value();
+ DCHECK(adapter_);
+
DBusThreadManager::Get()->GetBluetoothGattServiceClient()->AddObserver(this);
DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()->
AddObserver(this);
@@ -146,6 +151,11 @@ void BluetoothRemoteGattServiceChromeOS::Unregister(
error_callback.Run();
}
+scoped_refptr<device::BluetoothAdapter>
+BluetoothRemoteGattServiceChromeOS::GetAdapter() const {
+ return adapter_;
+}
+
void BluetoothRemoteGattServiceChromeOS::NotifyServiceChanged() {
FOR_EACH_OBSERVER(device::BluetoothGattService::Observer, observers_,
GattServiceChanged(this));
diff --git a/device/bluetooth/bluetooth_remote_gatt_service_chromeos.h b/device/bluetooth/bluetooth_remote_gatt_service_chromeos.h
index c9de5ce..ce481ec 100644
--- a/device/bluetooth/bluetooth_remote_gatt_service_chromeos.h
+++ b/device/bluetooth/bluetooth_remote_gatt_service_chromeos.h
@@ -9,6 +9,7 @@
#include <string>
#include <vector>
+#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "chromeos/dbus/bluetooth_gatt_characteristic_client.h"
@@ -19,12 +20,14 @@
namespace device {
+class BluetoothAdapter;
class BluetoothGattCharacteristic;
} // namespace device
namespace chromeos {
+class BluetoothAdapterChromeOS;
class BluetoothDeviceChromeOS;
class BluetoothRemoteGattCharacteristicChromeOS;
class BluetoothRemoteGattDescriptorChromeOS;
@@ -64,6 +67,9 @@ class BluetoothRemoteGattServiceChromeOS
// Object path of the underlying service.
const dbus::ObjectPath& object_path() const { return object_path_; }
+ // Returns the adapter associated with this service.
+ scoped_refptr<device::BluetoothAdapter> GetAdapter() const;
+
// Notifies its observers that the GATT service has changed. This is mainly
// used by BluetoothRemoteGattCharacteristicChromeOS instances to notify
// service observers when characteristic descriptors get added and removed.
@@ -99,7 +105,8 @@ class BluetoothRemoteGattServiceChromeOS
private:
friend class BluetoothDeviceChromeOS;
- BluetoothRemoteGattServiceChromeOS(BluetoothDeviceChromeOS* device,
+ BluetoothRemoteGattServiceChromeOS(BluetoothAdapterChromeOS* adapter,
+ BluetoothDeviceChromeOS* device,
const dbus::ObjectPath& object_path);
virtual ~BluetoothRemoteGattServiceChromeOS();
@@ -123,7 +130,12 @@ class BluetoothRemoteGattServiceChromeOS
// List of observers interested in event notifications from us.
ObserverList<device::BluetoothGattService::Observer> observers_;
- // The device this GATT service belongs to.
+ // The adapter associated with this service. It's ok to store a raw pointer
+ // here since |adapter_| indirectly owns this instance.
+ BluetoothAdapterChromeOS* adapter_;
+
+ // The device this GATT service belongs to. It's ok to store a raw pointer
+ // here since |device_| owns this instance.
BluetoothDeviceChromeOS* device_;
// Mapping from GATT characteristic object paths to characteristic objects.
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_characteristic.cc b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.cc
index 5cc4da3..9cacfbe 100644
--- a/device/bluetooth/test/mock_bluetooth_gatt_characteristic.cc
+++ b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.cc
@@ -27,6 +27,7 @@ MockBluetoothGattCharacteristic::MockBluetoothGattCharacteristic(
ON_CALL(*this, GetService()).WillByDefault(Return(service));
ON_CALL(*this, GetProperties()).WillByDefault(Return(properties));
ON_CALL(*this, GetPermissions()).WillByDefault(Return(permissions));
+ ON_CALL(*this, IsNotifying()).WillByDefault(Return(false));
ON_CALL(*this, GetDescriptors())
.WillByDefault(Return(std::vector<BluetoothGattDescriptor*>()));
}
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h
index 85323f4..8789b82 100644
--- a/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h
+++ b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h
@@ -37,11 +37,14 @@ class MockBluetoothGattCharacteristic : public BluetoothGattCharacteristic {
MOCK_CONST_METHOD0(GetService, BluetoothGattService*());
MOCK_CONST_METHOD0(GetProperties, Properties());
MOCK_CONST_METHOD0(GetPermissions, Permissions());
+ MOCK_CONST_METHOD0(IsNotifying, bool());
MOCK_CONST_METHOD0(GetDescriptors, std::vector<BluetoothGattDescriptor*>());
MOCK_CONST_METHOD1(GetDescriptor,
BluetoothGattDescriptor*(const std::string&));
MOCK_METHOD1(AddDescriptor, bool(BluetoothGattDescriptor*));
MOCK_METHOD1(UpdateValue, bool(const std::vector<uint8>&));
+ MOCK_METHOD2(StartNotifySession,
+ void(const NotifySessionCallback&, const ErrorCallback&));
MOCK_METHOD2(ReadRemoteCharacteristic,
void(const ValueCallback&, const ErrorCallback&));
MOCK_METHOD3(WriteRemoteCharacteristic,
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_notify_session.cc b/device/bluetooth/test/mock_bluetooth_gatt_notify_session.cc
new file mode 100644
index 0000000..a4e9843
--- /dev/null
+++ b/device/bluetooth/test/mock_bluetooth_gatt_notify_session.cc
@@ -0,0 +1,21 @@
+// 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/test/mock_bluetooth_gatt_notify_session.h"
+
+using testing::Return;
+
+namespace device {
+
+MockBluetoothGattNotifySession::MockBluetoothGattNotifySession(
+ const std::string& characteristic_identifier) {
+ ON_CALL(*this, GetCharacteristicIdentifier())
+ .WillByDefault(Return(characteristic_identifier));
+ ON_CALL(*this, IsActive()).WillByDefault(Return(true));
+}
+
+MockBluetoothGattNotifySession::~MockBluetoothGattNotifySession() {
+}
+
+} // namespace device
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_notify_session.h b/device/bluetooth/test/mock_bluetooth_gatt_notify_session.h
new file mode 100644
index 0000000..97c28e7b
--- /dev/null
+++ b/device/bluetooth/test/mock_bluetooth_gatt_notify_session.h
@@ -0,0 +1,32 @@
+// 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_TEST_MOCK_BLUETOOTH_GATT_NOTIFY_SESSION_H_
+#define DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_GATT_NOTIFY_SESSION_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "device/bluetooth/bluetooth_gatt_notify_session.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace device {
+
+class MockBluetoothGattNotifySession : public BluetoothGattNotifySession {
+ public:
+ explicit MockBluetoothGattNotifySession(
+ const std::string& characteristic_identifier);
+ virtual ~MockBluetoothGattNotifySession();
+
+ MOCK_CONST_METHOD0(GetCharacteristicIdentifier, std::string());
+ MOCK_METHOD0(IsActive, bool());
+ MOCK_METHOD1(Stop, void(const base::Closure&));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockBluetoothGattNotifySession);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_GATT_NOTIFY_SESSION_H_