普通文本  |  1347行  |  53.79 KB

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "chromeos/dbus/fake_bluetooth_adapter_client.h"
#include "chromeos/dbus/fake_bluetooth_agent_manager_client.h"
#include "chromeos/dbus/fake_bluetooth_device_client.h"
#include "chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h"
#include "chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h"
#include "chromeos/dbus/fake_bluetooth_gatt_service_client.h"
#include "chromeos/dbus/fake_bluetooth_input_client.h"
#include "chromeos/dbus/fake_dbus_thread_manager.h"
#include "dbus/object_path.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_gatt_connection.h"
#include "device/bluetooth/bluetooth_gatt_descriptor.h"
#include "device/bluetooth/bluetooth_gatt_notify_session.h"
#include "device/bluetooth/bluetooth_gatt_service.h"
#include "device/bluetooth/bluetooth_uuid.h"
#include "testing/gtest/include/gtest/gtest.h"

using device::BluetoothAdapter;
using device::BluetoothDevice;
using device::BluetoothGattCharacteristic;
using device::BluetoothGattConnection;
using device::BluetoothGattDescriptor;
using device::BluetoothGattService;
using device::BluetoothGattNotifySession;
using device::BluetoothUUID;

namespace chromeos {

namespace {

const BluetoothUUID kHeartRateMeasurementUUID(
    FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID);
const BluetoothUUID kBodySensorLocationUUID(
    FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID);
const BluetoothUUID kHeartRateControlPointUUID(
    FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID);

// Compares GATT characteristic/descriptor values. Returns true, if the values
// are equal.
bool ValuesEqual(const std::vector<uint8>& value0,
                 const std::vector<uint8>& value1) {
  if (value0.size() != value1.size())
    return false;
  for (size_t i = 0; i < value0.size(); ++i)
    if (value0[i] != value1[i])
      return false;
  return true;
}

class TestDeviceObserver : public BluetoothDevice::Observer {
 public:
  TestDeviceObserver(scoped_refptr<BluetoothAdapter> adapter,
                     BluetoothDevice* device)
      : gatt_service_added_count_(0),
        gatt_service_removed_count_(0),
        device_address_(device->GetAddress()),
        adapter_(adapter) {
    device->AddObserver(this);
  }

  virtual ~TestDeviceObserver() {
    BluetoothDevice* device = adapter_->GetDevice(device_address_);
    if (device)
      device->RemoveObserver(this);
  }

  // BluetoothDevice::Observer overrides.
  virtual void GattServiceAdded(
      BluetoothDevice* device,
      BluetoothGattService* service) OVERRIDE {
    ASSERT_EQ(device_address_, device->GetAddress());

    ++gatt_service_added_count_;
    last_gatt_service_id_ = service->GetIdentifier();
    last_gatt_service_uuid_ = service->GetUUID();

    EXPECT_FALSE(service->IsLocal());
    EXPECT_TRUE(service->IsPrimary());

    EXPECT_EQ(device->GetGattService(last_gatt_service_id_), service);

    QuitMessageLoop();
  }

  virtual void GattServiceRemoved(
      BluetoothDevice* device,
      BluetoothGattService* service) OVERRIDE {
    ASSERT_EQ(device_address_, device->GetAddress());

    ++gatt_service_removed_count_;
    last_gatt_service_id_ = service->GetIdentifier();
    last_gatt_service_uuid_ = service->GetUUID();

    EXPECT_FALSE(service->IsLocal());
    EXPECT_TRUE(service->IsPrimary());

    // The device should return NULL for this service.
    EXPECT_FALSE(device->GetGattService(last_gatt_service_id_));

    QuitMessageLoop();
  }

  int gatt_service_added_count_;
  int gatt_service_removed_count_;
  std::string last_gatt_service_id_;
  BluetoothUUID last_gatt_service_uuid_;

 private:
  // Some tests use a message loop since background processing is simulated;
  // break out of those loops.
  void QuitMessageLoop() {
    if (base::MessageLoop::current() &&
        base::MessageLoop::current()->is_running())
      base::MessageLoop::current()->Quit();
  }

  std::string device_address_;
  scoped_refptr<BluetoothAdapter> adapter_;
};

class TestGattServiceObserver : public BluetoothGattService::Observer {
 public:
  TestGattServiceObserver(scoped_refptr<BluetoothAdapter> adapter,
                          BluetoothDevice* device,
                          BluetoothGattService* service)
      : gatt_service_changed_count_(0),
        gatt_characteristic_added_count_(0),
        gatt_characteristic_removed_count_(0),
        gatt_characteristic_value_changed_count_(0),
        gatt_descriptor_added_count_(0),
        gatt_descriptor_removed_count_(0),
        gatt_descriptor_value_changed_count_(0),
        device_address_(device->GetAddress()),
        gatt_service_id_(service->GetIdentifier()),
        adapter_(adapter) {
    service->AddObserver(this);
  }

  virtual ~TestGattServiceObserver() {
    // See if either the device or the service even exist.
    BluetoothDevice* device = adapter_->GetDevice(device_address_);
    if (!device)
      return;

    BluetoothGattService* service = device->GetGattService(gatt_service_id_);
    if (!service)
      return;

    service->RemoveObserver(this);
  }

  // BluetoothGattService::Observer overrides.
  virtual void GattServiceChanged(BluetoothGattService* service) OVERRIDE {
    ASSERT_EQ(gatt_service_id_, service->GetIdentifier());
    ++gatt_service_changed_count_;

    QuitMessageLoop();
  }

  virtual void GattCharacteristicAdded(
      BluetoothGattService* service,
      BluetoothGattCharacteristic* characteristic) OVERRIDE {
    ASSERT_EQ(gatt_service_id_, service->GetIdentifier());

    ++gatt_characteristic_added_count_;
    last_gatt_characteristic_id_ = characteristic->GetIdentifier();
    last_gatt_characteristic_uuid_ = characteristic->GetUUID();

    EXPECT_EQ(service->GetCharacteristic(last_gatt_characteristic_id_),
              characteristic);
    EXPECT_EQ(service, characteristic->GetService());

    QuitMessageLoop();
  }

