summaryrefslogtreecommitdiffstats
path: root/device/bluetooth
diff options
context:
space:
mode:
authorkeybuk@chromium.org <keybuk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-18 04:41:42 +0000
committerkeybuk@chromium.org <keybuk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-18 04:41:42 +0000
commit896619e714a5510c34a8d2b3d80741786d96e30b (patch)
tree9ef02ddc4df476097001c764e8172d18df804071 /device/bluetooth
parentb520cfffb5be192d59638f5a8530ba88933f8973 (diff)
downloadchromium_src-896619e714a5510c34a8d2b3d80741786d96e30b.zip
chromium_src-896619e714a5510c34a8d2b3d80741786d96e30b.tar.gz
chromium_src-896619e714a5510c34a8d2b3d80741786d96e30b.tar.bz2
Bluetooth: implement BlueZ 5 backend for Chrome OS
Provides an implementation for adapter control, device discovery, pairing, connection, disconnection and unpairing using the BlueZ 5.x backend on Chrome OS. Uses Fake* classes for testing instead of Mocks. TBR=youngki@chromium.org BUG=220951 TEST=device_unittests Review URL: https://chromiumcodereview.appspot.com/13927010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@194760 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'device/bluetooth')
-rw-r--r--device/bluetooth/bluetooth_adapter_experimental_chromeos.cc335
-rw-r--r--device/bluetooth/bluetooth_adapter_experimental_chromeos.h66
-rw-r--r--device/bluetooth/bluetooth_device_experimental_chromeos.cc609
-rw-r--r--device/bluetooth/bluetooth_device_experimental_chromeos.h200
-rw-r--r--device/bluetooth/bluetooth_experimental_chromeos_unittest.cc1848
5 files changed, 3044 insertions, 14 deletions
diff --git a/device/bluetooth/bluetooth_adapter_experimental_chromeos.cc b/device/bluetooth/bluetooth_adapter_experimental_chromeos.cc
index 8cdfb96..2af9376 100644
--- a/device/bluetooth/bluetooth_adapter_experimental_chromeos.cc
+++ b/device/bluetooth/bluetooth_adapter_experimental_chromeos.cc
@@ -6,32 +6,77 @@
#include <string>
+#include "base/bind.h"
+#include "base/logging.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/experimental_bluetooth_adapter_client.h"
+#include "chromeos/dbus/experimental_bluetooth_device_client.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_device_experimental_chromeos.h"
+
using device::BluetoothAdapter;
+using device::BluetoothDevice;
namespace chromeos {
BluetoothAdapterExperimentalChromeOS::BluetoothAdapterExperimentalChromeOS()
- : BluetoothAdapter(),
- weak_ptr_factory_(this) {
+ : weak_ptr_factory_(this) {
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ AddObserver(this);
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ AddObserver(this);
+
+ std::vector<dbus::ObjectPath> object_paths =
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ GetAdapters();
+
+ if (!object_paths.empty()) {
+ VLOG(1) << object_paths.size() << " Bluetooth adapter(s) available.";
+ SetAdapter(object_paths[0]);
+ }
}
BluetoothAdapterExperimentalChromeOS::~BluetoothAdapterExperimentalChromeOS() {
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ RemoveObserver(this);
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ RemoveObserver(this);
}
void BluetoothAdapterExperimentalChromeOS::AddObserver(
BluetoothAdapter::Observer* observer) {
+ DCHECK(observer);
+ observers_.AddObserver(observer);
}
void BluetoothAdapterExperimentalChromeOS::RemoveObserver(
BluetoothAdapter::Observer* observer) {
+ DCHECK(observer);
+ observers_.RemoveObserver(observer);
}
std::string BluetoothAdapterExperimentalChromeOS::GetAddress() const {
- return std::string();
+ if (!IsPresent())
+ return std::string();
+
+ ExperimentalBluetoothAdapterClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ GetProperties(object_path_);
+ DCHECK(properties);
+
+ return properties->address.value();
}
std::string BluetoothAdapterExperimentalChromeOS::GetName() const {
- return std::string();
+ if (!IsPresent())
+ return std::string();
+
+ ExperimentalBluetoothAdapterClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ GetProperties(object_path_);
+ DCHECK(properties);
+
+ return properties->alias.value();
}
bool BluetoothAdapterExperimentalChromeOS::IsInitialized() const {
@@ -39,33 +84,78 @@ bool BluetoothAdapterExperimentalChromeOS::IsInitialized() const {
}
bool BluetoothAdapterExperimentalChromeOS::IsPresent() const {
- return false;
+ return !object_path_.value().empty();
}
bool BluetoothAdapterExperimentalChromeOS::IsPowered() const {
- return false;
+ if (!IsPresent())
+ return false;
+
+ ExperimentalBluetoothAdapterClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ GetProperties(object_path_);
+
+ return properties->powered.value();
}
-void BluetoothAdapterExperimentalChromeOS::SetPowered(bool powered,
- const base::Closure& callback,
- const ErrorCallback& error_callback) {
- error_callback.Run();
+void BluetoothAdapterExperimentalChromeOS::SetPowered(
+ bool powered,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ GetProperties(object_path_)->powered.Set(
+ powered,
+ base::Bind(&BluetoothAdapterExperimentalChromeOS::OnSetPowered,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ error_callback));
}
bool BluetoothAdapterExperimentalChromeOS::IsDiscovering() const {
- return false;
+ if (!IsPresent())
+ return false;
+
+ ExperimentalBluetoothAdapterClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ GetProperties(object_path_);
+
+ return properties->discovering.value();
}
void BluetoothAdapterExperimentalChromeOS::StartDiscovering(
const base::Closure& callback,
const ErrorCallback& error_callback) {
- error_callback.Run();
+ // BlueZ counts discovery sessions, and permits multiple sessions for a
+ // single connection, so issue a StartDiscovery() call for every use
+ // within Chromium for the right behavior.
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ StartDiscovery(
+ object_path_,
+ base::Bind(
+ &BluetoothAdapterExperimentalChromeOS::OnStartDiscovery,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(
+ &BluetoothAdapterExperimentalChromeOS::OnStartDiscoveryError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
}
void BluetoothAdapterExperimentalChromeOS::StopDiscovering(
const base::Closure& callback,
const ErrorCallback& error_callback) {
- error_callback.Run();
+ // Inform BlueZ to stop one of our open discovery sessions.
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ StopDiscovery(
+ object_path_,
+ base::Bind(
+ &BluetoothAdapterExperimentalChromeOS::OnStopDiscovery,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(
+ &BluetoothAdapterExperimentalChromeOS::OnStopDiscoveryError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
}
void BluetoothAdapterExperimentalChromeOS::ReadLocalOutOfBandPairingData(
@@ -74,4 +164,223 @@ void BluetoothAdapterExperimentalChromeOS::ReadLocalOutOfBandPairingData(
error_callback.Run();
}
+void BluetoothAdapterExperimentalChromeOS::AdapterAdded(
+ const dbus::ObjectPath& object_path) {
+ // Set the adapter to the newly added adapter only if no adapter is present.
+ if (!IsPresent())
+ SetAdapter(object_path);
+}
+
+void BluetoothAdapterExperimentalChromeOS::AdapterRemoved(
+ const dbus::ObjectPath& object_path) {
+ if (object_path == object_path_)
+ RemoveAdapter();
+}
+
+void BluetoothAdapterExperimentalChromeOS::AdapterPropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) {
+ if (object_path != object_path_)
+ return;
+
+ ExperimentalBluetoothAdapterClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ GetProperties(object_path_);
+
+ if (property_name == properties->powered.name())
+ PoweredChanged(properties->powered.value());
+ else if (property_name == properties->discovering.name())
+ DiscoveringChanged(properties->discovering.value());
+}
+
+void BluetoothAdapterExperimentalChromeOS::DeviceAdded(
+ const dbus::ObjectPath& object_path) {
+ ExperimentalBluetoothDeviceClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ GetProperties(object_path);
+ if (properties->adapter.value() != object_path_)
+ return;
+
+ BluetoothDeviceExperimentalChromeOS* device_chromeos =
+ new BluetoothDeviceExperimentalChromeOS(this, object_path);
+ DCHECK(devices_.find(device_chromeos->GetAddress()) == devices_.end());
+
+ devices_[device_chromeos->GetAddress()] = device_chromeos;
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ DeviceAdded(this, device_chromeos));
+}
+
+void BluetoothAdapterExperimentalChromeOS::DeviceRemoved(
+ const dbus::ObjectPath& object_path) {
+ for (DevicesMap::iterator iter = devices_.begin();
+ iter != devices_.end(); ++iter) {
+ BluetoothDeviceExperimentalChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceExperimentalChromeOS*>(iter->second);
+ if (device_chromeos->object_path() == object_path) {
+ devices_.erase(iter);
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ DeviceRemoved(this, device_chromeos));
+ delete device_chromeos;
+ return;
+ }
+ }
+}
+
+void BluetoothAdapterExperimentalChromeOS::DevicePropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) {
+ BluetoothDeviceExperimentalChromeOS* device_chromeos =
+ GetDeviceWithPath(object_path);
+ if (!device_chromeos)
+ return;
+
+ ExperimentalBluetoothDeviceClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ GetProperties(object_path);
+
+ if (property_name == properties->bluetooth_class.name() ||
+ property_name == properties->alias.name() ||
+ property_name == properties->paired.name() ||
+ property_name == properties->connected.name() ||
+ property_name == properties->uuids.name()) {
+ FOR_EACH_OBSERVER(
+ BluetoothAdapter::Observer, observers_,
+ DeviceChanged(this, device_chromeos));
+ }
+}
+
+BluetoothDeviceExperimentalChromeOS*
+BluetoothAdapterExperimentalChromeOS::GetDeviceWithPath(
+ const dbus::ObjectPath& object_path) {
+ for (DevicesMap::iterator iter = devices_.begin();
+ iter != devices_.end(); ++iter) {
+ BluetoothDeviceExperimentalChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceExperimentalChromeOS*>(iter->second);
+ if (device_chromeos->object_path() == object_path)
+ return device_chromeos;
+ }
+
+ return NULL;
+}
+
+void BluetoothAdapterExperimentalChromeOS::SetAdapter(
+ const dbus::ObjectPath& object_path) {
+ DCHECK(!IsPresent());
+ object_path_ = object_path;
+
+ VLOG(1) << object_path_.value() << ": using adapter.";
+
+ ExperimentalBluetoothAdapterClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ GetProperties(object_path_);
+
+ PresentChanged(true);
+
+ if (properties->powered.value())
+ PoweredChanged(true);
+ if (properties->discovering.value())
+ DiscoveringChanged(true);
+
+ std::vector<dbus::ObjectPath> device_paths =
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ GetDevicesForAdapter(object_path_);
+
+ for (std::vector<dbus::ObjectPath>::iterator iter = device_paths.begin();
+ iter != device_paths.end(); ++iter) {
+ BluetoothDeviceExperimentalChromeOS* device_chromeos =
+ new BluetoothDeviceExperimentalChromeOS(this, *iter);
+
+ devices_[device_chromeos->GetAddress()] = device_chromeos;
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ DeviceAdded(this, device_chromeos));
+ }
+}
+
+void BluetoothAdapterExperimentalChromeOS::RemoveAdapter() {
+ DCHECK(IsPresent());
+ VLOG(1) << object_path_.value() << ": adapter removed.";
+
+ ExperimentalBluetoothAdapterClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ GetProperties(object_path_);
+
+ object_path_ = dbus::ObjectPath("");
+
+ if (properties->powered.value())
+ PoweredChanged(false);
+ if (properties->discovering.value())
+ DiscoveringChanged(false);
+
+ // Copy the devices list here and clear the original so that when we
+ // send DeviceRemoved(), GetDevices() returns no devices.
+ DevicesMap devices = devices_;
+ devices_.clear();
+
+ for (DevicesMap::iterator iter = devices.begin();
+ iter != devices.end(); ++iter) {
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ DeviceRemoved(this, iter->second));
+ delete iter->second;
+ }
+
+ PresentChanged(false);
+}
+
+void BluetoothAdapterExperimentalChromeOS::PoweredChanged(bool powered) {
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ AdapterPoweredChanged(this, powered));
+}
+
+void BluetoothAdapterExperimentalChromeOS::DiscoveringChanged(
+ bool discovering) {
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ AdapterDiscoveringChanged(this, discovering));
+}
+
+void BluetoothAdapterExperimentalChromeOS::PresentChanged(bool present) {
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ AdapterPresentChanged(this, present));
+}
+
+void BluetoothAdapterExperimentalChromeOS::OnSetPowered(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback,
+ bool success) {
+ if (success)
+ callback.Run();
+ else
+ error_callback.Run();
+}
+
+void BluetoothAdapterExperimentalChromeOS::OnStartDiscovery(
+ const base::Closure& callback) {
+ callback.Run();
+}
+
+void BluetoothAdapterExperimentalChromeOS::OnStartDiscoveryError(
+ const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(WARNING) << object_path_.value() << ": Failed to start discovery: "
+ << error_name << ": " << error_message;
+ error_callback.Run();
+}
+
+void BluetoothAdapterExperimentalChromeOS::OnStopDiscovery(
+ const base::Closure& callback) {
+ callback.Run();
+}
+
+void BluetoothAdapterExperimentalChromeOS::OnStopDiscoveryError(
+ const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(WARNING) << object_path_.value() << ": Failed to stop discovery: "
+ << error_name << ": " << error_message;
+ error_callback.Run();
+}
+
} // namespace chromeos
diff --git a/device/bluetooth/bluetooth_adapter_experimental_chromeos.h b/device/bluetooth/bluetooth_adapter_experimental_chromeos.h
index 1379352..8471b0f 100644
--- a/device/bluetooth/bluetooth_adapter_experimental_chromeos.h
+++ b/device/bluetooth/bluetooth_adapter_experimental_chromeos.h
@@ -8,6 +8,9 @@
#include <string>
#include "base/memory/weak_ptr.h"
+#include "chromeos/dbus/experimental_bluetooth_adapter_client.h"
+#include "chromeos/dbus/experimental_bluetooth_device_client.h"
+#include "dbus/object_path.h"
#include "device/bluetooth/bluetooth_adapter.h"
namespace device {
@@ -18,12 +21,17 @@ class BluetoothAdapterFactory;
namespace chromeos {
+class BluetoothDeviceExperimentalChromeOS;
+class BluetoothExperimentalChromeOSTest;
+
// The BluetoothAdapterExperimentalChromeOS class is an alternate implementation
// of BluetoothAdapter for the Chrome OS platform using the Bluetooth Smart
// capable backend. It will become the sole implementation for Chrome OS, and
// be renamed to BluetoothAdapterChromeOS, once the backend is switched,
class BluetoothAdapterExperimentalChromeOS
- : public device::BluetoothAdapter {
+ : public device::BluetoothAdapter,
+ private chromeos::ExperimentalBluetoothAdapterClient::Observer,
+ private chromeos::ExperimentalBluetoothDeviceClient::Observer {
public:
// BluetoothAdapter override
virtual void AddObserver(
@@ -53,10 +61,66 @@ class BluetoothAdapterExperimentalChromeOS
private:
friend class device::BluetoothAdapterFactory;
+ friend class BluetoothDeviceExperimentalChromeOS;
+ friend class BluetoothExperimentalChromeOSTest;
BluetoothAdapterExperimentalChromeOS();
virtual ~BluetoothAdapterExperimentalChromeOS();
+ // ExperimentalBluetoothAdapterClient::Observer override.
+ virtual void AdapterAdded(const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void AdapterRemoved(const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void AdapterPropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) OVERRIDE;
+
+ // ExperimentalBluetoothDeviceClient::Observer override.
+ virtual void DeviceAdded(const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void DeviceRemoved(const dbus::ObjectPath& object_path) OVERRIDE;
+ virtual void DevicePropertyChanged(const dbus::ObjectPath& object_path,
+ const std::string& property_name) OVERRIDE;
+
+ // Internal method used to locate the device object by object path
+ // (the devices map and BluetoothDevice methods are by address)
+ BluetoothDeviceExperimentalChromeOS* GetDeviceWithPath(
+ const dbus::ObjectPath& object_path);
+
+ // Set the tracked adapter to the one in |object_path|, this object will
+ // subsequently operate on that adapter until it is removed.
+ void SetAdapter(const dbus::ObjectPath& object_path);
+
+ // Remove the currently tracked adapter. IsPresent() will return false after
+ // this is called.
+ void RemoveAdapter();
+
+ // Announce to observers a change in the adapter state.
+ void PoweredChanged(bool powered);
+ void DiscoveringChanged(bool discovering);
+ void PresentChanged(bool present);
+
+ // Called by dbus:: on completion of the powered property change.
+ void OnSetPowered(const base::Closure& callback,
+ const ErrorCallback& error_callback,
+ bool success);
+
+ // Called by dbus:: on completion of the D-Bus method call to start discovery.
+ void OnStartDiscovery(const base::Closure& callback);
+ void OnStartDiscoveryError(const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Called by dbus:: on completion of the D-Bus method call to stop discovery.
+ void OnStopDiscovery(const base::Closure& callback);
+ void OnStopDiscoveryError(const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Object path of the adapter we track.
+ dbus::ObjectPath object_path_;
+
+ // List of observers interested in event notifications from us.
+ ObserverList<device::BluetoothAdapter::Observer> observers_;
+
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
base::WeakPtrFactory<BluetoothAdapterExperimentalChromeOS> weak_ptr_factory_;
diff --git a/device/bluetooth/bluetooth_device_experimental_chromeos.cc b/device/bluetooth/bluetooth_device_experimental_chromeos.cc
new file mode 100644
index 0000000..fb36c845
--- /dev/null
+++ b/device/bluetooth/bluetooth_device_experimental_chromeos.cc
@@ -0,0 +1,609 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_device_experimental_chromeos.h"
+
+#include "base/bind.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/experimental_bluetooth_adapter_client.h"
+#include "chromeos/dbus/experimental_bluetooth_agent_manager_client.h"
+#include "chromeos/dbus/experimental_bluetooth_agent_service_provider.h"
+#include "chromeos/dbus/experimental_bluetooth_device_client.h"
+#include "dbus/bus.h"
+#include "device/bluetooth/bluetooth_adapter_experimental_chromeos.h"
+#include "device/bluetooth/bluetooth_socket.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+using device::BluetoothDevice;
+
+namespace {
+
+// The agent path is relatively meaningless since BlueZ only supports one
+// at time and will fail in an attempt to register another with "Already Exists"
+// (which we fail in OnRegisterAgentError with ERROR_INPROGRESS).
+const char kAgentPath[] = "/org/chromium/bluetooth_agent";
+
+} // namespace
+
+namespace chromeos {
+
+BluetoothDeviceExperimentalChromeOS::BluetoothDeviceExperimentalChromeOS(
+ BluetoothAdapterExperimentalChromeOS* adapter,
+ const dbus::ObjectPath& object_path)
+ : adapter_(adapter),
+ object_path_(object_path),
+ num_connecting_calls_(0),
+ pairing_delegate_(NULL),
+ weak_ptr_factory_(this) {
+}
+
+BluetoothDeviceExperimentalChromeOS::~BluetoothDeviceExperimentalChromeOS() {
+}
+
+uint32 BluetoothDeviceExperimentalChromeOS::GetBluetoothClass() const {
+ ExperimentalBluetoothDeviceClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ GetProperties(object_path_);
+ DCHECK(properties);
+
+ return properties->bluetooth_class.value();
+}
+
+std::string BluetoothDeviceExperimentalChromeOS::GetDeviceName() const {
+ ExperimentalBluetoothDeviceClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ GetProperties(object_path_);
+ DCHECK(properties);
+
+ return properties->alias.value();
+}
+
+std::string BluetoothDeviceExperimentalChromeOS::GetAddress() const {
+ ExperimentalBluetoothDeviceClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ GetProperties(object_path_);
+ DCHECK(properties);
+
+ return properties->address.value();
+}
+
+bool BluetoothDeviceExperimentalChromeOS::IsPaired() const {
+ ExperimentalBluetoothDeviceClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ GetProperties(object_path_);
+ DCHECK(properties);
+
+ return properties->paired.value();
+}
+
+bool BluetoothDeviceExperimentalChromeOS::IsConnected() const {
+ ExperimentalBluetoothDeviceClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ GetProperties(object_path_);
+ DCHECK(properties);
+
+ return properties->connected.value();
+}
+
+bool BluetoothDeviceExperimentalChromeOS::IsConnectable() const {
+ // TODO(deymo): implement
+ return false;
+}
+
+bool BluetoothDeviceExperimentalChromeOS::IsConnecting() const {
+ return num_connecting_calls_ > 0;
+}
+
+BluetoothDeviceExperimentalChromeOS::ServiceList
+BluetoothDeviceExperimentalChromeOS::GetServices() const {
+ ExperimentalBluetoothDeviceClient::Properties* properties =
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ GetProperties(object_path_);
+ DCHECK(properties);
+
+ return properties->uuids.value();
+}
+
+void BluetoothDeviceExperimentalChromeOS::GetServiceRecords(
+ const ServiceRecordsCallback& callback,
+ const ErrorCallback& error_callback) {
+ // TODO(keybuk): not implemented; remove
+ error_callback.Run();
+}
+
+void BluetoothDeviceExperimentalChromeOS::ProvidesServiceWithName(
+ const std::string& name,
+ const ProvidesServiceCallback& callback) {
+ // TODO(keybuk): not implemented; remove
+ callback.Run(false);
+}
+
+bool BluetoothDeviceExperimentalChromeOS::ExpectingPinCode() const {
+ return !pincode_callback_.is_null();
+}
+
+bool BluetoothDeviceExperimentalChromeOS::ExpectingPasskey() const {
+ return !passkey_callback_.is_null();
+}
+
+bool BluetoothDeviceExperimentalChromeOS::ExpectingConfirmation() const {
+ return !confirmation_callback_.is_null();
+}
+
+void BluetoothDeviceExperimentalChromeOS::Connect(
+ BluetoothDevice::PairingDelegate* pairing_delegate,
+ const base::Closure& callback,
+ const ConnectErrorCallback& error_callback) {
+ ++num_connecting_calls_;
+ VLOG(1) << object_path_.value() << ": Connecting, " << num_connecting_calls_
+ << " in progress";
+
+ if (IsPaired() || IsConnected() || !pairing_delegate) {
+ // No need to pair, skip straight to connection.
+ ConnectInternal(callback, error_callback);
+ } else {
+ // Initiate high-security connection with pairing.
+ DCHECK(!pairing_delegate_);
+ DCHECK(agent_.get() == NULL);
+
+ pairing_delegate_ = pairing_delegate;
+
+ // The agent path is relatively meaningless since BlueZ only supports
+ // one per application at a time.
+ dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
+ agent_.reset(ExperimentalBluetoothAgentServiceProvider::Create(
+ system_bus, dbus::ObjectPath(kAgentPath), this));
+ DCHECK(agent_.get());
+
+ VLOG(1) << object_path_.value() << ": Registering agent for pairing";
+ DBusThreadManager::Get()->GetExperimentalBluetoothAgentManagerClient()->
+ RegisterAgent(
+ dbus::ObjectPath(kAgentPath),
+ bluetooth_agent_manager::kKeyboardDisplayCapability,
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnRegisterAgent,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ error_callback),
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnRegisterAgentError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+ }
+}
+
+void BluetoothDeviceExperimentalChromeOS::SetPinCode(
+ const std::string& pincode) {
+ if (!agent_.get() || pincode_callback_.is_null())
+ return;
+
+ pincode_callback_.Run(SUCCESS, pincode);
+ pincode_callback_.Reset();
+}
+
+void BluetoothDeviceExperimentalChromeOS::SetPasskey(uint32 passkey) {
+ if (!agent_.get() || passkey_callback_.is_null())
+ return;
+
+ passkey_callback_.Run(SUCCESS, passkey);
+ passkey_callback_.Reset();
+}
+
+void BluetoothDeviceExperimentalChromeOS::ConfirmPairing() {
+ if (!agent_.get() || confirmation_callback_.is_null())
+ return;
+
+ confirmation_callback_.Run(SUCCESS);
+ confirmation_callback_.Reset();
+}
+
+void BluetoothDeviceExperimentalChromeOS::RejectPairing() {
+ RunPairingCallbacks(REJECTED);
+}
+
+void BluetoothDeviceExperimentalChromeOS::CancelPairing() {
+ // If there wasn't a callback in progress that we can reply to then we
+ // have to send a CancelPairing() to the device instead.
+ if (!RunPairingCallbacks(CANCELLED)) {
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ CancelPairing(
+ object_path_,
+ base::Bind(&base::DoNothing),
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnCancelPairingError,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
+void BluetoothDeviceExperimentalChromeOS::Disconnect(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ VLOG(1) << object_path_.value() << ": Disconnecting";
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ Disconnect(
+ object_path_,
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnDisconnect,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnDisconnectError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+}
+
+void BluetoothDeviceExperimentalChromeOS::Forget(
+ const ErrorCallback& error_callback) {
+ VLOG(1) << object_path_.value() << ": Removing device";
+ DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
+ RemoveDevice(
+ adapter_->object_path_,
+ object_path_,
+ base::Bind(&base::DoNothing),
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnForgetError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+}
+
+void BluetoothDeviceExperimentalChromeOS::ConnectToService(
+ const std::string& service_uuid,
+ const SocketCallback& callback) {
+ // TODO(keybuk): implement
+ callback.Run(scoped_refptr<device::BluetoothSocket>());
+}
+
+void BluetoothDeviceExperimentalChromeOS::SetOutOfBandPairingData(
+ const device::BluetoothOutOfBandPairingData& data,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ // TODO(keybuk): implement
+ error_callback.Run();
+}
+
+void BluetoothDeviceExperimentalChromeOS::ClearOutOfBandPairingData(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ // TODO(keybuk): implement
+ error_callback.Run();
+}
+
+
+void BluetoothDeviceExperimentalChromeOS::Release() {
+ DCHECK(agent_.get());
+ DCHECK(pairing_delegate_);
+ VLOG(1) << object_path_.value() << ": Release";
+
+ pincode_callback_.Reset();
+ passkey_callback_.Reset();
+ confirmation_callback_.Reset();
+
+ UnregisterAgent();
+}
+
+void BluetoothDeviceExperimentalChromeOS::RequestPinCode(
+ const dbus::ObjectPath& device_path,
+ const PinCodeCallback& callback) {
+ DCHECK(agent_.get());
+ DCHECK(device_path == object_path_);
+ VLOG(1) << object_path_.value() << ": RequestPinCode";
+
+ DCHECK(pairing_delegate_);
+ DCHECK(pincode_callback_.is_null());
+ pincode_callback_ = callback;
+ pairing_delegate_->RequestPinCode(this);
+}
+
+void BluetoothDeviceExperimentalChromeOS::DisplayPinCode(
+ const dbus::ObjectPath& device_path,
+ const std::string& pincode) {
+ DCHECK(agent_.get());
+ DCHECK(device_path == object_path_);
+ VLOG(1) << object_path_.value() << ": DisplayPinCode: " << pincode;
+
+ DCHECK(pairing_delegate_);
+ pairing_delegate_->DisplayPinCode(this, pincode);
+}
+
+void BluetoothDeviceExperimentalChromeOS::RequestPasskey(
+ const dbus::ObjectPath& device_path,
+ const PasskeyCallback& callback) {
+ DCHECK(agent_.get());
+ DCHECK(device_path == object_path_);
+ VLOG(1) << object_path_.value() << ": RequestPasskey";
+
+ DCHECK(pairing_delegate_);
+ DCHECK(passkey_callback_.is_null());
+ passkey_callback_ = callback;
+ pairing_delegate_->RequestPasskey(this);
+}
+
+void BluetoothDeviceExperimentalChromeOS::DisplayPasskey(
+ const dbus::ObjectPath& device_path,
+ uint32 passkey, int16 entered) {
+ DCHECK(agent_.get());
+ DCHECK(device_path == object_path_);
+ VLOG(1) << object_path_.value() << ": DisplayPasskey: " << passkey
+ << " (" << entered << " entered)";
+
+ // TODO(keybuk): disambiguate entered vs display
+ if (entered > 0)
+ return;
+
+ DCHECK(pairing_delegate_);
+ pairing_delegate_->DisplayPasskey(this, passkey);
+}
+
+void BluetoothDeviceExperimentalChromeOS::RequestConfirmation(
+ const dbus::ObjectPath& device_path,
+ uint32 passkey,
+ const ConfirmationCallback& callback) {
+ DCHECK(agent_.get());
+ DCHECK(device_path == object_path_);
+ VLOG(1) << object_path_.value() << ": RequestConfirmation: " << passkey;
+
+ DCHECK(pairing_delegate_);
+ DCHECK(confirmation_callback_.is_null());
+ confirmation_callback_ = callback;
+ pairing_delegate_->ConfirmPasskey(this, passkey);
+}
+
+void BluetoothDeviceExperimentalChromeOS::RequestAuthorization(
+ const dbus::ObjectPath& device_path,
+ const ConfirmationCallback& callback) {
+ // TODO(keybuk): implement
+ callback.Run(CANCELLED);
+}
+
+void BluetoothDeviceExperimentalChromeOS::AuthorizeService(
+ const dbus::ObjectPath& device_path,
+ const std::string& uuid,
+ const ConfirmationCallback& callback) {
+ // TODO(keybuk): implement
+ callback.Run(CANCELLED);
+}
+
+void BluetoothDeviceExperimentalChromeOS::Cancel() {
+ DCHECK(agent_.get());
+ VLOG(1) << object_path_.value() << ": Cancel";
+
+ DCHECK(pairing_delegate_);
+ pairing_delegate_->DismissDisplayOrConfirm();
+}
+
+void BluetoothDeviceExperimentalChromeOS::ConnectInternal(
+ const base::Closure& callback,
+ const ConnectErrorCallback& error_callback) {
+ VLOG(1) << object_path_.value() << ": Connecting";
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ Connect(
+ object_path_,
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnConnect,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnConnectError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+}
+
+void BluetoothDeviceExperimentalChromeOS::OnConnect(
+ const base::Closure& callback) {
+ --num_connecting_calls_;
+ DCHECK(num_connecting_calls_ >= 0);
+ VLOG(1) << object_path_.value() << ": Connected, " << num_connecting_calls_
+ << " still in progress";
+
+ callback.Run();
+}
+
+void BluetoothDeviceExperimentalChromeOS::OnConnectError(
+ const ConnectErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ --num_connecting_calls_;
+ DCHECK(num_connecting_calls_ >= 0);
+ LOG(WARNING) << object_path_.value() << ": Failed to connect device: "
+ << error_name << ": " << error_message;
+ VLOG(1) << object_path_.value() << ": " << num_connecting_calls_
+ << " still in progress";
+
+ // Determine the error code from error_name.
+ ConnectErrorCode error_code = ERROR_UNKNOWN;
+ if (error_name == bluetooth_adapter::kErrorFailed) {
+ error_code = ERROR_FAILED;
+ } else if (error_name == bluetooth_adapter::kErrorInProgress) {
+ error_code = ERROR_INPROGRESS;
+ } else if (error_name == bluetooth_adapter::kErrorNotSupported) {
+ error_code = ERROR_UNSUPPORTED_DEVICE;
+ }
+
+ error_callback.Run(error_code);
+}
+
+void BluetoothDeviceExperimentalChromeOS::OnRegisterAgent(
+ const base::Closure& callback,
+ const ConnectErrorCallback& error_callback) {
+ VLOG(1) << object_path_.value() << ": Agent registered, now pairing";
+
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ Pair(object_path_,
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnPair,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback, error_callback),
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnPairError,
+ weak_ptr_factory_.GetWeakPtr(),
+ error_callback));
+}
+
+void BluetoothDeviceExperimentalChromeOS::OnRegisterAgentError(
+ const ConnectErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ --num_connecting_calls_;
+ DCHECK(num_connecting_calls_ >= 0);
+ LOG(WARNING) << object_path_.value() << ": Failed to register agent: "
+ << error_name << ": " << error_message;
+ VLOG(1) << object_path_.value() << ": " << num_connecting_calls_
+ << " still in progress";
+
+ UnregisterAgent();
+
+ // Determine the error code from error_name.
+ ConnectErrorCode error_code = ERROR_UNKNOWN;
+ if (error_name == bluetooth_adapter::kErrorAlreadyExists)
+ error_code = ERROR_INPROGRESS;
+
+ error_callback.Run(error_code);
+}
+
+void BluetoothDeviceExperimentalChromeOS::OnPair(
+ const base::Closure& callback,
+ const ConnectErrorCallback& error_callback) {
+ VLOG(1) << object_path_.value() << ": Paired";
+
+ // Now that we're paired, we need to set the device as trusted so that
+ // incoming connections will be accepted. This should only ever fail if
+ // the device is removed mid-pairing, so do it in the background while
+ // we connect and don't worry about errors.
+ DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
+ GetProperties(object_path_)->trusted.Set(
+ true,
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnSetTrusted,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ UnregisterAgent();
+
+ // Now we can connect to the device!
+ ConnectInternal(callback, error_callback);
+}
+
+void BluetoothDeviceExperimentalChromeOS::OnPairError(
+ const ConnectErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ --num_connecting_calls_;
+ DCHECK(num_connecting_calls_ >= 0);
+ LOG(WARNING) << object_path_.value() << ": Failed to pair device: "
+ << error_name << ": " << error_message;
+ VLOG(1) << object_path_.value() << ": " << num_connecting_calls_
+ << " still in progress";
+
+ UnregisterAgent();
+
+ // Determine the error code from error_name.
+ ConnectErrorCode error_code = ERROR_UNKNOWN;
+ if (error_name == bluetooth_adapter::kErrorConnectionAttemptFailed) {
+ error_code = ERROR_FAILED;
+ } else if (error_name == bluetooth_adapter::kErrorAuthenticationFailed) {
+ error_code = ERROR_AUTH_FAILED;
+ } else if (error_name == bluetooth_adapter::kErrorAuthenticationCanceled) {
+ error_code = ERROR_AUTH_CANCELED;
+ } else if (error_name == bluetooth_adapter::kErrorAuthenticationRejected) {
+ error_code = ERROR_AUTH_REJECTED;
+ } else if (error_name == bluetooth_adapter::kErrorAuthenticationTimeout) {
+ error_code = ERROR_AUTH_TIMEOUT;
+ }
+
+ error_callback.Run(error_code);
+}
+
+void BluetoothDeviceExperimentalChromeOS::OnCancelPairingError(
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(WARNING) << object_path_.value() << ": Failed to cancel pairing: "
+ << error_name << ": " << error_message;
+}
+
+void BluetoothDeviceExperimentalChromeOS::OnSetTrusted(bool success) {
+ LOG_IF(WARNING, !success) << object_path_.value()
+ << ": Failed to set device as trusted";
+}
+
+void BluetoothDeviceExperimentalChromeOS::UnregisterAgent() {
+ DCHECK(agent_.get());
+ DCHECK(pairing_delegate_);
+
+ DCHECK(pincode_callback_.is_null());
+ DCHECK(passkey_callback_.is_null());
+ DCHECK(confirmation_callback_.is_null());
+
+ pairing_delegate_->DismissDisplayOrConfirm();
+ pairing_delegate_ = NULL;
+
+ agent_.reset();
+
+ // Clean up after ourselves.
+ VLOG(1) << object_path_.value() << ": Unregistering pairing agent";
+ DBusThreadManager::Get()->GetExperimentalBluetoothAgentManagerClient()->
+ UnregisterAgent(
+ dbus::ObjectPath(kAgentPath),
+ base::Bind(&base::DoNothing),
+ base::Bind(
+ &BluetoothDeviceExperimentalChromeOS::OnUnregisterAgentError,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BluetoothDeviceExperimentalChromeOS::OnUnregisterAgentError(
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(WARNING) << object_path_.value() << ": Failed to unregister agent: "
+ << error_name << ": " << error_message;
+}
+
+void BluetoothDeviceExperimentalChromeOS::OnDisconnect(
+ const base::Closure& callback) {
+ VLOG(1) << object_path_.value() << ": Disconnected";
+ callback.Run();
+}
+
+void BluetoothDeviceExperimentalChromeOS::OnDisconnectError(
+ const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(WARNING) << object_path_.value() << ": Failed to disconnect device: "
+ << error_name << ": " << error_message;
+ error_callback.Run();
+}
+
+void BluetoothDeviceExperimentalChromeOS::OnForgetError(
+ const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(WARNING) << object_path_.value() << ": Failed to remove device: "
+ << error_name << ": " << error_message;
+ error_callback.Run();
+}
+
+bool BluetoothDeviceExperimentalChromeOS::RunPairingCallbacks(Status status) {
+ if (!agent_.get())
+ return false;
+
+ bool callback_run = false;
+ if (!pincode_callback_.is_null()) {
+ pincode_callback_.Run(status, "");
+ pincode_callback_.Reset();
+ callback_run = true;
+ }
+
+ if (!passkey_callback_.is_null()) {
+ passkey_callback_.Run(status, 0);
+ passkey_callback_.Reset();
+ callback_run = true;
+ }
+
+ if (!confirmation_callback_.is_null()) {
+ confirmation_callback_.Run(status);
+ confirmation_callback_.Reset();
+ callback_run = true;
+ }
+
+ return callback_run;
+}
+
+} // namespace chromeos
diff --git a/device/bluetooth/bluetooth_device_experimental_chromeos.h b/device/bluetooth/bluetooth_device_experimental_chromeos.h
new file mode 100644
index 0000000..aa24657
--- /dev/null
+++ b/device/bluetooth/bluetooth_device_experimental_chromeos.h
@@ -0,0 +1,200 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_EXPERIMENTAL_CHROMEOS_H
+#define DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_EXPERIMENTAL_CHROMEOS_H
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chromeos/dbus/experimental_bluetooth_agent_service_provider.h"
+#include "chromeos/dbus/experimental_bluetooth_device_client.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_device.h"
+
+namespace chromeos {
+
+class BluetoothAdapterExperimentalChromeOS;
+
+// The BluetoothDeviceExperimentalChromeOS class is an alternate implementation
+// of BluetoothDevice for the Chrome OS platform using the Bluetooth Smart
+// capable backend. It will become the sole implementation for Chrome OS, and
+// be renamed to BluetoothDeviceChromeOS, once the backend is switched.
+class BluetoothDeviceExperimentalChromeOS
+ : public device::BluetoothDevice,
+ private chromeos::ExperimentalBluetoothAgentServiceProvider::Delegate {
+ public:
+ // BluetoothDevice override
+ virtual std::string GetAddress() const OVERRIDE;
+ virtual bool IsPaired() const OVERRIDE;
+ virtual bool IsConnected() const OVERRIDE;
+ virtual bool IsConnectable() const OVERRIDE;
+ virtual bool IsConnecting() const OVERRIDE;
+ virtual ServiceList GetServices() const OVERRIDE;
+ virtual void GetServiceRecords(
+ const ServiceRecordsCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual void ProvidesServiceWithName(
+ const std::string& name,
+ const ProvidesServiceCallback& callback) OVERRIDE;
+ virtual bool ExpectingPinCode() const OVERRIDE;
+ virtual bool ExpectingPasskey() const OVERRIDE;
+ virtual bool ExpectingConfirmation() const OVERRIDE;
+ virtual void Connect(
+ device::BluetoothDevice::PairingDelegate* pairing_delegate,
+ const base::Closure& callback,
+ const ConnectErrorCallback& error_callback) OVERRIDE;
+ virtual void SetPinCode(const std::string& pincode) OVERRIDE;
+ virtual void SetPasskey(uint32 passkey) OVERRIDE;
+ virtual void ConfirmPairing() OVERRIDE;
+ virtual void RejectPairing() OVERRIDE;
+ virtual void CancelPairing() OVERRIDE;
+ virtual void Disconnect(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual void Forget(const ErrorCallback& error_callback) OVERRIDE;
+ virtual void ConnectToService(
+ const std::string& service_uuid,
+ const SocketCallback& callback) OVERRIDE;
+ virtual void SetOutOfBandPairingData(
+ const device::BluetoothOutOfBandPairingData& data,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+ virtual void ClearOutOfBandPairingData(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) OVERRIDE;
+
+ protected:
+ // BluetoothDevice override
+ virtual uint32 GetBluetoothClass() const OVERRIDE;
+ virtual std::string GetDeviceName() const OVERRIDE;
+
+ private:
+ friend class BluetoothAdapterExperimentalChromeOS;
+
+ BluetoothDeviceExperimentalChromeOS(
+ BluetoothAdapterExperimentalChromeOS* adapter,
+ const dbus::ObjectPath& object_path);
+ virtual ~BluetoothDeviceExperimentalChromeOS();
+
+ // ExperimentalBluetoothAgentServiceProvider::Delegate override.
+ virtual void Release() OVERRIDE;
+ virtual void RequestPinCode(const dbus::ObjectPath& device_path,
+ const PinCodeCallback& callback) OVERRIDE;
+ virtual void DisplayPinCode(const dbus::ObjectPath& device_path,
+ const std::string& pincode) OVERRIDE;
+ virtual void RequestPasskey(const dbus::ObjectPath& device_path,
+ const PasskeyCallback& callback) OVERRIDE;
+ virtual void DisplayPasskey(const dbus::ObjectPath& device_path,
+ uint32 passkey, int16 entered) OVERRIDE;
+ virtual void RequestConfirmation(const dbus::ObjectPath& device_path,
+ uint32 passkey,
+ const ConfirmationCallback& callback)
+ OVERRIDE;
+ virtual void RequestAuthorization(const dbus::ObjectPath& device_path,
+ const ConfirmationCallback& callback)
+ OVERRIDE;
+ virtual void AuthorizeService(const dbus::ObjectPath& device_path,
+ const std::string& uuid,
+ const ConfirmationCallback& callback) OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+
+ // Internal method to initiate a connection to this device, and methods called
+ // by dbus:: on completion of the D-Bus method call.
+ void ConnectInternal(const base::Closure& callback,
+ const ConnectErrorCallback& error_callback);
+ void OnConnect(const base::Closure& callback);
+ void OnConnectError(const ConnectErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Called by dbus:: on completion of the D-Bus method call to register the
+ // pairing agent.
+ void OnRegisterAgent(const base::Closure& callback,
+ const ConnectErrorCallback& error_callback);
+ void OnRegisterAgentError(const ConnectErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Called by dbus:: on completion of the D-Bus method call to pair the device.
+ void OnPair(const base::Closure& callback,
+ const ConnectErrorCallback& error_callback);
+ void OnPairError(const ConnectErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Called by dbus:: on failure of the D-Bus method call to cancel pairing,
+ // there is no matching completion call since we don't do anything special
+ // in that case.
+ void OnCancelPairingError(const std::string& error_name,
+ const std::string& error_message);
+
+ // Called by dbus:: on completion of the trusted property change.
+ void OnSetTrusted(bool success);
+
+ // Internal method to unregister the pairing agent and method called by dbus::
+ // on failure of the D-Bus method call. No completion call as success is
+ // ignored.
+ void UnregisterAgent();
+ void OnUnregisterAgentError(const std::string& error_name,
+ const std::string& error_message);
+
+ // Called by dbus:: on completion of the D-Bus method call to disconnect the
+ // device.
+ void OnDisconnect(const base::Closure& callback);
+ void OnDisconnectError(const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Called by dbus:: on failure of the D-Bus method call to unpair the device;
+ // there is no matching completion call since this object is deleted in the
+ // process of unpairing.
+ void OnForgetError(const ErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message);
+
+ // Run any outstanding pairing callbacks passing |status| as the result of
+ // pairing. Returns true if any callbacks were run, false if not.
+ bool RunPairingCallbacks(Status status);
+
+ // Return the object path of the device; used by
+ // BluetoothAdapterExperimentalChromeOS
+ const dbus::ObjectPath& object_path() const { return object_path_; }
+
+ // The adapter that owns this device instance.
+ BluetoothAdapterExperimentalChromeOS* adapter_;
+
+ // The dbus object path of the device object.
+ dbus::ObjectPath object_path_;
+
+ // Number of ongoing calls to Connect().
+ int num_connecting_calls_;
+
+ // During pairing this is set to an object that we don't own, but on which
+ // we can make method calls to request, display or confirm PIN Codes and
+ // Passkeys. Generally it is the object that owns this one.
+ PairingDelegate* pairing_delegate_;
+
+ // During pairing this is set to an instance of a D-Bus agent object
+ // intialized with our own class as its delegate.
+ scoped_ptr<ExperimentalBluetoothAgentServiceProvider> agent_;
+
+ // During pairing these callbacks are set to those provided by method calls
+ // made on us by |agent_| and are called by our own method calls such as
+ // SetPinCode() and SetPasskey().
+ PinCodeCallback pincode_callback_;
+ PasskeyCallback passkey_callback_;
+ ConfirmationCallback confirmation_callback_;
+
+ // Note: This should remain the last member so it'll be destroyed and
+ // invalidate its weak pointers before any other members are destroyed.
+ base::WeakPtrFactory<BluetoothDeviceExperimentalChromeOS> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceExperimentalChromeOS);
+};
+
+} // namespace chromeos
+
+#endif /* DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_EXPERIMENTAL_CHROMEOS_H */
diff --git a/device/bluetooth/bluetooth_experimental_chromeos_unittest.cc b/device/bluetooth/bluetooth_experimental_chromeos_unittest.cc
new file mode 100644
index 0000000..7bd7c25
--- /dev/null
+++ b/device/bluetooth/bluetooth_experimental_chromeos_unittest.cc
@@ -0,0 +1,1848 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "base/utf_string_conversions.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/fake_bluetooth_adapter_client.h"
+#include "chromeos/dbus/fake_bluetooth_device_client.h"
+#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_adapter_experimental_chromeos.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_device_experimental_chromeos.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using device::BluetoothAdapter;
+using device::BluetoothAdapterFactory;
+using device::BluetoothDevice;
+
+namespace chromeos {
+
+class TestObserver : public BluetoothAdapter::Observer {
+ public:
+ TestObserver(scoped_refptr<BluetoothAdapter> adapter)
+ : present_changed_count_(0),
+ powered_changed_count_(0),
+ discovering_changed_count_(0),
+ last_present_(false),
+ last_powered_(false),
+ last_discovering_(false),
+ device_added_count_(0),
+ device_changed_count_(0),
+ device_removed_count_(0),
+ last_device_(NULL),
+ adapter_(adapter) {
+ }
+ virtual ~TestObserver() {}
+
+ virtual void AdapterPresentChanged(BluetoothAdapter* adapter,
+ bool present) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+
+ ++present_changed_count_;
+ last_present_ = present;
+ }
+
+ virtual void AdapterPoweredChanged(BluetoothAdapter* adapter,
+ bool powered) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+
+ ++powered_changed_count_;
+ last_powered_ = powered;
+ }
+
+ virtual void AdapterDiscoveringChanged(BluetoothAdapter* adapter,
+ bool discovering) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+
+ ++discovering_changed_count_;
+ last_discovering_ = discovering;
+ }
+
+ virtual void DeviceAdded(BluetoothAdapter* adapter,
+ BluetoothDevice* device) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+
+ ++device_added_count_;
+ last_device_ = device;
+ last_device_address_ = device->GetAddress();
+
+ QuitMessageLoop();
+ }
+
+ virtual void DeviceChanged(BluetoothAdapter* adapter,
+ BluetoothDevice* device) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+
+ ++device_changed_count_;
+ last_device_ = device;
+ last_device_address_ = device->GetAddress();
+
+ QuitMessageLoop();
+ }
+
+ virtual void DeviceRemoved(BluetoothAdapter* adapter,
+ BluetoothDevice* device) OVERRIDE {
+ EXPECT_EQ(adapter_, adapter);
+
+ ++device_removed_count_;
+ // Can't save device, it may be freed
+ last_device_address_ = device->GetAddress();
+
+ QuitMessageLoop();
+ }
+
+ int present_changed_count_;
+ int powered_changed_count_;
+ int discovering_changed_count_;
+ bool last_present_;
+ bool last_powered_;
+ bool last_discovering_;
+ int device_added_count_;
+ int device_changed_count_;
+ int device_removed_count_;
+ BluetoothDevice* last_device_;
+ std::string last_device_address_;
+
+ private:
+ // Some tests use a message loop since background processing is simulated;
+ // break out of those loops.
+ void QuitMessageLoop() {
+ if (MessageLoop::current() && MessageLoop::current()->is_running())
+ MessageLoop::current()->Quit();
+ }
+
+ scoped_refptr<BluetoothAdapter> adapter_;
+};
+
+class TestPairingDelegate : public BluetoothDevice::PairingDelegate {
+ public:
+ TestPairingDelegate()
+ : call_count_(0),
+ request_pincode_count_(0),
+ request_passkey_count_(0),
+ display_pincode_count_(0),
+ display_passkey_count_(0),
+ confirm_passkey_count_(0),
+ dismiss_count_(0) {}
+ virtual ~TestPairingDelegate() {}
+
+ void RequestPinCode(BluetoothDevice* device) OVERRIDE {
+ ++call_count_;
+ ++request_pincode_count_;
+ QuitMessageLoop();
+ }
+
+ void RequestPasskey(BluetoothDevice* device) OVERRIDE {
+ ++call_count_;
+ ++request_passkey_count_;
+ QuitMessageLoop();
+ }
+
+ void DisplayPinCode(BluetoothDevice* device,
+ const std::string& pincode) OVERRIDE {
+ ++call_count_;
+ ++display_pincode_count_;
+ last_pincode_ = pincode;
+ QuitMessageLoop();
+ }
+
+ void DisplayPasskey(BluetoothDevice* device,
+ uint32 passkey) OVERRIDE {
+ ++call_count_;
+ ++display_passkey_count_;
+ last_passkey_ = passkey;
+ QuitMessageLoop();
+ }
+
+ void ConfirmPasskey(BluetoothDevice* device,
+ uint32 passkey) OVERRIDE {
+ ++call_count_;
+ ++confirm_passkey_count_;
+ last_passkey_ = passkey;
+ QuitMessageLoop();
+ }
+
+ void DismissDisplayOrConfirm() OVERRIDE {
+ ++call_count_;
+ ++dismiss_count_;
+ QuitMessageLoop();
+ }
+
+ int call_count_;
+ int request_pincode_count_;
+ int request_passkey_count_;
+ int display_pincode_count_;
+ int display_passkey_count_;
+ int confirm_passkey_count_;
+ int dismiss_count_;
+ uint32 last_passkey_;
+ std::string last_pincode_;
+
+ private:
+ // Some tests use a message loop since background processing is simulated;
+ // break out of those loops.
+ void QuitMessageLoop() {
+ if (MessageLoop::current() && MessageLoop::current()->is_running())
+ MessageLoop::current()->Quit();
+ }
+};
+
+class BluetoothExperimentalChromeOSTest : public testing::Test {
+ public:
+ virtual void SetUp() {
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kEnableExperimentalBluetooth))
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ chromeos::switches::kEnableExperimentalBluetooth);
+
+ mock_dbus_thread_manager_ =
+ new MockDBusThreadManagerWithoutGMock();
+ DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager_);
+
+ fake_bluetooth_adapter_client_ =
+ mock_dbus_thread_manager_->fake_bluetooth_adapter_client();
+ fake_bluetooth_device_client_ =
+ mock_dbus_thread_manager_->fake_bluetooth_device_client();
+
+ callback_count_ = 0;
+ error_callback_count_ = 0;
+ last_connect_error_ = BluetoothDevice::ERROR_UNKNOWN;
+ }
+
+ virtual void TearDown() {
+ adapter_ = NULL;
+ DBusThreadManager::Shutdown();
+ }
+
+ // Generic callbacks
+ void Callback() {
+ ++callback_count_;
+ }
+
+ void ErrorCallback() {
+ ++error_callback_count_;
+ }
+
+ void ConnectErrorCallback(enum BluetoothDevice::ConnectErrorCode error) {
+ ++error_callback_count_;
+ last_connect_error_ = error;
+ }
+
+ // Call to fill the adapter_ member with a BluetoothAdapter instance.
+ void GetAdapter() {
+ adapter_ = new BluetoothAdapterExperimentalChromeOS();
+ ASSERT_TRUE(adapter_ != NULL);
+ ASSERT_TRUE(adapter_->IsInitialized());
+ }
+
+ // Run a discovery phase until the named device is detected, or if the named
+ // device is not created, the discovery process ends without finding it.
+ //
+ // The correct behavior of discovery is tested by the "Discovery" test case
+ // without using this function.
+ void DiscoverDevice(const std::string& address) {
+ ASSERT_TRUE(adapter_ != NULL);
+
+ if (MessageLoop::current() == NULL) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ DiscoverDevices();
+ return;
+ }
+
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ adapter_->SetPowered(
+ true,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ adapter_->StartDiscovering(
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ ASSERT_EQ(2, callback_count_);
+ ASSERT_EQ(0, error_callback_count_);
+ callback_count_ = 0;
+
+ ASSERT_TRUE(adapter_->IsPowered());
+ ASSERT_TRUE(adapter_->IsDiscovering());
+
+ while (!observer.device_removed_count_ &&
+ observer.last_device_address_ != address)
+ MessageLoop::current()->Run();
+
+ adapter_->StopDiscovering(
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ ASSERT_EQ(1, callback_count_);
+ ASSERT_EQ(0, error_callback_count_);
+ callback_count_ = 0;
+
+ ASSERT_FALSE(adapter_->IsDiscovering());
+
+ adapter_->RemoveObserver(&observer);
+ }
+
+ // Run a discovery phase so we have devices that can be paired with.
+ void DiscoverDevices() {
+ // Pass an invalid address for the device so that the discovery process
+ // completes with all devices.
+ DiscoverDevice("does not exist");
+ }
+
+ protected:
+ FakeBluetoothAdapterClient* fake_bluetooth_adapter_client_;
+ FakeBluetoothDeviceClient* fake_bluetooth_device_client_;
+ MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager_;
+ scoped_refptr<BluetoothAdapter> adapter_;
+
+ int callback_count_;
+ int error_callback_count_;
+ enum BluetoothDevice::ConnectErrorCode last_connect_error_;
+};
+
+TEST_F(BluetoothExperimentalChromeOSTest, AlreadyPresent) {
+ GetAdapter();
+
+ // This verifies that the class gets the list of adapters when created;
+ // and initializes with an existing adapter if there is one.
+ EXPECT_TRUE(adapter_->IsPresent());
+ EXPECT_FALSE(adapter_->IsPowered());
+ EXPECT_EQ(FakeBluetoothAdapterClient::kAdapterAddress,
+ adapter_->GetAddress());
+ EXPECT_EQ(FakeBluetoothAdapterClient::kAdapterName, adapter_->GetName());
+ EXPECT_FALSE(adapter_->IsDiscovering());
+
+ // There should be a device
+ BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
+ EXPECT_EQ(1U, devices.size());
+ EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
+ devices[0]->GetAddress());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, BecomePresent) {
+ fake_bluetooth_adapter_client_->SetVisible(false);
+ GetAdapter();
+ ASSERT_FALSE(adapter_->IsPresent());
+
+ // Install an observer; expect the AdapterPresentChanged to be called
+ // with true, and IsPresent() to return true.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ fake_bluetooth_adapter_client_->SetVisible(true);
+
+ EXPECT_EQ(1, observer.present_changed_count_);
+ EXPECT_TRUE(observer.last_present_);
+
+ EXPECT_TRUE(adapter_->IsPresent());
+
+ // We should have had a device announced.
+ EXPECT_EQ(1, observer.device_added_count_);
+ EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
+ observer.last_device_address_);
+
+ // Other callbacks shouldn't be called if the values are false.
+ EXPECT_EQ(0, observer.powered_changed_count_);
+ EXPECT_EQ(0, observer.discovering_changed_count_);
+ EXPECT_FALSE(adapter_->IsPowered());
+ EXPECT_FALSE(adapter_->IsDiscovering());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, BecomeNotPresent) {
+ GetAdapter();
+ ASSERT_TRUE(adapter_->IsPresent());
+
+ // Install an observer; expect the AdapterPresentChanged to be called
+ // with false, and IsPresent() to return false.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ fake_bluetooth_adapter_client_->SetVisible(false);
+
+ EXPECT_EQ(1, observer.present_changed_count_);
+ EXPECT_FALSE(observer.last_present_);
+
+ EXPECT_FALSE(adapter_->IsPresent());
+
+ // We should have had a device removed.
+ EXPECT_EQ(1, observer.device_removed_count_);
+ EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
+ observer.last_device_address_);
+
+ // Other callbacks shouldn't be called since the values are false.
+ EXPECT_EQ(0, observer.powered_changed_count_);
+ EXPECT_EQ(0, observer.discovering_changed_count_);
+ EXPECT_FALSE(adapter_->IsPowered());
+ EXPECT_FALSE(adapter_->IsDiscovering());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, SecondAdapter) {
+ GetAdapter();
+ ASSERT_TRUE(adapter_->IsPresent());
+
+ // Install an observer, then add a second adapter. Nothing should change,
+ // we ignore the second adapter.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ fake_bluetooth_adapter_client_->SetSecondVisible(true);
+
+ EXPECT_EQ(0, observer.present_changed_count_);
+
+ EXPECT_TRUE(adapter_->IsPresent());
+ EXPECT_EQ(FakeBluetoothAdapterClient::kAdapterAddress,
+ adapter_->GetAddress());
+
+ // Try removing the first adapter, we should now act as if the adapter
+ // is no longer present rather than fall back to the second.
+ fake_bluetooth_adapter_client_->SetVisible(false);
+
+ EXPECT_EQ(1, observer.present_changed_count_);
+ EXPECT_FALSE(observer.last_present_);
+
+ EXPECT_FALSE(adapter_->IsPresent());
+
+ // We should have had a device removed.
+ EXPECT_EQ(1, observer.device_removed_count_);
+ EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
+ observer.last_device_address_);
+
+ // Other callbacks shouldn't be called since the values are false.
+ EXPECT_EQ(0, observer.powered_changed_count_);
+ EXPECT_EQ(0, observer.discovering_changed_count_);
+ EXPECT_FALSE(adapter_->IsPowered());
+ EXPECT_FALSE(adapter_->IsDiscovering());
+
+ observer.device_removed_count_ = 0;
+
+ // Removing the second adapter shouldn't set anything either.
+ fake_bluetooth_adapter_client_->SetSecondVisible(false);
+
+ EXPECT_EQ(0, observer.device_removed_count_);
+ EXPECT_EQ(0, observer.powered_changed_count_);
+ EXPECT_EQ(0, observer.discovering_changed_count_);
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, BecomePowered) {
+ GetAdapter();
+ ASSERT_FALSE(adapter_->IsPowered());
+
+ // Install an observer; expect the AdapterPoweredChanged to be called
+ // with true, and IsPowered() to return true.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ adapter_->SetPowered(
+ true,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ EXPECT_EQ(1, observer.powered_changed_count_);
+ EXPECT_TRUE(observer.last_powered_);
+
+ EXPECT_TRUE(adapter_->IsPowered());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, BecomeNotPowered) {
+ GetAdapter();
+ adapter_->SetPowered(
+ true,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ callback_count_ = 0;
+
+ ASSERT_TRUE(adapter_->IsPowered());
+
+ // Install an observer; expect the AdapterPoweredChanged to be called
+ // with false, and IsPowered() to return false.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ adapter_->SetPowered(
+ false,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ EXPECT_EQ(1, observer.powered_changed_count_);
+ EXPECT_FALSE(observer.last_powered_);
+
+ EXPECT_FALSE(adapter_->IsPowered());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, StopDiscovery) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+
+ GetAdapter();
+
+ adapter_->SetPowered(
+ true,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ adapter_->StartDiscovering(
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ callback_count_ = 0;
+
+ ASSERT_TRUE(adapter_->IsPowered());
+ ASSERT_TRUE(adapter_->IsDiscovering());
+
+ // Install an observer; aside from the callback, expect the
+ // AdapterDiscoveringChanged method to be called and no longer to be
+ // discovering,
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ adapter_->StopDiscovering(
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+
+ EXPECT_FALSE(adapter_->IsDiscovering());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, StopDiscoveryAfterTwoStarts) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+
+ GetAdapter();
+
+ adapter_->SetPowered(
+ true,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ adapter_->StartDiscovering(
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ callback_count_ = 0;
+
+ ASSERT_TRUE(adapter_->IsPowered());
+ ASSERT_TRUE(adapter_->IsDiscovering());
+
+ // Install an observer and start discovering again; only the callback
+ // should be called since we were already discovering to begin with.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ adapter_->StartDiscovering(
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ callback_count_ = 0;
+
+ EXPECT_EQ(0, observer.discovering_changed_count_);
+
+ // Stop discovering; only the callback should be called since we're still
+ // discovering. The adapter should be still discovering.
+ adapter_->StopDiscovering(
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ callback_count_ = 0;
+
+ EXPECT_EQ(0, observer.discovering_changed_count_);
+
+ EXPECT_TRUE(adapter_->IsDiscovering());
+
+ // Stop discovering one more time; aside from the callback, expect the
+ // AdapterDiscoveringChanged method to be called and no longer to be
+ // discovering,
+ adapter_->StopDiscovering(
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+
+ EXPECT_FALSE(adapter_->IsDiscovering());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, Discovery) {
+ // Test a simulated discovery session.
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+ GetAdapter();
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ adapter_->SetPowered(
+ true,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ adapter_->StartDiscovering(
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ callback_count_ = 0;
+
+ ASSERT_TRUE(adapter_->IsPowered());
+ ASSERT_TRUE(adapter_->IsDiscovering());
+
+ // First device to appear should be an Apple Mouse.
+ message_loop.Run();
+
+ EXPECT_EQ(1, observer.device_added_count_);
+ EXPECT_EQ(FakeBluetoothDeviceClient::kAppleMouseAddress,
+ observer.last_device_address_);
+
+ // Next we should get another two devices...
+ message_loop.Run();
+ EXPECT_EQ(3, observer.device_added_count_);
+
+ // Okay, let's run forward until a device is actually removed...
+ while (!observer.device_removed_count_)
+ message_loop.Run();
+
+ EXPECT_EQ(1, observer.device_removed_count_);
+ EXPECT_EQ(FakeBluetoothDeviceClient::kVanishingDeviceAddress,
+ observer.last_device_address_);
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PoweredAndDiscovering) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+
+ GetAdapter();
+ adapter_->SetPowered(
+ true,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ adapter_->StartDiscovering(
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(2, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+ callback_count_ = 0;
+
+ // Stop the timers that the simulation uses
+ fake_bluetooth_device_client_->EndDiscoverySimulation(
+ dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));
+
+ ASSERT_TRUE(adapter_->IsPowered());
+ ASSERT_TRUE(adapter_->IsDiscovering());
+
+ fake_bluetooth_adapter_client_->SetVisible(false);
+ ASSERT_FALSE(adapter_->IsPresent());
+
+ // Install an observer; expect the AdapterPresentChanged,
+ // AdapterPoweredChanged and AdapterDiscoveringChanged methods to be called
+ // with true, and IsPresent(), IsPowered() and IsDiscovering() to all
+ // return true.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ fake_bluetooth_adapter_client_->SetVisible(true);
+
+ EXPECT_EQ(1, observer.present_changed_count_);
+ EXPECT_TRUE(observer.last_present_);
+ EXPECT_TRUE(adapter_->IsPresent());
+
+ EXPECT_EQ(1, observer.powered_changed_count_);
+ EXPECT_TRUE(observer.last_powered_);
+ EXPECT_TRUE(adapter_->IsPowered());
+
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_TRUE(observer.last_discovering_);
+ EXPECT_TRUE(adapter_->IsDiscovering());
+
+ observer.present_changed_count_ = 0;
+ observer.powered_changed_count_ = 0;
+ observer.discovering_changed_count_ = 0;
+
+ // Now mark the adapter not present again. Expect the methods to be called
+ // again, to reset the properties back to false
+ fake_bluetooth_adapter_client_->SetVisible(false);
+
+ EXPECT_EQ(1, observer.present_changed_count_);
+ EXPECT_FALSE(observer.last_present_);
+ EXPECT_FALSE(adapter_->IsPresent());
+
+ EXPECT_EQ(1, observer.powered_changed_count_);
+ EXPECT_FALSE(observer.last_powered_);
+ EXPECT_FALSE(adapter_->IsPowered());
+
+ EXPECT_EQ(1, observer.discovering_changed_count_);
+ EXPECT_FALSE(observer.last_discovering_);
+ EXPECT_FALSE(adapter_->IsDiscovering());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, DeviceProperties) {
+ GetAdapter();
+
+ BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
+ ASSERT_EQ(1U, devices.size());
+ ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
+ devices[0]->GetAddress());
+
+ // Verify the other device properties.
+ EXPECT_EQ(UTF8ToUTF16(FakeBluetoothDeviceClient::kPairedDeviceName),
+ devices[0]->GetName());
+ EXPECT_EQ(BluetoothDevice::DEVICE_COMPUTER, devices[0]->GetDeviceType());
+ EXPECT_TRUE(devices[0]->IsPaired());
+ EXPECT_FALSE(devices[0]->IsConnected());
+ EXPECT_FALSE(devices[0]->IsConnectable());
+ EXPECT_FALSE(devices[0]->IsConnecting());
+
+ BluetoothDevice::ServiceList uuids = devices[0]->GetServices();
+ ASSERT_EQ(2U, uuids.size());
+ EXPECT_EQ(uuids[0], "00001800-0000-1000-8000-00805f9b34fb");
+ EXPECT_EQ(uuids[1], "00001801-0000-1000-8000-00805f9b34fb");
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, DeviceClassChanged) {
+ // Simulate a change of class of a device, as sometimes occurs
+ // during discovery.
+ GetAdapter();
+
+ BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
+ ASSERT_EQ(1U, devices.size());
+ ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
+ devices[0]->GetAddress());
+ ASSERT_EQ(BluetoothDevice::DEVICE_COMPUTER, devices[0]->GetDeviceType());
+
+ // Install an observer; expect the DeviceChanged method to be called when
+ // we change the class of the device.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath));
+
+ properties->bluetooth_class.ReplaceValue(0x002580);
+ properties->NotifyPropertyChanged(properties->bluetooth_class.name());
+
+ EXPECT_EQ(1, observer.device_changed_count_);
+ EXPECT_EQ(devices[0], observer.last_device_);
+
+ EXPECT_EQ(BluetoothDevice::DEVICE_MOUSE, devices[0]->GetDeviceType());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, DeviceNameChanged) {
+ // Simulate a change of name of a device.
+ GetAdapter();
+
+ BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
+ ASSERT_EQ(1U, devices.size());
+ ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
+ devices[0]->GetAddress());
+ ASSERT_EQ(UTF8ToUTF16(FakeBluetoothDeviceClient::kPairedDeviceName),
+ devices[0]->GetName());
+
+ // Install an observer; expect the DeviceChanged method to be called when
+ // we change the alias of the device.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath));
+
+ static const std::string new_name("New Device Name");
+ properties->alias.ReplaceValue(new_name);
+ properties->NotifyPropertyChanged(properties->alias.name());
+
+ EXPECT_EQ(1, observer.device_changed_count_);
+ EXPECT_EQ(devices[0], observer.last_device_);
+
+ EXPECT_EQ(UTF8ToUTF16(new_name), devices[0]->GetName());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, DeviceUuidsChanged) {
+ // Simulate a change of advertised services of a device.
+ GetAdapter();
+
+ BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
+ ASSERT_EQ(1U, devices.size());
+ ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
+ devices[0]->GetAddress());
+
+ BluetoothDevice::ServiceList uuids = devices[0]->GetServices();
+ ASSERT_EQ(2U, uuids.size());
+ ASSERT_EQ(uuids[0], "00001800-0000-1000-8000-00805f9b34fb");
+ ASSERT_EQ(uuids[1], "00001801-0000-1000-8000-00805f9b34fb");
+
+ // Install an observer; expect the DeviceChanged method to be called when
+ // we change the class of the device.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath));
+
+ uuids.push_back("0000110c-0000-1000-8000-00805f9b34fb");
+ uuids.push_back("0000110e-0000-1000-8000-00805f9b34fb");
+ uuids.push_back("0000110a-0000-1000-8000-00805f9b34fb");
+
+ properties->uuids.ReplaceValue(uuids);
+ properties->NotifyPropertyChanged(properties->uuids.name());
+
+ EXPECT_EQ(1, observer.device_changed_count_);
+ EXPECT_EQ(devices[0], observer.last_device_);
+
+ // Fetching the value should give the new one.
+ uuids = devices[0]->GetServices();
+ ASSERT_EQ(5U, uuids.size());
+ EXPECT_EQ(uuids[0], "00001800-0000-1000-8000-00805f9b34fb");
+ EXPECT_EQ(uuids[1], "00001801-0000-1000-8000-00805f9b34fb");
+ EXPECT_EQ(uuids[2], "0000110c-0000-1000-8000-00805f9b34fb");
+ EXPECT_EQ(uuids[3], "0000110e-0000-1000-8000-00805f9b34fb");
+ EXPECT_EQ(uuids[4], "0000110a-0000-1000-8000-00805f9b34fb");
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, ForgetDevice) {
+ GetAdapter();
+
+ BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
+ ASSERT_EQ(1U, devices.size());
+ ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
+ devices[0]->GetAddress());
+
+ std::string address = devices[0]->GetAddress();
+
+ // Install an observer; expect the DeviceRemoved method to be called
+ // with the device we remove.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ devices[0]->Forget(
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(0, error_callback_count_);
+
+ EXPECT_EQ(1, observer.device_removed_count_);
+ EXPECT_EQ(address, observer.last_device_address_);
+
+ // GetDevices shouldn't return the device either.
+ devices = adapter_->GetDevices();
+ ASSERT_EQ(0U, devices.size());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, ConnectPairedDevice) {
+ GetAdapter();
+
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kPairedDeviceAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_TRUE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ // Connect without a pairing delegate; since the device is already Paired
+ // this should succeed and the device should become connected.
+ device->Connect(
+ NULL,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ EXPECT_EQ(1, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_TRUE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, ConnectUnpairableDevice) {
+ GetAdapter();
+ DiscoverDevices();
+
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kMicrosoftMouseAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ // Connect without a pairing delegate; since the device does not require
+ // pairing, this should succeed and the device should become connected.
+ device->Connect(
+ NULL,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ EXPECT_EQ(1, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_TRUE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, ConnectConnectedDevice) {
+ GetAdapter();
+
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kPairedDeviceAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_TRUE(device->IsPaired());
+
+ device->Connect(
+ NULL,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ ASSERT_EQ(1, callback_count_);
+ ASSERT_EQ(0, error_callback_count_);
+ callback_count_ = 0;
+
+ ASSERT_TRUE(device->IsConnected());
+
+ // Connect again; since the device is already Connected, this shouldn't do
+ // anything, not even the observer method should be called.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ device->Connect(
+ NULL,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ EXPECT_EQ(0, observer.device_changed_count_);
+
+ EXPECT_TRUE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, ConnectDeviceFails) {
+ GetAdapter();
+ DiscoverDevices();
+
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kAppleMouseAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ // Connect without a pairing delegate; since the device requires pairing,
+ // this should fail with an error.
+ device->Connect(
+ NULL,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_);
+
+ EXPECT_EQ(0, observer.device_changed_count_);
+
+ EXPECT_FALSE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, DisconnectDevice) {
+ GetAdapter();
+
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kPairedDeviceAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_TRUE(device->IsPaired());
+
+ device->Connect(
+ NULL,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ ASSERT_EQ(1, callback_count_);
+ ASSERT_EQ(0, error_callback_count_);
+ callback_count_ = 0;
+
+ ASSERT_TRUE(device->IsConnected());
+ ASSERT_FALSE(device->IsConnecting());
+
+ // Disconnect the device, we should see the observer method fire and the
+ // device get dropped.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ device->Disconnect(
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ EXPECT_EQ(1, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_FALSE(device->IsConnected());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, DisconnectUnconnectedDevice) {
+ GetAdapter();
+
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kPairedDeviceAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_TRUE(device->IsPaired());
+ ASSERT_FALSE(device->IsConnected());
+
+ // Disconnect the device, we should see the observer method fire and the
+ // device get dropped.
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ device->Disconnect(
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+
+ EXPECT_EQ(0, observer.device_changed_count_);
+
+ EXPECT_FALSE(device->IsConnected());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairAppleMouse) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // The Apple Mouse requires no PIN or Passkey to pair; this is equivalent
+ // to Simple Secure Pairing or a device with a fixed 0000 PIN.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kAppleMouseAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(0, pairing_delegate.call_count_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ message_loop.Run();
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ // One change for connected, and one for paired.
+ EXPECT_EQ(2, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_TRUE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+
+ EXPECT_TRUE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+
+ // Make sure the trusted property has been set to true.
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kAppleMousePath));
+ EXPECT_TRUE(properties->trusted.value());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairAppleKeyboard) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // The Apple Keyboard requires that we display a randomly generated
+ // PIN on the screen.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kAppleKeyboardAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.display_pincode_count_);
+ EXPECT_EQ("123456", pairing_delegate.last_pincode_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ message_loop.Run();
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ // One change for connected, and one for paired.
+ EXPECT_EQ(2, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_TRUE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+
+ EXPECT_TRUE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(2, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+
+ // Make sure the trusted property has been set to true.
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kAppleKeyboardPath));
+ EXPECT_TRUE(properties->trusted.value());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairMotorolaKeyboard) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // The Motorola Keyboard requires that we display a randomly generated
+ // Passkey on the screen, and notifies us as it's typed in.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kMotorolaKeyboardAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.display_passkey_count_);
+ EXPECT_EQ(123456U, pairing_delegate.last_passkey_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ // TODO(keybuk): verify we get typing notifications
+
+ message_loop.Run();
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ // One change for connected, and one for paired.
+ EXPECT_EQ(2, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_TRUE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+
+ EXPECT_TRUE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(2, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+
+ // Make sure the trusted property has been set to true.
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kMotorolaKeyboardPath));
+ EXPECT_TRUE(properties->trusted.value());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairSonyHeadphones) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // The Sony Headphones fake requires that the user enters a PIN for them.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kSonyHeadphonesAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ // Set the PIN.
+ device->SetPinCode("1234");
+ message_loop.Run();
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ // One change for connected, and one for paired.
+ EXPECT_EQ(2, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_TRUE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+
+ EXPECT_TRUE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(2, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+
+ // Make sure the trusted property has been set to true.
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kSonyHeadphonesPath));
+ EXPECT_TRUE(properties->trusted.value());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairPhone) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // The fake phone requests that we confirm a displayed passkey.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kPhoneAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_);
+ EXPECT_EQ(123456U, pairing_delegate.last_passkey_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ // Confirm the passkey.
+ device->ConfirmPairing();
+ message_loop.Run();
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ // One change for connected, and one for paired.
+ EXPECT_EQ(2, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_TRUE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+
+ EXPECT_TRUE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(2, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+
+ // Make sure the trusted property has been set to true.
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kPhonePath));
+ EXPECT_TRUE(properties->trusted.value());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairWeirdDevice) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // Use the "weird device" fake that requires that the user enters a Passkey,
+ // this would be some kind of device that has a display, but doesn't use
+ // "just works" - maybe a car?
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kWeirdDeviceAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ // Set the Passkey.
+ device->SetPasskey(1234);
+ message_loop.Run();
+
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_EQ(0, error_callback_count_);
+
+ // One change for connected, and one for paired.
+ EXPECT_EQ(2, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_TRUE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+
+ EXPECT_TRUE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(2, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+
+ // Make sure the trusted property has been set to true.
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kWeirdDevicePath));
+ EXPECT_TRUE(properties->trusted.value());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairingFails) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevice(FakeBluetoothDeviceClient::kVanishingDeviceAddress);
+
+ // The vanishing device times out during pairing
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kVanishingDeviceAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(0, pairing_delegate.call_count_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ // Run the loop to get the error..
+ message_loop.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+
+ EXPECT_EQ(BluetoothDevice::ERROR_AUTH_TIMEOUT, last_connect_error_);
+
+ EXPECT_FALSE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+ EXPECT_FALSE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairingFailsAtConnection) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // Everything seems to go according to plan with the Microsoft Mouse, it
+ // pairs with 0000, but then you can't make connections to it after.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kMicrosoftMouseAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(0, pairing_delegate.call_count_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ message_loop.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_);
+
+ // Just one change for paired, the device should not be connected.
+ EXPECT_EQ(1, observer.device_changed_count_);
+ EXPECT_EQ(device, observer.last_device_);
+
+ EXPECT_FALSE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+
+ EXPECT_TRUE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+
+ // Make sure the trusted property has been set to true still (since pairing
+ // worked).
+ FakeBluetoothDeviceClient::Properties* properties =
+ fake_bluetooth_device_client_->GetProperties(
+ dbus::ObjectPath(FakeBluetoothDeviceClient::kMicrosoftMousePath));
+ EXPECT_TRUE(properties->trusted.value());
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairingRejectedAtPinCode) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // Reject the pairing after we receive a request for the PIN code.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kSonyHeadphonesAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ // Reject the pairing.
+ device->RejectPairing();
+ message_loop.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_);
+
+ // Should be no changes.
+ EXPECT_EQ(0, observer.device_changed_count_);
+ EXPECT_FALSE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+ EXPECT_FALSE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(2, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairingCancelledAtPinCode) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // Cancel the pairing after we receive a request for the PIN code.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kSonyHeadphonesAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ // Cancel the pairing.
+ device->CancelPairing();
+ message_loop.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_);
+
+ // Should be no changes.
+ EXPECT_EQ(0, observer.device_changed_count_);
+ EXPECT_FALSE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+ EXPECT_FALSE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(2, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairingRejectedAtPasskey) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // Reject the pairing after we receive a request for the passkey.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kWeirdDeviceAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ // Reject the pairing.
+ device->RejectPairing();
+ message_loop.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_);
+
+ // Should be no changes.
+ EXPECT_EQ(0, observer.device_changed_count_);
+ EXPECT_FALSE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+ EXPECT_FALSE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(2, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairingCancelledAtPasskey) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // Cancel the pairing after we receive a request for the passkey.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kWeirdDeviceAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ // Cancel the pairing.
+ device->CancelPairing();
+ message_loop.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_);
+
+ // Should be no changes.
+ EXPECT_EQ(0, observer.device_changed_count_);
+ EXPECT_FALSE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+ EXPECT_FALSE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(2, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairingRejectedAtConfirmation) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // Reject the pairing after we receive a request for passkey confirmation.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kPhoneAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ // Reject the pairing.
+ device->RejectPairing();
+ message_loop.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_);
+
+ // Should be no changes.
+ EXPECT_EQ(0, observer.device_changed_count_);
+ EXPECT_FALSE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+ EXPECT_FALSE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(2, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairingCancelledAtConfirmation) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // Cancel the pairing after we receive a request for the passkey.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kPhoneAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ // Cancel the pairing.
+ device->CancelPairing();
+ message_loop.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_);
+
+ // Should be no changes.
+ EXPECT_EQ(0, observer.device_changed_count_);
+ EXPECT_FALSE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+ EXPECT_FALSE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(2, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+}
+
+TEST_F(BluetoothExperimentalChromeOSTest, PairingCancelledInFlight) {
+ base::MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+ GetAdapter();
+ DiscoverDevices();
+
+ // Cancel the pairing while we're waiting for the remote host.
+ BluetoothDevice* device = adapter_->GetDevice(
+ FakeBluetoothDeviceClient::kAppleMouseAddress);
+ ASSERT_TRUE(device != NULL);
+ ASSERT_FALSE(device->IsPaired());
+
+ TestObserver observer(adapter_);
+ adapter_->AddObserver(&observer);
+
+ TestPairingDelegate pairing_delegate;
+ device->Connect(
+ &pairing_delegate,
+ base::Bind(&BluetoothExperimentalChromeOSTest::Callback,
+ base::Unretained(this)),
+ base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback,
+ base::Unretained(this)));
+
+ EXPECT_EQ(0, pairing_delegate.call_count_);
+ EXPECT_TRUE(device->IsConnecting());
+
+ // Cancel the pairing.
+ device->CancelPairing();
+ message_loop.Run();
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_EQ(1, error_callback_count_);
+ EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_);
+
+ // Should be no changes.
+ EXPECT_EQ(0, observer.device_changed_count_);
+ EXPECT_FALSE(device->IsConnected());
+ EXPECT_FALSE(device->IsConnecting());
+ EXPECT_FALSE(device->IsPaired());
+
+ // Pairing dialog should be dismissed
+ EXPECT_EQ(1, pairing_delegate.call_count_);
+ EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+}
+
+} // namespace chromeos