  virtual void GattCharacteristicRemoved(
      BluetoothGattService* service,
      BluetoothGattCharacteristic* characteristic) OVERRIDE {
    ASSERT_EQ(gatt_service_id_, service->GetIdentifier());

    ++gatt_characteristic_removed_count_;
    last_gatt_characteristic_id_ = characteristic->GetIdentifier();
    last_gatt_characteristic_uuid_ = characteristic->GetUUID();

    // The service should return NULL for this characteristic.
    EXPECT_FALSE(service->GetCharacteristic(last_gatt_characteristic_id_));
    EXPECT_EQ(service, characteristic->GetService());

    QuitMessageLoop();
  }

  virtual void GattCharacteristicValueChanged(
      BluetoothGattService* service,
      BluetoothGattCharacteristic* characteristic,
      const std::vector<uint8>& value) OVERRIDE {
    ASSERT_EQ(gatt_service_id_, service->GetIdentifier());

    ++gatt_characteristic_value_changed_count_;
    last_gatt_characteristic_id_ = characteristic->GetIdentifier();
    last_gatt_characteristic_uuid_ = characteristic->GetUUID();
    last_changed_characteristic_value_ = value;

    EXPECT_EQ(service->GetCharacteristic(last_gatt_characteristic_id_),
              characteristic);
    EXPECT_EQ(service, characteristic->GetService());

    QuitMessageLoop();
  }

  virtual void GattDescriptorAdded(
      BluetoothGattCharacteristic* characteristic,
      BluetoothGattDescriptor* descriptor) OVERRIDE {
    ASSERT_EQ(gatt_service_id_, characteristic->GetService()->GetIdentifier());

    ++gatt_descriptor_added_count_;
    last_gatt_descriptor_id_ = descriptor->GetIdentifier();
    last_gatt_descriptor_uuid_ = descriptor->GetUUID();

    EXPECT_EQ(characteristic->GetDescriptor(last_gatt_descriptor_id_),
              descriptor);
    EXPECT_EQ(characteristic, descriptor->GetCharacteristic());

    QuitMessageLoop();
  }

  virtual void GattDescriptorRemoved(
      BluetoothGattCharacteristic* characteristic,
      BluetoothGattDescriptor* descriptor) OVERRIDE {
    ASSERT_EQ(gatt_service_id_, characteristic->GetService()->GetIdentifier());

    ++gatt_descriptor_removed_count_;
    last_gatt_descriptor_id_ = descriptor->GetIdentifier();
    last_gatt_descriptor_uuid_ = descriptor->GetUUID();

    // The characteristic should return NULL for this descriptor..
    EXPECT_FALSE(characteristic->GetDescriptor(last_gatt_descriptor_id_));
    EXPECT_EQ(characteristic, descriptor->GetCharacteristic());

    QuitMessageLoop();
  }

  virtual void GattDescriptorValueChanged(
      BluetoothGattCharacteristic* characteristic,
      BluetoothGattDescriptor* descriptor,
      const std::vector<uint8>& value) OVERRIDE {
    ASSERT_EQ(gatt_service_id_, characteristic->GetService()->GetIdentifier());

    ++gatt_descriptor_value_changed_count_;
    last_gatt_descriptor_id_ = descriptor->GetIdentifier();
    last_gatt_descriptor_uuid_ = descriptor->GetUUID();
    last_changed_descriptor_value_ = value;

    EXPECT_EQ(characteristic->GetDescriptor(last_gatt_descriptor_id_),
              descriptor);
    EXPECT_EQ(characteristic, descriptor->GetCharacteristic());

    QuitMessageLoop();
  }

  int gatt_service_changed_count_;
  int gatt_characteristic_added_count_;
  int gatt_characteristic_removed_count_;
  int gatt_characteristic_value_changed_count_;
  int gatt_descriptor_added_count_;
  int gatt_descriptor_removed_count_;
  int gatt_descriptor_value_changed_count_;
  std::string last_gatt_characteristic_id_;
  BluetoothUUID last_gatt_characteristic_uuid_;
  std::vector<uint8> last_changed_characteristic_value_;
  std::string last_gatt_descriptor_id_;
  BluetoothUUID last_gatt_descriptor_uuid_;
  std::vector<uint8> last_changed_descriptor_value_;

 private:
  // Some tests use a message loop since background processing is simulated;
  // break out of those loops.
  void QuitMessageLoop() {
    if (base::MessageLoop::current() &&
        base::MessageLoop::current()->is_running())
      base::MessageLoop::current()->Quit();
  }

  std::string device_address_;
  std::string gatt_service_id_;
  scoped_refptr<BluetoothAdapter> adapter_;
};

}  // namespace

class BluetoothGattChromeOSTest : public testing::Test {
 public:
  BluetoothGattChromeOSTest()
      : fake_bluetooth_gatt_service_client_(NULL),
        success_callback_count_(0),
        error_callback_count_(0) {
  }

  virtual void SetUp() {
    FakeDBusThreadManager* fake_dbus_thread_manager = new FakeDBusThreadManager;
    fake_bluetooth_device_client_ = new FakeBluetoothDeviceClient;
    fake_bluetooth_gatt_service_client_ =
        new FakeBluetoothGattServiceClient;
    fake_bluetooth_gatt_characteristic_client_ =
        new FakeBluetoothGattCharacteristicClient;
    fake_bluetooth_gatt_descriptor_client_ =
        new FakeBluetoothGattDescriptorClient;
    fake_dbus_thread_manager->SetBluetoothDeviceClient(
        scoped_ptr<BluetoothDeviceClient>(
            fake_bluetooth_device_client_));
    fake_dbus_thread_manager->SetBluetoothGattServiceClient(
        scoped_ptr<BluetoothGattServiceClient>(
            fake_bluetooth_gatt_service_client_));
    fake_dbus_thread_manager->SetBluetoothGattCharacteristicClient(
        scoped_ptr<BluetoothGattCharacteristicClient>(
            fake_bluetooth_gatt_characteristic_client_));
    fake_dbus_thread_manager->SetBluetoothGattDescriptorClient(
        scoped_ptr<BluetoothGattDescriptorClient>(
            fake_bluetooth_gatt_descriptor_client_));
    fake_dbus_thread_manager->SetBluetoothAdapterClient(
        scoped_ptr<BluetoothAdapterClient>(new FakeBluetoothAdapterClient));
    fake_dbus_thread_manager->SetBluetoothInputClient(
        scoped_ptr<BluetoothInputClient>(new FakeBluetoothInputClient));
    fake_dbus_thread_manager->SetBluetoothAgentManagerClient(
        scoped_ptr<BluetoothAgentManagerClient>(
            new FakeBluetoothAgentManagerClient));
    DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager);

    GetAdapter();

    adapter_->SetPowered(
        true,
        base::Bind(&base::DoNothing),
        base::Bind(&base::DoNothing));
    ASSERT_TRUE(adapter_->IsPowered());
  }

  virtual void TearDown() {
    adapter_ = NULL;
    update_sessions_.clear();
    gatt_conn_.reset();
    DBusThreadManager::Shutdown();
  }

  void GetAdapter() {
    device::BluetoothAdapterFactory::GetAdapter(
        base::Bind(&BluetoothGattChromeOSTest::AdapterCallback,
                   base::Unretained(this)));
    ASSERT_TRUE(adapter_.get() != NULL);
    ASSERT_TRUE(adapter_->IsInitialized());
    ASSERT_TRUE(adapter_->IsPresent());
  }

  void AdapterCallback(scoped_refptr<BluetoothAdapter> adapter) {
    adapter_ = adapter;
  }

  void SuccessCallback() {
    ++success_callback_count_;
  }

  void ValueCallback(const std::vector<uint8>& value) {
    ++success_callback_count_;
    last_read_value_ = value;
  }

  void GattConnectionCallback(scoped_ptr<BluetoothGattConnection> conn) {
    ++success_callback_count_;
    gatt_conn_ = conn.Pass();
  }

  void NotifySessionCallback(scoped_ptr<BluetoothGattNotifySession> session) {
    ++success_callback_count_;
    update_sessions_.push_back(session.release());
    QuitMessageLoop();
  }

  void ErrorCallback() {
    ++error_callback_count_;
  }

  void DBusErrorCallback(const std::string& error_name,
                         const std::string& error_message) {
    ++error_callback_count_;
  }

  void ConnectErrorCallback(BluetoothDevice::ConnectErrorCode error) {
    ++error_callback_count_;
  }

 protected:
  void QuitMessageLoop() {
    if (base::MessageLoop::current() &&
        base::MessageLoop::current()->is_running())
      base::MessageLoop::current()->Quit();
  }

  base::MessageLoop message_loop_;

  FakeBluetoothDeviceClient* fake_bluetooth_device_client_;
  FakeBluetoothGattServiceClient* fake_bluetooth_gatt_service_client_;
  FakeBluetoothGattCharacteristicClient*
      fake_bluetooth_gatt_characteristic_client_;
  FakeBluetoothGattDescriptorClient* fake_bluetooth_gatt_descriptor_client_;
  scoped_ptr<device::BluetoothGattConnection> gatt_conn_;
  ScopedVector<BluetoothGattNotifySession> update_sessions_;
  scoped_refptr<BluetoothAdapter> adapter_;

  int success_callback_count_;
  int error_callback_count_;
  std::vector<uint8> last_read_value_;
};

TEST_F(BluetoothGattChromeOSTest, GattConnection) {
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kLowEnergyAddress);
  ASSERT_TRUE(device);
  ASSERT_FALSE(device->IsConnected());
  ASSERT_FALSE(gatt_conn_.get());
  ASSERT_EQ(0, success_callback_count_);
  ASSERT_EQ(0, error_callback_count_);

  device->CreateGattConnection(
      base::Bind(&BluetoothGattChromeOSTest::GattConnectionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(device->IsConnected());
  ASSERT_TRUE(gatt_conn_.get());
  EXPECT_TRUE(gatt_conn_->IsConnected());
  EXPECT_EQ(FakeBluetoothDeviceClient::kLowEnergyAddress,
            gatt_conn_->GetDeviceAddress());

  gatt_conn_->Disconnect(
      base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
                 base::Unretained(this)));
  EXPECT_EQ(2, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(device->IsConnected());
  EXPECT_FALSE(gatt_conn_->IsConnected());

  device->CreateGattConnection(
      base::Bind(&BluetoothGattChromeOSTest::GattConnectionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(3, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(device->IsConnected());
  ASSERT_TRUE(gatt_conn_.get());
  EXPECT_TRUE(gatt_conn_->IsConnected());

  device->Disconnect(
      base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(4, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  ASSERT_TRUE(gatt_conn_.get());
  EXPECT_FALSE(gatt_conn_->IsConnected());

  device->CreateGattConnection(
      base::Bind(&BluetoothGattChromeOSTest::GattConnectionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(5, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(device->IsConnected());
  EXPECT_TRUE(gatt_conn_->IsConnected());

  fake_bluetooth_device_client_->RemoveDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  ASSERT_TRUE(gatt_conn_.get());
  EXPECT_FALSE(gatt_conn_->IsConnected());
}

TEST_F(BluetoothGattChromeOSTest, GattServiceAddedAndRemoved) {
  // Create a fake LE device. We store the device pointer here because this is a
  // test. It's unsafe to do this in production as the device might get deleted.
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kLowEnergyAddress);
  ASSERT_TRUE(device);

  TestDeviceObserver observer(adapter_, device);
  EXPECT_EQ(0, observer.gatt_service_added_count_);
  EXPECT_EQ(0, observer.gatt_service_removed_count_);
  EXPECT_TRUE(observer.last_gatt_service_id_.empty());
  EXPECT_FALSE(observer.last_gatt_service_uuid_.IsValid());
  EXPECT_TRUE(device->GetGattServices().empty());

  // Expose the fake Heart Rate Service.
  fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  EXPECT_EQ(1, observer.gatt_service_added_count_);
  EXPECT_EQ(0, observer.gatt_service_removed_count_);
  EXPECT_FALSE(observer.last_gatt_service_id_.empty());
  EXPECT_EQ(1U, device->GetGattServices().size());
  EXPECT_EQ(
      BluetoothUUID(FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
      observer.last_gatt_service_uuid_);

  BluetoothGattService* service =
      device->GetGattService(observer.last_gatt_service_id_);
  EXPECT_FALSE(service->IsLocal());
  EXPECT_TRUE(service->IsPrimary());
  EXPECT_EQ(service, device->GetGattServices()[0]);
  EXPECT_EQ(service, device->GetGattService(service->GetIdentifier()));

  EXPECT_EQ(observer.last_gatt_service_uuid_, service->GetUUID());

  // Hide the service.
  observer.last_gatt_service_uuid_ = BluetoothUUID();
  observer.last_gatt_service_id_.clear();
  fake_bluetooth_gatt_service_client_->HideHeartRateService();

  EXPECT_EQ(1, observer.gatt_service_added_count_);
  EXPECT_EQ(1, observer.gatt_service_removed_count_);
  EXPECT_FALSE(observer.last_gatt_service_id_.empty());
  EXPECT_TRUE(device->GetGattServices().empty());
  EXPECT_EQ(
      BluetoothUUID(FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
      observer.last_gatt_service_uuid_);

  EXPECT_EQ(NULL, device->GetGattService(observer.last_gatt_service_id_));

  // Expose the service again.
  observer.last_gatt_service_uuid_ = BluetoothUUID();
  observer.last_gatt_service_id_.clear();
  fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  EXPECT_EQ(2, observer.gatt_service_added_count_);
  EXPECT_EQ(1, observer.gatt_service_removed_count_);
  EXPECT_FALSE(observer.last_gatt_service_id_.empty());
  EXPECT_EQ(1U, device->GetGattServices().size());
  EXPECT_EQ(
      BluetoothUUID(FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
      observer.last_gatt_service_uuid_);

  // The object |service| points to should have been deallocated. |device|
  // should contain a brand new instance.
  service = device->GetGattService(observer.last_gatt_service_id_);
  EXPECT_EQ(service, device->GetGattServices()[0]);
  EXPECT_FALSE(service->IsLocal());
  EXPECT_TRUE(service->IsPrimary());

  EXPECT_EQ(observer.last_gatt_service_uuid_, service->GetUUID());

  // Remove the device. The observer should be notified of the removed service.
  // |device| becomes invalid after this.
  observer.last_gatt_service_uuid_ = BluetoothUUID();
  observer.last_gatt_service_id_.clear();
  fake_bluetooth_device_client_->RemoveDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));

  EXPECT_EQ(2, observer.gatt_service_added_count_);
  EXPECT_EQ(2, observer.gatt_service_removed_count_);
  EXPECT_FALSE(observer.last_gatt_service_id_.empty());
  EXPECT_EQ(
      BluetoothUUID(FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
      observer.last_gatt_service_uuid_);
  EXPECT_EQ(
      NULL, adapter_->GetDevice(FakeBluetoothDeviceClient::kLowEnergyAddress));
}

TEST_F(BluetoothGattChromeOSTest, GattCharacteristicAddedAndRemoved) {
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kLowEnergyAddress);
  ASSERT_TRUE(device);

  TestDeviceObserver observer(adapter_, device);

  // Expose the fake Heart Rate service. This will asynchronously expose
  // characteristics.
  fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  ASSERT_EQ(1, observer.gatt_service_added_count_);

  BluetoothGattService* service =
      device->GetGattService(observer.last_gatt_service_id_);

  TestGattServiceObserver service_observer(adapter_, device, service);
  EXPECT_EQ(0, service_observer.gatt_service_changed_count_);
  EXPECT_EQ(0, service_observer.gatt_characteristic_added_count_);
  EXPECT_EQ(0, service_observer.gatt_characteristic_removed_count_);
  EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_TRUE(service->GetCharacteristics().empty());

  // Run the message loop so that the characteristics appear.
  base::MessageLoop::current()->Run();

  // 3 characteristics should appear. Only 1 of the characteristics sends
  // value changed signals. Service changed should be fired once for
  // descriptor added.
  EXPECT_EQ(4, service_observer.gatt_service_changed_count_);
  EXPECT_EQ(3, service_observer.gatt_characteristic_added_count_);
  EXPECT_EQ(0, service_observer.gatt_characteristic_removed_count_);
  EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_EQ(3U, service->GetCharacteristics().size());

  // Hide the characteristics. 3 removed signals should be received.
  fake_bluetooth_gatt_characteristic_client_->HideHeartRateCharacteristics();
  EXPECT_EQ(8, service_observer.gatt_service_changed_count_);
  EXPECT_EQ(3, service_observer.gatt_characteristic_added_count_);
  EXPECT_EQ(3, service_observer.gatt_characteristic_removed_count_);
  EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_TRUE(service->GetCharacteristics().empty());

  // Re-expose the heart rate characteristics.
  fake_bluetooth_gatt_characteristic_client_->ExposeHeartRateCharacteristics(
      fake_bluetooth_gatt_service_client_->GetHeartRateServicePath());
  EXPECT_EQ(12, service_observer.gatt_service_changed_count_);
  EXPECT_EQ(6, service_observer.gatt_characteristic_added_count_);
  EXPECT_EQ(3, service_observer.gatt_characteristic_removed_count_);
  EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_EQ(3U, service->GetCharacteristics().size());

  // Hide the service. All characteristics should disappear.
  fake_bluetooth_gatt_service_client_->HideHeartRateService();
  EXPECT_EQ(16, service_observer.gatt_service_changed_count_);
  EXPECT_EQ(6, service_observer.gatt_characteristic_added_count_);
  EXPECT_EQ(6, service_observer.gatt_characteristic_removed_count_);
  EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);
}

TEST_F(BluetoothGattChromeOSTest, GattDescriptorAddedAndRemoved) {
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kLowEnergyAddress);
  ASSERT_TRUE(device);

  TestDeviceObserver observer(adapter_, device);

  // Expose the fake Heart Rate service. This will asynchronously expose
  // characteristics.
  fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  ASSERT_EQ(1, observer.gatt_service_added_count_);

  BluetoothGattService* service =
      device->GetGattService(observer.last_gatt_service_id_);

  TestGattServiceObserver service_observer(adapter_, device, service);
  EXPECT_EQ(0, service_observer.gatt_service_changed_count_);
  EXPECT_EQ(0, service_observer.gatt_descriptor_added_count_);
  EXPECT_EQ(0, service_observer.gatt_descriptor_removed_count_);
  EXPECT_EQ(0, service_observer.gatt_descriptor_value_changed_count_);

  EXPECT_TRUE(service->GetCharacteristics().empty());

  // Run the message loop so that the characteristics appear.
  base::MessageLoop::current()->Run();
  EXPECT_EQ(4, service_observer.gatt_service_changed_count_);

  // Only the Heart Rate Measurement characteristic has a descriptor.
  EXPECT_EQ(1, service_observer.gatt_descriptor_added_count_);
  EXPECT_EQ(0, service_observer.gatt_descriptor_removed_count_);
  EXPECT_EQ(0, service_observer.gatt_descriptor_value_changed_count_);

  BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->
          GetBodySensorLocationPath().value());
  ASSERT_TRUE(characteristic);
  EXPECT_TRUE(characteristic->GetDescriptors().empty());

  characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->
          GetHeartRateControlPointPath().value());
  ASSERT_TRUE(characteristic);
  EXPECT_TRUE(characteristic->GetDescriptors().empty());

  characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->
          GetHeartRateMeasurementPath().value());
  ASSERT_TRUE(characteristic);
  EXPECT_EQ(1U, characteristic->GetDescriptors().size());

  BluetoothGattDescriptor* descriptor = characteristic->GetDescriptors()[0];
  EXPECT_FALSE(descriptor->IsLocal());
  EXPECT_EQ(BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid(),
            descriptor->GetUUID());
  EXPECT_EQ(descriptor->GetUUID(),
            service_observer.last_gatt_descriptor_uuid_);
  EXPECT_EQ(descriptor->GetIdentifier(),
            service_observer.last_gatt_descriptor_id_);

  // Hide the descriptor.
  fake_bluetooth_gatt_descriptor_client_->HideDescriptor(
      dbus::ObjectPath(descriptor->GetIdentifier()));
  EXPECT_TRUE(characteristic->GetDescriptors().empty());
  EXPECT_EQ(5, service_observer.gatt_service_changed_count_);
  EXPECT_EQ(1, service_observer.gatt_descriptor_added_count_);
  EXPECT_EQ(1, service_observer.gatt_descriptor_removed_count_);
  EXPECT_EQ(0, service_observer.gatt_descriptor_value_changed_count_);

  // Expose the descriptor again.
  service_observer.last_gatt_descriptor_id_.clear();
  service_observer.last_gatt_descriptor_uuid_ = BluetoothUUID();
  fake_bluetooth_gatt_descriptor_client_->ExposeDescriptor(
      dbus::ObjectPath(characteristic->GetIdentifier()),
      FakeBluetoothGattDescriptorClient::
          kClientCharacteristicConfigurationUUID);
  EXPECT_EQ(6, service_observer.gatt_service_changed_count_);
  EXPECT_EQ(1U, characteristic->GetDescriptors().size());
  EXPECT_EQ(2, service_observer.gatt_descriptor_added_count_);
  EXPECT_EQ(1, service_observer.gatt_descriptor_removed_count_);
  EXPECT_EQ(0, service_observer.gatt_descriptor_value_changed_count_);

  descriptor = characteristic->GetDescriptors()[0];
  EXPECT_FALSE(descriptor->IsLocal());
  EXPECT_EQ(BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid(),
            descriptor->GetUUID());
  EXPECT_EQ(descriptor->GetUUID(), service_observer.last_gatt_descriptor_uuid_);
  EXPECT_EQ(descriptor->GetIdentifier(),
            service_observer.last_gatt_descriptor_id_);
}

TEST_F(BluetoothGattChromeOSTest, AdapterAddedAfterGattService) {
  // This unit test tests that all remote GATT objects are created for D-Bus
  // objects that were already exposed.
  adapter_ = NULL;
  ASSERT_FALSE(device::BluetoothAdapterFactory::HasSharedInstanceForTesting());

  // Create the fake D-Bus objects.
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  while (!fake_bluetooth_gatt_characteristic_client_->IsHeartRateVisible())
    base::RunLoop().RunUntilIdle();
  ASSERT_TRUE(fake_bluetooth_gatt_service_client_->IsHeartRateVisible());
  ASSERT_TRUE(fake_bluetooth_gatt_characteristic_client_->IsHeartRateVisible());

  // Create the adapter. This should create all the GATT objects.
  GetAdapter();
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kLowEnergyAddress);
  ASSERT_TRUE(device);
  EXPECT_EQ(1U, device->GetGattServices().size());

  BluetoothGattService* service = device->GetGattServices()[0];
  ASSERT_TRUE(service);
  EXPECT_FALSE(service->IsLocal());
  EXPECT_TRUE(service->IsPrimary());
  EXPECT_EQ(
      BluetoothUUID(FakeBluetoothGattServiceClient::kHeartRateServiceUUID),
      service->GetUUID());
  EXPECT_EQ(service, device->GetGattServices()[0]);
  EXPECT_EQ(service, device->GetGattService(service->GetIdentifier()));
  EXPECT_FALSE(service->IsLocal());
  EXPECT_EQ(3U, service->GetCharacteristics().size());

  BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->
          GetBodySensorLocationPath().value());
  ASSERT_TRUE(characteristic);
  EXPECT_EQ(
      BluetoothUUID(FakeBluetoothGattCharacteristicClient::
          kBodySensorLocationUUID),
      characteristic->GetUUID());
  EXPECT_FALSE(characteristic->IsLocal());
  EXPECT_TRUE(characteristic->GetDescriptors().empty());

  characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->
          GetHeartRateControlPointPath().value());
  ASSERT_TRUE(characteristic);
  EXPECT_EQ(
      BluetoothUUID(FakeBluetoothGattCharacteristicClient::
          kHeartRateControlPointUUID),
      characteristic->GetUUID());
  EXPECT_FALSE(characteristic->IsLocal());
  EXPECT_TRUE(characteristic->GetDescriptors().empty());

  characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->
          GetHeartRateMeasurementPath().value());
  ASSERT_TRUE(characteristic);
  EXPECT_EQ(
      BluetoothUUID(FakeBluetoothGattCharacteristicClient::
          kHeartRateMeasurementUUID),
      characteristic->GetUUID());
  EXPECT_FALSE(characteristic->IsLocal());
  EXPECT_EQ(1U, characteristic->GetDescriptors().size());

  BluetoothGattDescriptor* descriptor = characteristic->GetDescriptors()[0];
  ASSERT_TRUE(descriptor);
  EXPECT_EQ(BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid(),
            descriptor->GetUUID());
  EXPECT_FALSE(descriptor->IsLocal());
}

TEST_F(BluetoothGattChromeOSTest, GattCharacteristicValue) {
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kLowEnergyAddress);
  ASSERT_TRUE(device);

  TestDeviceObserver observer(adapter_, device);

  // Expose the fake Heart Rate service. This will asynchronously expose
  // characteristics.
  fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  ASSERT_EQ(1, observer.gatt_service_added_count_);

  BluetoothGattService* service =
      device->GetGattService(observer.last_gatt_service_id_);

  TestGattServiceObserver service_observer(adapter_, device, service);
  EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);

  // Run the message loop so that the characteristics appear.
  base::MessageLoop::current()->Run();

  // Issue write request to non-writeable characteristics.
  service_observer.last_gatt_characteristic_id_.clear();
  service_observer.last_gatt_characteristic_uuid_ = BluetoothUUID();

  std::vector<uint8> write_value;
  write_value.push_back(0x01);
  BluetoothGattCharacteristic* characteristic =
      service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_->
          GetHeartRateMeasurementPath().value());
  ASSERT_TRUE(characteristic);
  EXPECT_FALSE(characteristic->IsNotifying());
  EXPECT_EQ(fake_bluetooth_gatt_characteristic_client_->
                GetHeartRateMeasurementPath().value(),
            characteristic->GetIdentifier());
  EXPECT_EQ(kHeartRateMeasurementUUID, characteristic->GetUUID());
  characteristic->WriteRemoteCharacteristic(
      write_value,
      base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_TRUE(service_observer.last_gatt_characteristic_id_.empty());
  EXPECT_FALSE(service_observer.last_gatt_characteristic_uuid_.IsValid());
  EXPECT_EQ(0, success_callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);

  characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->
          GetBodySensorLocationPath().value());
  ASSERT_TRUE(characteristic);
  EXPECT_EQ(fake_bluetooth_gatt_characteristic_client_->
                GetBodySensorLocationPath().value(),
            characteristic->GetIdentifier());
  EXPECT_EQ(kBodySensorLocationUUID, characteristic->GetUUID());
  characteristic->WriteRemoteCharacteristic(
      write_value,
      base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_TRUE(service_observer.last_gatt_characteristic_id_.empty());
  EXPECT_FALSE(service_observer.last_gatt_characteristic_uuid_.IsValid());
  EXPECT_EQ(0, success_callback_count_);
  EXPECT_EQ(2, error_callback_count_);
  EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);

  // Issue write request to writeable characteristic. The "Body Sensor Location"
  // characteristic does not send notifications and WriteValue does not result
  // in a CharacteristicValueChanged event, thus no such event should be
  // received.
  characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->
          GetHeartRateControlPointPath().value());
  ASSERT_TRUE(characteristic);
  EXPECT_EQ(fake_bluetooth_gatt_characteristic_client_->
                GetHeartRateControlPointPath().value(),
            characteristic->GetIdentifier());
  EXPECT_EQ(kHeartRateControlPointUUID, characteristic->GetUUID());
  characteristic->WriteRemoteCharacteristic(
      write_value,
      base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_TRUE(service_observer.last_gatt_characteristic_id_.empty());
  EXPECT_FALSE(service_observer.last_gatt_characteristic_uuid_.IsValid());
  EXPECT_EQ(1, success_callback_count_);
  EXPECT_EQ(2, error_callback_count_);
  EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);

  // Issue a read request. A successful read results in a
  // CharacteristicValueChanged notification.
  characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->
          GetBodySensorLocationPath().value());
  ASSERT_TRUE(characteristic);
  EXPECT_EQ(fake_bluetooth_gatt_characteristic_client_->
                GetBodySensorLocationPath().value(),
            characteristic->GetIdentifier());
  EXPECT_EQ(kBodySensorLocationUUID, characteristic->GetUUID());
  characteristic->ReadRemoteCharacteristic(
      base::Bind(&BluetoothGattChromeOSTest::ValueCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(2, success_callback_count_);
  EXPECT_EQ(2, error_callback_count_);
  EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_TRUE(ValuesEqual(characteristic->GetValue(), last_read_value_));
}

TEST_F(BluetoothGattChromeOSTest, GattCharacteristicProperties) {
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kLowEnergyAddress);
  ASSERT_TRUE(device);

  TestDeviceObserver observer(adapter_, device);

  // Expose the fake Heart Rate service. This will asynchronously expose
  // characteristics.
  fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));

  BluetoothGattService* service =
      device->GetGattService(observer.last_gatt_service_id_);

  TestGattServiceObserver service_observer(adapter_, device, service);
  EXPECT_TRUE(service->GetCharacteristics().empty());

  // Run the message loop so that the characteristics appear.
  base::MessageLoop::current()->Run();

  BluetoothGattCharacteristic *characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->
          GetBodySensorLocationPath().value());
  EXPECT_EQ(BluetoothGattCharacteristic::kPropertyRead,
            characteristic->GetProperties());

  characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->
          GetHeartRateControlPointPath().value());
  EXPECT_EQ(BluetoothGattCharacteristic::kPropertyWrite,
            characteristic->GetProperties());

  characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->
          GetHeartRateMeasurementPath().value());
  EXPECT_EQ(BluetoothGattCharacteristic::kPropertyNotify,
            characteristic->GetProperties());
}

TEST_F(BluetoothGattChromeOSTest, GattDescriptorValue) {
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kLowEnergyAddress);
  ASSERT_TRUE(device);

  TestDeviceObserver observer(adapter_, device);

  // Expose the fake Heart Rate service. This will asynchronously expose
  // characteristics.
  fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  ASSERT_EQ(1, observer.gatt_service_added_count_);

  BluetoothGattService* service =
      device->GetGattService(observer.last_gatt_service_id_);

  TestGattServiceObserver service_observer(adapter_, device, service);
  EXPECT_EQ(0, service_observer.gatt_service_changed_count_);
  EXPECT_EQ(0, service_observer.gatt_descriptor_value_changed_count_);
  EXPECT_TRUE(service->GetCharacteristics().empty());

  // Run the message loop so that the characteristics appear.
  base::MessageLoop::current()->Run();
  EXPECT_EQ(4, service_observer.gatt_service_changed_count_);

  // Only the Heart Rate Measurement characteristic has a descriptor.
  BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->
          GetHeartRateMeasurementPath().value());
  ASSERT_TRUE(characteristic);
  EXPECT_EQ(1U, characteristic->GetDescriptors().size());

  BluetoothGattDescriptor* descriptor = characteristic->GetDescriptors()[0];
  EXPECT_FALSE(descriptor->IsLocal());
  EXPECT_EQ(BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid(),
            descriptor->GetUUID());

  std::vector<uint8> desc_value;
  desc_value.push_back(1);
  desc_value.push_back(0);

  /* The cached value will be empty until the first read request */
  EXPECT_FALSE(ValuesEqual(desc_value, descriptor->GetValue()));
  EXPECT_TRUE(descriptor->GetValue().empty());

  EXPECT_EQ(0, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(last_read_value_.empty());

  // Read value. GattDescriptorValueChanged event will be sent after a
  // successful read.
  descriptor->ReadRemoteDescriptor(
      base::Bind(&BluetoothGattChromeOSTest::ValueCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(ValuesEqual(last_read_value_, descriptor->GetValue()));
  EXPECT_TRUE(ValuesEqual(desc_value, descriptor->GetValue()));
  EXPECT_EQ(4, service_observer.gatt_service_changed_count_);
  EXPECT_EQ(1, service_observer.gatt_descriptor_value_changed_count_);

  // Write value. Writes to this descriptor will fail.
  desc_value[0] = 0x03;
  descriptor->WriteRemoteDescriptor(
      desc_value,
      base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, success_callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_TRUE(ValuesEqual(last_read_value_, descriptor->GetValue()));
  EXPECT_FALSE(ValuesEqual(desc_value, descriptor->GetValue()));
  EXPECT_EQ(4, service_observer.gatt_service_changed_count_);
  EXPECT_EQ(1, service_observer.gatt_descriptor_value_changed_count_);

  // Read new value.
  descriptor->ReadRemoteDescriptor(
      base::Bind(&BluetoothGattChromeOSTest::ValueCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(2, success_callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_TRUE(ValuesEqual(last_read_value_, descriptor->GetValue()));
  EXPECT_FALSE(ValuesEqual(desc_value, descriptor->GetValue()));
  EXPECT_EQ(4, service_observer.gatt_service_changed_count_);
  EXPECT_EQ(2, service_observer.gatt_descriptor_value_changed_count_);
}

TEST_F(BluetoothGattChromeOSTest, NotifySessions) {
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  BluetoothDevice* device =
      adapter_->GetDevice(FakeBluetoothDeviceClient::kLowEnergyAddress);
  ASSERT_TRUE(device);

  TestDeviceObserver observer(adapter_, device);

  // Expose the fake Heart Rate service. This will asynchronously expose
  // characteristics.
  fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  ASSERT_EQ(1, observer.gatt_service_added_count_);

  BluetoothGattService* service =
      device->GetGattService(observer.last_gatt_service_id_);

  TestGattServiceObserver service_observer(adapter_, device, service);
  EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);

  // Run the message loop so that the characteristics appear.
  base::MessageLoop::current()->Run();

  BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath()
          .value());
  ASSERT_TRUE(characteristic);
  EXPECT_FALSE(characteristic->IsNotifying());
  EXPECT_TRUE(update_sessions_.empty());

  // Request to start notifications.
  characteristic->StartNotifySession(
      base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));

  // The operation still hasn't completed but we should have received the first
  // notification.
  EXPECT_EQ(0, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_TRUE(update_sessions_.empty());

  // Send a two more requests, which should get queued.
  characteristic->StartNotifySession(
      base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  characteristic->StartNotifySession(
      base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(0, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_TRUE(update_sessions_.empty());
  EXPECT_TRUE(characteristic->IsNotifying());

  // Run the main loop. The initial call should complete. The queued call should
  // succeed immediately.
  base::MessageLoop::current()->Run();

  EXPECT_EQ(3, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_EQ(3U, update_sessions_.size());

  // Notifications should be getting sent regularly now.
  base::MessageLoop::current()->Run();
  EXPECT_GT(service_observer.gatt_characteristic_value_changed_count_, 1);

  // Stop one of the sessions. The session should become inactive but the
  // characteristic should still be notifying.
  BluetoothGattNotifySession* session = update_sessions_[0];
  EXPECT_TRUE(session->IsActive());
  session->Stop(base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
                           base::Unretained(this)));
  EXPECT_EQ(4, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_FALSE(session->IsActive());
  EXPECT_EQ(characteristic->GetIdentifier(),
            session->GetCharacteristicIdentifier());
  EXPECT_TRUE(characteristic->IsNotifying());

  // Delete another session. Characteristic should still be notifying.
  update_sessions_.pop_back();
  EXPECT_EQ(2U, update_sessions_.size());
  EXPECT_TRUE(characteristic->IsNotifying());
  EXPECT_FALSE(update_sessions_[0]->IsActive());
  EXPECT_TRUE(update_sessions_[1]->IsActive());

  // Clear the last session.
  update_sessions_.clear();
  EXPECT_TRUE(update_sessions_.empty());
  EXPECT_FALSE(characteristic->IsNotifying());

  success_callback_count_ = 0;
  service_observer.gatt_characteristic_value_changed_count_ = 0;

  // Enable notifications again.
  characteristic->StartNotifySession(
      base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(0, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_TRUE(update_sessions_.empty());
  EXPECT_TRUE(characteristic->IsNotifying());

  // Run the message loop. Notifications should begin.
  base::MessageLoop::current()->Run();

  EXPECT_EQ(1, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_EQ(1U, update_sessions_.size());
  EXPECT_TRUE(update_sessions_[0]->IsActive());
  EXPECT_TRUE(characteristic->IsNotifying());

  // Check that notifications are happening.
  base::MessageLoop::current()->Run();
  EXPECT_GT(service_observer.gatt_characteristic_value_changed_count_, 1);

  // Request another session. This should return immediately.
  characteristic->StartNotifySession(
      base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(2, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(2U, update_sessions_.size());
  EXPECT_TRUE(update_sessions_[0]->IsActive());
  EXPECT_TRUE(update_sessions_[1]->IsActive());
  EXPECT_TRUE(characteristic->IsNotifying());

  // Hide the characteristic. The sessions should become inactive.
  fake_bluetooth_gatt_characteristic_client_->HideHeartRateCharacteristics();
  EXPECT_EQ(2U, update_sessions_.size());
  EXPECT_FALSE(update_sessions_[0]->IsActive());
  EXPECT_FALSE(update_sessions_[1]->IsActive());
}

TEST_F(BluetoothGattChromeOSTest, NotifySessionsMadeInactive) {
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  BluetoothDevice* device =
      adapter_->GetDevice(FakeBluetoothDeviceClient::kLowEnergyAddress);
  ASSERT_TRUE(device);

  TestDeviceObserver observer(adapter_, device);

  // Expose the fake Heart Rate service. This will asynchronously expose
  // characteristics.
  fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kLowEnergyPath));
  ASSERT_EQ(1, observer.gatt_service_added_count_);

  BluetoothGattService* service =
      device->GetGattService(observer.last_gatt_service_id_);

  TestGattServiceObserver service_observer(adapter_, device, service);
  EXPECT_EQ(0, service_observer.gatt_characteristic_value_changed_count_);

  // Run the message loop so that the characteristics appear.
  base::MessageLoop::current()->Run();

  BluetoothGattCharacteristic* characteristic = service->GetCharacteristic(
      fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath()
          .value());
  ASSERT_TRUE(characteristic);
  EXPECT_FALSE(characteristic->IsNotifying());
  EXPECT_TRUE(update_sessions_.empty());

  // Send several requests to start notifications.
  characteristic->StartNotifySession(
      base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  characteristic->StartNotifySession(
      base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  characteristic->StartNotifySession(
      base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  characteristic->StartNotifySession(
      base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));

  // The operation still hasn't completed but we should have received the first
  // notification.
  EXPECT_EQ(0, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_TRUE(characteristic->IsNotifying());
  EXPECT_TRUE(update_sessions_.empty());

  // Run the main loop. The initial call should complete. The queued calls
  // should succeed immediately.
  base::MessageLoop::current()->Run();

  EXPECT_EQ(4, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_TRUE(characteristic->IsNotifying());
  EXPECT_EQ(4U, update_sessions_.size());

  for (int i = 0; i < 4; i++)
    EXPECT_TRUE(update_sessions_[0]->IsActive());

  // Stop notifications directly through the client. The sessions should get
  // marked as inactive.
  fake_bluetooth_gatt_characteristic_client_->StopNotify(
      fake_bluetooth_gatt_characteristic_client_->GetHeartRateMeasurementPath(),
      base::Bind(&BluetoothGattChromeOSTest::SuccessCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::DBusErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(5, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_FALSE(characteristic->IsNotifying());
  EXPECT_EQ(4U, update_sessions_.size());

  for (int i = 0; i < 4; i++)
    EXPECT_FALSE(update_sessions_[0]->IsActive());

  // It should be possible to restart notifications and the call should reset
  // the session count and make a request through the client.
  update_sessions_.clear();
  success_callback_count_ = 0;
  service_observer.gatt_characteristic_value_changed_count_ = 0;
  characteristic->StartNotifySession(
      base::Bind(&BluetoothGattChromeOSTest::NotifySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothGattChromeOSTest::ErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(0, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_TRUE(characteristic->IsNotifying());
  EXPECT_TRUE(update_sessions_.empty());

  base::MessageLoop::current()->Run();

  EXPECT_EQ(1, success_callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(1, service_observer.gatt_characteristic_value_changed_count_);
  EXPECT_TRUE(characteristic->IsNotifying());
  EXPECT_EQ(1U, update_sessions_.size());
  EXPECT_TRUE(update_sessions_[0]->IsActive());
}

}  // namespace chromeos