Merge from Chromium at DEPS revision 257591

This commit was generated by merge_to_master.py.

Change-Id: I0010df2ec3fbb5d4947cd026de2feb150ce7a6b5
diff --git a/device/bluetooth/OWNERS b/device/bluetooth/OWNERS
index 5bdf96a..7bf8537 100644
--- a/device/bluetooth/OWNERS
+++ b/device/bluetooth/OWNERS
@@ -1,2 +1,2 @@
 keybuk@chromium.org
-youngki@chromium.org
+armansito@chromium.org
diff --git a/device/bluetooth/bluetooth.gyp b/device/bluetooth/bluetooth.gyp
index c4012bd..2d70842 100644
--- a/device/bluetooth/bluetooth.gyp
+++ b/device/bluetooth/bluetooth.gyp
@@ -15,9 +15,9 @@
         '../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
         '../../net/net.gyp:net',
         '../../third_party/libxml/libxml.gyp:libxml',
+        '../../ui/base/ui_base.gyp:ui_base',
         '../../ui/gfx/gfx.gyp:gfx',
         '../../ui/gfx/gfx.gyp:gfx_geometry',
-        '../../ui/ui.gyp:ui',
         'bluetooth_strings.gyp:device_bluetooth_strings',
       ],
       'sources': [
@@ -39,6 +39,8 @@
         'bluetooth_device_mac.mm',
         'bluetooth_device_win.cc',
         'bluetooth_device_win.h',
+        'bluetooth_discovery_session.cc',
+        'bluetooth_discovery_session.h',
         'bluetooth_gatt_characteristic.cc',
         'bluetooth_gatt_characteristic.h',
         'bluetooth_gatt_descriptor.cc',
@@ -48,6 +50,8 @@
         'bluetooth_init_win.cc',
         'bluetooth_init_win.h',
         'bluetooth_out_of_band_pairing_data.h',
+        'bluetooth_pairing_chromeos.cc',
+        'bluetooth_pairing_chromeos.h',
         'bluetooth_profile.cc',
         'bluetooth_profile.h',
         'bluetooth_profile_chromeos.cc',
@@ -119,6 +123,8 @@
         'test/mock_bluetooth_adapter.h',
         'test/mock_bluetooth_device.cc',
         'test/mock_bluetooth_device.h',
+        'test/mock_bluetooth_discovery_session.cc',
+        'test/mock_bluetooth_discovery_session.h',
         'test/mock_bluetooth_profile.cc',
         'test/mock_bluetooth_profile.h',
         'test/mock_bluetooth_socket.cc',
diff --git a/device/bluetooth/bluetooth_adapter.cc b/device/bluetooth/bluetooth_adapter.cc
index 8cce6ec..ffa27ef 100644
--- a/device/bluetooth/bluetooth_adapter.cc
+++ b/device/bluetooth/bluetooth_adapter.cc
@@ -4,18 +4,31 @@
 
 #include "device/bluetooth/bluetooth_adapter.h"
 
+#include "base/bind.h"
 #include "base/stl_util.h"
 #include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_discovery_session.h"
 
 namespace device {
 
-BluetoothAdapter::BluetoothAdapter() {
+BluetoothAdapter::BluetoothAdapter()
+    : weak_ptr_factory_(this) {
 }
 
 BluetoothAdapter::~BluetoothAdapter() {
   STLDeleteValues(&devices_);
 }
 
+void BluetoothAdapter::StartDiscoverySession(
+    const DiscoverySessionCallback& callback,
+    const ErrorCallback& error_callback) {
+  AddDiscoverySession(
+      base::Bind(&BluetoothAdapter::OnStartDiscoverySession,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 callback),
+      error_callback);
+}
+
 BluetoothAdapter::DeviceList BluetoothAdapter::GetDevices() {
   ConstDeviceList const_devices =
     const_cast<const BluetoothAdapter *>(this)->GetDevices();
@@ -52,4 +65,66 @@
   return NULL;
 }
 
+void BluetoothAdapter::AddPairingDelegate(
+    BluetoothDevice::PairingDelegate* pairing_delegate,
+    PairingDelegatePriority priority) {
+  // Remove the delegate, if it already exists, before inserting to allow a
+  // change of priority.
+  RemovePairingDelegate(pairing_delegate);
+
+  // Find the first point with a lower priority, or the end of the list.
+  std::list<PairingDelegatePair>::iterator iter = pairing_delegates_.begin();
+  while (iter != pairing_delegates_.end() && iter->second >= priority)
+    ++iter;
+
+  pairing_delegates_.insert(iter, std::make_pair(pairing_delegate, priority));
+}
+
+void BluetoothAdapter::RemovePairingDelegate(
+    BluetoothDevice::PairingDelegate* pairing_delegate) {
+  for (std::list<PairingDelegatePair>::iterator iter =
+       pairing_delegates_.begin(); iter != pairing_delegates_.end(); ++iter) {
+    if (iter->first == pairing_delegate) {
+      RemovePairingDelegateInternal(pairing_delegate);
+      pairing_delegates_.erase(iter);
+      return;
+    }
+  }
+}
+
+BluetoothDevice::PairingDelegate* BluetoothAdapter::DefaultPairingDelegate() {
+  if (pairing_delegates_.empty())
+    return NULL;
+
+  return pairing_delegates_.front().first;
+}
+
+void BluetoothAdapter::OnStartDiscoverySession(
+    const DiscoverySessionCallback& callback) {
+  VLOG(1) << "Discovery session started!";
+  scoped_ptr<BluetoothDiscoverySession> discovery_session(
+      new BluetoothDiscoverySession(scoped_refptr<BluetoothAdapter>(this)));
+  discovery_sessions_.insert(discovery_session.get());
+  callback.Run(discovery_session.Pass());
+}
+
+void BluetoothAdapter::MarkDiscoverySessionsAsInactive() {
+  // As sessions are marked as inactive they will notify the adapter that they
+  // have become inactive, upon which the adapter will remove them from
+  // |discovery_sessions_|. To avoid invalidating the iterator, make a copy
+  // here.
+  std::set<BluetoothDiscoverySession*> temp(discovery_sessions_);
+  for (std::set<BluetoothDiscoverySession*>::iterator
+          iter = temp.begin();
+       iter != temp.end(); ++iter) {
+    (*iter)->MarkAsInactive();
+  }
+}
+
+void BluetoothAdapter::DiscoverySessionBecameInactive(
+    BluetoothDiscoverySession* discovery_session) {
+  DCHECK(!discovery_session->IsActive());
+  discovery_sessions_.erase(discovery_session);
+}
+
 }  // namespace device
diff --git a/device/bluetooth/bluetooth_adapter.h b/device/bluetooth/bluetooth_adapter.h
index 24e9dcd..e713ff4 100644
--- a/device/bluetooth/bluetooth_adapter.h
+++ b/device/bluetooth/bluetooth_adapter.h
@@ -5,16 +5,20 @@
 #ifndef DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_H_
 #define DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_H_
 
+#include <list>
 #include <map>
+#include <set>
 #include <string>
-#include <vector>
+#include <utility>
 
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "device/bluetooth/bluetooth_device.h"
 
 namespace device {
 
-class BluetoothDevice;
+class BluetoothDiscoverySession;
 
 struct BluetoothOutOfBandPairingData;
 
@@ -76,7 +80,7 @@
 
   // The ErrorCallback is used for methods that can fail in which case it
   // is called, in the success case the callback is simply not called.
-  typedef base::Callback<void()> ErrorCallback;
+  typedef base::Closure ErrorCallback;
 
   // The BluetoothOutOfBandPairingDataCallback is used to return
   // BluetoothOutOfBandPairingData to the caller.
@@ -135,26 +139,27 @@
   // Indicates whether the adapter is currently discovering new devices.
   virtual bool IsDiscovering() const = 0;
 
-  // Requests that the adapter begin discovering new devices, code must
-  // always call this method if they require the adapter be in discovery
-  // and should not make it conditional on the value of IsDiscovering()
-  // as other adapter users may be making the same request. Code must also
-  // call StopDiscovering() when done. On success |callback| will be called,
-  // on failure |error_callback| will be called instead.
+  // Requests the adapter to start a new discovery session. On success, a new
+  // instance of BluetoothDiscoverySession will be returned to the caller via
+  // |callback| and the adapter will be discovering nearby Bluetooth devices.
+  // The returned BluetoothDiscoverySession is owned by the caller and it's the
+  // owner's responsibility to properly clean it up and stop the session when
+  // device discovery is no longer needed.
   //
-  // Since discovery may already be in progress when this method is called,
-  // callers should retrieve the current set of discovered devices by calling
-  // GetDevices() and checking for those with IsPaired() as false.
-  virtual void StartDiscovering(const base::Closure& callback,
-                                const ErrorCallback& error_callback) = 0;
-
-  // Requests that an earlier call to StartDiscovering() be cancelled; the
-  // adapter may not actually cease discovering devices if other callers
-  // have called StartDiscovering() and not yet called this method. On
-  // success |callback| will be called, on failure |error_callback| will be
-  // called instead.
-  virtual void StopDiscovering(const base::Closure& callback,
-                               const ErrorCallback& error_callback) = 0;
+  // If clients desire device discovery to run, they should always call this
+  // method and never make it conditional on the value of IsDiscovering(), as
+  // another client might cause discovery to stop unexpectedly. Hence, clients
+  // should always obtain a BluetoothDiscoverySession and call
+  // BluetoothDiscoverySession::Stop when done. When this method gets called,
+  // device discovery may actually be in progress. Clients can call GetDevices()
+  // and check for those with IsPaired() as false to obtain the list of devices
+  // that have been discovered so far. Otherwise, clients can be notified of all
+  // new and lost devices can by implementing the Observer methods "DeviceAdded"
+  // and "DeviceRemoved".
+  typedef base::Callback<void(scoped_ptr<BluetoothDiscoverySession>)>
+      DiscoverySessionCallback;
+  virtual void StartDiscoverySession(const DiscoverySessionCallback& callback,
+                                     const ErrorCallback& error_callback);
 
   // Requests the list of devices from the adapter, all are returned
   // including those currently connected and those paired. Use the
@@ -175,17 +180,118 @@
       const BluetoothOutOfBandPairingDataCallback& callback,
       const ErrorCallback& error_callback) = 0;
 
+  // Possible priorities for AddPairingDelegate(), low is intended for
+  // permanent UI and high is intended for interactive UI or applications.
+  enum PairingDelegatePriority {
+    PAIRING_DELEGATE_PRIORITY_LOW,
+    PAIRING_DELEGATE_PRIORITY_HIGH
+  };
+
+  // Adds a default pairing delegate with priority |priority|, method calls
+  // will be made on |pairing_delegate| for incoming pairing requests if the
+  // priority is higher than any other registered, or for those of the same
+  // priority, the first registered.
+  //
+  // |pairing_delegate| must not be freed without first calling
+  // RemovePairingDelegate().
+  virtual void AddPairingDelegate(
+      BluetoothDevice::PairingDelegate* pairing_delegate,
+      PairingDelegatePriority priority);
+
+  // Removes a previously added pairing delegate.
+  virtual void RemovePairingDelegate(
+      BluetoothDevice::PairingDelegate* pairing_delegate);
+
+  // Returns the first registered pairing delegate with the highest priority,
+  // or NULL if no delegate is registered. Used to select the delegate for
+  // incoming pairing requests.
+  virtual BluetoothDevice::PairingDelegate* DefaultPairingDelegate();
+
  protected:
   friend class base::RefCounted<BluetoothAdapter>;
+  friend class BluetoothDiscoverySession;
   BluetoothAdapter();
   virtual ~BluetoothAdapter();
 
+  // Internal methods for initiating and terminating device discovery sessions.
+  // An implementation of BluetoothAdapter keeps an internal reference count to
+  // make sure that the underlying controller is constantly searching for nearby
+  // devices and retrieving information from them as long as there are clients
+  // who have requested discovery. These methods behave in the following way:
+  //
+  // On a call to AddDiscoverySession:
+  //    - If there is a pending request to the subsystem, queue this request to
+  //      execute once the pending requests are done.
+  //    - If the count is 0, issue a request to the subsystem to start
+  //      device discovery. On success, increment the count to 1.
+  //    - If the count is greater than 0, increment the count and return
+  //      success.
+  //    As long as the count is non-zero, the underlying controller will be
+  //    discovering for devices. This means that Chrome will restart device
+  //    scan and inquiry sessions if they ever end, unless these sessions
+  //    terminate due to an unexpected reason.
+  //
+  // On a call to RemoveDiscoverySession:
+  //    - If there is a pending request to the subsystem, queue this request to
+  //      execute once the pending requests are done.
+  //    - If the count is 0, return failure, as there is no active discovery
+  //      session.
+  //    - If the count is 1, issue a request to the subsystem to stop device
+  //      discovery and decrement the count to 0 on success.
+  //    - If the count is greater than 1, decrement the count and return
+  //      success.
+  //
+  // These methods invoke |callback| for success and |error_callback| for
+  // failures.
+  virtual void AddDiscoverySession(const base::Closure& callback,
+                                   const ErrorCallback& error_callback) = 0;
+  virtual void RemoveDiscoverySession(const base::Closure& callback,
+                                      const ErrorCallback& error_callback) = 0;
+
+  // Called by RemovePairingDelegate() in order to perform any class-specific
+  // internal functionality necessary to remove the pairing delegate, such as
+  // cleaning up ongoing pairings using it.
+  virtual void RemovePairingDelegateInternal(
+      BluetoothDevice::PairingDelegate* pairing_delegate) = 0;
+
+  // Success callback passed to AddDiscoverySession by StartDiscoverySession.
+  void OnStartDiscoverySession(const DiscoverySessionCallback& callback);
+
+  // Marks all known DiscoverySession instances as inactive. Called by
+  // BluetoothAdapter in the event that the adapter unexpectedly stops
+  // discovering. This should be called by all platform implementations.
+  void MarkDiscoverySessionsAsInactive();
+
+  // Removes |discovery_session| from |discovery_sessions_|, if its in there.
+  // Called by DiscoverySession when an instance is destroyed or becomes
+  // inactive.
+  void DiscoverySessionBecameInactive(
+      BluetoothDiscoverySession* discovery_session);
+
   // Devices paired with, connected to, discovered by, or visible to the
   // adapter. The key is the Bluetooth address of the device and the value
   // is the BluetoothDevice object whose lifetime is managed by the
   // adapter instance.
   typedef std::map<const std::string, BluetoothDevice*> DevicesMap;
   DevicesMap devices_;
+
+  // Default pairing delegates registered with the adapter.
+  typedef std::pair<BluetoothDevice::PairingDelegate*,
+                    PairingDelegatePriority> PairingDelegatePair;
+  std::list<PairingDelegatePair> pairing_delegates_;
+
+ private:
+  // List of active DiscoverySession objects. This is used to notify sessions to
+  // become inactive in case of an unexpected change to the adapter discovery
+  // state. We keep raw pointers, with the invariant that a DiscoverySession
+  // will remove itself from this list when it gets destroyed or becomes
+  // inactive by calling DiscoverySessionBecameInactive(), hence no pointers to
+  // deallocated sessions are kept.
+  std::set<BluetoothDiscoverySession*> discovery_sessions_;
+
+  // Note: This should remain the last member so it'll be destroyed and
+  // invalidate its weak pointers before any other members are destroyed.
+  base::WeakPtrFactory<BluetoothAdapter> weak_ptr_factory_;
 };
 
 }  // namespace device
diff --git a/device/bluetooth/bluetooth_adapter_chromeos.cc b/device/bluetooth/bluetooth_adapter_chromeos.cc
index 0d962b9..820972e 100644
--- a/device/bluetooth/bluetooth_adapter_chromeos.cc
+++ b/device/bluetooth/bluetooth_adapter_chromeos.cc
@@ -11,23 +11,53 @@
 #include "base/metrics/histogram.h"
 #include "base/sys_info.h"
 #include "chromeos/dbus/bluetooth_adapter_client.h"
+#include "chromeos/dbus/bluetooth_agent_manager_client.h"
+#include "chromeos/dbus/bluetooth_agent_service_provider.h"
 #include "chromeos/dbus/bluetooth_device_client.h"
 #include "chromeos/dbus/bluetooth_input_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_device_chromeos.h"
+#include "device/bluetooth/bluetooth_pairing_chromeos.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
 
 using device::BluetoothAdapter;
 using device::BluetoothDevice;
 
+namespace {
+
+// The agent path is relatively meaningless since BlueZ only permits one to
+// exist per D-Bus connection, it just has to be unique within Chromium.
+const char kAgentPath[] = "/org/chromium/bluetooth_agent";
+
+void OnUnregisterAgentError(const std::string& error_name,
+                            const std::string& error_message) {
+  // It's okay if the agent didn't exist, it means we never saw an adapter.
+  if (error_name == bluetooth_agent_manager::kErrorDoesNotExist)
+    return;
+
+  LOG(WARNING) << "Failed to unregister pairing agent: "
+               << error_name << ": " << error_message;
+}
+
+}  // namespace
+
 namespace chromeos {
 
 BluetoothAdapterChromeOS::BluetoothAdapterChromeOS()
-    : weak_ptr_factory_(this) {
+    : num_discovery_sessions_(0),
+      discovery_request_pending_(false),
+      weak_ptr_factory_(this) {
   DBusThreadManager::Get()->GetBluetoothAdapterClient()->AddObserver(this);
   DBusThreadManager::Get()->GetBluetoothDeviceClient()->AddObserver(this);
   DBusThreadManager::Get()->GetBluetoothInputClient()->AddObserver(this);
 
+  // Register the pairing agent.
+  dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
+  agent_.reset(BluetoothAgentServiceProvider::Create(
+      system_bus, dbus::ObjectPath(kAgentPath), this));
+  DCHECK(agent_.get());
+
   std::vector<dbus::ObjectPath> object_paths =
       DBusThreadManager::Get()->GetBluetoothAdapterClient()->GetAdapters();
 
@@ -41,6 +71,13 @@
   DBusThreadManager::Get()->GetBluetoothAdapterClient()->RemoveObserver(this);
   DBusThreadManager::Get()->GetBluetoothDeviceClient()->RemoveObserver(this);
   DBusThreadManager::Get()->GetBluetoothInputClient()->RemoveObserver(this);
+
+  VLOG(1) << "Unregistering pairing agent";
+  DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
+      UnregisterAgent(
+          dbus::ObjectPath(kAgentPath),
+          base::Bind(&base::DoNothing),
+          base::Bind(&OnUnregisterAgentError));
 }
 
 void BluetoothAdapterChromeOS::AddObserver(
@@ -158,44 +195,28 @@
   return properties->discovering.value();
 }
 
-void BluetoothAdapterChromeOS::StartDiscovering(
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
-  // BlueZ counts discovery sessions, and permits multiple sessions for a
-  // single connection, so issue a StartDiscovery() call for every use
-  // within Chromium for the right behavior.
-  DBusThreadManager::Get()->GetBluetoothAdapterClient()->
-      StartDiscovery(
-          object_path_,
-          base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     callback),
-          base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     error_callback));
-}
-
-void BluetoothAdapterChromeOS::StopDiscovering(
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
-  // Inform BlueZ to stop one of our open discovery sessions.
-  DBusThreadManager::Get()->GetBluetoothAdapterClient()->
-      StopDiscovery(
-          object_path_,
-          base::Bind(&BluetoothAdapterChromeOS::OnStopDiscovery,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     callback),
-          base::Bind(&BluetoothAdapterChromeOS::OnStopDiscoveryError,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     error_callback));
-}
-
 void BluetoothAdapterChromeOS::ReadLocalOutOfBandPairingData(
     const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback,
     const ErrorCallback& error_callback) {
   error_callback.Run();
 }
 
+void BluetoothAdapterChromeOS::RemovePairingDelegateInternal(
+    BluetoothDevice::PairingDelegate* pairing_delegate) {
+  // Before removing a pairing delegate make sure that there aren't any devices
+  // currently using it; if there are, clear the pairing context which will
+  // make any responses no-ops.
+  for (DevicesMap::iterator iter = devices_.begin();
+       iter != devices_.end(); ++iter) {
+    BluetoothDeviceChromeOS* device_chromeos =
+        static_cast<BluetoothDeviceChromeOS*>(iter->second);
+
+    BluetoothPairingChromeOS* pairing = device_chromeos->GetPairing();
+    if (pairing && pairing->GetPairingDelegate() == pairing_delegate)
+      device_chromeos->EndPairing();
+  }
+}
+
 void BluetoothAdapterChromeOS::AdapterAdded(
     const dbus::ObjectPath& object_path) {
   // Set the adapter to the newly added adapter only if no adapter is present.
@@ -282,6 +303,12 @@
       property_name == properties->uuids.name())
     NotifyDeviceChanged(device_chromeos);
 
+  // When a device becomes paired, mark it as trusted so that the user does
+  // not need to approve every incoming connection
+  if (property_name == properties->paired.name() &&
+      properties->paired.value())
+    device_chromeos->SetTrusted();
+
   // UMA connection counting
   if (property_name == properties->connected.name()) {
     int count = 0;
@@ -315,6 +342,157 @@
     NotifyDeviceChanged(device_chromeos);
 }
 
+void BluetoothAdapterChromeOS::Release() {
+  DCHECK(agent_.get());
+  VLOG(1) << "Release";
+
+  // Called after we unregister the pairing agent, e.g. when changing I/O
+  // capabilities. Nothing much to be done right now.
+}
+
+void BluetoothAdapterChromeOS::RequestPinCode(
+    const dbus::ObjectPath& device_path,
+    const PinCodeCallback& callback) {
+  DCHECK(agent_.get());
+  VLOG(1) << device_path.value() << ": RequestPinCode";
+
+  BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+  if (!pairing) {
+    callback.Run(REJECTED, "");
+    return;
+  }
+
+  pairing->RequestPinCode(callback);
+}
+
+void BluetoothAdapterChromeOS::DisplayPinCode(
+    const dbus::ObjectPath& device_path,
+    const std::string& pincode) {
+  DCHECK(agent_.get());
+  VLOG(1) << device_path.value() << ": DisplayPinCode: " << pincode;
+
+  BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+  if (!pairing)
+    return;
+
+  pairing->DisplayPinCode(pincode);
+}
+
+void BluetoothAdapterChromeOS::RequestPasskey(
+    const dbus::ObjectPath& device_path,
+    const PasskeyCallback& callback) {
+  DCHECK(agent_.get());
+  VLOG(1) << device_path.value() << ": RequestPasskey";
+
+  BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+  if (!pairing) {
+    callback.Run(REJECTED, 0);
+    return;
+  }
+
+  pairing->RequestPasskey(callback);
+}
+
+void BluetoothAdapterChromeOS::DisplayPasskey(
+    const dbus::ObjectPath& device_path,
+    uint32 passkey,
+    uint16 entered) {
+  DCHECK(agent_.get());
+  VLOG(1) << device_path.value() << ": DisplayPasskey: " << passkey
+          << " (" << entered << " entered)";
+
+  BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+  if (!pairing)
+    return;
+
+  if (entered == 0)
+    pairing->DisplayPasskey(passkey);
+
+  pairing->KeysEntered(entered);
+}
+
+void BluetoothAdapterChromeOS::RequestConfirmation(
+    const dbus::ObjectPath& device_path,
+    uint32 passkey,
+    const ConfirmationCallback& callback) {
+  DCHECK(agent_.get());
+  VLOG(1) << device_path.value() << ": RequestConfirmation: " << passkey;
+
+  BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+  if (!pairing) {
+    callback.Run(REJECTED);
+    return;
+  }
+
+  pairing->RequestConfirmation(passkey, callback);
+}
+
+void BluetoothAdapterChromeOS::RequestAuthorization(
+    const dbus::ObjectPath& device_path,
+    const ConfirmationCallback& callback) {
+  DCHECK(agent_.get());
+  VLOG(1) << device_path.value() << ": RequestAuthorization";
+
+  BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+  if (!pairing) {
+    callback.Run(REJECTED);
+    return;
+  }
+
+  pairing->RequestAuthorization(callback);
+}
+
+void BluetoothAdapterChromeOS::AuthorizeService(
+    const dbus::ObjectPath& device_path,
+    const std::string& uuid,
+    const ConfirmationCallback& callback) {
+  DCHECK(agent_.get());
+  VLOG(1) << device_path.value() << ": AuthorizeService: " << uuid;
+
+  // TODO(keybuk): implement
+  callback.Run(CANCELLED);
+}
+
+void BluetoothAdapterChromeOS::Cancel() {
+  DCHECK(agent_.get());
+  VLOG(1) << "Cancel";
+}
+
+void BluetoothAdapterChromeOS::OnRegisterAgent() {
+  VLOG(1) << "Pairing agent registered, requesting to be made default";
+
+  DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
+      RequestDefaultAgent(
+          dbus::ObjectPath(kAgentPath),
+          base::Bind(&BluetoothAdapterChromeOS::OnRequestDefaultAgent,
+                     weak_ptr_factory_.GetWeakPtr()),
+          base::Bind(&BluetoothAdapterChromeOS::OnRequestDefaultAgentError,
+                     weak_ptr_factory_.GetWeakPtr()));
+
+}
+
+void BluetoothAdapterChromeOS::OnRegisterAgentError(
+    const std::string& error_name,
+    const std::string& error_message) {
+  // Our agent being already registered isn't an error.
+  if (error_name == bluetooth_agent_manager::kErrorAlreadyExists)
+    return;
+
+  LOG(WARNING) << ": Failed to register pairing agent: "
+               << error_name << ": " << error_message;
+}
+
+void BluetoothAdapterChromeOS::OnRequestDefaultAgent() {
+  VLOG(1) << "Pairing agent now default";
+}
+
+void BluetoothAdapterChromeOS::OnRequestDefaultAgentError(
+    const std::string& error_name,
+    const std::string& error_message) {
+  LOG(WARNING) << ": Failed to make pairing agent default: "
+               << error_name << ": " << error_message;
+}
+
 BluetoothDeviceChromeOS*
 BluetoothAdapterChromeOS::GetDeviceWithPath(
     const dbus::ObjectPath& object_path) {
@@ -329,12 +507,45 @@
   return NULL;
 }
 
+BluetoothPairingChromeOS* BluetoothAdapterChromeOS::GetPairing(
+    const dbus::ObjectPath& object_path)
+{
+  BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path);
+  if (!device_chromeos) {
+    LOG(WARNING) << "Pairing Agent request for unknown device: "
+                 << object_path.value();
+    return NULL;
+  }
+
+  BluetoothPairingChromeOS* pairing = device_chromeos->GetPairing();
+  if (pairing)
+    return pairing;
+
+  // The device doesn't have its own pairing context, so this is an incoming
+  // pairing request that should use our best default delegate (if we have one).
+  BluetoothDevice::PairingDelegate* pairing_delegate = DefaultPairingDelegate();
+  if (!pairing_delegate)
+    return NULL;
+
+  return device_chromeos->BeginPairing(pairing_delegate);
+}
+
 void BluetoothAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) {
   DCHECK(!IsPresent());
   object_path_ = object_path;
 
   VLOG(1) << object_path_.value() << ": using adapter.";
 
+  VLOG(1) << "Registering pairing agent";
+  DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
+      RegisterAgent(
+          dbus::ObjectPath(kAgentPath),
+          bluetooth_agent_manager::kKeyboardDisplayCapability,
+          base::Bind(&BluetoothAdapterChromeOS::OnRegisterAgent,
+                     weak_ptr_factory_.GetWeakPtr()),
+          base::Bind(&BluetoothAdapterChromeOS::OnRegisterAgentError,
+                     weak_ptr_factory_.GetWeakPtr()));
+
   SetDefaultAdapterName();
 
   BluetoothAdapterClient::Properties* properties =
@@ -424,6 +635,15 @@
 
 void BluetoothAdapterChromeOS::DiscoveringChanged(
     bool discovering) {
+  // If the adapter stopped discovery due to a reason other than a request by
+  // us, reset the count to 0.
+  VLOG(1) << "Discovering changed: " << discovering;
+  if (!discovering && !discovery_request_pending_
+      && num_discovery_sessions_ > 0) {
+    VLOG(1) << "Marking sessions as inactive.";
+    num_discovery_sessions_ = 0;
+    MarkDiscoverySessionsAsInactive();
+  }
   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
                     AdapterDiscoveringChanged(this, discovering));
 }
@@ -466,21 +686,144 @@
     error_callback.Run();
 }
 
+void BluetoothAdapterChromeOS::AddDiscoverySession(
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  VLOG(1) << __func__;
+  if (discovery_request_pending_) {
+    // The pending request is either to stop a previous session or to start a
+    // new one. Either way, queue this one.
+    DCHECK(num_discovery_sessions_ == 1 || num_discovery_sessions_ == 0);
+    VLOG(1) << "Pending request to start/stop device discovery. Queueing "
+            << "request to start a new discovery session.";
+    discovery_request_queue_.push(std::make_pair(callback, error_callback));
+    return;
+  }
+
+  // The adapter is already discovering.
+  if (num_discovery_sessions_ > 0) {
+    DCHECK(IsDiscovering());
+    DCHECK(!discovery_request_pending_);
+    num_discovery_sessions_++;
+    callback.Run();
+    return;
+  }
+
+  // There are no active discovery sessions.
+  DCHECK(num_discovery_sessions_ == 0);
+
+  // This is the first request to start device discovery.
+  discovery_request_pending_ = true;
+  DBusThreadManager::Get()->GetBluetoothAdapterClient()->
+      StartDiscovery(
+          object_path_,
+          base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     callback),
+          base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     callback,
+                     error_callback));
+}
+
+void BluetoothAdapterChromeOS::RemoveDiscoverySession(
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  VLOG(1) << __func__;
+  // There are active sessions other than the one currently being removed.
+  if (num_discovery_sessions_ > 1) {
+    DCHECK(IsDiscovering());
+    DCHECK(!discovery_request_pending_);
+    num_discovery_sessions_--;
+    callback.Run();
+    return;
+  }
+
+  // If there is a pending request to BlueZ, then queue this request.
+  if (discovery_request_pending_) {
+    VLOG(1) << "Pending request to start/stop device discovery. Queueing "
+            << "request to stop discovery session.";
+    error_callback.Run();
+    return;
+  }
+
+  // There are no active sessions. Return error.
+  if (num_discovery_sessions_ == 0) {
+    // TODO(armansito): This should never happen once we have the
+    // DiscoverySession API. Replace this case with an assert once it's
+    // the deprecated methods have been removed. (See crbug.com/3445008).
+    VLOG(1) << "No active discovery sessions. Returning error.";
+    error_callback.Run();
+    return;
+  }
+
+  // There is exactly one active discovery session. Request BlueZ to stop
+  // discovery.
+  DCHECK(num_discovery_sessions_ == 1);
+  discovery_request_pending_ = true;
+  DBusThreadManager::Get()->GetBluetoothAdapterClient()->
+      StopDiscovery(
+          object_path_,
+          base::Bind(&BluetoothAdapterChromeOS::OnStopDiscovery,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     callback),
+          base::Bind(&BluetoothAdapterChromeOS::OnStopDiscoveryError,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     error_callback));
+}
+
 void BluetoothAdapterChromeOS::OnStartDiscovery(const base::Closure& callback) {
+  // Report success on the original request and increment the count.
+  VLOG(1) << __func__;
+  DCHECK(discovery_request_pending_);
+  DCHECK(num_discovery_sessions_ == 0);
+  discovery_request_pending_ = false;
+  num_discovery_sessions_++;
   callback.Run();
+
+  // Try to add a new discovery session for each queued request.
+  ProcessQueuedDiscoveryRequests();
 }
 
 void BluetoothAdapterChromeOS::OnStartDiscoveryError(
+    const base::Closure& callback,
     const ErrorCallback& error_callback,
     const std::string& error_name,
     const std::string& error_message) {
   LOG(WARNING) << object_path_.value() << ": Failed to start discovery: "
                << error_name << ": " << error_message;
-  error_callback.Run();
+
+  // Failed to start discovery. This can only happen if the count is at 0.
+  DCHECK(num_discovery_sessions_ == 0);
+  DCHECK(discovery_request_pending_);
+  discovery_request_pending_ = false;
+
+  // Discovery request may fail if discovery was previously initiated by Chrome,
+  // but the session were invalidated due to the discovery state unexpectedly
+  // changing to false and then back to true. In this case, report success.
+  if (error_name == bluetooth_device::kErrorInProgress && IsDiscovering()) {
+    VLOG(1) << "Discovery previously initiated. Reporting success.";
+    num_discovery_sessions_++;
+    callback.Run();
+  } else {
+    error_callback.Run();
+  }
+
+  // Try to add a new discovery session for each queued request.
+  ProcessQueuedDiscoveryRequests();
 }
 
 void BluetoothAdapterChromeOS::OnStopDiscovery(const base::Closure& callback) {
+  // Report success on the original request and decrement the count.
+  VLOG(1) << __func__;
+  DCHECK(discovery_request_pending_);
+  DCHECK(num_discovery_sessions_ == 1);
+  discovery_request_pending_ = false;
+  num_discovery_sessions_--;
   callback.Run();
+
+  // Try to add a new discovery session for each queued request.
+  ProcessQueuedDiscoveryRequests();
 }
 
 void BluetoothAdapterChromeOS::OnStopDiscoveryError(
@@ -489,7 +832,30 @@
     const std::string& error_message) {
   LOG(WARNING) << object_path_.value() << ": Failed to stop discovery: "
                << error_name << ": " << error_message;
+
+  // Failed to stop discovery. This can only happen if the count is at 1.
+  DCHECK(discovery_request_pending_);
+  DCHECK(num_discovery_sessions_ == 1);
+  discovery_request_pending_ = false;
   error_callback.Run();
+
+  // Try to add a new discovery session for each queued request.
+  ProcessQueuedDiscoveryRequests();
+}
+
+void BluetoothAdapterChromeOS::ProcessQueuedDiscoveryRequests() {
+  while (!discovery_request_queue_.empty()) {
+    VLOG(1) << "Process queued discovery request.";
+    DiscoveryCallbackPair callbacks = discovery_request_queue_.front();
+    discovery_request_queue_.pop();
+    AddDiscoverySession(callbacks.first, callbacks.second);
+
+    // If the queued request resulted in a pending call, then let it
+    // asynchonously process the remaining queued requests once the pending
+    // call returns.
+    if (discovery_request_pending_)
+      return;
+  }
 }
 
 }  // namespace chromeos
diff --git a/device/bluetooth/bluetooth_adapter_chromeos.h b/device/bluetooth/bluetooth_adapter_chromeos.h
index b400ed3..f9e5ea2 100644
--- a/device/bluetooth/bluetooth_adapter_chromeos.h
+++ b/device/bluetooth/bluetooth_adapter_chromeos.h
@@ -5,14 +5,17 @@
 #ifndef DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_CHROMEOS_H_
 #define DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_CHROMEOS_H_
 
+#include <queue>
 #include <string>
 
 #include "base/memory/weak_ptr.h"
 #include "chromeos/dbus/bluetooth_adapter_client.h"
+#include "chromeos/dbus/bluetooth_agent_service_provider.h"
 #include "chromeos/dbus/bluetooth_device_client.h"
 #include "chromeos/dbus/bluetooth_input_client.h"
 #include "dbus/object_path.h"
 #include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_device.h"
 
 namespace device {
 
@@ -24,6 +27,7 @@
 
 class BluetoothChromeOSTest;
 class BluetoothDeviceChromeOS;
+class BluetoothPairingChromeOS;
 
 // The BluetoothAdapterChromeOS class implements BluetoothAdapter for the
 // Chrome OS platform.
@@ -31,7 +35,8 @@
     : public device::BluetoothAdapter,
       private chromeos::BluetoothAdapterClient::Observer,
       private chromeos::BluetoothDeviceClient::Observer,
-      private chromeos::BluetoothInputClient::Observer {
+      private chromeos::BluetoothInputClient::Observer,
+      private chromeos::BluetoothAgentServiceProvider::Delegate {
  public:
   // BluetoothAdapter override
   virtual void AddObserver(
@@ -56,17 +61,16 @@
       const base::Closure& callback,
       const ErrorCallback& error_callback) OVERRIDE;
   virtual bool IsDiscovering() const OVERRIDE;
-  virtual void StartDiscovering(
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) OVERRIDE;
-  virtual void StopDiscovering(
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) OVERRIDE;
   virtual void ReadLocalOutOfBandPairingData(
       const device::BluetoothAdapter::BluetoothOutOfBandPairingDataCallback&
           callback,
       const ErrorCallback& error_callback) OVERRIDE;
 
+ protected:
+  // BluetoothAdapter override
+  virtual void RemovePairingDelegateInternal(
+      device::BluetoothDevice::PairingDelegate* pairing_delegate) OVERRIDE;
+
  private:
   friend class device::BluetoothAdapterFactory;
   friend class BluetoothChromeOSTest;
@@ -74,6 +78,12 @@
   friend class BluetoothProfileChromeOS;
   friend class BluetoothProfileChromeOSTest;
 
+  // typedef for callback parameters that are passed to AddDiscoverySession
+  // and RemoveDiscoverySession. This is used to queue incoming requests while
+  // a call to BlueZ is pending.
+  typedef std::pair<base::Closure, ErrorCallback> DiscoveryCallbackPair;
+  typedef std::queue<DiscoveryCallbackPair> DiscoveryCallbackQueue;
+
   BluetoothAdapterChromeOS();
   virtual ~BluetoothAdapterChromeOS();
 
@@ -94,11 +104,52 @@
   virtual void InputPropertyChanged(const dbus::ObjectPath& object_path,
                                     const std::string& property_name) OVERRIDE;
 
+  // BluetoothAgentServiceProvider::Delegate override.
+  virtual void Release() OVERRIDE;
+  virtual void RequestPinCode(const dbus::ObjectPath& device_path,
+                              const PinCodeCallback& callback) OVERRIDE;
+  virtual void DisplayPinCode(const dbus::ObjectPath& device_path,
+                              const std::string& pincode) OVERRIDE;
+  virtual void RequestPasskey(const dbus::ObjectPath& device_path,
+                              const PasskeyCallback& callback) OVERRIDE;
+  virtual void DisplayPasskey(const dbus::ObjectPath& device_path,
+                              uint32 passkey, uint16 entered) OVERRIDE;
+  virtual void RequestConfirmation(const dbus::ObjectPath& device_path,
+                                   uint32 passkey,
+                                   const ConfirmationCallback& callback)
+      OVERRIDE;
+  virtual void RequestAuthorization(const dbus::ObjectPath& device_path,
+                                    const ConfirmationCallback& callback)
+      OVERRIDE;
+  virtual void AuthorizeService(const dbus::ObjectPath& device_path,
+                                const std::string& uuid,
+                                const ConfirmationCallback& callback) OVERRIDE;
+  virtual void Cancel() OVERRIDE;
+
+  // Called by dbus:: on completion of the D-Bus method call to register the
+  // pairing agent.
+  void OnRegisterAgent();
+  void OnRegisterAgentError(const std::string& error_name,
+                            const std::string& error_message);
+
+  // Called by dbus:: on completion of the D-Bus method call to request that
+  // the pairing agent be made the default.
+  void OnRequestDefaultAgent();
+  void OnRequestDefaultAgentError(const std::string& error_name,
+                                  const std::string& error_message);
+
   // Internal method used to locate the device object by object path
   // (the devices map and BluetoothDevice methods are by address)
   BluetoothDeviceChromeOS* GetDeviceWithPath(
       const dbus::ObjectPath& object_path);
 
+  // Internal method to obtain a BluetoothPairingChromeOS object for the device
+  // with path |object_path|. Returns the existing pairing object if the device
+  // already has one (usually an outgoing connection in progress) or a new
+  // pairing object with the default pairing delegate if not. If no default
+  // pairing object exists, NULL will be returned.
+  BluetoothPairingChromeOS* GetPairing(const dbus::ObjectPath& object_path);
+
   // Set the tracked adapter to the one in |object_path|, this object will
   // subsequently operate on that adapter until it is removed.
   void SetAdapter(const dbus::ObjectPath& object_path);
@@ -130,9 +181,18 @@
                                  const ErrorCallback& error_callback,
                                  bool success);
 
+  // BluetoothAdapter override.
+  virtual void AddDiscoverySession(
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) OVERRIDE;
+  virtual void RemoveDiscoverySession(
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) OVERRIDE;
+
   // Called by dbus:: on completion of the D-Bus method call to start discovery.
   void OnStartDiscovery(const base::Closure& callback);
-  void OnStartDiscoveryError(const ErrorCallback& error_callback,
+  void OnStartDiscoveryError(const base::Closure& callback,
+                             const ErrorCallback& error_callback,
                              const std::string& error_name,
                              const std::string& error_message);
 
@@ -142,12 +202,39 @@
                             const std::string& error_name,
                             const std::string& error_message);
 
+  // Processes the queued discovery requests. For each DiscoveryCallbackPair in
+  // the queue, this method will try to add a new discovery session. This method
+  // is called whenever a pending D-Bus call to start or stop discovery has
+  // ended (with either success or failure).
+  void ProcessQueuedDiscoveryRequests();
+
+  // Number of discovery sessions that have been added.
+  int num_discovery_sessions_;
+
+  // True, if there is a pending request to start or stop discovery.
+  bool discovery_request_pending_;
+
+  // List of queued requests to add new discovery sessions. While there is a
+  // pending request to BlueZ to start or stop discovery, many requests from
+  // within Chrome to start or stop discovery sessions may occur. We only
+  // queue requests to add new sessions to be processed later. All requests to
+  // remove a session while a call is pending immediately return failure. Note
+  // that since BlueZ keeps its own reference count of applications that have
+  // requested discovery, dropping our count to 0 won't necessarily result in
+  // the controller actually stopping discovery if, for example, an application
+  // other than Chrome, such as bt_console, was also used to start discovery.
+  DiscoveryCallbackQueue discovery_request_queue_;
+
   // Object path of the adapter we track.
   dbus::ObjectPath object_path_;
 
   // List of observers interested in event notifications from us.
   ObserverList<device::BluetoothAdapter::Observer> observers_;
 
+  // Instance of the D-Bus agent object used for pairing, initialized with
+  // our own class as its delegate.
+  scoped_ptr<BluetoothAgentServiceProvider> agent_;
+
   // Note: This should remain the last member so it'll be destroyed and
   // invalidate its weak pointers before any other members are destroyed.
   base::WeakPtrFactory<BluetoothAdapterChromeOS> weak_ptr_factory_;
diff --git a/device/bluetooth/bluetooth_adapter_mac.h b/device/bluetooth/bluetooth_adapter_mac.h
index 6639db0..064b6bf 100644
--- a/device/bluetooth/bluetooth_adapter_mac.h
+++ b/device/bluetooth/bluetooth_adapter_mac.h
@@ -63,13 +63,6 @@
       const base::Closure& callback,
       const ErrorCallback& error_callback) OVERRIDE;
   virtual bool IsDiscovering() const OVERRIDE;
-
-  virtual void StartDiscovering(
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) OVERRIDE;
-  virtual void StopDiscovering(
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) OVERRIDE;
   virtual void ReadLocalOutOfBandPairingData(
       const BluetoothOutOfBandPairingDataCallback& callback,
       const ErrorCallback& error_callback) OVERRIDE;
@@ -82,6 +75,11 @@
                              IOReturn error,
                              bool aborted);
 
+ protected:
+  // BluetoothAdapter override
+  virtual void RemovePairingDelegateInternal(
+      device::BluetoothDevice::PairingDelegate* pairing_delegate) OVERRIDE;
+
  private:
   friend class BluetoothAdapterFactory;
   friend class BluetoothAdapterMacTest;
@@ -96,6 +94,14 @@
   BluetoothAdapterMac();
   virtual ~BluetoothAdapterMac();
 
+  // BluetoothAdapter override.
+  virtual void AddDiscoverySession(
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) OVERRIDE;
+  virtual void RemoveDiscoverySession(
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) OVERRIDE;
+
   void Init();
   void InitForTest(scoped_refptr<base::SequencedTaskRunner> ui_task_runner);
   void PollAdapter();
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm
index 502ed81..9ac7caa 100644
--- a/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -168,7 +168,12 @@
       discovery_status_ == DISCOVERY_STOPPING;
 }
 
-void BluetoothAdapterMac::StartDiscovering(
+void BluetoothAdapterMac::ReadLocalOutOfBandPairingData(
+    const BluetoothOutOfBandPairingDataCallback& callback,
+    const ErrorCallback& error_callback) {
+}
+
+void BluetoothAdapterMac::AddDiscoverySession(
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
   if (discovery_status_ == DISCOVERING) {
@@ -181,8 +186,9 @@
   MaybeStartDeviceInquiry();
 }
 
-void BluetoothAdapterMac::StopDiscovering(const base::Closure& callback,
-                                          const ErrorCallback& error_callback) {
+void BluetoothAdapterMac::RemoveDiscoverySession(
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
   if (discovery_status_ == NOT_DISCOVERING) {
     error_callback.Run();
     return;
@@ -192,9 +198,8 @@
   MaybeStopDeviceInquiry();
 }
 
-void BluetoothAdapterMac::ReadLocalOutOfBandPairingData(
-    const BluetoothOutOfBandPairingDataCallback& callback,
-    const ErrorCallback& error_callback) {
+void BluetoothAdapterMac::RemovePairingDelegateInternal(
+    BluetoothDevice::PairingDelegate* pairing_delegate) {
 }
 
 void BluetoothAdapterMac::Init() {
diff --git a/device/bluetooth/bluetooth_adapter_unittest.cc b/device/bluetooth/bluetooth_adapter_unittest.cc
new file mode 100644
index 0000000..aac32da
--- /dev/null
+++ b/device/bluetooth/bluetooth_adapter_unittest.cc
@@ -0,0 +1,183 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/ref_counted.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using device::BluetoothAdapter;
+using device::BluetoothDevice;
+
+namespace device {
+
+class TestBluetoothAdapter : public BluetoothAdapter {
+ public:
+  TestBluetoothAdapter() {
+  }
+
+  virtual void AddObserver(BluetoothAdapter::Observer* observer) OVERRIDE {
+  }
+
+  virtual void RemoveObserver(BluetoothAdapter::Observer* observer) OVERRIDE {
+
+  }
+
+  virtual std::string GetAddress() const OVERRIDE {
+    return "";
+  }
+
+  virtual std::string GetName() const OVERRIDE {
+    return "";
+  }
+
+  virtual void SetName(const std::string& name,
+                       const base::Closure& callback,
+                       const ErrorCallback& error_callback) OVERRIDE {
+  }
+
+  virtual bool IsInitialized() const OVERRIDE {
+    return false;
+  }
+
+  virtual bool IsPresent() const OVERRIDE {
+    return false;
+  }
+
+  virtual bool IsPowered() const OVERRIDE {
+    return false;
+  }
+
+  virtual void SetPowered(
+      bool powered,
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) OVERRIDE {
+  }
+
+  virtual bool IsDiscoverable() const OVERRIDE {
+    return false;
+  }
+
+  virtual void SetDiscoverable(
+      bool discoverable,
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) OVERRIDE {
+  }
+
+  virtual bool IsDiscovering() const OVERRIDE {
+    return false;
+  }
+
+  virtual void StartDiscoverySession(
+      const DiscoverySessionCallback& callback,
+      const ErrorCallback& error_callback) OVERRIDE {
+  }
+
+  virtual void ReadLocalOutOfBandPairingData(
+      const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback,
+      const ErrorCallback& error_callback) OVERRIDE {
+  }
+
+ protected:
+  virtual ~TestBluetoothAdapter() {}
+
+  virtual void AddDiscoverySession(
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) OVERRIDE {
+  }
+
+  virtual void RemoveDiscoverySession(
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) OVERRIDE {
+  }
+
+  virtual void RemovePairingDelegateInternal(
+      BluetoothDevice::PairingDelegate* pairing_delegate) OVERRIDE {
+  }
+};
+
+class TestPairingDelegate : public BluetoothDevice::PairingDelegate {
+  public:
+   virtual void RequestPinCode(BluetoothDevice* device) OVERRIDE {}
+   virtual void RequestPasskey(BluetoothDevice* device) OVERRIDE {}
+   virtual void DisplayPinCode(BluetoothDevice* device,
+                               const std::string& pincode) OVERRIDE {}
+   virtual void DisplayPasskey(BluetoothDevice* device,
+                               uint32 passkey) OVERRIDE {}
+   virtual void KeysEntered(BluetoothDevice* device,
+                            uint32 entered) OVERRIDE {}
+   virtual void ConfirmPasskey(BluetoothDevice* device,
+                               uint32 passkey) OVERRIDE {}
+   virtual void AuthorizePairing(BluetoothDevice* device) OVERRIDE {}
+};
+
+
+TEST(BluetoothAdapterTest, NoDefaultPairingDelegate) {
+  scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();
+
+  // Verify that when there is no registered pairing delegate, NULL is returned.
+  EXPECT_TRUE(adapter->DefaultPairingDelegate() == NULL);
+}
+
+TEST(BluetoothAdapterTest, OneDefaultPairingDelegate) {
+  scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();
+
+  // Verify that when there is one registered pairing delegate, it is returned.
+  TestPairingDelegate delegate;
+
+  adapter->AddPairingDelegate(&delegate,
+                              BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
+
+  EXPECT_EQ(&delegate, adapter->DefaultPairingDelegate());
+}
+
+TEST(BluetoothAdapterTest, SamePriorityDelegates) {
+  scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();
+
+  // Verify that when there are two registered pairing delegates of the same
+  // priority, the first one registered is returned.
+  TestPairingDelegate delegate1, delegate2;
+
+  adapter->AddPairingDelegate(&delegate1,
+                              BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
+  adapter->AddPairingDelegate(&delegate2,
+                              BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
+
+  EXPECT_EQ(&delegate1, adapter->DefaultPairingDelegate());
+
+  // After unregistering the first, the second can be returned.
+  adapter->RemovePairingDelegate(&delegate1);
+
+  EXPECT_EQ(&delegate2, adapter->DefaultPairingDelegate());
+}
+
+TEST(BluetoothAdapterTest, HighestPriorityDelegate) {
+  scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();
+
+  // Verify that when there are two registered pairing delegates, the one with
+  // the highest priority is returned.
+  TestPairingDelegate delegate1, delegate2;
+
+  adapter->AddPairingDelegate(&delegate1,
+                              BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
+  adapter->AddPairingDelegate(&delegate2,
+                              BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+
+  EXPECT_EQ(&delegate2, adapter->DefaultPairingDelegate());
+}
+
+TEST(BluetoothAdapterTest, UnregisterDelegate) {
+  scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();
+
+  // Verify that after unregistering a delegate, NULL is returned.
+  TestPairingDelegate delegate;
+
+  adapter->AddPairingDelegate(&delegate,
+                              BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW);
+  adapter->RemovePairingDelegate(&delegate);
+
+  EXPECT_TRUE(adapter->DefaultPairingDelegate() == NULL);
+}
+
+}  // namespace device
diff --git a/device/bluetooth/bluetooth_adapter_win.cc b/device/bluetooth/bluetooth_adapter_win.cc
index 521afae..bf0a9b7 100644
--- a/device/bluetooth/bluetooth_adapter_win.cc
+++ b/device/bluetooth/bluetooth_adapter_win.cc
@@ -97,32 +97,6 @@
       discovery_status_ == DISCOVERY_STOPPING;
 }
 
-// If the method is called when |discovery_status_| is DISCOVERY_STOPPING,
-// starting again is handled by BluetoothAdapterWin::DiscoveryStopped().
-void BluetoothAdapterWin::StartDiscovering(
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
-  if (discovery_status_ == DISCOVERING) {
-    num_discovery_listeners_++;
-    callback.Run();
-    return;
-  }
-  on_start_discovery_callbacks_.push_back(
-      std::make_pair(callback, error_callback));
-  MaybePostStartDiscoveryTask();
-}
-
-void BluetoothAdapterWin::StopDiscovering(
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
-  if (discovery_status_ == NOT_DISCOVERING) {
-    error_callback.Run();
-    return;
-  }
-  on_stop_discovery_callbacks_.push_back(callback);
-  MaybePostStopDiscoveryTask();
-}
-
 void BluetoothAdapterWin::DiscoveryStarted(bool success) {
   discovery_status_ = success ? DISCOVERING : NOT_DISCOVERING;
   for (std::vector<std::pair<base::Closure, ErrorCallback> >::const_iterator
@@ -176,6 +150,10 @@
   NOTIMPLEMENTED();
 }
 
+void BluetoothAdapterWin::RemovePairingDelegateInternal(
+    BluetoothDevice::PairingDelegate* pairing_delegate) {
+}
+
 void BluetoothAdapterWin::AdapterStateChanged(
     const BluetoothTaskManagerWin::AdapterState& state) {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -226,6 +204,32 @@
   }
 }
 
+// If the method is called when |discovery_status_| is DISCOVERY_STOPPING,
+// starting again is handled by BluetoothAdapterWin::DiscoveryStopped().
+void BluetoothAdapterWin::AddDiscoverySession(
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  if (discovery_status_ == DISCOVERING) {
+    num_discovery_listeners_++;
+    callback.Run();
+    return;
+  }
+  on_start_discovery_callbacks_.push_back(
+      std::make_pair(callback, error_callback));
+  MaybePostStartDiscoveryTask();
+}
+
+void BluetoothAdapterWin::RemoveDiscoverySession(
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  if (discovery_status_ == NOT_DISCOVERING) {
+    error_callback.Run();
+    return;
+  }
+  on_stop_discovery_callbacks_.push_back(callback);
+  MaybePostStopDiscoveryTask();
+}
+
 void BluetoothAdapterWin::Init() {
   ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
   task_manager_ =
diff --git a/device/bluetooth/bluetooth_adapter_win.h b/device/bluetooth/bluetooth_adapter_win.h
index 773c6f3..6e7382f 100644
--- a/device/bluetooth/bluetooth_adapter_win.h
+++ b/device/bluetooth/bluetooth_adapter_win.h
@@ -55,13 +55,6 @@
       const base::Closure& callback,
       const ErrorCallback& error_callback) OVERRIDE;
   virtual bool IsDiscovering() const OVERRIDE;
-
-  virtual void StartDiscovering(
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) OVERRIDE;
-  virtual void StopDiscovering(
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) OVERRIDE;
   virtual void ReadLocalOutOfBandPairingData(
       const BluetoothOutOfBandPairingDataCallback& callback,
       const ErrorCallback& error_callback) OVERRIDE;
@@ -79,6 +72,11 @@
       const ScopedVector<BluetoothTaskManagerWin::DeviceState>& devices)
           OVERRIDE;
 
+ protected:
+  // BluetoothAdapter override
+  virtual void RemovePairingDelegateInternal(
+      device::BluetoothDevice::PairingDelegate* pairing_delegate) OVERRIDE;
+
  private:
   friend class BluetoothAdapterFactory;
   friend class BluetoothAdapterWinTest;
@@ -93,6 +91,14 @@
   explicit BluetoothAdapterWin(const InitCallback& init_callback);
   virtual ~BluetoothAdapterWin();
 
+  // BluetoothAdapter override.
+  virtual void AddDiscoverySession(
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) OVERRIDE;
+  virtual void RemoveDiscoverySession(
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) OVERRIDE;
+
   void Init();
   void InitForTest(
       scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
diff --git a/device/bluetooth/bluetooth_adapter_win_unittest.cc b/device/bluetooth/bluetooth_adapter_win_unittest.cc
index 4ea7a1e..9a9a23d 100644
--- a/device/bluetooth/bluetooth_adapter_win_unittest.cc
+++ b/device/bluetooth/bluetooth_adapter_win_unittest.cc
@@ -131,6 +131,18 @@
     num_stop_discovery_error_callbacks_++;
   }
 
+  void CallAddDiscoverySession(
+      const base::Closure& callback,
+      const BluetoothAdapter::ErrorCallback& error_callback) {
+    adapter_win_->AddDiscoverySession(callback, error_callback);
+  }
+
+  void CallRemoveDiscoverySession(
+      const base::Closure& callback,
+      const BluetoothAdapter::ErrorCallback& error_callback) {
+    adapter_win_->RemoveDiscoverySession(callback, error_callback);
+  }
+
  protected:
   scoped_refptr<base::TestSimpleTaskRunner> ui_task_runner_;
   scoped_refptr<base::TestSimpleTaskRunner> bluetooth_task_runner_;
@@ -194,7 +206,7 @@
 
 TEST_F(BluetoothAdapterWinTest, SingleStartDiscovery) {
   bluetooth_task_runner_->ClearPendingTasks();
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
                  base::Unretained(this)),
       BluetoothAdapter::ErrorCallback());
@@ -210,7 +222,7 @@
 }
 
 TEST_F(BluetoothAdapterWinTest, SingleStartDiscoveryFailure) {
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Closure(),
       base::Bind(
           &BluetoothAdapterWinTest::IncrementNumStartDiscoveryErrorCallbacks,
@@ -226,7 +238,7 @@
   bluetooth_task_runner_->ClearPendingTasks();
   int num_discoveries = 5;
   for (int i = 0; i < num_discoveries; i++) {
-    adapter_win_->StartDiscovering(
+    CallAddDiscoverySession(
         base::Bind(
             &BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
             base::Unretained(this)),
@@ -246,7 +258,7 @@
 TEST_F(BluetoothAdapterWinTest, MultipleStartDiscoveriesFailure) {
   int num_discoveries = 5;
   for (int i = 0; i < num_discoveries; i++) {
-    adapter_win_->StartDiscovering(
+    CallAddDiscoverySession(
         base::Closure(),
         base::Bind(
             &BluetoothAdapterWinTest::IncrementNumStartDiscoveryErrorCallbacks,
@@ -260,7 +272,7 @@
 }
 
 TEST_F(BluetoothAdapterWinTest, MultipleStartDiscoveriesAfterDiscovering) {
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
                  base::Unretained(this)),
       BluetoothAdapter::ErrorCallback());
@@ -272,7 +284,7 @@
   bluetooth_task_runner_->ClearPendingTasks();
   for (int i = 0; i < 5; i++) {
     int num_start_discovery_callbacks = num_start_discovery_callbacks_;
-    adapter_win_->StartDiscovering(
+    CallAddDiscoverySession(
         base::Bind(
            &BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
            base::Unretained(this)),
@@ -287,7 +299,7 @@
 }
 
 TEST_F(BluetoothAdapterWinTest, StartDiscoveryAfterDiscoveringFailure) {
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Closure(),
       base::Bind(
           &BluetoothAdapterWinTest::IncrementNumStartDiscoveryErrorCallbacks,
@@ -297,7 +309,7 @@
   EXPECT_FALSE(adapter_->IsDiscovering());
   EXPECT_EQ(1, num_start_discovery_error_callbacks_);
 
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
                  base::Unretained(this)),
       BluetoothAdapter::ErrorCallback());
@@ -308,11 +320,11 @@
 }
 
 TEST_F(BluetoothAdapterWinTest, SingleStopDiscovery) {
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Closure(), BluetoothAdapter::ErrorCallback());
   adapter_win_->DiscoveryStarted(true);
   ui_task_runner_->ClearPendingTasks();
-  adapter_win_->StopDiscovering(
+  CallRemoveDiscoverySession(
       base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
                  base::Unretained(this)),
       BluetoothAdapter::ErrorCallback());
@@ -330,14 +342,14 @@
 TEST_F(BluetoothAdapterWinTest, MultipleStopDiscoveries) {
   int num_discoveries = 5;
   for (int i = 0; i < num_discoveries; i++) {
-    adapter_win_->StartDiscovering(
+    CallAddDiscoverySession(
         base::Closure(), BluetoothAdapter::ErrorCallback());
   }
   adapter_win_->DiscoveryStarted(true);
   ui_task_runner_->ClearPendingTasks();
   bluetooth_task_runner_->ClearPendingTasks();
   for (int i = 0; i < num_discoveries - 1; i++) {
-    adapter_win_->StopDiscovering(
+    CallRemoveDiscoverySession(
         base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
                    base::Unretained(this)),
         BluetoothAdapter::ErrorCallback());
@@ -345,7 +357,7 @@
     ui_task_runner_->RunPendingTasks();
     EXPECT_EQ(i + 1, num_stop_discovery_callbacks_);
   }
-  adapter_win_->StopDiscovering(
+  CallRemoveDiscoverySession(
       base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
                  base::Unretained(this)),
       BluetoothAdapter::ErrorCallback());
@@ -360,23 +372,23 @@
 
 TEST_F(BluetoothAdapterWinTest,
        StartDiscoveryAndStartDiscoveryAndStopDiscoveries) {
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
                  base::Unretained(this)),
       BluetoothAdapter::ErrorCallback());
   adapter_win_->DiscoveryStarted(true);
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
                  base::Unretained(this)),
       BluetoothAdapter::ErrorCallback());
   ui_task_runner_->ClearPendingTasks();
   bluetooth_task_runner_->ClearPendingTasks();
-  adapter_win_->StopDiscovering(
+  CallRemoveDiscoverySession(
       base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
                  base::Unretained(this)),
       BluetoothAdapter::ErrorCallback());
   EXPECT_TRUE(bluetooth_task_runner_->GetPendingTasks().empty());
-  adapter_win_->StopDiscovering(
+  CallRemoveDiscoverySession(
       base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
                  base::Unretained(this)),
       BluetoothAdapter::ErrorCallback());
@@ -385,27 +397,27 @@
 
 TEST_F(BluetoothAdapterWinTest,
        StartDiscoveryAndStopDiscoveryAndStartDiscovery) {
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Closure(), BluetoothAdapter::ErrorCallback());
   adapter_win_->DiscoveryStarted(true);
   EXPECT_TRUE(adapter_->IsDiscovering());
-  adapter_win_->StopDiscovering(
+  CallRemoveDiscoverySession(
       base::Closure(), BluetoothAdapter::ErrorCallback());
   adapter_win_->DiscoveryStopped();
   EXPECT_FALSE(adapter_->IsDiscovering());
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Closure(), BluetoothAdapter::ErrorCallback());
   adapter_win_->DiscoveryStarted(true);
   EXPECT_TRUE(adapter_->IsDiscovering());
 }
 
 TEST_F(BluetoothAdapterWinTest, StartDiscoveryBeforeDiscoveryStopped) {
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Closure(), BluetoothAdapter::ErrorCallback());
   adapter_win_->DiscoveryStarted(true);
-  adapter_win_->StopDiscovering(
+  CallRemoveDiscoverySession(
       base::Closure(), BluetoothAdapter::ErrorCallback());
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Closure(), BluetoothAdapter::ErrorCallback());
   bluetooth_task_runner_->ClearPendingTasks();
   adapter_win_->DiscoveryStopped();
@@ -413,7 +425,7 @@
 }
 
 TEST_F(BluetoothAdapterWinTest, StopDiscoveryWithoutStartDiscovery) {
-  adapter_win_->StopDiscovering(
+  CallRemoveDiscoverySession(
       base::Closure(),
       base::Bind(
           &BluetoothAdapterWinTest::IncrementNumStopDiscoveryErrorCallbacks,
@@ -422,9 +434,9 @@
 }
 
 TEST_F(BluetoothAdapterWinTest, StopDiscoveryBeforeDiscoveryStarted) {
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Closure(), BluetoothAdapter::ErrorCallback());
-  adapter_win_->StopDiscovering(
+  CallRemoveDiscoverySession(
       base::Closure(), BluetoothAdapter::ErrorCallback());
   bluetooth_task_runner_->ClearPendingTasks();
   adapter_win_->DiscoveryStarted(true);
@@ -435,14 +447,14 @@
   int num_expected_start_discoveries = 3;
   int num_expected_stop_discoveries = 2;
   for (int i = 0; i < num_expected_start_discoveries; i++) {
-    adapter_win_->StartDiscovering(
+    CallAddDiscoverySession(
         base::Bind(
             &BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
             base::Unretained(this)),
         BluetoothAdapter::ErrorCallback());
   }
   for (int i = 0; i < num_expected_stop_discoveries; i++) {
-    adapter_win_->StopDiscovering(
+    CallRemoveDiscoverySession(
         base::Bind(
             &BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
             base::Unretained(this)),
@@ -457,12 +469,12 @@
 }
 
 TEST_F(BluetoothAdapterWinTest, StopDiscoveryBeforeDiscoveryStartedAndFailed) {
-  adapter_win_->StartDiscovering(
+  CallAddDiscoverySession(
       base::Closure(),
       base::Bind(
           &BluetoothAdapterWinTest::IncrementNumStartDiscoveryErrorCallbacks,
           base::Unretained(this)));
-  adapter_win_->StopDiscovering(
+  CallRemoveDiscoverySession(
       base::Bind(
           &BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
           base::Unretained(this)),
diff --git a/device/bluetooth/bluetooth_chromeos_unittest.cc b/device/bluetooth/bluetooth_chromeos_unittest.cc
index a1cc67d..ca5ff03 100644
--- a/device/bluetooth/bluetooth_chromeos_unittest.cc
+++ b/device/bluetooth/bluetooth_chromeos_unittest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/memory/scoped_vector.h"
 #include "base/message_loop/message_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chromeos/dbus/fake_bluetooth_adapter_client.h"
@@ -15,11 +16,15 @@
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_device_chromeos.h"
+#include "device/bluetooth/bluetooth_discovery_session.h"
+#include "device/bluetooth/bluetooth_pairing_chromeos.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
 
 using device::BluetoothAdapter;
 using device::BluetoothAdapterFactory;
 using device::BluetoothDevice;
+using device::BluetoothDiscoverySession;
 
 namespace chromeos {
 
@@ -38,8 +43,12 @@
         device_removed_count_(0),
         last_device_(NULL),
         adapter_(adapter) {
+    adapter_->AddObserver(this);
   }
-  virtual ~TestObserver() {}
+
+  virtual ~TestObserver() {
+    adapter_->RemoveObserver(this);
+  }
 
   virtual void AdapterPresentChanged(BluetoothAdapter* adapter,
                                      bool present) OVERRIDE {
@@ -140,7 +149,7 @@
         display_passkey_count_(0),
         keys_entered_count_(0),
         confirm_passkey_count_(0),
-        dismiss_count_(0),
+        authorize_pairing_count_(0),
         last_passkey_(9999999U),
         last_entered_(999U) {}
   virtual ~TestPairingDelegate() {}
@@ -188,9 +197,9 @@
     QuitMessageLoop();
   }
 
-  virtual void DismissDisplayOrConfirm() OVERRIDE {
+  virtual void AuthorizePairing(BluetoothDevice* device) OVERRIDE {
     ++call_count_;
-    ++dismiss_count_;
+    ++authorize_pairing_count_;
     QuitMessageLoop();
   }
 
@@ -201,7 +210,7 @@
   int display_passkey_count_;
   int keys_entered_count_;
   int confirm_passkey_count_;
-  int dismiss_count_;
+  int authorize_pairing_count_;
   uint32 last_passkey_;
   uint32 last_entered_;
   std::string last_pincode_;
@@ -233,12 +242,32 @@
             new FakeBluetoothAgentManagerClient));
     DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager);
 
+    fake_bluetooth_adapter_client_->SetSimulationIntervalMs(10);
+
     callback_count_ = 0;
     error_callback_count_ = 0;
     last_connect_error_ = BluetoothDevice::ERROR_UNKNOWN;
+    last_client_error_ = "";
   }
 
   virtual void TearDown() {
+    for (ScopedVector<BluetoothDiscoverySession>::iterator iter =
+            discovery_sessions_.begin();
+         iter != discovery_sessions_.end();
+         ++iter) {
+      BluetoothDiscoverySession* session = *iter;
+      if (!session->IsActive())
+        continue;
+      callback_count_ = 0;
+      session->Stop(
+          base::Bind(&BluetoothChromeOSTest::Callback,
+                     base::Unretained(this)),
+          base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                     base::Unretained(this)));
+      message_loop_.Run();
+      ASSERT_EQ(1, callback_count_);
+    }
+    discovery_sessions_.clear();
     adapter_ = NULL;
     DBusThreadManager::Shutdown();
   }
@@ -246,13 +275,29 @@
   // Generic callbacks
   void Callback() {
     ++callback_count_;
+    QuitMessageLoop();
+  }
+
+  void DiscoverySessionCallback(
+      scoped_ptr<BluetoothDiscoverySession> discovery_session) {
+    ++callback_count_;
+    discovery_sessions_.push_back(discovery_session.release());
+    QuitMessageLoop();
   }
 
   void ErrorCallback() {
     ++error_callback_count_;
+    QuitMessageLoop();
   }
 
-  void ConnectErrorCallback(enum BluetoothDevice::ConnectErrorCode error) {
+  void DBusErrorCallback(const std::string& error_name,
+                         const std::string& error_message) {
+    ++error_callback_count_;
+    last_client_error_ = error_name;
+    QuitMessageLoop();
+  }
+
+  void ConnectErrorCallback(BluetoothDevice::ConnectErrorCode error) {
     ++error_callback_count_;
     last_connect_error_ = error;
   }
@@ -271,17 +316,10 @@
   // without using this function.
   void DiscoverDevice(const std::string& address) {
     ASSERT_TRUE(adapter_.get() != NULL);
-
-    if (base::MessageLoop::current() == NULL) {
-      base::MessageLoop message_loop;
-      DiscoverDevices();
-      return;
-    }
-
+    ASSERT_TRUE(base::MessageLoop::current() != NULL);
     fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
     TestObserver observer(adapter_);
-    adapter_->AddObserver(&observer);
 
     adapter_->SetPowered(
         true,
@@ -289,13 +327,16 @@
                    base::Unretained(this)),
         base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                    base::Unretained(this)));
-    adapter_->StartDiscovering(
-        base::Bind(&BluetoothChromeOSTest::Callback,
+    adapter_->StartDiscoverySession(
+        base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                    base::Unretained(this)),
         base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                    base::Unretained(this)));
+    base::MessageLoop::current()->Run();
     ASSERT_EQ(2, callback_count_);
     ASSERT_EQ(0, error_callback_count_);
+    ASSERT_EQ((size_t)1, discovery_sessions_.size());
+    ASSERT_TRUE(discovery_sessions_[0]->IsActive());
     callback_count_ = 0;
 
     ASSERT_TRUE(adapter_->IsPowered());
@@ -305,18 +346,17 @@
            observer.last_device_address_ != address)
       base::MessageLoop::current()->Run();
 
-    adapter_->StopDiscovering(
+    discovery_sessions_[0]->Stop(
         base::Bind(&BluetoothChromeOSTest::Callback,
                    base::Unretained(this)),
         base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                    base::Unretained(this)));
+    base::MessageLoop::current()->Run();
     ASSERT_EQ(1, callback_count_);
     ASSERT_EQ(0, error_callback_count_);
     callback_count_ = 0;
 
     ASSERT_FALSE(adapter_->IsDiscovering());
-
-    adapter_->RemoveObserver(&observer);
   }
 
   // Run a discovery phase so we have devices that can be paired with.
@@ -327,6 +367,7 @@
   }
 
  protected:
+  base::MessageLoop message_loop_;
   FakeBluetoothAdapterClient* fake_bluetooth_adapter_client_;
   FakeBluetoothDeviceClient* fake_bluetooth_device_client_;
   scoped_refptr<BluetoothAdapter> adapter_;
@@ -334,6 +375,17 @@
   int callback_count_;
   int error_callback_count_;
   enum BluetoothDevice::ConnectErrorCode last_connect_error_;
+  std::string last_client_error_;
+  ScopedVector<BluetoothDiscoverySession> discovery_sessions_;
+
+ private:
+  // Some tests use a message loop since background processing is simulated;
+  // break out of those loops.
+  void QuitMessageLoop() {
+    if (base::MessageLoop::current() &&
+        base::MessageLoop::current()->is_running())
+      base::MessageLoop::current()->Quit();
+  }
 };
 
 TEST_F(BluetoothChromeOSTest, AlreadyPresent) {
@@ -362,7 +414,6 @@
   // 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);
 
@@ -390,7 +441,6 @@
   // 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);
 
@@ -418,7 +468,6 @@
   // 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);
 
@@ -465,7 +514,6 @@
   // 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,
@@ -499,7 +547,6 @@
   // 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,
@@ -540,7 +587,6 @@
   // Install an observer; expect the AdapterDiscoverableChanged to be called
   // with true, and IsDiscoverable() to return true.
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   adapter_->SetDiscoverable(
       true,
@@ -573,7 +619,6 @@
   // Install an observer; expect the AdapterDiscoverableChanged to be called
   // with false, and IsDiscoverable() to return false.
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   adapter_->SetDiscoverable(
       false,
@@ -590,8 +635,6 @@
 }
 
 TEST_F(BluetoothChromeOSTest, StopDiscovery) {
-  base::MessageLoop message_loop;
-
   GetAdapter();
 
   adapter_->SetPowered(
@@ -600,100 +643,32 @@
                  base::Unretained(this)),
       base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                  base::Unretained(this)));
-  adapter_->StartDiscovering(
-      base::Bind(&BluetoothChromeOSTest::Callback,
+  adapter_->StartDiscoverySession(
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                  base::Unretained(this)),
       base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                  base::Unretained(this)));
+  message_loop_.Run();
   EXPECT_EQ(2, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   callback_count_ = 0;
 
   ASSERT_TRUE(adapter_->IsPowered());
   ASSERT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)1, discovery_sessions_.size());
+  ASSERT_TRUE(discovery_sessions_[0]->IsActive());
 
   // 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(
+  discovery_sessions_[0]->Stop(
       base::Bind(&BluetoothChromeOSTest::Callback,
                  base::Unretained(this)),
       base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                  base::Unretained(this)));
-  EXPECT_EQ(1, callback_count_);
-  EXPECT_EQ(0, error_callback_count_);
-
-  EXPECT_EQ(1, observer.discovering_changed_count_);
-  EXPECT_FALSE(observer.last_discovering_);
-
-  EXPECT_FALSE(adapter_->IsDiscovering());
-}
-
-TEST_F(BluetoothChromeOSTest, StopDiscoveryAfterTwoStarts) {
-  base::MessageLoop message_loop;
-
-  GetAdapter();
-
-  adapter_->SetPowered(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
-  adapter_->StartDiscovering(
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
-  EXPECT_EQ(2, callback_count_);
-  EXPECT_EQ(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(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
-  EXPECT_EQ(1, callback_count_);
-  EXPECT_EQ(0, error_callback_count_);
-  callback_count_ = 0;
-
-  EXPECT_EQ(0, observer.discovering_changed_count_);
-
-  // Stop discovering; only the callback should be called since we're still
-  // discovering. The adapter should be still discovering.
-  adapter_->StopDiscovering(
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
-  EXPECT_EQ(1, callback_count_);
-  EXPECT_EQ(0, error_callback_count_);
-  callback_count_ = 0;
-
-  EXPECT_EQ(0, observer.discovering_changed_count_);
-
-  EXPECT_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(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  message_loop_.Run();
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 
@@ -705,13 +680,10 @@
 
 TEST_F(BluetoothChromeOSTest, Discovery) {
   // Test a simulated discovery session.
-  base::MessageLoop message_loop;
-
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
   GetAdapter();
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   adapter_->SetPowered(
       true,
@@ -719,32 +691,35 @@
                  base::Unretained(this)),
       base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                  base::Unretained(this)));
-  adapter_->StartDiscovering(
-      base::Bind(&BluetoothChromeOSTest::Callback,
+  adapter_->StartDiscoverySession(
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                  base::Unretained(this)),
       base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                  base::Unretained(this)));
+  message_loop_.Run();
   EXPECT_EQ(2, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   callback_count_ = 0;
 
   ASSERT_TRUE(adapter_->IsPowered());
   ASSERT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)1, discovery_sessions_.size());
+  ASSERT_TRUE(discovery_sessions_[0]->IsActive());
 
-  // First device to appear should be an Apple Mouse.
-  message_loop.Run();
+  // First device to appear.
+  message_loop_.Run();
 
   EXPECT_EQ(1, observer.device_added_count_);
-  EXPECT_EQ(FakeBluetoothDeviceClient::kAppleMouseAddress,
+  EXPECT_EQ(FakeBluetoothDeviceClient::kLegacyAutopairAddress,
             observer.last_device_address_);
 
   // Next we should get another two devices...
-  message_loop.Run();
+  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();
+    message_loop_.Run();
 
   EXPECT_EQ(1, observer.device_removed_count_);
   EXPECT_EQ(FakeBluetoothDeviceClient::kVanishingDeviceAddress,
@@ -752,8 +727,6 @@
 }
 
 TEST_F(BluetoothChromeOSTest, PoweredAndDiscovering) {
-  base::MessageLoop message_loop;
-
   GetAdapter();
   adapter_->SetPowered(
       true,
@@ -761,14 +734,17 @@
                  base::Unretained(this)),
       base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                  base::Unretained(this)));
-  adapter_->StartDiscovering(
-      base::Bind(&BluetoothChromeOSTest::Callback,
+  adapter_->StartDiscoverySession(
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                  base::Unretained(this)),
       base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                  base::Unretained(this)));
+  message_loop_.Run();
   EXPECT_EQ(2, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   callback_count_ = 0;
+  ASSERT_EQ((size_t)1, discovery_sessions_.size());
+  ASSERT_TRUE(discovery_sessions_[0]->IsActive());
 
   // Stop the timers that the simulation uses
   fake_bluetooth_device_client_->EndDiscoverySimulation(
@@ -779,13 +755,13 @@
 
   fake_bluetooth_adapter_client_->SetVisible(false);
   ASSERT_FALSE(adapter_->IsPresent());
+  ASSERT_FALSE(discovery_sessions_[0]->IsActive());
 
   // Install an observer; expect the AdapterPresentChanged,
   // AdapterPoweredChanged and AdapterDiscoveringChanged methods to be called
   // with true, and IsPresent(), IsPowered() and IsDiscovering() to all
   // return true.
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   fake_bluetooth_adapter_client_->SetVisible(true);
 
@@ -822,6 +798,635 @@
   EXPECT_FALSE(adapter_->IsDiscovering());
 }
 
+// This unit test asserts that the basic reference counting logic works
+// correctly for discovery requests done via the BluetoothAdapter.
+TEST_F(BluetoothChromeOSTest, MultipleDiscoverySessions) {
+  GetAdapter();
+  adapter_->SetPowered(
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  EXPECT_EQ(1, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(adapter_->IsPowered());
+  callback_count_ = 0;
+
+  TestObserver observer(adapter_);
+
+  EXPECT_EQ(0, observer.discovering_changed_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+
+  // Request device discovery 3 times.
+  for (int i = 0; i < 3; i++) {
+    adapter_->StartDiscoverySession(
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  }
+  // Run only once, as there should have been one D-Bus call.
+  message_loop_.Run();
+
+  // The observer should have received the discovering changed event exactly
+  // once, the success callback should have been called 3 times and the adapter
+  // should be discovering.
+  EXPECT_EQ(1, observer.discovering_changed_count_);
+  EXPECT_EQ(3, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)3, discovery_sessions_.size());
+
+  // Request to stop discovery twice.
+  for (int i = 0; i < 2; i++) {
+    discovery_sessions_[i]->Stop(
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  }
+
+  // The observer should have received no additional discovering changed events,
+  // the success callback should have been called 2 times and the adapter should
+  // still be discovering.
+  EXPECT_EQ(1, observer.discovering_changed_count_);
+  EXPECT_EQ(5, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  EXPECT_FALSE(discovery_sessions_[0]->IsActive());
+  EXPECT_FALSE(discovery_sessions_[1]->IsActive());
+  EXPECT_TRUE(discovery_sessions_[2]->IsActive());
+
+  // Request device discovery 3 times.
+  for (int i = 0; i < 3; i++) {
+    adapter_->StartDiscoverySession(
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  }
+
+  // The observer should have received no additional discovering changed events,
+  // the success callback should have been called 3 times and the adapter should
+  // still be discovering.
+  EXPECT_EQ(1, observer.discovering_changed_count_);
+  EXPECT_EQ(8, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)6, discovery_sessions_.size());
+
+  // Request to stop discovery 4 times.
+  for (int i = 2; i < 6; i++) {
+    discovery_sessions_[i]->Stop(
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  }
+  // Run only once, as there should have been one D-Bus call.
+  message_loop_.Run();
+
+  // The observer should have received the discovering changed event exactly
+  // once, the success callback should have been called 4 times and the adapter
+  // should no longer be discovering.
+  EXPECT_EQ(2, observer.discovering_changed_count_);
+  EXPECT_EQ(12, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+
+  // All discovery sessions should be inactive.
+  for (int i = 0; i < 6; i++)
+    EXPECT_FALSE(discovery_sessions_[i]->IsActive());
+
+  // Request to stop discovery on of the inactive sessions.
+  discovery_sessions_[0]->Stop(
+    base::Bind(&BluetoothChromeOSTest::Callback,
+               base::Unretained(this)),
+    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+               base::Unretained(this)));
+
+  // The call should have failed.
+  EXPECT_EQ(2, observer.discovering_changed_count_);
+  EXPECT_EQ(12, callback_count_);
+  EXPECT_EQ(1, error_callback_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+}
+
+// This unit test asserts that the reference counting logic works correctly in
+// the cases when the adapter gets reset and D-Bus calls are made outside of
+// the BluetoothAdapter.
+TEST_F(BluetoothChromeOSTest,
+       UnexpectedChangesDuringMultipleDiscoverySessions) {
+  GetAdapter();
+  adapter_->SetPowered(
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  EXPECT_EQ(1, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(adapter_->IsPowered());
+  callback_count_ = 0;
+
+  TestObserver observer(adapter_);
+
+  EXPECT_EQ(0, observer.discovering_changed_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+
+  // Request device discovery 3 times.
+  for (int i = 0; i < 3; i++) {
+    adapter_->StartDiscoverySession(
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  }
+  // Run only once, as there should have been one D-Bus call.
+  message_loop_.Run();
+
+  // The observer should have received the discovering changed event exactly
+  // once, the success callback should have been called 3 times and the adapter
+  // should be discovering.
+  EXPECT_EQ(1, observer.discovering_changed_count_);
+  EXPECT_EQ(3, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)3, discovery_sessions_.size());
+
+  for (int i = 0; i < 3; i++)
+    EXPECT_TRUE(discovery_sessions_[i]->IsActive());
+
+  // Stop the timers that the simulation uses
+  fake_bluetooth_device_client_->EndDiscoverySimulation(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));
+
+  ASSERT_TRUE(adapter_->IsPowered());
+  ASSERT_TRUE(adapter_->IsDiscovering());
+
+  // Stop device discovery behind the adapter. The adapter and the observer
+  // should be notified of the change and the reference count should be reset.
+  // Even though FakeBluetoothAdapterClient does its own reference counting and
+  // we called 3 BluetoothAdapter::StartDiscoverySession 3 times, the
+  // FakeBluetoothAdapterClient's count should be only 1 and a single call to
+  // FakeBluetoothAdapterClient::StopDiscovery should work.
+  fake_bluetooth_adapter_client_->StopDiscovery(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                 base::Unretained(this)));
+  message_loop_.Run();
+  EXPECT_EQ(2, observer.discovering_changed_count_);
+  EXPECT_EQ(4, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+
+  // All discovery session instances should have been updated.
+  for (int i = 0; i < 3; i++)
+    EXPECT_FALSE(discovery_sessions_[i]->IsActive());
+  discovery_sessions_.clear();
+
+  // It should be possible to successfully start discovery.
+  for (int i = 0; i < 2; i++) {
+    adapter_->StartDiscoverySession(
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  }
+  // Run only once, as there should have been one D-Bus call.
+  message_loop_.Run();
+  EXPECT_EQ(3, observer.discovering_changed_count_);
+  EXPECT_EQ(6, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)2, discovery_sessions_.size());
+
+  for (int i = 0; i < 2; i++)
+    EXPECT_TRUE(discovery_sessions_[i]->IsActive());
+
+  fake_bluetooth_device_client_->EndDiscoverySimulation(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));
+
+  // Make the adapter disappear and appear. This will make it come back as
+  // discovering. When this happens, the reference count should become and
+  // remain 0 as no new request was made through the BluetoothAdapter.
+  fake_bluetooth_adapter_client_->SetVisible(false);
+  ASSERT_FALSE(adapter_->IsPresent());
+  EXPECT_EQ(4, observer.discovering_changed_count_);
+  EXPECT_EQ(6, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+
+  for (int i = 0; i < 2; i++)
+    EXPECT_FALSE(discovery_sessions_[i]->IsActive());
+  discovery_sessions_.clear();
+
+  fake_bluetooth_adapter_client_->SetVisible(true);
+  ASSERT_TRUE(adapter_->IsPresent());
+  EXPECT_EQ(5, observer.discovering_changed_count_);
+  EXPECT_EQ(6, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+
+  // Start and stop discovery. At this point, FakeBluetoothAdapterClient has
+  // a reference count that is equal to 1. Pretend that this was done by an
+  // application other than us. Starting and stopping discovery will succeed
+  // but it won't cause the discovery state to change.
+  adapter_->StartDiscoverySession(
+    base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+               base::Unretained(this)),
+    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+               base::Unretained(this)));
+  message_loop_.Run();  // Run the loop, as there should have been a D-Bus call.
+  EXPECT_EQ(5, observer.discovering_changed_count_);
+  EXPECT_EQ(7, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)1, discovery_sessions_.size());
+  EXPECT_TRUE(discovery_sessions_[0]->IsActive());
+
+  discovery_sessions_[0]->Stop(
+    base::Bind(&BluetoothChromeOSTest::Callback,
+               base::Unretained(this)),
+    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+               base::Unretained(this)));
+  message_loop_.Run();  // Run the loop, as there should have been a D-Bus call.
+  EXPECT_EQ(5, observer.discovering_changed_count_);
+  EXPECT_EQ(8, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  EXPECT_FALSE(discovery_sessions_[0]->IsActive());
+  discovery_sessions_.clear();
+
+  // Start discovery again.
+  adapter_->StartDiscoverySession(
+    base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+               base::Unretained(this)),
+    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+               base::Unretained(this)));
+  message_loop_.Run();  // Run the loop, as there should have been a D-Bus call.
+  EXPECT_EQ(5, observer.discovering_changed_count_);
+  EXPECT_EQ(9, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)1, discovery_sessions_.size());
+  EXPECT_TRUE(discovery_sessions_[0]->IsActive());
+
+  // Stop discovery via D-Bus. The fake client's reference count will drop but
+  // the discovery state won't change since our BluetoothAdapter also just
+  // requested it via D-Bus.
+  fake_bluetooth_adapter_client_->StopDiscovery(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                 base::Unretained(this)));
+  message_loop_.Run();
+  EXPECT_EQ(5, observer.discovering_changed_count_);
+  EXPECT_EQ(10, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+
+  // Now end the discovery session. This should change the adapter's discovery
+  // state.
+  discovery_sessions_[0]->Stop(
+    base::Bind(&BluetoothChromeOSTest::Callback,
+               base::Unretained(this)),
+    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+               base::Unretained(this)));
+  message_loop_.Run();
+  EXPECT_EQ(6, observer.discovering_changed_count_);
+  EXPECT_EQ(11, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+  EXPECT_FALSE(discovery_sessions_[0]->IsActive());
+}
+
+TEST_F(BluetoothChromeOSTest, InvalidatedDiscoverySessions) {
+  GetAdapter();
+  adapter_->SetPowered(
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  EXPECT_EQ(1, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(adapter_->IsPowered());
+  callback_count_ = 0;
+
+  TestObserver observer(adapter_);
+
+  EXPECT_EQ(0, observer.discovering_changed_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+
+  // Request device discovery 3 times.
+  for (int i = 0; i < 3; i++) {
+    adapter_->StartDiscoverySession(
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  }
+  // Run only once, as there should have been one D-Bus call.
+  message_loop_.Run();
+
+  // The observer should have received the discovering changed event exactly
+  // once, the success callback should have been called 3 times and the adapter
+  // should be discovering.
+  EXPECT_EQ(1, observer.discovering_changed_count_);
+  EXPECT_EQ(3, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)3, discovery_sessions_.size());
+
+  for (int i = 0; i < 3; i++)
+    EXPECT_TRUE(discovery_sessions_[i]->IsActive());
+
+  // Stop the timers that the simulation uses
+  fake_bluetooth_device_client_->EndDiscoverySimulation(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));
+
+  ASSERT_TRUE(adapter_->IsPowered());
+  ASSERT_TRUE(adapter_->IsDiscovering());
+
+  // Delete all but one discovery session.
+  discovery_sessions_.pop_back();
+  discovery_sessions_.pop_back();
+  ASSERT_EQ((size_t)1, discovery_sessions_.size());
+  EXPECT_TRUE(discovery_sessions_[0]->IsActive());
+  EXPECT_TRUE(adapter_->IsDiscovering());
+
+  // Stop device discovery behind the adapter. The one active discovery session
+  // should become inactive, but more importantly, we shouldn't run into any
+  // memory errors as the sessions that we explicitly deleted should get
+  // cleaned up.
+  fake_bluetooth_adapter_client_->StopDiscovery(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                 base::Unretained(this)));
+  message_loop_.Run();
+  EXPECT_EQ(2, observer.discovering_changed_count_);
+  EXPECT_EQ(4, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+  EXPECT_FALSE(discovery_sessions_[0]->IsActive());
+}
+
+TEST_F(BluetoothChromeOSTest, QueuedDiscoveryRequests) {
+  GetAdapter();
+
+  adapter_->SetPowered(
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  EXPECT_EQ(1, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(adapter_->IsPowered());
+  callback_count_ = 0;
+
+  TestObserver observer(adapter_);
+
+  EXPECT_EQ(0, observer.discovering_changed_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+
+  // Request to start discovery. The call should be pending.
+  adapter_->StartDiscoverySession(
+    base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+               base::Unretained(this)),
+    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+               base::Unretained(this)));
+  EXPECT_EQ(0, callback_count_);
+
+  fake_bluetooth_device_client_->EndDiscoverySimulation(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));
+
+  // The underlying adapter has started discovery, but our call hasn't returned
+  // yet.
+  EXPECT_EQ(1, observer.discovering_changed_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  EXPECT_TRUE(discovery_sessions_.empty());
+
+  // Request to start discovery twice. These should get queued and there should
+  // be no change in state.
+  for (int i = 0; i < 2; i++) {
+    adapter_->StartDiscoverySession(
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  }
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_EQ(1, observer.discovering_changed_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  EXPECT_TRUE(discovery_sessions_.empty());
+
+  // Process the pending call. The queued calls should execute and the discovery
+  // session reference count should increase.
+  message_loop_.Run();
+  EXPECT_EQ(3, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_EQ(1, observer.discovering_changed_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)3, discovery_sessions_.size());
+
+  // Verify the reference count by removing sessions 3 times. The last request
+  // should remain pending.
+  for (int i = 0; i < 3; i++) {
+    discovery_sessions_[i]->Stop(
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  }
+  EXPECT_EQ(5, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_EQ(2, observer.discovering_changed_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+  EXPECT_FALSE(discovery_sessions_[0]->IsActive());
+  EXPECT_FALSE(discovery_sessions_[1]->IsActive());
+  EXPECT_TRUE(discovery_sessions_[2]->IsActive());
+
+  // Request to stop the session whose call is pending should fail.
+  discovery_sessions_[2]->Stop(
+    base::Bind(&BluetoothChromeOSTest::Callback,
+               base::Unretained(this)),
+    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+               base::Unretained(this)));
+  EXPECT_EQ(5, callback_count_);
+  EXPECT_EQ(1, error_callback_count_);
+  EXPECT_EQ(2, observer.discovering_changed_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+  EXPECT_TRUE(discovery_sessions_[2]->IsActive());
+
+  // Request to start should get queued.
+  adapter_->StartDiscoverySession(
+    base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+               base::Unretained(this)),
+    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+               base::Unretained(this)));
+  EXPECT_EQ(5, callback_count_);
+  EXPECT_EQ(1, error_callback_count_);
+  EXPECT_EQ(2, observer.discovering_changed_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)3, discovery_sessions_.size());
+
+  // Run the pending request.
+  message_loop_.Run();
+  EXPECT_EQ(6, callback_count_);
+  EXPECT_EQ(1, error_callback_count_);
+  EXPECT_EQ(3, observer.discovering_changed_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)3, discovery_sessions_.size());
+  EXPECT_FALSE(discovery_sessions_[2]->IsActive());
+
+  // The queued request to start discovery should have been issued but is still
+  // pending. Run the loop and verify.
+  message_loop_.Run();
+  EXPECT_EQ(7, callback_count_);
+  EXPECT_EQ(1, error_callback_count_);
+  EXPECT_EQ(3, observer.discovering_changed_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)4, discovery_sessions_.size());
+  EXPECT_TRUE(discovery_sessions_[3]->IsActive());
+}
+
+TEST_F(BluetoothChromeOSTest, StartDiscoverySession) {
+  GetAdapter();
+
+  adapter_->SetPowered(
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  EXPECT_EQ(1, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(adapter_->IsPowered());
+  callback_count_ = 0;
+
+  TestObserver observer(adapter_);
+
+  EXPECT_EQ(0, observer.discovering_changed_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+  EXPECT_TRUE(discovery_sessions_.empty());
+
+  // Request a new discovery session.
+  adapter_->StartDiscoverySession(
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  message_loop_.Run();
+  EXPECT_EQ(1, observer.discovering_changed_count_);
+  EXPECT_EQ(1, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)1, discovery_sessions_.size());
+  EXPECT_TRUE(discovery_sessions_[0]->IsActive());
+
+  // Start another session. A new one should be returned in the callback, which
+  // in turn will destroy the previous session. Adapter should still be
+  // discovering and the reference count should be 1.
+  adapter_->StartDiscoverySession(
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  message_loop_.Run();
+  EXPECT_EQ(1, observer.discovering_changed_count_);
+  EXPECT_EQ(2, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)2, discovery_sessions_.size());
+  EXPECT_TRUE(discovery_sessions_[0]->IsActive());
+
+  // Request a new session.
+  adapter_->StartDiscoverySession(
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  message_loop_.Run();
+  EXPECT_EQ(1, observer.discovering_changed_count_);
+  EXPECT_EQ(3, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)3, discovery_sessions_.size());
+  EXPECT_TRUE(discovery_sessions_[1]->IsActive());
+  EXPECT_NE(discovery_sessions_[0], discovery_sessions_[1]);
+
+  // Stop the previous discovery session. The session should end but discovery
+  // should continue.
+  discovery_sessions_[0]->Stop(
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                 base::Unretained(this)));
+  message_loop_.Run();
+  EXPECT_EQ(1, observer.discovering_changed_count_);
+  EXPECT_EQ(4, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_TRUE(observer.last_discovering_);
+  EXPECT_TRUE(adapter_->IsDiscovering());
+  ASSERT_EQ((size_t)3, discovery_sessions_.size());
+  EXPECT_FALSE(discovery_sessions_[0]->IsActive());
+  EXPECT_TRUE(discovery_sessions_[1]->IsActive());
+
+  // Delete the current active session. Discovery should eventually stop.
+  discovery_sessions_.clear();
+  while (observer.last_discovering_)
+    message_loop_.RunUntilIdle();
+
+  EXPECT_EQ(2, observer.discovering_changed_count_);
+  EXPECT_EQ(4, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_FALSE(observer.last_discovering_);
+  EXPECT_FALSE(adapter_->IsDiscovering());
+}
+
 TEST_F(BluetoothChromeOSTest, DeviceProperties) {
   GetAdapter();
 
@@ -846,6 +1451,7 @@
   EXPECT_EQ(uuids[0], "00001800-0000-1000-8000-00805f9b34fb");
   EXPECT_EQ(uuids[1], "00001801-0000-1000-8000-00805f9b34fb");
 
+  EXPECT_EQ(BluetoothDevice::VENDOR_ID_USB, devices[0]->GetVendorIDSource());
   EXPECT_EQ(0x05ac, devices[0]->GetVendorID());
   EXPECT_EQ(0x030d, devices[0]->GetProductID());
   EXPECT_EQ(0x0306, devices[0]->GetDeviceID());
@@ -865,7 +1471,6 @@
   // 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(
@@ -893,7 +1498,6 @@
   // 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(
@@ -925,7 +1529,6 @@
   // 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(
@@ -963,7 +1566,6 @@
   // Install an observer; expect the DeviceRemoved method to be called
   // with the device we remove.
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   devices[0]->Forget(
       base::Bind(&BluetoothChromeOSTest::ErrorCallback,
@@ -983,7 +1585,7 @@
   DiscoverDevices();
 
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kMicrosoftMouseAddress);
+      FakeBluetoothDeviceClient::kConnectUnpairableAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
@@ -1005,13 +1607,12 @@
   // Make sure the trusted property has been set to true.
   FakeBluetoothDeviceClient::Properties* properties =
       fake_bluetooth_device_client_->GetProperties(
-          dbus::ObjectPath(FakeBluetoothDeviceClient::kMicrosoftMousePath));
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kConnectUnpairablePath));
   ASSERT_TRUE(properties->trusted.value());
 
   // Install an observer; expect the DeviceRemoved method to be called
   // with the device we remove.
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   device->Forget(
       base::Bind(&BluetoothChromeOSTest::ErrorCallback,
@@ -1019,12 +1620,12 @@
   EXPECT_EQ(0, error_callback_count_);
 
   EXPECT_EQ(1, observer.device_removed_count_);
-  EXPECT_EQ(FakeBluetoothDeviceClient::kMicrosoftMouseAddress,
+  EXPECT_EQ(FakeBluetoothDeviceClient::kConnectUnpairableAddress,
             observer.last_device_address_);
 
   // GetDevices shouldn't return the device either.
   device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kMicrosoftMouseAddress);
+      FakeBluetoothDeviceClient::kConnectUnpairableAddress);
   EXPECT_FALSE(device != NULL);
 }
 
@@ -1037,7 +1638,6 @@
   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.
@@ -1065,12 +1665,11 @@
   DiscoverDevices();
 
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kMicrosoftMouseAddress);
+      FakeBluetoothDeviceClient::kConnectUnpairableAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   // Connect without a pairing delegate; since the device does not require
   // pairing, this should succeed and the device should become connected.
@@ -1095,7 +1694,7 @@
   // Make sure the trusted property has been set to true.
   FakeBluetoothDeviceClient::Properties* properties =
       fake_bluetooth_device_client_->GetProperties(
-          dbus::ObjectPath(FakeBluetoothDeviceClient::kMicrosoftMousePath));
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kConnectUnpairablePath));
   EXPECT_TRUE(properties->trusted.value());
 
   // Verify is a HID device and is not connectable.
@@ -1129,7 +1728,6 @@
   // Connect again; since the device is already Connected, this shouldn't do
   // anything to initiate the connection.
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   device->Connect(
       NULL,
@@ -1154,12 +1752,11 @@
   DiscoverDevices();
 
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kAppleMouseAddress);
+      FakeBluetoothDeviceClient::kLegacyAutopairAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   // Connect without a pairing delegate; since the device requires pairing,
   // this should fail with an error.
@@ -1205,7 +1802,6 @@
   // Disconnect the device, we should see the observer method fire and the
   // device get dropped.
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   device->Disconnect(
       base::Bind(&BluetoothChromeOSTest::Callback,
@@ -1234,7 +1830,6 @@
   // Disconnect the device, we should see the observer method fire and the
   // device get dropped.
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   device->Disconnect(
       base::Bind(&BluetoothChromeOSTest::Callback,
@@ -1250,22 +1845,20 @@
   EXPECT_FALSE(device->IsConnected());
 }
 
-TEST_F(BluetoothChromeOSTest, PairAppleMouse) {
-  base::MessageLoop message_loop;
+TEST_F(BluetoothChromeOSTest, PairLegacyAutopair) {
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
   DiscoverDevices();
 
-  // The Apple Mouse requires no PIN or Passkey to pair; this is equivalent
-  // to Simple Secure Pairing or a device with a fixed 0000 PIN.
+  // The Legacy Autopair device requires no PIN or Passkey to pair because
+  // the daemon provides 0000 to the device for us.
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kAppleMouseAddress);
+      FakeBluetoothDeviceClient::kLegacyAutopairAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -1278,7 +1871,7 @@
   EXPECT_EQ(0, pairing_delegate.call_count_);
   EXPECT_TRUE(device->IsConnecting());
 
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -1300,33 +1893,26 @@
   EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb");
   EXPECT_TRUE(device->IsConnectable());
 
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(1, pairing_delegate.call_count_);
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
-
   // Make sure the trusted property has been set to true.
   FakeBluetoothDeviceClient::Properties* properties =
       fake_bluetooth_device_client_->GetProperties(
-          dbus::ObjectPath(FakeBluetoothDeviceClient::kAppleMousePath));
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kLegacyAutopairPath));
   EXPECT_TRUE(properties->trusted.value());
 }
 
-TEST_F(BluetoothChromeOSTest, PairAppleKeyboard) {
-  base::MessageLoop message_loop;
+TEST_F(BluetoothChromeOSTest, PairDisplayPinCode) {
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
   DiscoverDevices();
 
-  // The Apple Keyboard requires that we display a randomly generated
-  // PIN on the screen.
+  // Requires that we display a randomly generated PIN on the screen.
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kAppleKeyboardAddress);
+      FakeBluetoothDeviceClient::kDisplayPinCodeAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -1341,7 +1927,7 @@
   EXPECT_EQ("123456", pairing_delegate.last_pincode_);
   EXPECT_TRUE(device->IsConnecting());
 
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -1363,32 +1949,27 @@
   EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb");
   EXPECT_TRUE(device->IsConnectable());
 
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
-
   // Make sure the trusted property has been set to true.
   FakeBluetoothDeviceClient::Properties* properties =
       fake_bluetooth_device_client_->GetProperties(
-          dbus::ObjectPath(FakeBluetoothDeviceClient::kAppleKeyboardPath));
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kDisplayPinCodePath));
   EXPECT_TRUE(properties->trusted.value());
 }
 
-TEST_F(BluetoothChromeOSTest, PairMotorolaKeyboard) {
-  base::MessageLoop message_loop;
+TEST_F(BluetoothChromeOSTest, PairDisplayPasskey) {
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
   DiscoverDevices();
 
-  // The Motorola Keyboard requires that we display a randomly generated
-  // Passkey on the screen, and notifies us as it's typed in.
+  // Requires that we display a randomly generated Passkey on the screen,
+  // and notifies us as it's typed in.
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kMotorolaKeyboardAddress);
+      FakeBluetoothDeviceClient::kDisplayPasskeyAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -1409,18 +1990,18 @@
 
   // One call to KeysEntered() for each key, including [enter].
   for(int i = 1; i <= 7; ++i) {
-    message_loop.Run();
+    message_loop_.Run();
 
     EXPECT_EQ(2 + i, pairing_delegate.call_count_);
     EXPECT_EQ(1 + i, pairing_delegate.keys_entered_count_);
     EXPECT_EQ(static_cast<uint32_t>(i), pairing_delegate.last_entered_);
   }
 
-  message_loop.Run();
+  message_loop_.Run();
 
-  // 8 KeysEntered notifications (0 to 7, inclusive). Two aditional calls for
-  // DisplayPasskey() and DismissDisplayOrConfirm().
-  EXPECT_EQ(10, pairing_delegate.call_count_);
+  // 8 KeysEntered notifications (0 to 7, inclusive) and one aditional call for
+  // DisplayPasskey().
+  EXPECT_EQ(9, pairing_delegate.call_count_);
   EXPECT_EQ(8, pairing_delegate.keys_entered_count_);
   EXPECT_EQ(7U, pairing_delegate.last_entered_);
 
@@ -1443,34 +2024,29 @@
   ASSERT_EQ(1U, uuids.size());
   EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb");
 
-  // Fake MotorolaKeyboard is not connectable.
+  // And usually not connectable.
   EXPECT_FALSE(device->IsConnectable());
 
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
-
   // Make sure the trusted property has been set to true.
   FakeBluetoothDeviceClient::Properties* properties =
       fake_bluetooth_device_client_->GetProperties(
-          dbus::ObjectPath(FakeBluetoothDeviceClient::kMotorolaKeyboardPath));
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kDisplayPasskeyPath));
   EXPECT_TRUE(properties->trusted.value());
 }
 
-TEST_F(BluetoothChromeOSTest, PairSonyHeadphones) {
-  base::MessageLoop message_loop;
+TEST_F(BluetoothChromeOSTest, PairRequestPinCode) {
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
   DiscoverDevices();
 
-  // The Sony Headphones fake requires that the user enters a PIN for them.
+  // Requires that the user enters a PIN for them.
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kSonyHeadphonesAddress);
+      FakeBluetoothDeviceClient::kRequestPinCodeAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -1486,7 +2062,7 @@
 
   // Set the PIN.
   device->SetPinCode("1234");
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -1508,32 +2084,26 @@
   // Non HID devices are always connectable.
   EXPECT_TRUE(device->IsConnectable());
 
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(2, pairing_delegate.call_count_);
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
-
   // Make sure the trusted property has been set to true.
   FakeBluetoothDeviceClient::Properties* properties =
       fake_bluetooth_device_client_->GetProperties(
-          dbus::ObjectPath(FakeBluetoothDeviceClient::kSonyHeadphonesPath));
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath));
   EXPECT_TRUE(properties->trusted.value());
 }
 
-TEST_F(BluetoothChromeOSTest, PairPhone) {
-  base::MessageLoop message_loop;
+TEST_F(BluetoothChromeOSTest, PairConfirmPasskey) {
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
   DiscoverDevices();
 
-  // The fake phone requests that we confirm a displayed passkey.
+  // Requests that we confirm a displayed passkey.
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kPhoneAddress);
+      FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -1550,7 +2120,7 @@
 
   // Confirm the passkey.
   device->ConfirmPairing();
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -1568,34 +2138,27 @@
   // Non HID devices are always connectable.
   EXPECT_TRUE(device->IsConnectable());
 
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(2, pairing_delegate.call_count_);
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
-
   // Make sure the trusted property has been set to true.
   FakeBluetoothDeviceClient::Properties* properties =
       fake_bluetooth_device_client_->GetProperties(
-          dbus::ObjectPath(FakeBluetoothDeviceClient::kPhonePath));
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath));
   EXPECT_TRUE(properties->trusted.value());
 }
 
-TEST_F(BluetoothChromeOSTest, PairWeirdDevice) {
-  base::MessageLoop message_loop;
+TEST_F(BluetoothChromeOSTest, PairRequestPasskey) {
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
   DiscoverDevices();
 
-  // Use the "weird device" fake that requires that the user enters a Passkey,
-  // this would be some kind of device that has a display, but doesn't use
-  // "just works" - maybe a car?
+  // Requires that the user enters a Passkey, this would be some kind of
+  // device that has a display, but doesn't use "just works" - maybe a car?
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kWeirdDeviceAddress);
+      FakeBluetoothDeviceClient::kRequestPasskeyAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -1611,7 +2174,7 @@
 
   // Set the Passkey.
   device->SetPasskey(1234);
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -1629,19 +2192,64 @@
   // Non HID devices are always connectable.
   EXPECT_TRUE(device->IsConnectable());
 
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(2, pairing_delegate.call_count_);
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+  // Make sure the trusted property has been set to true.
+  FakeBluetoothDeviceClient::Properties* properties =
+      fake_bluetooth_device_client_->GetProperties(
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
+  EXPECT_TRUE(properties->trusted.value());
+}
+
+TEST_F(BluetoothChromeOSTest, PairJustWorks) {
+  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+  GetAdapter();
+  DiscoverDevices();
+
+  // Uses just-works pairing, since this is an outgoing pairing, no delegate
+  // interaction is required.
+  BluetoothDevice* device = adapter_->GetDevice(
+      FakeBluetoothDeviceClient::kJustWorksAddress);
+  ASSERT_TRUE(device != NULL);
+  ASSERT_FALSE(device->IsPaired());
+
+  TestObserver observer(adapter_);
+
+  TestPairingDelegate pairing_delegate;
+  device->Connect(
+      &pairing_delegate,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                 base::Unretained(this)));
+
+  EXPECT_EQ(0, pairing_delegate.call_count_);
+
+  message_loop_.Run();
+
+  EXPECT_EQ(1, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+
+  // Two changes for connecting, one change for connected, one for paired and
+  // two for trusted (after pairing and connection).
+  EXPECT_EQ(6, observer.device_changed_count_);
+  EXPECT_EQ(device, observer.last_device_);
+
+  EXPECT_TRUE(device->IsConnected());
+  EXPECT_FALSE(device->IsConnecting());
+
+  EXPECT_TRUE(device->IsPaired());
+
+  // Non HID devices are always connectable.
+  EXPECT_TRUE(device->IsConnectable());
 
   // Make sure the trusted property has been set to true.
   FakeBluetoothDeviceClient::Properties* properties =
       fake_bluetooth_device_client_->GetProperties(
-          dbus::ObjectPath(FakeBluetoothDeviceClient::kWeirdDevicePath));
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath));
   EXPECT_TRUE(properties->trusted.value());
 }
 
 TEST_F(BluetoothChromeOSTest, PairUnpairableDeviceFails) {
-  base::MessageLoop message_loop;
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
@@ -1653,7 +2261,6 @@
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -1667,7 +2274,7 @@
   EXPECT_TRUE(device->IsConnecting());
 
   // Run the loop to get the error..
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
@@ -1677,14 +2284,9 @@
   EXPECT_FALSE(device->IsConnected());
   EXPECT_FALSE(device->IsConnecting());
   EXPECT_FALSE(device->IsPaired());
-
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(1, pairing_delegate.call_count_);
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
 }
 
 TEST_F(BluetoothChromeOSTest, PairingFails) {
-  base::MessageLoop message_loop;
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
@@ -1697,7 +2299,6 @@
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -1711,7 +2312,7 @@
   EXPECT_TRUE(device->IsConnecting());
 
   // Run the loop to get the error..
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
@@ -1721,14 +2322,9 @@
   EXPECT_FALSE(device->IsConnected());
   EXPECT_FALSE(device->IsConnecting());
   EXPECT_FALSE(device->IsPaired());
-
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(1, pairing_delegate.call_count_);
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
 }
 
 TEST_F(BluetoothChromeOSTest, PairingFailsAtConnection) {
-  base::MessageLoop message_loop;
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
@@ -1742,7 +2338,6 @@
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -1755,7 +2350,7 @@
   EXPECT_EQ(0, pairing_delegate.call_count_);
   EXPECT_TRUE(device->IsConnecting());
 
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
@@ -1771,10 +2366,6 @@
 
   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 =
@@ -1785,7 +2376,6 @@
 }
 
 TEST_F(BluetoothChromeOSTest, PairingRejectedAtPinCode) {
-  base::MessageLoop message_loop;
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
@@ -1793,12 +2383,11 @@
 
   // Reject the pairing after we receive a request for the PIN code.
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kSonyHeadphonesAddress);
+      FakeBluetoothDeviceClient::kRequestPinCodeAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -1814,7 +2403,7 @@
 
   // Reject the pairing.
   device->RejectPairing();
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
@@ -1825,14 +2414,9 @@
   EXPECT_FALSE(device->IsConnected());
   EXPECT_FALSE(device->IsConnecting());
   EXPECT_FALSE(device->IsPaired());
-
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(2, pairing_delegate.call_count_);
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
 }
 
 TEST_F(BluetoothChromeOSTest, PairingCancelledAtPinCode) {
-  base::MessageLoop message_loop;
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
@@ -1840,12 +2424,11 @@
 
   // Cancel the pairing after we receive a request for the PIN code.
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kSonyHeadphonesAddress);
+      FakeBluetoothDeviceClient::kRequestPinCodeAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -1861,7 +2444,7 @@
 
   // Cancel the pairing.
   device->CancelPairing();
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
@@ -1872,14 +2455,9 @@
   EXPECT_FALSE(device->IsConnected());
   EXPECT_FALSE(device->IsConnecting());
   EXPECT_FALSE(device->IsPaired());
-
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(2, pairing_delegate.call_count_);
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
 }
 
 TEST_F(BluetoothChromeOSTest, PairingRejectedAtPasskey) {
-  base::MessageLoop message_loop;
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
@@ -1887,12 +2465,11 @@
 
   // Reject the pairing after we receive a request for the passkey.
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kWeirdDeviceAddress);
+      FakeBluetoothDeviceClient::kRequestPasskeyAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -1908,7 +2485,7 @@
 
   // Reject the pairing.
   device->RejectPairing();
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
@@ -1919,14 +2496,9 @@
   EXPECT_FALSE(device->IsConnected());
   EXPECT_FALSE(device->IsConnecting());
   EXPECT_FALSE(device->IsPaired());
-
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(2, pairing_delegate.call_count_);
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
 }
 
 TEST_F(BluetoothChromeOSTest, PairingCancelledAtPasskey) {
-  base::MessageLoop message_loop;
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
@@ -1934,12 +2506,11 @@
 
   // Cancel the pairing after we receive a request for the passkey.
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kWeirdDeviceAddress);
+      FakeBluetoothDeviceClient::kRequestPasskeyAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -1955,7 +2526,7 @@
 
   // Cancel the pairing.
   device->CancelPairing();
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
@@ -1966,14 +2537,9 @@
   EXPECT_FALSE(device->IsConnected());
   EXPECT_FALSE(device->IsConnecting());
   EXPECT_FALSE(device->IsPaired());
-
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(2, pairing_delegate.call_count_);
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
 }
 
 TEST_F(BluetoothChromeOSTest, PairingRejectedAtConfirmation) {
-  base::MessageLoop message_loop;
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
@@ -1981,12 +2547,11 @@
 
   // Reject the pairing after we receive a request for passkey confirmation.
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kPhoneAddress);
+      FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -2002,7 +2567,7 @@
 
   // Reject the pairing.
   device->RejectPairing();
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
@@ -2013,14 +2578,9 @@
   EXPECT_FALSE(device->IsConnected());
   EXPECT_FALSE(device->IsConnecting());
   EXPECT_FALSE(device->IsPaired());
-
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(2, pairing_delegate.call_count_);
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
 }
 
 TEST_F(BluetoothChromeOSTest, PairingCancelledAtConfirmation) {
-  base::MessageLoop message_loop;
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
@@ -2028,12 +2588,11 @@
 
   // Cancel the pairing after we receive a request for the passkey.
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kPhoneAddress);
+      FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -2049,7 +2608,7 @@
 
   // Cancel the pairing.
   device->CancelPairing();
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
@@ -2060,14 +2619,9 @@
   EXPECT_FALSE(device->IsConnected());
   EXPECT_FALSE(device->IsConnecting());
   EXPECT_FALSE(device->IsPaired());
-
-  // Pairing dialog should be dismissed
-  EXPECT_EQ(2, pairing_delegate.call_count_);
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
 }
 
 TEST_F(BluetoothChromeOSTest, PairingCancelledInFlight) {
-  base::MessageLoop message_loop;
   fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
 
   GetAdapter();
@@ -2075,12 +2629,11 @@
 
   // Cancel the pairing while we're waiting for the remote host.
   BluetoothDevice* device = adapter_->GetDevice(
-      FakeBluetoothDeviceClient::kAppleMouseAddress);
+      FakeBluetoothDeviceClient::kLegacyAutopairAddress);
   ASSERT_TRUE(device != NULL);
   ASSERT_FALSE(device->IsPaired());
 
   TestObserver observer(adapter_);
-  adapter_->AddObserver(&observer);
 
   TestPairingDelegate pairing_delegate;
   device->Connect(
@@ -2095,7 +2648,7 @@
 
   // Cancel the pairing.
   device->CancelPairing();
-  message_loop.Run();
+  message_loop_.Run();
 
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
@@ -2106,10 +2659,512 @@
   EXPECT_FALSE(device->IsConnected());
   EXPECT_FALSE(device->IsConnecting());
   EXPECT_FALSE(device->IsPaired());
+}
 
-  // Pairing dialog should be dismissed
+TEST_F(BluetoothChromeOSTest, IncomingPairRequestPinCode) {
+  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+  GetAdapter();
+
+  TestPairingDelegate pairing_delegate;
+  adapter_->AddPairingDelegate(
+      &pairing_delegate,
+      BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+
+  // Requires that we provide a PIN code.
+  fake_bluetooth_device_client_->CreateDevice(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath));
+  BluetoothDevice* device = adapter_->GetDevice(
+      FakeBluetoothDeviceClient::kRequestPinCodeAddress);
+  ASSERT_TRUE(device != NULL);
+  ASSERT_FALSE(device->IsPaired());
+
+  TestObserver observer(adapter_);
+
+  fake_bluetooth_device_client_->SimulatePairing(
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath),
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                 base::Unretained(this)));
+
   EXPECT_EQ(1, pairing_delegate.call_count_);
-  EXPECT_EQ(1, pairing_delegate.dismiss_count_);
+  EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
+
+  // Set the PIN.
+  device->SetPinCode("1234");
+  message_loop_.Run();
+
+  EXPECT_EQ(1, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+
+  // One change for paired, and one for trusted.
+  EXPECT_EQ(2, observer.device_changed_count_);
+  EXPECT_EQ(device, observer.last_device_);
+
+  EXPECT_TRUE(device->IsPaired());
+
+  // Make sure the trusted property has been set to true.
+  FakeBluetoothDeviceClient::Properties* properties =
+      fake_bluetooth_device_client_->GetProperties(
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath));
+  ASSERT_TRUE(properties->trusted.value());
+
+  // No pairing context should remain on the device.
+  BluetoothDeviceChromeOS* device_chromeos =
+      static_cast<BluetoothDeviceChromeOS*>(device);
+  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairConfirmPasskey) {
+  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+  GetAdapter();
+
+  TestPairingDelegate pairing_delegate;
+  adapter_->AddPairingDelegate(
+      &pairing_delegate,
+      BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+
+  // Requests that we confirm a displayed passkey.
+  fake_bluetooth_device_client_->CreateDevice(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath));
+  BluetoothDevice* device = adapter_->GetDevice(
+      FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
+  ASSERT_TRUE(device != NULL);
+  ASSERT_FALSE(device->IsPaired());
+
+  TestObserver observer(adapter_);
+
+  fake_bluetooth_device_client_->SimulatePairing(
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath),
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                 base::Unretained(this)));
+
+  EXPECT_EQ(1, pairing_delegate.call_count_);
+  EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_);
+  EXPECT_EQ(123456U, pairing_delegate.last_passkey_);
+
+  // Confirm the passkey.
+  device->ConfirmPairing();
+  message_loop_.Run();
+
+  EXPECT_EQ(1, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+
+  // One change for paired, and one for trusted.
+  EXPECT_EQ(2, observer.device_changed_count_);
+  EXPECT_EQ(device, observer.last_device_);
+
+  EXPECT_TRUE(device->IsPaired());
+
+  // Make sure the trusted property has been set to true.
+  FakeBluetoothDeviceClient::Properties* properties =
+      fake_bluetooth_device_client_->GetProperties(
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath));
+  ASSERT_TRUE(properties->trusted.value());
+
+  // No pairing context should remain on the device.
+  BluetoothDeviceChromeOS* device_chromeos =
+      static_cast<BluetoothDeviceChromeOS*>(device);
+  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairRequestPasskey) {
+  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+  GetAdapter();
+
+  TestPairingDelegate pairing_delegate;
+  adapter_->AddPairingDelegate(
+      &pairing_delegate,
+      BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+
+  // Requests that we provide a Passkey.
+  fake_bluetooth_device_client_->CreateDevice(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
+  BluetoothDevice* device = adapter_->GetDevice(
+      FakeBluetoothDeviceClient::kRequestPasskeyAddress);
+  ASSERT_TRUE(device != NULL);
+  ASSERT_FALSE(device->IsPaired());
+
+  TestObserver observer(adapter_);
+
+  fake_bluetooth_device_client_->SimulatePairing(
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath),
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                 base::Unretained(this)));
+
+  EXPECT_EQ(1, pairing_delegate.call_count_);
+  EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
+
+  // Set the Passkey.
+  device->SetPasskey(1234);
+  message_loop_.Run();
+
+  EXPECT_EQ(1, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+
+  // One change for paired, and one for trusted.
+  EXPECT_EQ(2, observer.device_changed_count_);
+  EXPECT_EQ(device, observer.last_device_);
+
+  EXPECT_TRUE(device->IsPaired());
+
+  // Make sure the trusted property has been set to true.
+  FakeBluetoothDeviceClient::Properties* properties =
+      fake_bluetooth_device_client_->GetProperties(
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
+  ASSERT_TRUE(properties->trusted.value());
+
+  // No pairing context should remain on the device.
+  BluetoothDeviceChromeOS* device_chromeos =
+      static_cast<BluetoothDeviceChromeOS*>(device);
+  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairJustWorks) {
+  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+  GetAdapter();
+
+  TestPairingDelegate pairing_delegate;
+  adapter_->AddPairingDelegate(
+      &pairing_delegate,
+      BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+
+  // Uses just-works pairing so, sinec this an incoming pairing, require
+  // authorization from the user.
+  fake_bluetooth_device_client_->CreateDevice(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath));
+  BluetoothDevice* device = adapter_->GetDevice(
+      FakeBluetoothDeviceClient::kJustWorksAddress);
+  ASSERT_TRUE(device != NULL);
+  ASSERT_FALSE(device->IsPaired());
+
+  TestObserver observer(adapter_);
+
+  fake_bluetooth_device_client_->SimulatePairing(
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath),
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                 base::Unretained(this)));
+
+  EXPECT_EQ(1, pairing_delegate.call_count_);
+  EXPECT_EQ(1, pairing_delegate.authorize_pairing_count_);
+
+  // Confirm the pairing.
+  device->ConfirmPairing();
+  message_loop_.Run();
+
+  EXPECT_EQ(1, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+
+  // One change for paired, and one for trusted.
+  EXPECT_EQ(2, observer.device_changed_count_);
+  EXPECT_EQ(device, observer.last_device_);
+
+  EXPECT_TRUE(device->IsPaired());
+
+  // Make sure the trusted property has been set to true.
+  FakeBluetoothDeviceClient::Properties* properties =
+      fake_bluetooth_device_client_->GetProperties(
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath));
+  ASSERT_TRUE(properties->trusted.value());
+
+  // No pairing context should remain on the device.
+  BluetoothDeviceChromeOS* device_chromeos =
+      static_cast<BluetoothDeviceChromeOS*>(device);
+  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairRequestPinCodeWithoutDelegate) {
+  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+  GetAdapter();
+
+  // Requires that we provide a PIN Code, without a pairing delegate,
+  // that will be rejected.
+  fake_bluetooth_device_client_->CreateDevice(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath));
+  BluetoothDevice* device = adapter_->GetDevice(
+      FakeBluetoothDeviceClient::kRequestPinCodeAddress);
+  ASSERT_TRUE(device != NULL);
+  ASSERT_FALSE(device->IsPaired());
+
+  TestObserver observer(adapter_);
+
+  fake_bluetooth_device_client_->SimulatePairing(
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath),
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                 base::Unretained(this)));
+
+  message_loop_.Run();
+
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(1, error_callback_count_);
+  EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_);
+
+  // No changes should be observer.
+  EXPECT_EQ(0, observer.device_changed_count_);
+
+  EXPECT_FALSE(device->IsPaired());
+
+  // No pairing context should remain on the device.
+  BluetoothDeviceChromeOS* device_chromeos =
+      static_cast<BluetoothDeviceChromeOS*>(device);
+  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairConfirmPasskeyWithoutDelegate) {
+  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+  GetAdapter();
+
+  // Requests that we confirm a displayed passkey, without a pairing delegate,
+  // that will be rejected.
+  fake_bluetooth_device_client_->CreateDevice(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath));
+  BluetoothDevice* device = adapter_->GetDevice(
+      FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
+  ASSERT_TRUE(device != NULL);
+  ASSERT_FALSE(device->IsPaired());
+
+  TestObserver observer(adapter_);
+
+  fake_bluetooth_device_client_->SimulatePairing(
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath),
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                 base::Unretained(this)));
+
+  message_loop_.Run();
+
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(1, error_callback_count_);
+  EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_);
+
+  // No changes should be observer.
+  EXPECT_EQ(0, observer.device_changed_count_);
+
+  EXPECT_FALSE(device->IsPaired());
+
+  // No pairing context should remain on the device.
+  BluetoothDeviceChromeOS* device_chromeos =
+      static_cast<BluetoothDeviceChromeOS*>(device);
+  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairRequestPasskeyWithoutDelegate) {
+  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+  GetAdapter();
+
+  // Requests that we provide a displayed passkey, without a pairing delegate,
+  // that will be rejected.
+  fake_bluetooth_device_client_->CreateDevice(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
+  BluetoothDevice* device = adapter_->GetDevice(
+      FakeBluetoothDeviceClient::kRequestPasskeyAddress);
+  ASSERT_TRUE(device != NULL);
+  ASSERT_FALSE(device->IsPaired());
+
+  TestObserver observer(adapter_);
+
+  fake_bluetooth_device_client_->SimulatePairing(
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath),
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                 base::Unretained(this)));
+
+  message_loop_.Run();
+
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(1, error_callback_count_);
+  EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_);
+
+  // No changes should be observer.
+  EXPECT_EQ(0, observer.device_changed_count_);
+
+  EXPECT_FALSE(device->IsPaired());
+
+  // No pairing context should remain on the device.
+  BluetoothDeviceChromeOS* device_chromeos =
+      static_cast<BluetoothDeviceChromeOS*>(device);
+  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, IncomingPairJustWorksWithoutDelegate) {
+  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+  GetAdapter();
+
+  // Uses just-works pairing and thus requires authorization for incoming
+  // pairings, without a pairing delegate, that will be rejected.
+  fake_bluetooth_device_client_->CreateDevice(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath));
+  BluetoothDevice* device = adapter_->GetDevice(
+      FakeBluetoothDeviceClient::kJustWorksAddress);
+  ASSERT_TRUE(device != NULL);
+  ASSERT_FALSE(device->IsPaired());
+
+  TestObserver observer(adapter_);
+
+  fake_bluetooth_device_client_->SimulatePairing(
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath),
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                 base::Unretained(this)));
+
+  message_loop_.Run();
+
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(1, error_callback_count_);
+  EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_);
+
+  // No changes should be observer.
+  EXPECT_EQ(0, observer.device_changed_count_);
+
+  EXPECT_FALSE(device->IsPaired());
+
+  // No pairing context should remain on the device.
+  BluetoothDeviceChromeOS* device_chromeos =
+      static_cast<BluetoothDeviceChromeOS*>(device);
+  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+}
+
+TEST_F(BluetoothChromeOSTest, RemovePairingDelegateDuringPairing) {
+  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
+
+  GetAdapter();
+
+  TestPairingDelegate pairing_delegate;
+  adapter_->AddPairingDelegate(
+      &pairing_delegate,
+      BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+
+  // Requests that we provide a Passkey.
+  fake_bluetooth_device_client_->CreateDevice(
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
+  BluetoothDevice* device = adapter_->GetDevice(
+      FakeBluetoothDeviceClient::kRequestPasskeyAddress);
+  ASSERT_TRUE(device != NULL);
+  ASSERT_FALSE(device->IsPaired());
+
+  TestObserver observer(adapter_);
+
+  fake_bluetooth_device_client_->SimulatePairing(
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath),
+      true,
+      base::Bind(&BluetoothChromeOSTest::Callback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                 base::Unretained(this)));
+
+  EXPECT_EQ(1, pairing_delegate.call_count_);
+  EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
+
+  // A pairing context should now be set on the device.
+  BluetoothDeviceChromeOS* device_chromeos =
+      static_cast<BluetoothDeviceChromeOS*>(device);
+  ASSERT_TRUE(device_chromeos->GetPairing() != NULL);
+
+  // Removing the pairing delegate should remove that pairing context.
+  adapter_->RemovePairingDelegate(&pairing_delegate);
+
+  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
+
+  // Set the Passkey, this should now have no effect since the pairing has
+  // been, in-effect, cancelled
+  device->SetPasskey(1234);
+
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_EQ(0, observer.device_changed_count_);
+
+  EXPECT_FALSE(device->IsPaired());
+}
+
+TEST_F(BluetoothChromeOSTest, DeviceId) {
+  GetAdapter();
+
+  // Use the built-in paired device for this test, grab its Properties
+  // structure so we can adjust the underlying modalias property.
+  BluetoothDevice* device = adapter_->GetDevice(
+      FakeBluetoothDeviceClient::kPairedDeviceAddress);
+  FakeBluetoothDeviceClient::Properties* properties =
+      fake_bluetooth_device_client_->GetProperties(
+          dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath));
+
+  ASSERT_TRUE(device != NULL);
+  ASSERT_TRUE(properties != NULL);
+
+  // Valid USB IF-assigned identifier.
+  ASSERT_EQ("usb:v05ACp030Dd0306", properties->modalias.value());
+
+  EXPECT_EQ(BluetoothDevice::VENDOR_ID_USB, device->GetVendorIDSource());
+  EXPECT_EQ(0x05ac, device->GetVendorID());
+  EXPECT_EQ(0x030d, device->GetProductID());
+  EXPECT_EQ(0x0306, device->GetDeviceID());
+
+  // Valid Bluetooth SIG-assigned identifier.
+  properties->modalias.ReplaceValue("bluetooth:v00E0p2400d0400");
+
+  EXPECT_EQ(BluetoothDevice::VENDOR_ID_BLUETOOTH, device->GetVendorIDSource());
+  EXPECT_EQ(0x00e0, device->GetVendorID());
+  EXPECT_EQ(0x2400, device->GetProductID());
+  EXPECT_EQ(0x0400, device->GetDeviceID());
+
+  // Invalid USB IF-assigned identifier.
+  properties->modalias.ReplaceValue("usb:x00E0p2400d0400");
+
+  EXPECT_EQ(BluetoothDevice::VENDOR_ID_UNKNOWN, device->GetVendorIDSource());
+  EXPECT_EQ(0, device->GetVendorID());
+  EXPECT_EQ(0, device->GetProductID());
+  EXPECT_EQ(0, device->GetDeviceID());
+
+  // Invalid Bluetooth SIG-assigned identifier.
+  properties->modalias.ReplaceValue("bluetooth:x00E0p2400d0400");
+
+  EXPECT_EQ(BluetoothDevice::VENDOR_ID_UNKNOWN, device->GetVendorIDSource());
+  EXPECT_EQ(0, device->GetVendorID());
+  EXPECT_EQ(0, device->GetProductID());
+  EXPECT_EQ(0, device->GetDeviceID());
+
+  // Unknown vendor specification identifier.
+  properties->modalias.ReplaceValue("chrome:v00E0p2400d0400");
+
+  EXPECT_EQ(BluetoothDevice::VENDOR_ID_UNKNOWN, device->GetVendorIDSource());
+  EXPECT_EQ(0, device->GetVendorID());
+  EXPECT_EQ(0, device->GetProductID());
+  EXPECT_EQ(0, device->GetDeviceID());
 }
 
 }  // namespace chromeos
diff --git a/device/bluetooth/bluetooth_device.h b/device/bluetooth/bluetooth_device.h
index 95fe683..cb1e7ec 100644
--- a/device/bluetooth/bluetooth_device.h
+++ b/device/bluetooth/bluetooth_device.h
@@ -33,6 +33,15 @@
 // for devices coming and going, as well as properties being updated.
 class BluetoothDevice {
  public:
+  // Possible values that may be returned by GetVendorIDSource(),
+  // indicating different organisations that allocate the identifiers returned
+  // by GetVendorID().
+  enum VendorIDSource {
+    VENDOR_ID_UNKNOWN,
+    VENDOR_ID_BLUETOOTH,
+    VENDOR_ID_USB
+  };
+
   // Possible values that may be returned by GetDeviceType(), representing
   // different types of bluetooth device that we support or are aware of
   // decoded from the bluetooth class information.
@@ -102,9 +111,7 @@
 
     // This method will be called when the Bluetooth daemon requires that the
     // user enter the PIN code |pincode| into the device |device| so that it
-    // may be authenticated. The DismissDisplayOrConfirm() method
-    // will be called to dismiss the display once pairing is complete or
-    // cancelled.
+    // may be authenticated.
     //
     // This is used for Bluetooth 2.0 and earlier keyboard devices, the
     // |pincode| will always be a six-digit numeric in the range 000000-999999
@@ -114,8 +121,7 @@
 
     // This method will be called when the Bluetooth daemon requires that the
     // user enter the Passkey |passkey| into the device |device| so that it
-    // may be authenticated. The DismissDisplayOrConfirm() method will be
-    // called to dismiss the display once pairing is complete or cancelled.
+    // may be authenticated.
     //
     // This is used for Bluetooth 2.1 and later devices that support input
     // but not display, such as keyboards. The Passkey is a numeric in the
@@ -129,8 +135,7 @@
     // using a PIN code or a Passkey.
     //
     // This method will be called only after DisplayPinCode() or
-    // DisplayPasskey() is called and before the corresponding
-    // DismissDisplayOrConfirm() is called, but is not warranted to be called
+    // DisplayPasskey() method is called, but is not warranted to be called
     // on every pairing process that requires a PIN code or a Passkey because
     // some device may not support this feature.
     //
@@ -155,10 +160,14 @@
     virtual void ConfirmPasskey(BluetoothDevice* device,
                                 uint32 passkey) = 0;
 
-    // This method will be called when any previous DisplayPinCode(),
-    // DisplayPasskey() or ConfirmPasskey() request should be concluded
-    // and removed from the user.
-    virtual void DismissDisplayOrConfirm() = 0;
+    // This method will be called when the Bluetooth daemon requires that a
+    // pairing request, usually only incoming, using the just-works model is
+    // authorized. The delegate should decide whether the user should confirm
+    // or not, then call ConfirmPairing() on the device to confirm the pairing
+    // (whether by user action or by default), RejectPairing() on the device to
+    // reject or CancelPairing() on the device to cancel authorization for
+    // any other reason.
+    virtual void AuthorizePairing(BluetoothDevice* device) = 0;
   };
 
   // Returns true if uuid is in a a valid canonical format
@@ -175,6 +184,10 @@
   // a unique key to identify the device and copied where needed.
   virtual std::string GetAddress() const = 0;
 
+  // Returns the allocation source of the identifier returned by GetVendorID(),
+  // where available, or VENDOR_ID_UNKNOWN where not.
+  virtual VendorIDSource GetVendorIDSource() const = 0;
+
   // Returns the Vendor ID of the device, where available.
   virtual uint16 GetVendorID() const = 0;
 
@@ -283,7 +296,7 @@
   // If the request fails, |error_callback| will be called; otherwise,
   // |callback| is called when the request is complete.
   // After calling Connect, CancelPairing should be called to cancel the pairing
-  // process and release |pairing_delegate_| if user cancels the pairing and
+  // process and release the pairing delegate if user cancels the pairing and
   // closes the pairing UI.
   virtual void Connect(PairingDelegate* pairing_delegate,
                        const base::Closure& callback,
@@ -309,8 +322,8 @@
   // Rejects a pairing or connection request from a remote device.
   virtual void RejectPairing() = 0;
 
-  // Cancels a pairing or connection attempt to a remote device or release
-  // |pairing_deleage_| and |agent_|.
+  // Cancels a pairing or connection attempt to a remote device, releasing
+  // the pairing delegate.
   virtual void CancelPairing() = 0;
 
   // Disconnects the device, terminating the low-level ACL connection
diff --git a/device/bluetooth/bluetooth_device_chromeos.cc b/device/bluetooth/bluetooth_device_chromeos.cc
index 76e2097..aa1d66b 100644
--- a/device/bluetooth/bluetooth_device_chromeos.cc
+++ b/device/bluetooth/bluetooth_device_chromeos.cc
@@ -4,18 +4,19 @@
 
 #include "device/bluetooth/bluetooth_device_chromeos.h"
 
+#include <stdio.h>
+
 #include "base/bind.h"
 #include "base/metrics/histogram.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "chromeos/dbus/bluetooth_adapter_client.h"
-#include "chromeos/dbus/bluetooth_agent_manager_client.h"
-#include "chromeos/dbus/bluetooth_agent_service_provider.h"
 #include "chromeos/dbus/bluetooth_device_client.h"
 #include "chromeos/dbus/bluetooth_input_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "dbus/bus.h"
 #include "device/bluetooth/bluetooth_adapter_chromeos.h"
+#include "device/bluetooth/bluetooth_pairing_chromeos.h"
 #include "device/bluetooth/bluetooth_profile_chromeos.h"
 #include "device/bluetooth/bluetooth_socket.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
@@ -24,24 +25,6 @@
 
 namespace {
 
-// The agent path is relatively meaningless since BlueZ only supports one
-// at time and will fail in an attempt to register another with "Already Exists"
-// (which we fail in OnRegisterAgentError with ERROR_INPROGRESS).
-const char kAgentPath[] = "/org/chromium/bluetooth_agent";
-
-// Histogram enumerations for pairing methods.
-enum UMAPairingMethod {
-  UMA_PAIRING_METHOD_NONE,
-  UMA_PAIRING_METHOD_REQUEST_PINCODE,
-  UMA_PAIRING_METHOD_REQUEST_PASSKEY,
-  UMA_PAIRING_METHOD_DISPLAY_PINCODE,
-  UMA_PAIRING_METHOD_DISPLAY_PASSKEY,
-  UMA_PAIRING_METHOD_CONFIRM_PASSKEY,
-  // NOTE: Add new pairing methods immediately above this line. Make sure to
-  // update the enum list in tools/histogram/histograms.xml accordinly.
-  UMA_PAIRING_METHOD_COUNT
-};
-
 // Histogram enumerations for pairing results.
 enum UMAPairingResult {
   UMA_PAIRING_RESULT_SUCCESS,
@@ -59,35 +42,37 @@
 };
 
 void ParseModalias(const dbus::ObjectPath& object_path,
-                   uint16 *vendor_id,
-                   uint16 *product_id,
-                   uint16 *device_id) {
+                   BluetoothDevice::VendorIDSource* vendor_id_source,
+                   uint16* vendor_id,
+                   uint16* product_id,
+                   uint16* device_id) {
   chromeos::BluetoothDeviceClient::Properties* properties =
       chromeos::DBusThreadManager::Get()->GetBluetoothDeviceClient()->
           GetProperties(object_path);
   DCHECK(properties);
 
   std::string modalias = properties->modalias.value();
-  if (StartsWithASCII(modalias, "usb:", false) && modalias.length() == 19) {
-    // usb:vXXXXpXXXXdXXXX
-    if (modalias[4] == 'v' && vendor_id != NULL) {
-      uint64 component = 0;
-      base::HexStringToUInt64(modalias.substr(5, 4), &component);
-      *vendor_id = component;
-    }
+  BluetoothDevice::VendorIDSource source_value;
+  int vendor_value, product_value, device_value;
 
-    if (modalias[9] == 'p' && product_id != NULL) {
-      uint64 component = 0;
-      base::HexStringToUInt64(modalias.substr(10, 4), &component);
-      *product_id = component;
-    }
-
-    if (modalias[14] == 'd' && device_id != NULL) {
-      uint64 component = 0;
-      base::HexStringToUInt64(modalias.substr(15, 4), &component);
-      *device_id = component;
-    }
+  if (sscanf(modalias.c_str(), "bluetooth:v%04xp%04xd%04x",
+             &vendor_value, &product_value, &device_value) == 3) {
+    source_value = BluetoothDevice::VENDOR_ID_BLUETOOTH;
+  } else if (sscanf(modalias.c_str(), "usb:v%04xp%04xd%04x",
+                    &vendor_value, &product_value, &device_value) == 3) {
+    source_value = BluetoothDevice::VENDOR_ID_USB;
+  } else {
+    return;
   }
+
+  if (vendor_id_source != NULL)
+    *vendor_id_source = source_value;
+  if (vendor_id != NULL)
+    *vendor_id = vendor_value;
+  if (product_id != NULL)
+    *product_id = product_value;
+  if (device_id != NULL)
+    *device_id = device_value;
 }
 
 void RecordPairingResult(BluetoothDevice::ConnectErrorCode error_code) {
@@ -133,8 +118,6 @@
     : adapter_(adapter),
       object_path_(object_path),
       num_connecting_calls_(0),
-      pairing_delegate_(NULL),
-      pairing_delegate_used_(false),
       weak_ptr_factory_(this) {
 }
 
@@ -168,21 +151,28 @@
   return properties->address.value();
 }
 
+BluetoothDevice::VendorIDSource
+BluetoothDeviceChromeOS::GetVendorIDSource() const {
+  VendorIDSource vendor_id_source = VENDOR_ID_UNKNOWN;
+  ParseModalias(object_path_, &vendor_id_source, NULL, NULL, NULL);
+  return vendor_id_source;
+}
+
 uint16 BluetoothDeviceChromeOS::GetVendorID() const {
   uint16 vendor_id  = 0;
-  ParseModalias(object_path_, &vendor_id, NULL, NULL);
+  ParseModalias(object_path_, NULL, &vendor_id, NULL, NULL);
   return vendor_id;
 }
 
 uint16 BluetoothDeviceChromeOS::GetProductID() const {
   uint16 product_id  = 0;
-  ParseModalias(object_path_, NULL, &product_id, NULL);
+  ParseModalias(object_path_, NULL, NULL, &product_id, NULL);
   return product_id;
 }
 
 uint16 BluetoothDeviceChromeOS::GetDeviceID() const {
   uint16 device_id  = 0;
-  ParseModalias(object_path_, NULL, NULL, &device_id);
+  ParseModalias(object_path_, NULL, NULL, NULL, &device_id);
   return device_id;
 }
 
@@ -248,15 +238,15 @@
 }
 
 bool BluetoothDeviceChromeOS::ExpectingPinCode() const {
-  return !pincode_callback_.is_null();
+  return pairing_.get() && pairing_->ExpectingPinCode();
 }
 
 bool BluetoothDeviceChromeOS::ExpectingPasskey() const {
-  return !passkey_callback_.is_null();
+  return pairing_.get() && pairing_->ExpectingPasskey();
 }
 
 bool BluetoothDeviceChromeOS::ExpectingConfirmation() const {
-  return !confirmation_callback_.is_null();
+  return pairing_.get() && pairing_->ExpectingConfirmation();
 }
 
 void BluetoothDeviceChromeOS::Connect(
@@ -274,77 +264,72 @@
     ConnectInternal(false, callback, error_callback);
   } else {
     // Initiate high-security connection with pairing.
-    DCHECK(!pairing_delegate_);
-    DCHECK(agent_.get() == NULL);
+    BeginPairing(pairing_delegate);
 
-    pairing_delegate_ = pairing_delegate;
-    pairing_delegate_used_ = false;
-
-    // The agent path is relatively meaningless since BlueZ only supports
-    // one per application at a time.
-    dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
-    agent_.reset(BluetoothAgentServiceProvider::Create(
-        system_bus, dbus::ObjectPath(kAgentPath), this));
-    DCHECK(agent_.get());
-
-    VLOG(1) << object_path_.value() << ": Registering agent for pairing";
-    DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
-        RegisterAgent(
-            dbus::ObjectPath(kAgentPath),
-            bluetooth_agent_manager::kKeyboardDisplayCapability,
-            base::Bind(&BluetoothDeviceChromeOS::OnRegisterAgent,
-                       weak_ptr_factory_.GetWeakPtr(),
-                       callback,
-                       error_callback),
-            base::Bind(&BluetoothDeviceChromeOS::OnRegisterAgentError,
-                       weak_ptr_factory_.GetWeakPtr(),
-                       error_callback));
+    DBusThreadManager::Get()->GetBluetoothDeviceClient()->
+        Pair(object_path_,
+             base::Bind(&BluetoothDeviceChromeOS::OnPair,
+                        weak_ptr_factory_.GetWeakPtr(),
+                        callback, error_callback),
+             base::Bind(&BluetoothDeviceChromeOS::OnPairError,
+                        weak_ptr_factory_.GetWeakPtr(),
+                        error_callback));
   }
 }
 
 void BluetoothDeviceChromeOS::SetPinCode(const std::string& pincode) {
-  if (!agent_.get() || pincode_callback_.is_null())
+  if (!pairing_.get())
     return;
 
-  pincode_callback_.Run(SUCCESS, pincode);
-  pincode_callback_.Reset();
+  pairing_->SetPinCode(pincode);
 }
 
 void BluetoothDeviceChromeOS::SetPasskey(uint32 passkey) {
-  if (!agent_.get() || passkey_callback_.is_null())
+  if (!pairing_.get())
     return;
 
-  passkey_callback_.Run(SUCCESS, passkey);
-  passkey_callback_.Reset();
+  pairing_->SetPasskey(passkey);
 }
 
 void BluetoothDeviceChromeOS::ConfirmPairing() {
-  if (!agent_.get() || confirmation_callback_.is_null())
+  if (!pairing_.get())
     return;
 
-  confirmation_callback_.Run(SUCCESS);
-  confirmation_callback_.Reset();
+  pairing_->ConfirmPairing();
 }
 
 void BluetoothDeviceChromeOS::RejectPairing() {
-  RunPairingCallbacks(REJECTED);
+  if (!pairing_.get())
+    return;
+
+  pairing_->RejectPairing();
 }
 
 void BluetoothDeviceChromeOS::CancelPairing() {
-  // If there wasn't a callback in progress that we can reply to then we
-  // have to send a CancelPairing() to the device instead.
-  if (!RunPairingCallbacks(CANCELLED)) {
+  bool canceled = false;
+
+  // If there is a callback in progress that we can reply to then use that
+  // to cancel the current pairing request.
+  if (pairing_.get() && pairing_->CancelPairing())
+    canceled = true;
+
+  // If not we have to send an explicit CancelPairing() to the device instead.
+  if (!canceled) {
+    VLOG(1) << object_path_.value() << ": No pairing context or callback. "
+            << "Sending explicit cancel";
     DBusThreadManager::Get()->GetBluetoothDeviceClient()->
         CancelPairing(
             object_path_,
             base::Bind(&base::DoNothing),
             base::Bind(&BluetoothDeviceChromeOS::OnCancelPairingError,
                        weak_ptr_factory_.GetWeakPtr()));
-
-    // Since there's no calback to this method, it's possible that the pairing
-    // delegate is going to be freed before things complete.
-    UnregisterAgent();
   }
+
+  // Since there is no callback to this method it's possible that the pairing
+  // delegate is going to be freed before things complete (indeed it's
+  // documented that this is the method you should call while freeing the
+  // pairing delegate), so clear our the context holding on to it.
+  EndPairing();
 }
 
 void BluetoothDeviceChromeOS::Disconnect(const base::Closure& callback,
@@ -419,132 +404,18 @@
   error_callback.Run();
 }
 
-
-void BluetoothDeviceChromeOS::Release() {
-  DCHECK(agent_.get());
-  DCHECK(pairing_delegate_);
-  VLOG(1) << object_path_.value() << ": Release";
-
-  pincode_callback_.Reset();
-  passkey_callback_.Reset();
-  confirmation_callback_.Reset();
-
-  UnregisterAgent();
+BluetoothPairingChromeOS* BluetoothDeviceChromeOS::BeginPairing(
+    BluetoothDevice::PairingDelegate* pairing_delegate) {
+  pairing_.reset(new BluetoothPairingChromeOS(this, pairing_delegate));
+  return pairing_.get();
 }
 
-void BluetoothDeviceChromeOS::RequestPinCode(
-    const dbus::ObjectPath& device_path,
-    const PinCodeCallback& callback) {
-  DCHECK(agent_.get());
-  DCHECK(device_path == object_path_);
-  VLOG(1) << object_path_.value() << ": RequestPinCode";
-
-  UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
-                            UMA_PAIRING_METHOD_REQUEST_PINCODE,
-                            UMA_PAIRING_METHOD_COUNT);
-
-  DCHECK(pairing_delegate_);
-  DCHECK(pincode_callback_.is_null());
-  pincode_callback_ = callback;
-  pairing_delegate_->RequestPinCode(this);
-  pairing_delegate_used_ = true;
+void BluetoothDeviceChromeOS::EndPairing() {
+  pairing_.reset();
 }
 
-void BluetoothDeviceChromeOS::DisplayPinCode(
-    const dbus::ObjectPath& device_path,
-    const std::string& pincode) {
-  DCHECK(agent_.get());
-  DCHECK(device_path == object_path_);
-  VLOG(1) << object_path_.value() << ": DisplayPinCode: " << pincode;
-
-  UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
-                            UMA_PAIRING_METHOD_DISPLAY_PINCODE,
-                            UMA_PAIRING_METHOD_COUNT);
-
-  DCHECK(pairing_delegate_);
-  pairing_delegate_->DisplayPinCode(this, pincode);
-  pairing_delegate_used_ = true;
-}
-
-void BluetoothDeviceChromeOS::RequestPasskey(
-    const dbus::ObjectPath& device_path,
-    const PasskeyCallback& callback) {
-  DCHECK(agent_.get());
-  DCHECK(device_path == object_path_);
-  VLOG(1) << object_path_.value() << ": RequestPasskey";
-
-  UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
-                            UMA_PAIRING_METHOD_REQUEST_PASSKEY,
-                            UMA_PAIRING_METHOD_COUNT);
-
-  DCHECK(pairing_delegate_);
-  DCHECK(passkey_callback_.is_null());
-  passkey_callback_ = callback;
-  pairing_delegate_->RequestPasskey(this);
-  pairing_delegate_used_ = true;
-}
-
-void BluetoothDeviceChromeOS::DisplayPasskey(
-    const dbus::ObjectPath& device_path,
-    uint32 passkey,
-    uint16 entered) {
-  DCHECK(agent_.get());
-  DCHECK(device_path == object_path_);
-  VLOG(1) << object_path_.value() << ": DisplayPasskey: " << passkey
-          << " (" << entered << " entered)";
-
-  if (entered == 0)
-    UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
-                              UMA_PAIRING_METHOD_DISPLAY_PASSKEY,
-                              UMA_PAIRING_METHOD_COUNT);
-
-  DCHECK(pairing_delegate_);
-  if (entered == 0)
-    pairing_delegate_->DisplayPasskey(this, passkey);
-  pairing_delegate_->KeysEntered(this, entered);
-  pairing_delegate_used_ = true;
-}
-
-void BluetoothDeviceChromeOS::RequestConfirmation(
-    const dbus::ObjectPath& device_path,
-    uint32 passkey,
-    const ConfirmationCallback& callback) {
-  DCHECK(agent_.get());
-  DCHECK(device_path == object_path_);
-  VLOG(1) << object_path_.value() << ": RequestConfirmation: " << passkey;
-
-  UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
-                            UMA_PAIRING_METHOD_CONFIRM_PASSKEY,
-                            UMA_PAIRING_METHOD_COUNT);
-
-  DCHECK(pairing_delegate_);
-  DCHECK(confirmation_callback_.is_null());
-  confirmation_callback_ = callback;
-  pairing_delegate_->ConfirmPasskey(this, passkey);
-  pairing_delegate_used_ = true;
-}
-
-void BluetoothDeviceChromeOS::RequestAuthorization(
-    const dbus::ObjectPath& device_path,
-    const ConfirmationCallback& callback) {
-  // TODO(keybuk): implement
-  callback.Run(CANCELLED);
-}
-
-void BluetoothDeviceChromeOS::AuthorizeService(
-    const dbus::ObjectPath& device_path,
-    const std::string& uuid,
-    const ConfirmationCallback& callback) {
-  // TODO(keybuk): implement
-  callback.Run(CANCELLED);
-}
-
-void BluetoothDeviceChromeOS::Cancel() {
-  DCHECK(agent_.get());
-  VLOG(1) << object_path_.value() << ": Cancel";
-
-  DCHECK(pairing_delegate_);
-  pairing_delegate_->DismissDisplayOrConfirm();
+BluetoothPairingChromeOS* BluetoothDeviceChromeOS::GetPairing() const {
+  return pairing_.get();
 }
 
 void BluetoothDeviceChromeOS::ConnectInternal(
@@ -613,56 +484,13 @@
   error_callback.Run(error_code);
 }
 
-void BluetoothDeviceChromeOS::OnRegisterAgent(
-    const base::Closure& callback,
-    const ConnectErrorCallback& error_callback) {
-  VLOG(1) << object_path_.value() << ": Agent registered, now pairing";
-
-  DBusThreadManager::Get()->GetBluetoothDeviceClient()->
-      Pair(object_path_,
-           base::Bind(&BluetoothDeviceChromeOS::OnPair,
-                      weak_ptr_factory_.GetWeakPtr(),
-                      callback, error_callback),
-           base::Bind(&BluetoothDeviceChromeOS::OnPairError,
-                      weak_ptr_factory_.GetWeakPtr(),
-                      error_callback));
-}
-
-void BluetoothDeviceChromeOS::OnRegisterAgentError(
-    const ConnectErrorCallback& error_callback,
-    const std::string& error_name,
-    const std::string& error_message) {
-  if (--num_connecting_calls_ == 0)
-    adapter_->NotifyDeviceChanged(this);
-
-  DCHECK(num_connecting_calls_ >= 0);
-  LOG(WARNING) << object_path_.value() << ": Failed to register agent: "
-               << error_name << ": " << error_message;
-  VLOG(1) << object_path_.value() << ": " << num_connecting_calls_
-          << " still in progress";
-
-  UnregisterAgent();
-
-  // Determine the error code from error_name.
-  ConnectErrorCode error_code = ERROR_UNKNOWN;
-  if (error_name == bluetooth_agent_manager::kErrorAlreadyExists)
-    error_code = ERROR_INPROGRESS;
-
-  RecordPairingResult(error_code);
-  error_callback.Run(error_code);
-}
-
 void BluetoothDeviceChromeOS::OnPair(
     const base::Closure& callback,
     const ConnectErrorCallback& error_callback) {
   VLOG(1) << object_path_.value() << ": Paired";
 
-  if (!pairing_delegate_used_)
-    UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
-                              UMA_PAIRING_METHOD_NONE,
-                              UMA_PAIRING_METHOD_COUNT);
-  UnregisterAgent();
-  SetTrusted();
+  EndPairing();
+
   ConnectInternal(true, callback, error_callback);
 }
 
@@ -679,7 +507,7 @@
   VLOG(1) << object_path_.value() << ": " << num_connecting_calls_
           << " still in progress";
 
-  UnregisterAgent();
+  EndPairing();
 
   // Determine the error code from error_name.
   ConnectErrorCode error_code = ERROR_UNKNOWN;
@@ -725,38 +553,6 @@
                             << ": Failed to set device as trusted";
 }
 
-void BluetoothDeviceChromeOS::UnregisterAgent() {
-  if (!agent_.get())
-    return;
-
-  DCHECK(pairing_delegate_);
-
-  DCHECK(pincode_callback_.is_null());
-  DCHECK(passkey_callback_.is_null());
-  DCHECK(confirmation_callback_.is_null());
-
-  pairing_delegate_->DismissDisplayOrConfirm();
-  pairing_delegate_ = NULL;
-
-  agent_.reset();
-
-  // Clean up after ourselves.
-  VLOG(1) << object_path_.value() << ": Unregistering pairing agent";
-  DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
-      UnregisterAgent(
-          dbus::ObjectPath(kAgentPath),
-          base::Bind(&base::DoNothing),
-          base::Bind(&BluetoothDeviceChromeOS::OnUnregisterAgentError,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-void BluetoothDeviceChromeOS::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 BluetoothDeviceChromeOS::OnDisconnect(const base::Closure& callback) {
   VLOG(1) << object_path_.value() << ": Disconnected";
   callback.Run();
@@ -780,32 +576,6 @@
   error_callback.Run();
 }
 
-bool BluetoothDeviceChromeOS::RunPairingCallbacks(Status status) {
-  if (!agent_.get())
-    return false;
-
-  bool callback_run = false;
-  if (!pincode_callback_.is_null()) {
-    pincode_callback_.Run(status, "");
-    pincode_callback_.Reset();
-    callback_run = true;
-  }
-
-  if (!passkey_callback_.is_null()) {
-    passkey_callback_.Run(status, 0);
-    passkey_callback_.Reset();
-    callback_run = true;
-  }
-
-  if (!confirmation_callback_.is_null()) {
-    confirmation_callback_.Run(status);
-    confirmation_callback_.Reset();
-    callback_run = true;
-  }
-
-  return callback_run;
-}
-
 void BluetoothDeviceChromeOS::OnConnectProfile(
     device::BluetoothProfile* profile,
     const base::Closure& callback) {
diff --git a/device/bluetooth/bluetooth_device_chromeos.h b/device/bluetooth/bluetooth_device_chromeos.h
index fef7cfd..bc94c3c 100644
--- a/device/bluetooth/bluetooth_device_chromeos.h
+++ b/device/bluetooth/bluetooth_device_chromeos.h
@@ -9,7 +9,6 @@
 
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/dbus/bluetooth_agent_service_provider.h"
 #include "chromeos/dbus/bluetooth_device_client.h"
 #include "dbus/object_path.h"
 #include "device/bluetooth/bluetooth_device.h"
@@ -17,16 +16,17 @@
 namespace chromeos {
 
 class BluetoothAdapterChromeOS;
+class BluetoothPairingChromeOS;
 
 // The BluetoothDeviceChromeOS class implements BluetoothDevice for the
 // Chrome OS platform.
 class BluetoothDeviceChromeOS
-    : public device::BluetoothDevice,
-      private chromeos::BluetoothAgentServiceProvider::Delegate {
+    : public device::BluetoothDevice {
  public:
   // BluetoothDevice override
   virtual uint32 GetBluetoothClass() const OVERRIDE;
   virtual std::string GetAddress() const OVERRIDE;
+  virtual VendorIDSource GetVendorIDSource() const OVERRIDE;
   virtual uint16 GetVendorID() const OVERRIDE;
   virtual uint16 GetProductID() const OVERRIDE;
   virtual uint16 GetDeviceID() const OVERRIDE;
@@ -72,6 +72,19 @@
       const base::Closure& callback,
       const ErrorCallback& error_callback) OVERRIDE;
 
+  // Creates a pairing object with the given delegate |pairing_delegate| and
+  // establishes it as the pairing context for this device. All pairing-related
+  // method calls will be forwarded to this object until it is released.
+  BluetoothPairingChromeOS* BeginPairing(
+      BluetoothDevice::PairingDelegate* pairing_delegate);
+
+  // Releases the current pairing object, any pairing-related method calls will
+  // be ignored.
+  void EndPairing();
+
+  // Returns the current pairing object or NULL if no pairing is in progress.
+  BluetoothPairingChromeOS* GetPairing() const;
+
  protected:
    // BluetoothDevice override
   virtual std::string GetDeviceName() const OVERRIDE;
@@ -83,28 +96,6 @@
                           const dbus::ObjectPath& object_path);
   virtual ~BluetoothDeviceChromeOS();
 
-  // BluetoothAgentServiceProvider::Delegate override.
-  virtual void Release() OVERRIDE;
-  virtual void RequestPinCode(const dbus::ObjectPath& device_path,
-                              const PinCodeCallback& callback) OVERRIDE;
-  virtual void DisplayPinCode(const dbus::ObjectPath& device_path,
-                              const std::string& pincode) OVERRIDE;
-  virtual void RequestPasskey(const dbus::ObjectPath& device_path,
-                              const PasskeyCallback& callback) OVERRIDE;
-  virtual void DisplayPasskey(const dbus::ObjectPath& device_path,
-                              uint32 passkey, uint16 entered) OVERRIDE;
-  virtual void RequestConfirmation(const dbus::ObjectPath& device_path,
-                                   uint32 passkey,
-                                   const ConfirmationCallback& callback)
-      OVERRIDE;
-  virtual void RequestAuthorization(const dbus::ObjectPath& device_path,
-                                    const ConfirmationCallback& callback)
-      OVERRIDE;
-  virtual void AuthorizeService(const dbus::ObjectPath& device_path,
-                                const std::string& uuid,
-                                const ConfirmationCallback& callback) OVERRIDE;
-  virtual void Cancel() OVERRIDE;
-
   // Internal method to initiate a connection to this device, and methods called
   // by dbus:: on completion of the D-Bus method call.
   void ConnectInternal(bool after_pairing,
@@ -117,14 +108,6 @@
                       const std::string& error_name,
                       const std::string& error_message);
 
-  // Called by dbus:: on completion of the D-Bus method call to register the
-  // pairing agent.
-  void OnRegisterAgent(const base::Closure& callback,
-                       const ConnectErrorCallback& error_callback);
-  void OnRegisterAgentError(const ConnectErrorCallback& error_callback,
-                            const std::string& error_name,
-                            const std::string& error_message);
-
   // Called by dbus:: on completion of the D-Bus method call to pair the device.
   void OnPair(const base::Closure& callback,
               const ConnectErrorCallback& error_callback);
@@ -145,13 +128,6 @@
   void SetTrusted();
   void OnSetTrusted(bool success);
 
-  // Internal method to unregister the pairing agent and method called by dbus::
-  // on failure of the D-Bus method call. No completion call as success is
-  // ignored.
-  void UnregisterAgent();
-  void OnUnregisterAgentError(const std::string& error_name,
-                              const std::string& error_message);
-
   // Called by dbus:: on completion of the D-Bus method call to disconnect the
   // device.
   void OnDisconnect(const base::Closure& callback);
@@ -166,10 +142,6 @@
                      const std::string& error_name,
                      const std::string& error_message);
 
-  // Run any outstanding pairing callbacks passing |status| as the result of
-  // pairing. Returns true if any callbacks were run, false if not.
-  bool RunPairingCallbacks(Status status);
-
   // Called by dbus:: on completion of the D-Bus method call to
   // connect a peofile.
   void OnConnectProfile(device::BluetoothProfile* profile,
@@ -179,7 +151,7 @@
                              const std::string& error_name,
                              const std::string& error_message);
 
-  // Return the object path of the device; used by BluetoothAdapterChromeOS
+  // Returns the object path of the device; used by BluetoothAdapterChromeOS
   const dbus::ObjectPath& object_path() const { return object_path_; }
 
   // The adapter that owns this device instance.
@@ -194,22 +166,7 @@
   // During pairing this is set to an object that we don't own, but on which
   // we can make method calls to request, display or confirm PIN Codes and
   // Passkeys. Generally it is the object that owns this one.
-  PairingDelegate* pairing_delegate_;
-
-  // Flag to indicate whether a pairing delegate method has been called during
-  // pairing.
-  bool pairing_delegate_used_;
-
-  // During pairing this is set to an instance of a D-Bus agent object
-  // intialized with our own class as its delegate.
-  scoped_ptr<BluetoothAgentServiceProvider> agent_;
-
-  // During pairing these callbacks are set to those provided by method calls
-  // made on us by |agent_| and are called by our own method calls such as
-  // SetPinCode() and SetPasskey().
-  PinCodeCallback pincode_callback_;
-  PasskeyCallback passkey_callback_;
-  ConfirmationCallback confirmation_callback_;
+  scoped_ptr<BluetoothPairingChromeOS> pairing_;
 
   // Note: This should remain the last member so it'll be destroyed and
   // invalidate its weak pointers before any other members are destroyed.
diff --git a/device/bluetooth/bluetooth_device_mac.h b/device/bluetooth/bluetooth_device_mac.h
index bc887b9..bc3ce19 100644
--- a/device/bluetooth/bluetooth_device_mac.h
+++ b/device/bluetooth/bluetooth_device_mac.h
@@ -26,6 +26,7 @@
   // BluetoothDevice override
   virtual uint32 GetBluetoothClass() const OVERRIDE;
   virtual std::string GetAddress() const OVERRIDE;
+  virtual VendorIDSource GetVendorIDSource() const OVERRIDE;
   virtual uint16 GetVendorID() const OVERRIDE;
   virtual uint16 GetProductID() const OVERRIDE;
   virtual uint16 GetDeviceID() const OVERRIDE;
diff --git a/device/bluetooth/bluetooth_device_mac.mm b/device/bluetooth/bluetooth_device_mac.mm
index 39acbef..50762d2 100644
--- a/device/bluetooth/bluetooth_device_mac.mm
+++ b/device/bluetooth/bluetooth_device_mac.mm
@@ -82,6 +82,11 @@
   return base::SysNSStringToUTF8([device_ addressString]);
 }
 
+BluetoothDevice::VendorIDSource
+BluetoothDeviceMac::GetVendorIDSource() const {
+  return VENDOR_ID_UNKNOWN;
+}
+
 uint16 BluetoothDeviceMac::GetVendorID() const {
   return 0;
 }
diff --git a/device/bluetooth/bluetooth_device_win.cc b/device/bluetooth/bluetooth_device_win.cc
index bfd9bc6..3f95efd 100644
--- a/device/bluetooth/bluetooth_device_win.cc
+++ b/device/bluetooth/bluetooth_device_win.cc
@@ -71,6 +71,11 @@
   return address_;
 }
 
+BluetoothDevice::VendorIDSource
+BluetoothDeviceWin::GetVendorIDSource() const {
+  return VENDOR_ID_UNKNOWN;
+}
+
 uint16 BluetoothDeviceWin::GetVendorID() const {
   return 0;
 }
diff --git a/device/bluetooth/bluetooth_device_win.h b/device/bluetooth/bluetooth_device_win.h
index 452f6f2..274364b 100644
--- a/device/bluetooth/bluetooth_device_win.h
+++ b/device/bluetooth/bluetooth_device_win.h
@@ -26,6 +26,7 @@
   // BluetoothDevice override
   virtual uint32 GetBluetoothClass() const OVERRIDE;
   virtual std::string GetAddress() const OVERRIDE;
+  virtual VendorIDSource GetVendorIDSource() const OVERRIDE;
   virtual uint16 GetVendorID() const OVERRIDE;
   virtual uint16 GetProductID() const OVERRIDE;
   virtual uint16 GetDeviceID() const OVERRIDE;
diff --git a/device/bluetooth/bluetooth_discovery_session.cc b/device/bluetooth/bluetooth_discovery_session.cc
new file mode 100644
index 0000000..ba5f5fd
--- /dev/null
+++ b/device/bluetooth/bluetooth_discovery_session.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_discovery_session.h"
+
+#include "base/bind.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+
+namespace device {
+
+BluetoothDiscoverySession::BluetoothDiscoverySession(
+    scoped_refptr<BluetoothAdapter> adapter)
+    : active_(true), adapter_(adapter), weak_ptr_factory_(this) {
+  DCHECK(adapter_.get());
+}
+
+BluetoothDiscoverySession::BluetoothDiscoverySession()
+    : active_(false), weak_ptr_factory_(this) {}
+
+BluetoothDiscoverySession::~BluetoothDiscoverySession() {
+  // |adapter_| may be NULL if this instance was initialized as a mock.
+  if (!adapter_.get()) {
+    DCHECK(!active_);
+    return;
+  }
+  Stop(base::Bind(&base::DoNothing), base::Bind(&base::DoNothing));
+  MarkAsInactive();
+}
+
+bool BluetoothDiscoverySession::IsActive() const {
+  return active_;
+}
+
+void BluetoothDiscoverySession::Stop(
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  if (!active_) {
+    LOG(WARNING) << "Discovery session not active. Cannot stop.";
+    error_callback.Run();
+    return;
+  }
+  VLOG(1) << "Stopping device discovery session.";
+  DCHECK(adapter_.get());
+  adapter_->RemoveDiscoverySession(
+      base::Bind(&BluetoothDiscoverySession::OnStop,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 callback),
+      error_callback);
+}
+
+void BluetoothDiscoverySession::OnStop(const base::Closure& callback) {
+  MarkAsInactive();
+  callback.Run();
+}
+
+void BluetoothDiscoverySession::MarkAsInactive() {
+  if (!active_)
+    return;
+  active_ = false;
+  DCHECK(adapter_.get());
+  adapter_->DiscoverySessionBecameInactive(this);
+}
+
+}  // namespace device
diff --git a/device/bluetooth/bluetooth_discovery_session.h b/device/bluetooth/bluetooth_discovery_session.h
new file mode 100644
index 0000000..666538b
--- /dev/null
+++ b/device/bluetooth/bluetooth_discovery_session.h
@@ -0,0 +1,90 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_DISCOVERY_SESSION_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_DISCOVERY_SESSION_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+
+namespace device {
+
+class BluetoothAdapter;
+
+// BluetoothDiscoverySession represents a current active or inactive device
+// discovery session. Instances of this class are obtained by calling
+// BluetoothAdapter::StartDiscoverySession. The Bluetooth adapter will be
+// constantly searching for nearby devices, as long as at least one instance
+// of an active BluetoothDiscoverySession exists. A BluetoothDiscoverySession is
+// considered active, as long as the adapter is discovering AND the owner of the
+// instance has not called BluetoothDiscoverySession::Stop. A
+// BluetoothDiscoverySession might unexpectedly become inactive, if the adapter
+// unexpectedly stops discovery. Users can implement the
+// AdapterDiscoveringChanged method of the BluetoothAdapter::Observer interface
+// to be notified of such a change and promptly request a new
+// BluetoothDiscoverySession if their existing sessions have become inactive.
+class BluetoothDiscoverySession {
+ public:
+  // The ErrorCallback is used by methods to asynchronously report errors.
+  typedef base::Closure ErrorCallback;
+
+  // Destructor automatically terminates the discovery session. If this
+  // results in a call to the underlying system to stop device discovery
+  // (i.e. this instance represents the last active discovery session),
+  // the call may not always succeed. To be notified of such failures,
+  // users are highly encouraged to call BluetoothDiscoverySession::Stop,
+  // instead of relying on the destructor.
+  virtual ~BluetoothDiscoverySession();
+
+  // Returns true if the session is active, false otherwise. If false, the
+  // adapter might still be discovering as there might still be other active
+  // sessions; this just means that this instance no longer has a say in
+  // whether or not discovery should continue. In this case, the application
+  // should request a new BluetoothDiscoverySession to make sure that device
+  // discovery continues.
+  virtual bool IsActive() const;
+
+  // Requests this discovery session instance to stop. If this instance is
+  // active, the session will stop. On success, |callback| is called and
+  // on error |error_callback| is called. After a successful invocation, the
+  // adapter may or may not stop device discovery, depending on whether or not
+  // other active discovery sessions are present. Users are highly encouraged
+  // to call this method to end a discovery session, instead of relying on the
+  // destructor, so that they can be notified of the result via the callback
+  // arguments.
+  virtual void Stop(const base::Closure& callback,
+                    const ErrorCallback& error_callback);
+
+ protected:
+  BluetoothDiscoverySession();  // Called by mock.
+
+ private:
+  friend class BluetoothAdapter;
+  explicit BluetoothDiscoverySession(scoped_refptr<BluetoothAdapter> adapter);
+
+  // Internal callback invoked when a call to Stop has succeeded.
+  void OnStop(const base::Closure& callback);
+
+  // Marks this instance as inactive. Called by BluetoothAdapter to mark a
+  // session as inactive in the case of an unexpected change to the adapter
+  // discovery state.
+  void MarkAsInactive();
+
+  // Whether or not this instance represents an active discovery session.
+  bool active_;
+
+  // The adapter that created this instance.
+  scoped_refptr<BluetoothAdapter> adapter_;
+
+  // Note: This should remain the last member so it'll be destroyed and
+  // invalidate its weak pointers before any other members are destroyed.
+  base::WeakPtrFactory<BluetoothDiscoverySession> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(BluetoothDiscoverySession);
+};
+
+}  // namespace device
+
+#endif  // DEVICE_BLUETOOTH_BLUETOOTH_DISCOVERY_SESSION_H_
diff --git a/device/bluetooth/bluetooth_pairing_chromeos.cc b/device/bluetooth/bluetooth_pairing_chromeos.cc
new file mode 100644
index 0000000..dc73f6d
--- /dev/null
+++ b/device/bluetooth/bluetooth_pairing_chromeos.cc
@@ -0,0 +1,273 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_pairing_chromeos.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_device_chromeos.h"
+
+using device::BluetoothDevice;
+
+namespace {
+
+// Histogram enumerations for pairing methods.
+enum UMAPairingMethod {
+  UMA_PAIRING_METHOD_NONE,
+  UMA_PAIRING_METHOD_REQUEST_PINCODE,
+  UMA_PAIRING_METHOD_REQUEST_PASSKEY,
+  UMA_PAIRING_METHOD_DISPLAY_PINCODE,
+  UMA_PAIRING_METHOD_DISPLAY_PASSKEY,
+  UMA_PAIRING_METHOD_CONFIRM_PASSKEY,
+  // NOTE: Add new pairing methods immediately above this line. Make sure to
+  // update the enum list in tools/histogram/histograms.xml accordingly.
+  UMA_PAIRING_METHOD_COUNT
+};
+
+// Number of keys that will be entered for a passkey, six digits plus the
+// final enter.
+const uint16 kPasskeyMaxKeysEntered = 7;
+
+}  // namespace
+
+namespace chromeos {
+
+BluetoothPairingChromeOS::BluetoothPairingChromeOS(
+    BluetoothDeviceChromeOS* device,
+    BluetoothDevice::PairingDelegate* pairing_delegate)
+    : device_(device),
+      pairing_delegate_(pairing_delegate),
+      pairing_delegate_used_(false) {
+  VLOG(1) << "Created BluetoothPairingChromeOS for "
+          << device_->GetAddress();
+}
+
+BluetoothPairingChromeOS::~BluetoothPairingChromeOS() {
+  VLOG(1) << "Destroying BluetoothPairingChromeOS for "
+          << device_->GetAddress();
+
+  if (!pairing_delegate_used_) {
+    UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+                              UMA_PAIRING_METHOD_NONE,
+                              UMA_PAIRING_METHOD_COUNT);
+  }
+
+  if (!pincode_callback_.is_null()) {
+    pincode_callback_.Run(
+        BluetoothAgentServiceProvider::Delegate::CANCELLED, "");
+  }
+
+  if (!passkey_callback_.is_null()) {
+    passkey_callback_.Run(
+        BluetoothAgentServiceProvider::Delegate::CANCELLED, 0);
+  }
+
+  if (!confirmation_callback_.is_null()) {
+    confirmation_callback_.Run(
+        BluetoothAgentServiceProvider::Delegate::CANCELLED);
+  }
+
+  pairing_delegate_ = NULL;
+}
+
+void BluetoothPairingChromeOS::RequestPinCode(
+    const BluetoothAgentServiceProvider::Delegate::PinCodeCallback& callback) {
+  UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+                            UMA_PAIRING_METHOD_REQUEST_PINCODE,
+                            UMA_PAIRING_METHOD_COUNT);
+
+  ResetCallbacks();
+  pincode_callback_ = callback;
+  pairing_delegate_used_ = true;
+  pairing_delegate_->RequestPinCode(device_);
+}
+
+bool BluetoothPairingChromeOS::ExpectingPinCode() const {
+  return !pincode_callback_.is_null();
+}
+
+void BluetoothPairingChromeOS::SetPinCode(const std::string& pincode) {
+  if (pincode_callback_.is_null())
+    return;
+
+  pincode_callback_.Run(BluetoothAgentServiceProvider::Delegate::SUCCESS,
+                        pincode);
+  pincode_callback_.Reset();
+
+  // If this is not an outgoing connection to the device, clean up the pairing
+  // context since the pairing is done. The outgoing connection case is cleaned
+  // up in the callback for the underlying Pair() call.
+  if (!device_->IsConnecting())
+    device_->EndPairing();
+}
+
+void BluetoothPairingChromeOS::DisplayPinCode(const std::string& pincode) {
+  UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+                            UMA_PAIRING_METHOD_DISPLAY_PINCODE,
+                            UMA_PAIRING_METHOD_COUNT);
+
+  ResetCallbacks();
+  pairing_delegate_used_ = true;
+  pairing_delegate_->DisplayPinCode(device_, pincode);
+
+  // If this is not an outgoing connection to the device, the pairing context
+  // needs to be cleaned up again as there's no reliable indication of
+  // completion of incoming pairing.
+  if (!device_->IsConnecting())
+    device_->EndPairing();
+}
+
+void BluetoothPairingChromeOS::RequestPasskey(
+    const BluetoothAgentServiceProvider::Delegate::PasskeyCallback& callback) {
+  UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+                            UMA_PAIRING_METHOD_REQUEST_PASSKEY,
+                            UMA_PAIRING_METHOD_COUNT);
+
+  ResetCallbacks();
+  passkey_callback_ = callback;
+  pairing_delegate_used_ = true;
+  pairing_delegate_->RequestPasskey(device_);
+}
+
+bool BluetoothPairingChromeOS::ExpectingPasskey() const {
+  return !passkey_callback_.is_null();
+}
+
+void BluetoothPairingChromeOS::SetPasskey(uint32 passkey) {
+  if (passkey_callback_.is_null())
+    return;
+
+  passkey_callback_.Run(BluetoothAgentServiceProvider::Delegate::SUCCESS,
+                        passkey);
+  passkey_callback_.Reset();
+
+  // If this is not an outgoing connection to the device, clean up the pairing
+  // context since the pairing is done. The outgoing connection case is cleaned
+  // up in the callback for the underlying Pair() call.
+  if (!device_->IsConnecting())
+    device_->EndPairing();
+}
+
+void BluetoothPairingChromeOS::DisplayPasskey(uint32 passkey) {
+  UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+                            UMA_PAIRING_METHOD_DISPLAY_PASSKEY,
+                            UMA_PAIRING_METHOD_COUNT);
+
+  ResetCallbacks();
+  pairing_delegate_used_ = true;
+  pairing_delegate_->DisplayPasskey(device_, passkey);
+
+}
+
+void BluetoothPairingChromeOS::KeysEntered(uint16 entered) {
+  pairing_delegate_used_ = true;
+  pairing_delegate_->KeysEntered(device_, entered);
+
+  // If this is not an outgoing connection to the device, the pairing context
+  // needs to be cleaned up again as there's no reliable indication of
+  // completion of incoming pairing.
+  if (entered >= kPasskeyMaxKeysEntered && !device_->IsConnecting())
+    device_->EndPairing();
+}
+
+void BluetoothPairingChromeOS::RequestConfirmation(
+    uint32 passkey,
+    const BluetoothAgentServiceProvider::Delegate::ConfirmationCallback&
+        callback) {
+  UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+                            UMA_PAIRING_METHOD_CONFIRM_PASSKEY,
+                            UMA_PAIRING_METHOD_COUNT);
+
+  ResetCallbacks();
+  confirmation_callback_ = callback;
+  pairing_delegate_used_ = true;
+  pairing_delegate_->ConfirmPasskey(device_, passkey);
+}
+
+void BluetoothPairingChromeOS::RequestAuthorization(
+    const BluetoothAgentServiceProvider::Delegate::ConfirmationCallback&
+        callback) {
+  UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
+                            UMA_PAIRING_METHOD_NONE,
+                            UMA_PAIRING_METHOD_COUNT);
+
+  ResetCallbacks();
+  confirmation_callback_ = callback;
+  pairing_delegate_used_ = true;
+  pairing_delegate_->AuthorizePairing(device_);
+}
+
+bool BluetoothPairingChromeOS::ExpectingConfirmation() const {
+  return !confirmation_callback_.is_null();
+}
+
+void BluetoothPairingChromeOS::ConfirmPairing() {
+  if (confirmation_callback_.is_null())
+    return;
+
+  confirmation_callback_.Run(BluetoothAgentServiceProvider::Delegate::SUCCESS);
+  confirmation_callback_.Reset();
+
+  // If this is not an outgoing connection to the device, clean up the pairing
+  // context since the pairing is done. The outgoing connection case is cleaned
+  // up in the callback for the underlying Pair() call.
+  if (!device_->IsConnecting())
+    device_->EndPairing();
+}
+
+bool BluetoothPairingChromeOS::RejectPairing() {
+  return RunPairingCallbacks(
+      BluetoothAgentServiceProvider::Delegate::REJECTED);
+}
+
+bool BluetoothPairingChromeOS::CancelPairing() {
+  return RunPairingCallbacks(
+      BluetoothAgentServiceProvider::Delegate::CANCELLED);
+}
+
+BluetoothDevice::PairingDelegate*
+BluetoothPairingChromeOS::GetPairingDelegate() const {
+  return pairing_delegate_;
+}
+
+void BluetoothPairingChromeOS::ResetCallbacks() {
+  pincode_callback_.Reset();
+  passkey_callback_.Reset();
+  confirmation_callback_.Reset();
+}
+
+bool BluetoothPairingChromeOS::RunPairingCallbacks(
+    BluetoothAgentServiceProvider::Delegate::Status status) {
+  pairing_delegate_used_ = true;
+
+  bool callback_run = false;
+  if (!pincode_callback_.is_null()) {
+    pincode_callback_.Run(status, "");
+    pincode_callback_.Reset();
+    callback_run = true;
+  }
+
+  if (!passkey_callback_.is_null()) {
+    passkey_callback_.Run(status, 0);
+    passkey_callback_.Reset();
+    callback_run = true;
+  }
+
+  if (!confirmation_callback_.is_null()) {
+    confirmation_callback_.Run(status);
+    confirmation_callback_.Reset();
+    callback_run = true;
+  }
+
+  // If this is not an outgoing connection to the device, clean up the pairing
+  // context since the pairing is done. The outgoing connection case is cleaned
+  // up in the callback for the underlying Pair() call.
+  if (!device_->IsConnecting())
+    device_->EndPairing();
+
+  return callback_run;
+}
+
+}  // namespace chromeos
diff --git a/device/bluetooth/bluetooth_pairing_chromeos.h b/device/bluetooth/bluetooth_pairing_chromeos.h
new file mode 100644
index 0000000..ae906bc
--- /dev/null
+++ b/device/bluetooth/bluetooth_pairing_chromeos.h
@@ -0,0 +1,146 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_BLUETOOTH_PAIRING_CHROMEOS_H_
+#define DEVICE_BLUETOOTH_BLUETOOTH_PAIRING_CHROMEOS_H_
+
+#include "chromeos/dbus/bluetooth_agent_service_provider.h"
+#include "device/bluetooth/bluetooth_device.h"
+
+namespace chromeos {
+
+class BluetoothDeviceChromeOS;
+
+// The BluetoothPairingChromeOS class encapsulates the logic for an individual
+// device pairing, acting as a bridge between BluetoothAdapterChromeOS which
+// communicates with the underlying Controller and Host Subsystem, and
+// BluetoothDeviceChromeOS which presents the pairing logic to the application.
+class BluetoothPairingChromeOS {
+ public:
+  BluetoothPairingChromeOS(
+      BluetoothDeviceChromeOS* device,
+      device::BluetoothDevice::PairingDelegate* pairing_delegate);
+  ~BluetoothPairingChromeOS();
+
+  // Indicates whether the device is currently pairing and expecting a
+  // Passkey to be returned.
+  bool ExpectingPasskey() const;
+
+  // Indicates whether the device is currently pairing and expecting
+  // confirmation of a displayed passkey.
+  bool ExpectingConfirmation() const;
+
+  // Requests a PIN code for the current device from the current pairing
+  // delegate, the SetPinCode(), RejectPairing() and CancelPairing() method
+  // calls on this object are translated into the appropriate response to
+  // |callback|.
+  void RequestPinCode(
+      const BluetoothAgentServiceProvider::Delegate::PinCodeCallback& callback);
+
+  // Indicates whether the device is currently pairing and expecting a
+  // PIN Code to be returned.
+  bool ExpectingPinCode() const;
+
+  // Sends the PIN code |pincode| to the remote device during pairing.
+  //
+  // PIN Codes are generally required for Bluetooth 2.0 and earlier devices
+  // for which there is no automatic pairing or special handling.
+  void SetPinCode(const std::string& pincode);
+
+  // Requests a PIN code for the current device be displayed by the current
+  // pairing delegate. No response is expected from the delegate.
+  void DisplayPinCode(const std::string& pincode);
+
+  // Requests a Passkey for the current device from the current pairing
+  // delegate, the SetPasskey(), RejectPairing() and CancelPairing() method
+  // calls on this object are translated into the appropriate response to
+  // |callback|.
+  void RequestPasskey(
+      const BluetoothAgentServiceProvider::Delegate::PasskeyCallback& callback);
+
+  // Sends the Passkey |passkey| to the remote device during pairing.
+  //
+  // Passkeys are generally required for Bluetooth 2.1 and later devices
+  // which cannot provide input or display on their own, and don't accept
+  // passkey-less pairing, and are a numeric in the range 0-999999.
+  void SetPasskey(uint32 passkey);
+
+  // Requests a Passkey for the current device be displayed by the current
+  // pairing delegate. No response is expected from the delegate.
+  void DisplayPasskey(uint32 passkey);
+
+  // Informs the current pairing delegate that |entered| keys have been
+  // provided to the remote device since the DisplayPasskey() call. No
+  // response is expected from the delegate.
+  void KeysEntered(uint16 entered);
+
+  // Requests confirmation that |passkey| is displayed on the current device
+  // from the current pairing delegate. The ConfirmPairing(), RejectPairing()
+  // and CancelPairing() method calls on this object are translated into the
+  // appropriate response to |callback|.
+  void RequestConfirmation(
+      uint32 passkey,
+      const BluetoothAgentServiceProvider::Delegate::ConfirmationCallback&
+          callback);
+
+  // Requests authorization that the current device be allowed to pair with
+  // this device from the current pairing delegate. The ConfirmPairing(),
+  // RejectPairing() and CancelPairing() method calls on this object are
+  // translated into the appropriate response to |callback|.
+  void RequestAuthorization(
+      const BluetoothAgentServiceProvider::Delegate::ConfirmationCallback&
+          callback);
+
+  // Confirms to the remote device during pairing that a passkey provided by
+  // the ConfirmPasskey() delegate call is displayed on both devices.
+  void ConfirmPairing();
+
+  // Rejects a pairing or connection request from a remote device, returns
+  // false if there was no way to reject the pairing.
+  bool RejectPairing();
+
+  // Cancels a pairing or connection attempt to a remote device, returns
+  // false if there was no way to cancel the pairing.
+  bool CancelPairing();
+
+  // Returns the pairing delegate being used by this pairing object.
+  device::BluetoothDevice::PairingDelegate* GetPairingDelegate() const;
+
+ private:
+  // Internal method to reset the current set of callbacks because a new
+  // request has arrived that supercedes them.
+  void ResetCallbacks();
+
+  // Internal method to respond to the relevant callback for a RejectPairing
+  // or CancelPairing call.
+  bool RunPairingCallbacks(
+      BluetoothAgentServiceProvider::Delegate::Status status);
+
+  // The underlying BluetoothDeviceChromeOS that owns this pairing context.
+  BluetoothDeviceChromeOS* device_;
+
+  // UI Pairing Delegate to make method calls on, this must live as long as
+  // the object capturing the PairingContext.
+  device::BluetoothDevice::PairingDelegate* pairing_delegate_;
+
+  // Flag to indicate whether any pairing delegate method has been called
+  // during pairing. Used to determine whether we need to log the
+  // "no pairing interaction" metric.
+  bool pairing_delegate_used_;
+
+  // During pairing these callbacks are set to those provided by method calls
+  // made on the BluetoothAdapterChromeOS instance by its respective
+  // BluetoothAgentServiceProvider instance, and are called by our own
+  // method calls such as SetPinCode() and SetPasskey().
+  BluetoothAgentServiceProvider::Delegate::PinCodeCallback pincode_callback_;
+  BluetoothAgentServiceProvider::Delegate::PasskeyCallback passkey_callback_;
+  BluetoothAgentServiceProvider::Delegate::ConfirmationCallback
+      confirmation_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(BluetoothPairingChromeOS);
+};
+
+}  // namespace chromeos
+
+#endif  // DEVICE_BLUETOOTH_BLUETOOTH_PAIRING_CHROMEOS_H_
diff --git a/device/bluetooth/bluetooth_profile_chromeos.cc b/device/bluetooth/bluetooth_profile_chromeos.cc
index 31e165e..5fe87fe 100644
--- a/device/bluetooth/bluetooth_profile_chromeos.cc
+++ b/device/bluetooth/bluetooth_profile_chromeos.cc
@@ -30,6 +30,7 @@
 #include "device/bluetooth/bluetooth_profile.h"
 #include "device/bluetooth/bluetooth_socket.h"
 #include "device/bluetooth/bluetooth_socket_chromeos.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
 
 using device::BluetoothAdapter;
 using device::BluetoothAdapterFactory;
@@ -60,6 +61,11 @@
 BluetoothProfileChromeOS::~BluetoothProfileChromeOS() {
   DCHECK(object_path_.value().empty());
   DCHECK(profile_.get() == NULL);
+
+  if (adapter_.get()) {
+    adapter_->RemoveObserver(this);
+    adapter_ = NULL;
+  }
 }
 
 void BluetoothProfileChromeOS::Init(
@@ -76,16 +82,15 @@
 
   uuid_ = uuid;
 
-  BluetoothProfileManagerClient::Options bluetooth_options;
-  bluetooth_options.name = options.name;
-  bluetooth_options.service = uuid;
-  bluetooth_options.channel = options.channel;
-  bluetooth_options.psm = options.psm;
-  bluetooth_options.require_authentication = options.require_authentication;
-  bluetooth_options.require_authorization = options.require_authorization;
-  bluetooth_options.auto_connect = options.auto_connect;
-  bluetooth_options.version = options.version;
-  bluetooth_options.features = options.features;
+  options_.name = options.name;
+  options_.service = uuid;
+  options_.channel = options.channel;
+  options_.psm = options.psm;
+  options_.require_authentication = options.require_authentication;
+  options_.require_authorization = options.require_authorization;
+  options_.auto_connect = options.auto_connect;
+  options_.version = options.version;
+  options_.features = options.features;
 
   // The object path is relatively meaningless, but has to be unique, so we
   // use the UUID of the profile.
@@ -100,18 +105,12 @@
       system_bus, object_path_, this));
   DCHECK(profile_.get());
 
-  VLOG(1) << object_path_.value() << ": Register profile";
-  DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
-      RegisterProfile(
-          object_path_,
-          uuid,
-          bluetooth_options,
-          base::Bind(&BluetoothProfileChromeOS::OnRegisterProfile,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     callback),
-          base::Bind(&BluetoothProfileChromeOS::OnRegisterProfileError,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     callback));
+  // Now the profile object is registered we need an adapter to register it
+  // with.
+  BluetoothAdapterFactory::GetAdapter(
+      base::Bind(&BluetoothProfileChromeOS::OnGetAdapter,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 callback));
 }
 
 void BluetoothProfileChromeOS::Unregister() {
@@ -135,6 +134,44 @@
   connection_callback_ = callback;
 }
 
+void BluetoothProfileChromeOS::AdapterPresentChanged(BluetoothAdapter* adapter,
+                                                     bool present) {
+  if (!present)
+    return;
+
+  VLOG(1) << object_path_.value() << ": Register profile";
+  DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
+      RegisterProfile(
+          object_path_,
+          uuid_,
+          options_,
+          base::Bind(&BluetoothProfileChromeOS::OnInternalRegisterProfile,
+                     weak_ptr_factory_.GetWeakPtr()),
+          base::Bind(&BluetoothProfileChromeOS::OnInternalRegisterProfileError,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BluetoothProfileChromeOS::OnGetAdapter(
+    const ProfileCallback& callback,
+    scoped_refptr<device::BluetoothAdapter> adapter) {
+  DCHECK(!adapter_.get());
+  adapter_ = adapter;
+  adapter_->AddObserver(this);
+
+  VLOG(1) << object_path_.value() << ": Register profile";
+  DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
+      RegisterProfile(
+          object_path_,
+          uuid_,
+          options_,
+          base::Bind(&BluetoothProfileChromeOS::OnRegisterProfile,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     callback),
+          base::Bind(&BluetoothProfileChromeOS::OnRegisterProfileError,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     callback));
+}
+
 void BluetoothProfileChromeOS::Release() {
   VLOG(1) << object_path_.value() << ": Release";
 }
@@ -145,22 +182,22 @@
     const BluetoothProfileServiceProvider::Delegate::Options& options,
     const ConfirmationCallback& callback) {
   VLOG(1) << object_path_.value() << ": New connection from device: "
-          << device_path.value();;
+          << device_path.value();
   if (connection_callback_.is_null()) {
     callback.Run(REJECTED);
     return;
   }
 
   // Punt descriptor validity check to a worker thread where i/o is permitted;
-  // on return we'll fetch the adapter and then call the connection callback.
+  // on return we'll call the connection callback.
   //
   // base::Passed is used to take ownership of the file descriptor during the
-  // CheckValidity() call and pass that ownership to the GetAdapter() call.
+  // CheckValidity() call and pass that ownership to callback.
   base::PostTaskAndReplyWithResult(
       base::WorkerPool::GetTaskRunner(false).get(),
       FROM_HERE,
       base::Bind(&CheckValidity, base::Passed(&fd)),
-      base::Bind(&BluetoothProfileChromeOS::GetAdapter,
+      base::Bind(&BluetoothProfileChromeOS::OnCheckValidity,
                  weak_ptr_factory_.GetWeakPtr(),
                  device_path,
                  options,
@@ -178,6 +215,22 @@
   VLOG(1) << object_path_.value() << ": Cancel";
 }
 
+void BluetoothProfileChromeOS::OnInternalRegisterProfile() {
+  VLOG(1) << object_path_.value() << ": Profile registered";
+}
+
+void BluetoothProfileChromeOS::OnInternalRegisterProfileError(
+    const std::string& error_name,
+    const std::string& error_message) {
+  // It's okay if the profile already exists, it means we registered it on
+  // initialization.
+  if (error_name == bluetooth_profile_manager::kErrorAlreadyExists)
+    return;
+
+  LOG(WARNING) << object_path_.value() << ": Failed to register profile: "
+               << error_name << ": " << error_message;
+}
+
 void BluetoothProfileChromeOS::OnRegisterProfile(
     const ProfileCallback& callback) {
   VLOG(1) << object_path_.value() << ": Profile registered";
@@ -188,29 +241,38 @@
     const ProfileCallback& callback,
     const std::string& error_name,
     const std::string& error_message) {
+  // It's okay if the profile already exists, it means we registered it when
+  // we first saw the adapter.
+  if (error_name == bluetooth_profile_manager::kErrorAlreadyExists)
+    return;
+
   LOG(WARNING) << object_path_.value() << ": Failed to register profile: "
                << error_name << ": " << error_message;
   callback.Run(NULL);
-
-  Unregister();
 }
 
 void BluetoothProfileChromeOS::OnUnregisterProfile() {
   VLOG(1) << object_path_.value() << ": Profile unregistered";
   object_path_ = dbus::ObjectPath("");
+  adapter_ = NULL;
   delete this;
 }
 
 void BluetoothProfileChromeOS::OnUnregisterProfileError(
     const std::string& error_name,
     const std::string& error_message) {
+  // It's okay if the profile didn't exist, it means we never saw an adapter.
+  if (error_name == bluetooth_profile_manager::kErrorDoesNotExist)
+    return;
+
   LOG(WARNING) << object_path_.value() << ": Failed to unregister profile: "
                << error_name << ": " << error_message;
   object_path_ = dbus::ObjectPath("");
+  adapter_ = NULL;
   delete this;
 }
 
-void BluetoothProfileChromeOS::GetAdapter(
+void BluetoothProfileChromeOS::OnCheckValidity(
       const dbus::ObjectPath& device_path,
       const BluetoothProfileServiceProvider::Delegate::Options& options,
       const ConfirmationCallback& callback,
@@ -221,27 +283,10 @@
     return;
   }
 
-  BluetoothAdapterFactory::GetAdapter(
-      base::Bind(&BluetoothProfileChromeOS::OnGetAdapter,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 device_path,
-                 options,
-                 callback,
-                 base::Passed(&fd)));
-}
-
-void BluetoothProfileChromeOS::OnGetAdapter(
-    const dbus::ObjectPath& device_path,
-    const BluetoothProfileServiceProvider::Delegate::Options&
-        options,
-    const ConfirmationCallback& callback,
-    scoped_ptr<dbus::FileDescriptor> fd,
-    scoped_refptr<BluetoothAdapter> adapter) {
-  VLOG(1) << object_path_.value() << ": Obtained adapter reference";
   callback.Run(SUCCESS);
 
   BluetoothDeviceChromeOS* device =
-      static_cast<BluetoothAdapterChromeOS*>(adapter.get())->
+      static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
           GetDeviceWithPath(device_path);
   DCHECK(device);
 
diff --git a/device/bluetooth/bluetooth_profile_chromeos.h b/device/bluetooth/bluetooth_profile_chromeos.h
index c13951e..84b7043 100644
--- a/device/bluetooth/bluetooth_profile_chromeos.h
+++ b/device/bluetooth/bluetooth_profile_chromeos.h
@@ -12,8 +12,10 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/bluetooth_profile_manager_client.h"
 #include "chromeos/dbus/bluetooth_profile_service_provider.h"
 #include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_profile.h"
 
 namespace dbus {
@@ -22,18 +24,13 @@
 
 }  // namespace dbus
 
-namespace device {
-
-class BluetoothAdapter;
-
-}  // namespace device
-
 namespace chromeos {
 
 // The BluetoothProfileChromeOS class implements BluetoothProfile for the
 // Chrome OS platform.
 class CHROMEOS_EXPORT BluetoothProfileChromeOS
     : public device::BluetoothProfile,
+      private device::BluetoothAdapter::Observer,
       private BluetoothProfileServiceProvider::Delegate {
  public:
   // BluetoothProfile override.
@@ -69,8 +66,22 @@
       const ConfirmationCallback& callback) OVERRIDE;
   virtual void Cancel() OVERRIDE;
 
+  // device::BluetoothAdapter::Observer override.
+  virtual void AdapterPresentChanged(device::BluetoothAdapter* adapter,
+                                     bool present) OVERRIDE;
+
   // Called by dbus:: on completion of the D-Bus method call to register the
-  // profile object.
+  // profile object as a result of the adapter becoming present.
+  void OnInternalRegisterProfile();
+  void OnInternalRegisterProfileError(const std::string& error_name,
+                                      const std::string& error_message);
+
+  // Internal method run to get the adapter object during initialization.
+  void OnGetAdapter(const ProfileCallback& callback,
+                    scoped_refptr<device::BluetoothAdapter> adapter);
+
+  // Called by dbus:: on completion of the D-Bus method call to register the
+  // profile object during initialization.
   void OnRegisterProfile(const ProfileCallback& callback);
   void OnRegisterProfileError(const ProfileCallback& callback,
                               const std::string& error_name,
@@ -83,27 +94,22 @@
                                 const std::string& error_message);
 
   // Method run once the file descriptor has been validated in order to get
-  // the default adapter, and method run once the default adapter has been
-  // obtained in order to get the device object to be passed to the connection
-  // callback.
+  // the device object to be passed to the connection callback.
   //
   // The |fd| argument is moved compared to the NewConnection() call since it
   // becomes the result of a PostTaskAndReplyWithResult() call.
-  void GetAdapter(
+  void OnCheckValidity(
       const dbus::ObjectPath& device_path,
       const BluetoothProfileServiceProvider::Delegate::Options& options,
       const ConfirmationCallback& callback,
       scoped_ptr<dbus::FileDescriptor> fd);
-  void OnGetAdapter(
-      const dbus::ObjectPath& device_path,
-      const BluetoothProfileServiceProvider::Delegate::Options& options,
-      const ConfirmationCallback& callback,
-      scoped_ptr<dbus::FileDescriptor> fd,
-      scoped_refptr<device::BluetoothAdapter>);
 
   // UUID of the profile passed during initialization.
   std::string uuid_;
 
+  // Copy of the profile options passed during initialization.
+  BluetoothProfileManagerClient::Options options_;
+
   // Object path of the local profile D-Bus object.
   dbus::ObjectPath object_path_;
 
@@ -111,6 +117,10 @@
   // from BlueZ.
   scoped_ptr<BluetoothProfileServiceProvider> profile_;
 
+  // Reference to the adapter object, the profile is re-registered when the
+  // adapter changes.
+  scoped_refptr<device::BluetoothAdapter> adapter_;
+
   // Callback used on both outgoing and incoming connections to pass the
   // connected socket to profile object owner.
   ConnectionCallback connection_callback_;
diff --git a/device/bluetooth/bluetooth_profile_chromeos_unittest.cc b/device/bluetooth/bluetooth_profile_chromeos_unittest.cc
index dd7c945..da2f659 100644
--- a/device/bluetooth/bluetooth_profile_chromeos_unittest.cc
+++ b/device/bluetooth/bluetooth_profile_chromeos_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "base/message_loop/message_loop.h"
 #include "chromeos/dbus/fake_bluetooth_adapter_client.h"
+#include "chromeos/dbus/fake_bluetooth_agent_manager_client.h"
 #include "chromeos/dbus/fake_bluetooth_device_client.h"
 #include "chromeos/dbus/fake_bluetooth_input_client.h"
 #include "chromeos/dbus/fake_bluetooth_profile_manager_client.h"
@@ -45,6 +46,9 @@
     fake_dbus_thread_manager->SetBluetoothProfileManagerClient(
         scoped_ptr<BluetoothProfileManagerClient>(
             fake_bluetooth_profile_manager_client_));
+    fake_dbus_thread_manager->SetBluetoothAgentManagerClient(
+        scoped_ptr<BluetoothAgentManagerClient>(
+            new FakeBluetoothAgentManagerClient));
     fake_dbus_thread_manager->SetBluetoothAdapterClient(
         scoped_ptr<BluetoothAdapterClient>(new FakeBluetoothAdapterClient));
     fake_dbus_thread_manager->SetBluetoothDeviceClient(
diff --git a/device/bluetooth/bluetooth_socket_chromeos.cc b/device/bluetooth/bluetooth_socket_chromeos.cc
index 3a881d1..85544d2 100644
--- a/device/bluetooth/bluetooth_socket_chromeos.cc
+++ b/device/bluetooth/bluetooth_socket_chromeos.cc
@@ -160,7 +160,7 @@
   DCHECK(fd->is_valid());
 
   BluetoothSocketChromeOS* bluetooth_socket =
-      new BluetoothSocketChromeOS(fd->TakeValue());;
+      new BluetoothSocketChromeOS(fd->TakeValue());
   return scoped_refptr<BluetoothSocketChromeOS>(bluetooth_socket);
 }
 
diff --git a/device/bluetooth/bluetooth_strings.grd b/device/bluetooth/bluetooth_strings.grd
index 7e10b62..9d3eec4 100644
--- a/device/bluetooth/bluetooth_strings.grd
+++ b/device/bluetooth/bluetooth_strings.grd
@@ -12,34 +12,34 @@
     </output>
     <output filename="device_bluetooth_strings_am.pak" type="data_package" lang="am" />
     <output filename="device_bluetooth_strings_ar.pak" type="data_package" lang="ar" />
-    <if expr="pp_ifdef('use_third_party_translations')">
+    <if expr="use_third_party_translations">
       <output filename="device_bluetooth_strings_ast.pak" type="data_package" lang="ast" />
     </if>
     <output filename="device_bluetooth_strings_bg.pak" type="data_package" lang="bg" />
     <output filename="device_bluetooth_strings_bn.pak" type="data_package" lang="bn" />
-    <if expr="pp_ifdef('use_third_party_translations')">
+    <if expr="use_third_party_translations">
       <output filename="device_bluetooth_strings_bs.pak" type="data_package" lang="bs" />
     </if>
     <output filename="device_bluetooth_strings_ca.pak" type="data_package" lang="ca" />
-    <if expr="pp_ifdef('use_third_party_translations')">
+    <if expr="use_third_party_translations">
       <output filename="device_bluetooth_strings_ca@valencia.pak" type="data_package" lang="ca@valencia" />
     </if>
     <output filename="device_bluetooth_strings_cs.pak" type="data_package" lang="cs" />
     <output filename="device_bluetooth_strings_da.pak" type="data_package" lang="da" />
     <output filename="device_bluetooth_strings_de.pak" type="data_package" lang="de" />
     <output filename="device_bluetooth_strings_el.pak" type="data_package" lang="el" />
-    <if expr="pp_ifdef('use_third_party_translations')">
+    <if expr="use_third_party_translations">
       <output filename="device_bluetooth_strings_en-AU.pak" type="data_package" lang="en-AU" />
     </if>
     <output filename="device_bluetooth_strings_en-GB.pak" type="data_package" lang="en-GB" />
     <output filename="device_bluetooth_strings_en-US.pak" type="data_package" lang="en" />
-    <if expr="pp_ifdef('use_third_party_translations')">
+    <if expr="use_third_party_translations">
       <output filename="device_bluetooth_strings_eo.pak" type="data_package" lang="eo" />
     </if>
     <output filename="device_bluetooth_strings_es.pak" type="data_package" lang="es" />
     <output filename="device_bluetooth_strings_es-419.pak" type="data_package" lang="es-419" />
     <output filename="device_bluetooth_strings_et.pak" type="data_package" lang="et" />
-    <if expr="pp_ifdef('use_third_party_translations')">
+    <if expr="use_third_party_translations">
       <output filename="device_bluetooth_strings_eu.pak" type="data_package" lang="eu" />
     </if>
     <output filename="device_bluetooth_strings_fa.pak" type="data_package" lang="fa" />
@@ -47,7 +47,7 @@
     <output filename="device_bluetooth_strings_fi.pak" type="data_package" lang="fi" />
     <output filename="device_bluetooth_strings_fil.pak" type="data_package" lang="fil" />
     <output filename="device_bluetooth_strings_fr.pak" type="data_package" lang="fr" />
-    <if expr="pp_ifdef('use_third_party_translations')">
+    <if expr="use_third_party_translations">
       <output filename="device_bluetooth_strings_gl.pak" type="data_package" lang="gl" />
     </if>
     <output filename="device_bluetooth_strings_gu.pak" type="data_package" lang="gu" />
@@ -55,19 +55,19 @@
     <output filename="device_bluetooth_strings_hi.pak" type="data_package" lang="hi" />
     <output filename="device_bluetooth_strings_hr.pak" type="data_package" lang="hr" />
     <output filename="device_bluetooth_strings_hu.pak" type="data_package" lang="hu" />
-    <if expr="pp_ifdef('use_third_party_translations')">
+    <if expr="use_third_party_translations">
       <output filename="device_bluetooth_strings_hy.pak" type="data_package" lang="hy" />
       <output filename="device_bluetooth_strings_ia.pak" type="data_package" lang="ia" />
     </if>
     <output filename="device_bluetooth_strings_id.pak" type="data_package" lang="id" />
     <output filename="device_bluetooth_strings_it.pak" type="data_package" lang="it" />
     <output filename="device_bluetooth_strings_ja.pak" type="data_package" lang="ja" />
-    <if expr="pp_ifdef('use_third_party_translations')">
+    <if expr="use_third_party_translations">
       <output filename="device_bluetooth_strings_ka.pak" type="data_package" lang="ka" />
     </if>
     <output filename="device_bluetooth_strings_kn.pak" type="data_package" lang="kn" />
     <output filename="device_bluetooth_strings_ko.pak" type="data_package" lang="ko" />
-    <if expr="pp_ifdef('use_third_party_translations')">
+    <if expr="use_third_party_translations">
       <output filename="device_bluetooth_strings_ku.pak" type="data_package" lang="ku" />
       <output filename="device_bluetooth_strings_kw.pak" type="data_package" lang="kw" />
     </if>
@@ -94,7 +94,7 @@
     <output filename="device_bluetooth_strings_te.pak" type="data_package" lang="te" />
     <output filename="device_bluetooth_strings_th.pak" type="data_package" lang="th" />
     <output filename="device_bluetooth_strings_tr.pak" type="data_package" lang="tr" />
-    <if expr="pp_ifdef('use_third_party_translations')">
+    <if expr="use_third_party_translations">
       <output filename="device_bluetooth_strings_ug.pak" type="data_package" lang="ug" />
     </if>
     <output filename="device_bluetooth_strings_uk.pak" type="data_package" lang="uk" />
diff --git a/device/bluetooth/test/mock_bluetooth_adapter.cc b/device/bluetooth/test/mock_bluetooth_adapter.cc
index 5848db3..9938cc3 100644
--- a/device/bluetooth/test/mock_bluetooth_adapter.cc
+++ b/device/bluetooth/test/mock_bluetooth_adapter.cc
@@ -14,4 +14,12 @@
 
 MockBluetoothAdapter::~MockBluetoothAdapter() {}
 
+void MockBluetoothAdapter::AddDiscoverySession(
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {}
+
+void MockBluetoothAdapter::RemoveDiscoverySession(
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {}
+
 }  // namespace device
diff --git a/device/bluetooth/test/mock_bluetooth_adapter.h b/device/bluetooth/test/mock_bluetooth_adapter.h
index 0266f03..32f40d1 100644
--- a/device/bluetooth/test/mock_bluetooth_adapter.h
+++ b/device/bluetooth/test/mock_bluetooth_adapter.h
@@ -52,11 +52,8 @@
                     const base::Closure& callback,
                     const ErrorCallback& error_callback));
   MOCK_CONST_METHOD0(IsDiscovering, bool());
-  MOCK_METHOD2(StartDiscovering,
-               void(const base::Closure& callback,
-                    const ErrorCallback& error_callback));
-  MOCK_METHOD2(StopDiscovering,
-               void(const base::Closure& callback,
+  MOCK_METHOD2(StartDiscoverySession,
+               void(const DiscoverySessionCallback& callback,
                     const ErrorCallback& error_callback));
   MOCK_CONST_METHOD0(GetDevices, BluetoothAdapter::ConstDeviceList());
   MOCK_METHOD1(GetDevice, BluetoothDevice*(const std::string& address));
@@ -66,8 +63,22 @@
       ReadLocalOutOfBandPairingData,
       void(const BluetoothOutOfBandPairingDataCallback& callback,
            const ErrorCallback& error_callback));
+  MOCK_METHOD2(AddPairingDelegate,
+               void(BluetoothDevice::PairingDelegate* pairing_delegate,
+                    enum PairingDelegatePriority priority));
+  MOCK_METHOD1(RemovePairingDelegate,
+               void(BluetoothDevice::PairingDelegate* pairing_delegate));
+  MOCK_METHOD0(DefaultPairingDelegate, BluetoothDevice::PairingDelegate*());
+
  protected:
+  virtual void AddDiscoverySession(const base::Closure& callback,
+                                   const ErrorCallback& error_callback);
+  virtual void RemoveDiscoverySession(const base::Closure& callback,
+                                      const ErrorCallback& error_callback);
   virtual ~MockBluetoothAdapter();
+
+  MOCK_METHOD1(RemovePairingDelegateInternal,
+               void(BluetoothDevice::PairingDelegate* pairing_delegate));
 };
 
 }  // namespace device
diff --git a/device/bluetooth/test/mock_bluetooth_device.cc b/device/bluetooth/test/mock_bluetooth_device.cc
index c1fe56f..4804dc0 100644
--- a/device/bluetooth/test/mock_bluetooth_device.cc
+++ b/device/bluetooth/test/mock_bluetooth_device.cc
@@ -24,6 +24,16 @@
       .WillByDefault(testing::Return(name_));
   ON_CALL(*this, GetAddress())
       .WillByDefault(testing::Return(address_));
+  ON_CALL(*this, GetDeviceType())
+      .WillByDefault(testing::Return(DEVICE_UNKNOWN));
+  ON_CALL(*this, GetVendorIDSource())
+      .WillByDefault(testing::Return(VENDOR_ID_UNKNOWN));
+  ON_CALL(*this, GetVendorID())
+      .WillByDefault(testing::Return(0));
+  ON_CALL(*this, GetProductID())
+      .WillByDefault(testing::Return(0));
+  ON_CALL(*this, GetDeviceID())
+      .WillByDefault(testing::Return(0));
   ON_CALL(*this, IsPaired())
       .WillByDefault(testing::Return(paired));
   ON_CALL(*this, IsConnected())
diff --git a/device/bluetooth/test/mock_bluetooth_device.h b/device/bluetooth/test/mock_bluetooth_device.h
index de2907d..bc028e5 100644
--- a/device/bluetooth/test/mock_bluetooth_device.h
+++ b/device/bluetooth/test/mock_bluetooth_device.h
@@ -29,6 +29,7 @@
   MOCK_CONST_METHOD0(GetBluetoothClass, uint32());
   MOCK_CONST_METHOD0(GetDeviceName, std::string());
   MOCK_CONST_METHOD0(GetAddress, std::string());
+  MOCK_CONST_METHOD0(GetVendorIDSource, BluetoothDevice::VendorIDSource());
   MOCK_CONST_METHOD0(GetVendorID, uint16());
   MOCK_CONST_METHOD0(GetProductID, uint16());
   MOCK_CONST_METHOD0(GetDeviceID, uint16());
diff --git a/device/bluetooth/test/mock_bluetooth_discovery_session.cc b/device/bluetooth/test/mock_bluetooth_discovery_session.cc
new file mode 100644
index 0000000..72449ee
--- /dev/null
+++ b/device/bluetooth/test/mock_bluetooth_discovery_session.cc
@@ -0,0 +1,14 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/test/mock_bluetooth_discovery_session.h"
+
+#include "device/bluetooth/bluetooth_adapter.h"
+
+namespace device {
+
+MockBluetoothDiscoverySession::MockBluetoothDiscoverySession() {}
+MockBluetoothDiscoverySession::~MockBluetoothDiscoverySession() {}
+
+}  // namespace device
diff --git a/device/bluetooth/test/mock_bluetooth_discovery_session.h b/device/bluetooth/test/mock_bluetooth_discovery_session.h
new file mode 100644
index 0000000..98f2125
--- /dev/null
+++ b/device/bluetooth/test/mock_bluetooth_discovery_session.h
@@ -0,0 +1,32 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_DISCOVERY_SESSION_H_
+#define DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_DISCOVERY_SESSION_H_
+
+#include "base/callback.h"
+#include "device/bluetooth/bluetooth_discovery_session.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace device {
+
+class BluetoothAdapter;
+
+class MockBluetoothDiscoverySession : public BluetoothDiscoverySession {
+ public:
+  MockBluetoothDiscoverySession();
+  virtual ~MockBluetoothDiscoverySession();
+
+  MOCK_CONST_METHOD0(IsActive, bool());
+  MOCK_METHOD2(Stop,
+               void(const base::Closure& callback,
+                    const ErrorCallback& error_callback));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockBluetoothDiscoverySession);
+};
+
+}  // namespac device
+
+#endif  // DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_DISCOVERY_SESSION_H_
diff --git a/device/device_tests.gyp b/device/device_tests.gyp
index 268b270..0cadffb 100644
--- a/device/device_tests.gyp
+++ b/device/device_tests.gyp
@@ -23,6 +23,7 @@
       ],
       'sources': [
         'bluetooth/bluetooth_adapter_mac_unittest.mm',
+        'bluetooth/bluetooth_adapter_unittest.cc',
         'bluetooth/bluetooth_adapter_win_unittest.cc',
         'bluetooth/bluetooth_device_win_unittest.cc',
         'bluetooth/bluetooth_chromeos_unittest.cc',
@@ -55,7 +56,8 @@
         }],
         ['os_posix == 1 and OS != "mac" and OS != "android" and OS != "ios"', {
           'conditions': [
-            ['linux_use_tcmalloc == 1', {
+            # TODO(dmikurube): Kill linux_use_tcmalloc. http://crbug.com/345554
+            ['(use_allocator!="none" and use_allocator!="see_use_tcmalloc") or (use_allocator=="see_use_tcmalloc" and linux_use_tcmalloc==1)', {
               'dependencies': [
                 '../base/allocator/allocator.gyp:allocator',
               ],
diff --git a/device/hid/hid_connection.cc b/device/hid/hid_connection.cc
index 5678407..c134bf2 100644
--- a/device/hid/hid_connection.cc
+++ b/device/hid/hid_connection.cc
@@ -6,13 +6,17 @@
 
 namespace device {
 
-HidConnection::HidConnection(HidDeviceInfo device_info)
+PendingHidReport::PendingHidReport() {}
+
+PendingHidReport::~PendingHidReport() {}
+
+PendingHidRead::PendingHidRead() {}
+
+PendingHidRead::~PendingHidRead() {}
+
+HidConnection::HidConnection(const HidDeviceInfo& device_info)
     : device_info_(device_info) {}
 
 HidConnection::~HidConnection() {}
 
-const HidDeviceInfo& HidConnection::device_info() const {
-  return device_info_;
-}
-
 }  // namespace device
diff --git a/device/hid/hid_connection.h b/device/hid/hid_connection.h
index 27792c4..5c08fbc 100644
--- a/device/hid/hid_connection.h
+++ b/device/hid/hid_connection.h
@@ -1,18 +1,16 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef DEVICE_HID_HID_CONNECTION_H_
 #define DEVICE_HID_HID_CONNECTION_H_
 
+#include <stdint.h>
+
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
-#include "base/threading/thread_checker.h"
 #include "device/hid/hid_device_info.h"
-
-namespace net {
-class IOBuffer;
-}
+#include "net/base/io_buffer.h"
 
 namespace device {
 
@@ -20,35 +18,48 @@
  public:
   typedef base::Callback<void(bool success, size_t size)> IOCallback;
 
-  virtual void Read(scoped_refptr<net::IOBuffer> buffer,
-                    size_t size,
+  virtual void Read(scoped_refptr<net::IOBufferWithSize> buffer,
                     const IOCallback& callback) = 0;
-  virtual void Write(scoped_refptr<net::IOBuffer> buffer,
-                     size_t size,
+  virtual void Write(uint8_t report_id,
+                     scoped_refptr<net::IOBufferWithSize> buffer,
                      const IOCallback& callback) = 0;
-  virtual void GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                size_t size,
+  virtual void GetFeatureReport(uint8_t report_id,
+                                scoped_refptr<net::IOBufferWithSize> buffer,
                                 const IOCallback& callback) = 0;
-  virtual void SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                 size_t size,
+  virtual void SendFeatureReport(uint8_t report_id,
+                                 scoped_refptr<net::IOBufferWithSize> buffer,
                                  const IOCallback& callback) = 0;
 
-  const HidDeviceInfo& device_info() const;
+  const HidDeviceInfo& device_info() const { return device_info_; }
 
  protected:
   friend class base::RefCountedThreadSafe<HidConnection>;
   friend struct HidDeviceInfo;
 
-  HidConnection(HidDeviceInfo device_info);
+  explicit HidConnection(const HidDeviceInfo& device_info);
   virtual ~HidConnection();
 
+ private:
   const HidDeviceInfo device_info_;
 
-  base::ThreadChecker thread_checker_;
-
   DISALLOW_COPY_AND_ASSIGN(HidConnection);
 };
 
+struct PendingHidReport {
+  PendingHidReport();
+  ~PendingHidReport();
+
+  scoped_refptr<net::IOBufferWithSize> buffer;
+};
+
+struct PendingHidRead {
+  PendingHidRead();
+  ~PendingHidRead();
+
+  scoped_refptr<net::IOBufferWithSize> buffer;
+  HidConnection::IOCallback callback;
+};
+
 }  // namespace device
 
 #endif  // DEVICE_HID_HID_CONNECTION_H_
diff --git a/device/hid/hid_connection_linux.cc b/device/hid/hid_connection_linux.cc
index 0722fab..75c9e07 100644
--- a/device/hid/hid_connection_linux.cc
+++ b/device/hid/hid_connection_linux.cc
@@ -8,26 +8,47 @@
 #include <fcntl.h>
 #include <libudev.h>
 #include <linux/hidraw.h>
+#include <sys/ioctl.h>
+
 #include <string>
 
+#include "base/files/file_path.h"
+#include "base/posix/eintr_wrapper.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/tuple.h"
 #include "device/hid/hid_service.h"
 #include "device/hid/hid_service_linux.h"
 
+// These are already defined in newer versions of linux/hidraw.h.
+#ifndef HIDIOCSFEATURE
+#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x06, len)
+#endif
+#ifndef HIDIOCGFEATURE
+#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE | _IOC_READ, 'H', 0x07, len)
+#endif
 
 namespace device {
 
 namespace {
 
+// Copies a buffer into a new one with a report ID byte inserted at the front.
+scoped_refptr<net::IOBufferWithSize> CopyBufferWithReportId(
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    uint8_t report_id) {
+  scoped_refptr<net::IOBufferWithSize> new_buffer(
+      new net::IOBufferWithSize(buffer->size() + 1));
+  new_buffer->data()[0] = report_id;
+  memcpy(new_buffer->data() + 1, buffer->data(), buffer->size());
+  return new_buffer;
+}
+
 const char kHidrawSubsystem[] = "hidraw";
 
 }  // namespace
 
 HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info,
                                        ScopedUdevDevicePtr udev_raw_device)
-    : HidConnection(device_info),
-      initialized_(false) {
+    : HidConnection(device_info) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   udev_device* dev = udev_raw_device.get();
@@ -37,42 +58,32 @@
     return;
   }
 
-  base::PlatformFileError error;
+  int flags = base::File::FLAG_OPEN |
+              base::File::FLAG_READ |
+              base::File::FLAG_WRITE |
+              base::File::FLAG_EXCLUSIVE_READ |
+              base::File::FLAG_EXCLUSIVE_WRITE;
 
-  int flags = base::PLATFORM_FILE_OPEN |
-              base::PLATFORM_FILE_READ |
-              base::PLATFORM_FILE_WRITE |
-              base::PLATFORM_FILE_EXCLUSIVE_READ |
-              base::PLATFORM_FILE_EXCLUSIVE_WRITE;
-
-  base::PlatformFile device_file = base::CreatePlatformFile(
-      base::FilePath(dev_node),
-      flags,
-      NULL,
-      &error);
-  if (error || device_file <= 0) {
-    LOG(ERROR) << error;
-    if (device_file)
-      base::ClosePlatformFile(device_file);
+  base::File device_file(base::FilePath(dev_node), flags);
+  if (!device_file.IsValid()) {
+    LOG(ERROR) << device_file.error_details();
     return;
   }
-  if (fcntl(device_file, F_SETFL, fcntl(device_file, F_GETFL) | O_NONBLOCK)) {
+  if (fcntl(device_file.GetPlatformFile(), F_SETFL,
+            fcntl(device_file.GetPlatformFile(), F_GETFL) | O_NONBLOCK)) {
     PLOG(ERROR) << "Failed to set non-blocking flag to device file.";
     return;
   }
-  device_file_ = device_file;
+  device_file_ = device_file.Pass();
 
   if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
-      device_file_,
+      device_file_.GetPlatformFile(),
       true,
       base::MessageLoopForIO::WATCH_READ_WRITE,
       &device_file_watcher_,
       this)) {
-    LOG(ERROR) << "Cannot start watching file descriptor.";
-    return;
+    LOG(ERROR) << "Failed to start watching device file.";
   }
-
-  initialized_ = true;
 }
 
 HidConnectionLinux::~HidConnectionLinux() {
@@ -82,22 +93,23 @@
 
 void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_EQ(fd, device_file_);
-  DCHECK(initialized_);
+  DCHECK_EQ(fd, device_file_.GetPlatformFile());
 
   uint8 buffer[1024] = {0};
-  int bytes = read(device_file_, buffer, 1024);
-  if (bytes < 0) {
+  int bytes_read =
+      HANDLE_EINTR(read(device_file_.GetPlatformFile(), buffer, 1024));
+  if (bytes_read < 0) {
     if (errno == EAGAIN) {
       return;
     }
     Disconnect();
     return;
   }
-  scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(bytes));
-  memcpy(io_buffer->data(), buffer, bytes);
-  input_reports_.push(std::make_pair(io_buffer, bytes));
 
+  PendingHidReport report;
+  report.buffer = new net::IOBufferWithSize(bytes_read);
+  memcpy(report.buffer->data(), buffer, bytes_read);
+  pending_reports_.push(report);
   ProcessReadQueue();
 }
 
@@ -105,89 +117,91 @@
 
 void HidConnectionLinux::Disconnect() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (!initialized_)
-    return;
-
-  initialized_ = false;
   device_file_watcher_.StopWatchingFileDescriptor();
-  close(device_file_);
-  while (!read_queue_.empty()) {
-    PendingRequest callback = read_queue_.front();
-    read_queue_.pop();
-    callback.c.Run(false, 0);
+  device_file_.Close();
+  while (!pending_reads_.empty()) {
+    PendingHidRead pending_read = pending_reads_.front();
+    pending_reads_.pop();
+    pending_read.callback.Run(false, 0);
   }
 }
 
-void HidConnectionLinux::Read(scoped_refptr<net::IOBuffer> buffer,
-                              size_t size,
+void HidConnectionLinux::Read(scoped_refptr<net::IOBufferWithSize> buffer,
                               const IOCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (!initialized_) {
-    DCHECK(read_queue_.empty());
-    // There might be unread reports.
-    if (!input_reports_.empty()){
-      read_queue_.push(MakeTuple(buffer, size, callback));
-      ProcessReadQueue();
-    }
-    callback.Run(false, 0);
-    return;
-  } else {
-    read_queue_.push(MakeTuple(buffer, size, callback));
-    ProcessReadQueue();
-  }
+  PendingHidRead pending_read;
+  pending_read.buffer = buffer;
+  pending_read.callback = callback;
+  pending_reads_.push(pending_read);
+  ProcessReadQueue();
 }
 
-void HidConnectionLinux::Write(scoped_refptr<net::IOBuffer> buffer,
-                               size_t size,
+void HidConnectionLinux::Write(uint8_t report_id,
+                               scoped_refptr<net::IOBufferWithSize> buffer,
                                const IOCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (!initialized_) {
+  // If report ID is non-zero, insert it into a new copy of the buffer.
+  if (report_id != 0)
+    buffer = CopyBufferWithReportId(buffer, report_id);
+  int bytes_written = HANDLE_EINTR(
+      write(device_file_.GetPlatformFile(), buffer->data(), buffer->size()));
+  if (bytes_written < 0) {
+    Disconnect();
     callback.Run(false, 0);
-    return;
   } else {
-    int bytes = write(device_file_, buffer->data(), size);
-    if (bytes < 0) {
-      Disconnect();
-      callback.Run(false, 0);
-    } else {
-      callback.Run(true, bytes);
-    }
+    callback.Run(true, bytes_written);
   }
 }
 
-void HidConnectionLinux::GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                          size_t size,
-                                          const IOCallback& callback) {
+void HidConnectionLinux::GetFeatureReport(
+    uint8_t report_id,
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    const IOCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (!initialized_) {
+
+  if (buffer->size() == 0) {
     callback.Run(false, 0);
     return;
   }
-  NOTIMPLEMENTED();
+
+  // The first byte of the destination buffer is the report ID being requested.
+  buffer->data()[0] = report_id;
+  int result = ioctl(device_file_.GetPlatformFile(),
+                     HIDIOCGFEATURE(buffer->size()),
+                     buffer->data());
+  if (result < 0)
+    callback.Run(false, 0);
+  else
+    callback.Run(true, result);
 }
 
-void HidConnectionLinux::SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                           size_t size,
-                                           const IOCallback& callback) {
+void HidConnectionLinux::SendFeatureReport(
+    uint8_t report_id,
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    const IOCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (!initialized_) {
+  if (report_id != 0)
+    buffer = CopyBufferWithReportId(buffer, report_id);
+  int result = ioctl(device_file_.GetPlatformFile(),
+                     HIDIOCSFEATURE(buffer->size()),
+                     buffer->data());
+  if (result < 0)
     callback.Run(false, 0);
-    return;
-  }
-  NOTIMPLEMENTED();
+  else
+    callback.Run(true, result);
 }
 
 void HidConnectionLinux::ProcessReadQueue() {
-  while(read_queue_.size() && input_reports_.size()) {
-    PendingRequest request = read_queue_.front();
-    read_queue_.pop();
-    PendingReport report = input_reports_.front();
-    if (report.second > request.b) {
-      request.c.Run(false, report.second);
+  while (pending_reads_.size() && pending_reports_.size()) {
+    PendingHidRead read = pending_reads_.front();
+    pending_reads_.pop();
+    PendingHidReport report = pending_reports_.front();
+    if (report.buffer->size() > read.buffer->size()) {
+      read.callback.Run(false, report.buffer->size());
     } else {
-      memcpy(request.a->data(), report.first->data(), report.second);
-      input_reports_.pop();
-      request.c.Run(true, report.second);
+      memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
+      pending_reports_.pop();
+      read.callback.Run(true, report.buffer->size());
     }
   }
 }
@@ -195,31 +209,31 @@
 bool HidConnectionLinux::FindHidrawDevNode(udev_device* parent,
                                            std::string* result) {
   udev* udev = udev_device_get_udev(parent);
-  if (!udev)
+  if (!udev) {
       return false;
-
+  }
   ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev));
-  if (!enumerate)
+  if (!enumerate) {
     return false;
-
+  }
   if (udev_enumerate_add_match_subsystem(enumerate.get(), kHidrawSubsystem)) {
     return false;
   }
   if (udev_enumerate_scan_devices(enumerate.get())) {
     return false;
   }
-
-  const char* parent_path = udev_device_get_devpath(parent);
+  std::string parent_path(udev_device_get_devpath(parent));
+  if (parent_path.length() == 0 || *parent_path.rbegin() != '/')
+    parent_path += '/';
   udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
   for (udev_list_entry* i = devices; i != NULL;
-      i = udev_list_entry_get_next(i)) {
+       i = udev_list_entry_get_next(i)) {
     ScopedUdevDevicePtr hid_dev(
         udev_device_new_from_syspath(udev, udev_list_entry_get_name(i)));
     const char* raw_path = udev_device_get_devnode(hid_dev.get());
-    if (strncmp(parent_path,
-                udev_device_get_devpath(hid_dev.get()),
-                strlen(parent_path)) == 0 &&
-        raw_path) {
+    std::string device_path = udev_device_get_devpath(hid_dev.get());
+    if (raw_path &&
+        !device_path.compare(0, parent_path.length(), parent_path)) {
       *result = raw_path;
       return true;
     }
diff --git a/device/hid/hid_connection_linux.h b/device/hid/hid_connection_linux.h
index 37d6cb0..67d6299 100644
--- a/device/hid/hid_connection_linux.h
+++ b/device/hid/hid_connection_linux.h
@@ -5,13 +5,11 @@
 #ifndef DEVICE_HID_HID_CONNECTION_LINUX_H_
 #define DEVICE_HID_HID_CONNECTION_LINUX_H_
 
+#include "base/files/file.h"
 #include "base/memory/ref_counted.h"
-#include "base/platform_file.h"
-#include "base/tuple.h"
 #include "device/hid/hid_connection.h"
 #include "device/hid/hid_device_info.h"
 #include "device/hid/hid_service_linux.h"
-#include "net/base/io_buffer.h"
 
 namespace device {
 
@@ -21,21 +19,18 @@
   HidConnectionLinux(HidDeviceInfo device_info,
                      ScopedUdevDevicePtr udev_raw_device);
 
-  virtual void Read(scoped_refptr<net::IOBuffer> buffer,
-                    size_t size,
+  virtual void Read(scoped_refptr<net::IOBufferWithSize> buffer,
                     const IOCallback& callback) OVERRIDE;
-  virtual void Write(scoped_refptr<net::IOBuffer> buffer,
-                     size_t size,
+  virtual void Write(uint8_t report_id,
+                     scoped_refptr<net::IOBufferWithSize> buffer,
                      const IOCallback& callback) OVERRIDE;
-  virtual void GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                size_t size,
+  virtual void GetFeatureReport(uint8_t report_id,
+                                scoped_refptr<net::IOBufferWithSize> buffer,
                                 const IOCallback& callback) OVERRIDE;
-  virtual void SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                 size_t size,
+  virtual void SendFeatureReport(uint8_t report_id,
+                                 scoped_refptr<net::IOBufferWithSize> buffer,
                                  const IOCallback& callback) OVERRIDE;
 
-  bool initialized() const { return initialized_; }
-
   // Implements base::MessagePumpLibevent::Watcher
   virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
   virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
@@ -49,17 +44,13 @@
   void ProcessReadQueue();
   void Disconnect();
 
-  base::PlatformFile device_file_;
+  base::File device_file_;
   base::MessagePumpLibevent::FileDescriptorWatcher device_file_watcher_;
 
-  typedef std::pair<scoped_refptr<net::IOBuffer>, size_t> PendingReport;
-  typedef Tuple3<scoped_refptr<net::IOBuffer>, size_t, IOCallback>
-      PendingRequest;
+  std::queue<PendingHidReport> pending_reports_;
+  std::queue<PendingHidRead> pending_reads_;
 
-  std::queue<PendingReport> input_reports_;
-  std::queue<PendingRequest> read_queue_;
-
-  bool initialized_;
+  base::ThreadChecker thread_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(HidConnectionLinux);
 };
diff --git a/device/hid/hid_connection_mac.cc b/device/hid/hid_connection_mac.cc
index bce7113..63b7998 100644
--- a/device/hid/hid_connection_mac.cc
+++ b/device/hid/hid_connection_mac.cc
@@ -5,184 +5,170 @@
 #include "device/hid/hid_connection_mac.h"
 
 #include "base/bind.h"
-#include "base/callback.h"
 #include "base/mac/foundation_util.h"
+#include "base/message_loop/message_loop.h"
 #include "base/threading/thread_restrictions.h"
-#include "base/tuple.h"
-#include "device/hid/hid_service.h"
-#include "device/hid/hid_service_mac.h"
-#include "net/base/io_buffer.h"
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <IOKit/hid/IOHIDManager.h>
+#include "device/hid/hid_connection_mac.h"
 
 namespace device {
 
-HidConnectionMac::HidConnectionMac(HidServiceMac* service,
-                                   HidDeviceInfo device_info,
-                                   IOHIDDeviceRef device)
+HidConnectionMac::HidConnectionMac(HidDeviceInfo device_info)
     : HidConnection(device_info),
-      service_(service),
-      device_(device),
-      disconnected_(false) {
+      device_(device_info.device_id, base::scoped_policy::RETAIN) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   message_loop_ = base::MessageLoopProxy::current();
 
-  CFRetain(device);
-  inbound_buffer_.reset((uint8_t*) malloc(device_info.input_report_size + 1));
-  IOHIDDeviceRegisterInputReportCallback(
-      device_.get(),
-      inbound_buffer_.get(),
-      device_info.input_report_size + 1,
-      &HidConnectionMac::InputReportCallback,
-      this);
+  DCHECK(device_.get());
+  inbound_buffer_.reset((uint8_t*)malloc(device_info.input_report_size));
+  IOHIDDeviceRegisterInputReportCallback(device_.get(),
+                                         inbound_buffer_.get(),
+                                         device_info.input_report_size,
+                                         &HidConnectionMac::InputReportCallback,
+                                         this);
   IOHIDDeviceOpen(device_, kIOHIDOptionsTypeNone);
 }
+
 HidConnectionMac::~HidConnectionMac() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  while (read_queue_.size()) {
-    read_queue_.front().c.Run(false, 0);
-    read_queue_.pop();
+  while (!pending_reads_.empty()) {
+    pending_reads_.front().callback.Run(false, 0);
+    pending_reads_.pop();
   }
 
   IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone);
 }
 
-void HidConnectionMac::InputReportCallback(void * context,
+void HidConnectionMac::Read(scoped_refptr<net::IOBufferWithSize> buffer,
+                            const IOCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!device_) {
+    callback.Run(false, 0);
+    return;
+  }
+  PendingHidRead read;
+  read.buffer = buffer;
+  read.callback = callback;
+  pending_reads_.push(read);
+  ProcessReadQueue();
+}
+
+void HidConnectionMac::Write(uint8_t report_id,
+                             scoped_refptr<net::IOBufferWithSize> buffer,
+                             const IOCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  WriteReport(kIOHIDReportTypeOutput, report_id, buffer, callback);
+}
+
+void HidConnectionMac::GetFeatureReport(
+    uint8_t report_id,
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    const IOCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (device_info().feature_report_size == 0) {
+    callback.Run(false, 0);
+    return;
+  }
+
+  if (buffer->size() < device_info().feature_report_size) {
+    callback.Run(false, 0);
+    return;
+  }
+
+  uint8_t* feature_report_buffer = reinterpret_cast<uint8_t*>(buffer->data());
+  CFIndex feature_report_size = device_info().feature_report_size;
+  IOReturn result = IOHIDDeviceGetReport(device_,
+                                         kIOHIDReportTypeFeature,
+                                         report_id,
+                                         feature_report_buffer,
+                                         &feature_report_size);
+  if (result == kIOReturnSuccess)
+    callback.Run(true, feature_report_size);
+  else
+    callback.Run(false, 0);
+}
+
+void HidConnectionMac::SendFeatureReport(
+    uint8_t report_id,
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    const IOCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  WriteReport(kIOHIDReportTypeFeature, report_id, buffer, callback);
+}
+
+void HidConnectionMac::InputReportCallback(void* context,
                                            IOReturn result,
-                                           void * sender,
+                                           void* sender,
                                            IOHIDReportType type,
-                                           uint32_t reportID,
-                                           uint8_t * report,
-                                           CFIndex reportLength) {
-  HidConnectionMac* connection = reinterpret_cast<HidConnectionMac*>(context);
-  size_t length = reportLength + (reportID != 0);
-  scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(length));
-  if (reportID) {
-    buffer->data()[0] = reportID;
-    memcpy(buffer->data() + 1, report, reportLength);
+                                           uint32_t report_id,
+                                           uint8_t* report_bytes,
+                                           CFIndex report_length) {
+  HidConnectionMac* connection = static_cast<HidConnectionMac*>(context);
+  // If a report ID was received, inject it into a copy of the received
+  // report. This is consistent with how input reports are received on
+  // other platforms.
+  scoped_refptr<net::IOBufferWithSize> buffer;
+  if (report_id != 0) {
+    buffer = new net::IOBufferWithSize(report_length + 1);
+    buffer->data()[0] = static_cast<uint8_t>(report_id);
+    memcpy(buffer->data() + 1, report_bytes, report_length);
   } else {
-    memcpy(buffer->data(), report, reportLength);
+    buffer = new net::IOBufferWithSize(report_length);
+    memcpy(buffer->data(), report_bytes, report_length);
   }
   connection->message_loop_->PostTask(
       FROM_HERE,
-      base::Bind(&HidConnectionMac::ProcessInputReport,
-                 connection,
-                 type,
-                 buffer,
-                 length));
+      base::Bind(
+          &HidConnectionMac::ProcessInputReport, connection, type, buffer));
 }
 
 void HidConnectionMac::ProcessReadQueue() {
   DCHECK(thread_checker_.CalledOnValidThread());
-
-  while(read_queue_.size() && input_reports_.size()) {
-    PendingRead read = read_queue_.front();
-    read_queue_.pop();
-    PendingReport report = input_reports_.front();
-
-    if (read.b < report.second) {
-      read.c.Run(false, report.second);
+  while (pending_reads_.size() && pending_reports_.size()) {
+    PendingHidRead read = pending_reads_.front();
+    pending_reads_.pop();
+    PendingHidReport report = pending_reports_.front();
+    if (read.buffer->size() < report.buffer->size()) {
+      read.callback.Run(false, report.buffer->size());
     } else {
-      memcpy(read.a->data(), report.first->data(), report.second);
-      input_reports_.pop();
-      read.c.Run(true, report.second);
+      memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
+      pending_reports_.pop();
+      read.callback.Run(true, report.buffer->size());
     }
   }
 }
 
-void HidConnectionMac::ProcessInputReport(IOHIDReportType type,
-                                          scoped_refptr<net::IOBuffer> report,
-                                          CFIndex reportLength) {
+void HidConnectionMac::ProcessInputReport(
+    IOHIDReportType type,
+    scoped_refptr<net::IOBufferWithSize> buffer) {
   DCHECK(thread_checker_.CalledOnValidThread());
-
-  input_reports_.push(std::make_pair(report, reportLength));
+  PendingHidReport report;
+  report.buffer = buffer;
+  pending_reports_.push(report);
   ProcessReadQueue();
 }
 
 void HidConnectionMac::WriteReport(IOHIDReportType type,
-                                   scoped_refptr<net::IOBuffer> buffer,
-                                   size_t size,
+                                   uint8_t report_id,
+                                   scoped_refptr<net::IOBufferWithSize> buffer,
                                    const IOCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (disconnected_ || !device_) {
+  if (!device_) {
     callback.Run(false, 0);
     return;
   }
-  const unsigned char* data_to_send =
-      reinterpret_cast<const unsigned char*>(buffer->data());
-  size_t length_to_send = size;
-  if (data_to_send[0] == 0x0) {
-      /* Not using numbered Reports.
-       Don't send the report number. */
-      ++data_to_send;
-      --length_to_send;
-  }
-  IOReturn res = IOHIDDeviceSetReport(device_.get(),
-                                      type,
-                                      buffer->data()[0], /* Report ID*/
-                                      data_to_send,
-                                      length_to_send);
+  IOReturn res =
+      IOHIDDeviceSetReport(device_.get(),
+                           type,
+                           report_id,
+                           reinterpret_cast<uint8_t*>(buffer->data()),
+                           buffer->size());
   if (res != kIOReturnSuccess) {
     callback.Run(false, 0);
   } else {
-    callback.Run(true, size);
+    callback.Run(true, buffer->size());
   }
 }
 
-void HidConnectionMac::Read(scoped_refptr<net::IOBuffer> buffer,
-                            size_t size,
-                            const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (disconnected_ || !device_) {
-    callback.Run(false, 0);
-    return;
-  }
-  read_queue_.push(MakeTuple(buffer, size, callback));
-  ProcessReadQueue();
-}
-
-void HidConnectionMac::Write(scoped_refptr<net::IOBuffer> buffer,
-                             size_t size,
-                             const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  WriteReport(kIOHIDReportTypeOutput, buffer, size, callback);
-}
-
-void HidConnectionMac::SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                         size_t size,
-                                         const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  WriteReport(kIOHIDReportTypeFeature, buffer, size, callback);
-}
-
-void HidConnectionMac::GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                        size_t size,
-                                        const IOCallback& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (disconnected_ || !device_ || device_info_.feature_report_size == 0) {
-    callback.Run(false, 0);
-    return;
-  }
-
-  if (device_info_.feature_report_size != 0 &&
-      device_info_.feature_report_size != size) {
-    callback.Run(false, 0);
-    return;
-  }
-
-  CFIndex len = device_info_.feature_report_size;
-  IOReturn res = IOHIDDeviceGetReport(device_,
-                                      kIOHIDReportTypeFeature,
-                                      0,
-                                      (uint8_t*) buffer->data(),
-                                      &len);
-  if (res == kIOReturnSuccess)
-    callback.Run(true, len);
-  else
-    callback.Run(false, 0);
-}
-
 }  // namespace device
diff --git a/device/hid/hid_connection_mac.h b/device/hid/hid_connection_mac.h
index 730eeae..c307fb6 100644
--- a/device/hid/hid_connection_mac.h
+++ b/device/hid/hid_connection_mac.h
@@ -5,12 +5,21 @@
 #ifndef DEVICE_HID_HID_CONNECTION_MAC_H_
 #define DEVICE_HID_HID_CONNECTION_MAC_H_
 
-#include "base/callback.h"
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/hid/IOHIDManager.h>
+
+#include <queue>
+
+#include "base/mac/foundation_util.h"
 #include "base/memory/ref_counted.h"
-#include "base/tuple.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
 #include "device/hid/hid_connection.h"
 #include "device/hid/hid_device_info.h"
-#include "device/hid/hid_service_mac.h"
+
+namespace base {
+class MessageLoopProxy;
+}
 
 namespace net {
 class IOBuffer;
@@ -20,53 +29,48 @@
 
 class HidConnectionMac : public HidConnection {
  public:
-  HidConnectionMac(HidServiceMac* service,
-                   HidDeviceInfo device_info,
-                   IOHIDDeviceRef device);
+  explicit HidConnectionMac(HidDeviceInfo device_info);
 
-  virtual void Read(scoped_refptr<net::IOBuffer> buffer,
-                    size_t size,
+  virtual void Read(scoped_refptr<net::IOBufferWithSize> buffer,
                     const IOCallback& callback) OVERRIDE;
-  virtual void Write(scoped_refptr<net::IOBuffer> buffer,
-                     size_t size,
+  virtual void Write(uint8_t report_id,
+                     scoped_refptr<net::IOBufferWithSize> buffer,
                      const IOCallback& callback) OVERRIDE;
-  virtual void GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                size_t size,
+  virtual void GetFeatureReport(uint8_t report_id,
+                                scoped_refptr<net::IOBufferWithSize> buffer,
                                 const IOCallback& callback) OVERRIDE;
-  virtual void SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                 size_t size,
+  virtual void SendFeatureReport(uint8_t report_id,
+                                 scoped_refptr<net::IOBufferWithSize> buffer,
                                  const IOCallback& callback) OVERRIDE;
 
  private:
   virtual ~HidConnectionMac();
 
-  static void InputReportCallback(void * context,
+  static void InputReportCallback(void* context,
                                   IOReturn result,
-                                  void * sender,
+                                  void* sender,
                                   IOHIDReportType type,
-                                  uint32_t reportID,
-                                  uint8_t * report,
-                                  CFIndex reportLength);
+                                  uint32_t report_id,
+                                  uint8_t* report_bytes,
+                                  CFIndex report_length);
   void ProcessReadQueue();
   void ProcessInputReport(IOHIDReportType type,
-                          scoped_refptr<net::IOBuffer> report,
-                          CFIndex reportLength);
+                          scoped_refptr<net::IOBufferWithSize> buffer);
 
   void WriteReport(IOHIDReportType type,
-                   scoped_refptr<net::IOBuffer> buffer,
-                   size_t size,
+                   uint8_t report_id,
+                   scoped_refptr<net::IOBufferWithSize> buffer,
                    const IOCallback& callback);
 
-  HidServiceMac* service_;
   scoped_refptr<base::MessageLoopProxy> message_loop_;
-  base::ScopedCFTypeRef<IOHIDDeviceRef> device_;
-  scoped_ptr_malloc<uint8_t> inbound_buffer_;
-  bool disconnected_;
 
-  typedef std::pair<scoped_refptr<net::IOBuffer>, size_t> PendingReport;
-  std::queue<PendingReport> input_reports_;
-  typedef Tuple3<scoped_refptr<net::IOBuffer>, size_t, IOCallback> PendingRead;
-  std::queue<PendingRead> read_queue_;
+  base::ScopedCFTypeRef<IOHIDDeviceRef> device_;
+  scoped_ptr<uint8_t, base::FreeDeleter> inbound_buffer_;
+
+  std::queue<PendingHidReport> pending_reports_;
+  std::queue<PendingHidRead> pending_reads_;
+
+  base::ThreadChecker thread_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(HidConnectionMac);
 };
diff --git a/device/hid/hid_connection_unittest.cc b/device/hid/hid_connection_unittest.cc
index 14a2ef9..2725481 100644
--- a/device/hid/hid_connection_unittest.cc
+++ b/device/hid/hid_connection_unittest.cc
@@ -17,7 +17,7 @@
 
 namespace {
 
-using net::IOBuffer;
+using net::IOBufferWithSize;
 
 const int kUSBLUFADemoVID = 0x03eb;
 const int kUSBLUFADemoPID = 0x204f;
@@ -27,7 +27,7 @@
 void Read(scoped_refptr<HidConnection> conn);
 
 void OnRead(scoped_refptr<HidConnection> conn,
-            scoped_refptr<net::IOBuffer> buffer,
+            scoped_refptr<IOBufferWithSize> buffer,
             bool success,
             size_t bytes) {
   EXPECT_TRUE(success);
@@ -53,8 +53,8 @@
 }
 
 void Read(scoped_refptr<HidConnection> conn) {
-  scoped_refptr<IOBuffer> buffer(new IOBuffer(8));
-  conn->Read(buffer, 8, base::Bind(OnRead, conn, buffer));
+  scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(8));
+  conn->Read(buffer, base::Bind(OnRead, conn, buffer));
 }
 
 void OnWriteNormal(bool success,
@@ -64,10 +64,10 @@
 }
 
 void WriteNormal(scoped_refptr<HidConnection> conn) {
-  scoped_refptr<IOBuffer> buffer(new IOBuffer(8));
+  scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(8));
   *(int64_t*)buffer->data() = kReport;
 
-  conn->Write(buffer, 8, base::Bind(OnWriteNormal));
+  conn->Write(0, buffer, base::Bind(OnWriteNormal));
 }
 
 }  // namespace
@@ -81,6 +81,7 @@
 
     std::vector<HidDeviceInfo> devices;
     service_->GetDevices(&devices);
+    device_id_ = kInvalidHidDeviceId;
     for (std::vector<HidDeviceInfo>::iterator it = devices.begin();
         it != devices.end();
         ++it) {
@@ -97,21 +98,19 @@
     message_loop_.reset(NULL);
   }
 
-  std::string device_id_;
+  HidDeviceId device_id_;
   scoped_ptr<base::MessageLoopForIO> message_loop_;
   scoped_ptr<HidService> service_;
 };
 
 TEST_F(HidConnectionTest, Create) {
   scoped_refptr<HidConnection> connection = service_->Connect(device_id_);
-  ASSERT_TRUE(connection || device_id_.empty());
+  ASSERT_TRUE(connection || device_id_ == kInvalidHidDeviceId);
 }
 
 TEST_F(HidConnectionTest, Read) {
   scoped_refptr<HidConnection> connection = service_->Connect(device_id_);
-
-  if (!device_id_.empty()) {
-    ASSERT_TRUE(connection);
+  if (connection) {
     message_loop_->PostTask(FROM_HERE, base::Bind(Read, connection));
     message_loop_->Run();
   }
@@ -120,8 +119,7 @@
 TEST_F(HidConnectionTest, Write) {
   scoped_refptr<HidConnection> connection = service_->Connect(device_id_);
 
-  if (!device_id_.empty()) {
-    ASSERT_TRUE(connection);
+  if (connection) {
     message_loop_->PostTask(FROM_HERE, base::Bind(WriteNormal, connection));
     message_loop_->Run();
   }
diff --git a/device/hid/hid_connection_win.cc b/device/hid/hid_connection_win.cc
index bbb158f..44bd847 100644
--- a/device/hid/hid_connection_win.cc
+++ b/device/hid/hid_connection_win.cc
@@ -9,11 +9,10 @@
 #include "base/message_loop/message_loop.h"
 #include "base/stl_util.h"
 #include "base/threading/thread_restrictions.h"
+#include "base/win/object_watcher.h"
+#include "base/win/scoped_handle.h"
 #include "device/hid/hid_service.h"
 #include "device/hid/hid_service_win.h"
-#include "net/base/io_buffer.h"
-
-#if defined(OS_WIN)
 
 #define INITGUID
 
@@ -21,41 +20,69 @@
 #include <hidclass.h>
 
 extern "C" {
-
 #include <hidsdi.h>
-
 }
 
 #include <setupapi.h>
 #include <winioctl.h>
-#include "base/win/scoped_handle.h"
-
-#endif  // defined(OS_WIN)
 
 namespace device {
 
-HidConnectionWin::PendingTransfer::PendingTransfer(
-    scoped_refptr<HidConnectionWin> conn,
-    scoped_refptr<net::IOBuffer> target,
-    scoped_refptr<net::IOBuffer> receiving,
-    bool is_input,
-    IOCallback callback)
-    : conn_(conn),
-      is_input_(is_input),
-      target_(target),
-      receiving_(receiving),
+struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>,
+                            public base::win::ObjectWatcher::Delegate,
+                            public base::MessageLoop::DestructionObserver {
+  PendingHidTransfer(scoped_refptr<HidConnectionWin> connection,
+                     scoped_refptr<net::IOBufferWithSize> target_buffer,
+                     scoped_refptr<net::IOBufferWithSize> receive_buffer,
+                     HidConnection::IOCallback callback);
+
+  void TakeResultFromWindowsAPI(BOOL result);
+
+  OVERLAPPED* GetOverlapped() { return &overlapped_; }
+
+  // Implements base::win::ObjectWatcher::Delegate.
+  virtual void OnObjectSignaled(HANDLE object) OVERRIDE;
+
+  // Implements base::MessageLoop::DestructionObserver
+  virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
+
+  scoped_refptr<HidConnectionWin> connection_;
+  scoped_refptr<net::IOBufferWithSize> target_buffer_;
+  scoped_refptr<net::IOBufferWithSize> receive_buffer_;
+  HidConnection::IOCallback callback_;
+  OVERLAPPED overlapped_;
+  base::win::ScopedHandle event_;
+  base::win::ObjectWatcher watcher_;
+
+ private:
+  friend class base::RefCounted<PendingHidTransfer>;
+
+  virtual ~PendingHidTransfer();
+
+  DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer);
+};
+
+PendingHidTransfer::PendingHidTransfer(
+    scoped_refptr<HidConnectionWin> connection,
+    scoped_refptr<net::IOBufferWithSize> target_buffer,
+    scoped_refptr<net::IOBufferWithSize> receive_buffer,
+    HidConnection::IOCallback callback)
+    : connection_(connection),
+      target_buffer_(target_buffer),
+      receive_buffer_(receive_buffer),
       callback_(callback),
       event_(CreateEvent(NULL, FALSE, FALSE, NULL)) {
   memset(&overlapped_, 0, sizeof(OVERLAPPED));
   overlapped_.hEvent = event_.Get();
 }
-HidConnectionWin::PendingTransfer::~PendingTransfer() {
+
+PendingHidTransfer::~PendingHidTransfer() {
   base::MessageLoop::current()->RemoveDestructionObserver(this);
 }
 
-void HidConnectionWin::PendingTransfer::TakeResultFromWindowsAPI(BOOL result) {
+void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) {
   if (result || GetLastError() != ERROR_IO_PENDING) {
-    conn_->OnTransferFinished(this);
+    connection_->OnTransferFinished(this);
   } else {
     base::MessageLoop::current()->AddDestructionObserver(this);
     AddRef();
@@ -63,46 +90,18 @@
   }
 }
 
-void HidConnectionWin::PendingTransfer::OnObjectSignaled(HANDLE event_handle) {
-  conn_->OnTransferFinished(this);
+void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) {
+  connection_->OnTransferFinished(this);
   Release();
 }
 
-void HidConnectionWin::PendingTransfer::WillDestroyCurrentMessageLoop() {
+void PendingHidTransfer::WillDestroyCurrentMessageLoop() {
   watcher_.StopWatching();
-  conn_->OnTransferCanceled(this);
+  connection_->OnTransferCanceled(this);
 }
 
-void HidConnectionWin::OnTransferFinished(
-    scoped_refptr<PendingTransfer> transfer) {
-  DWORD bytes_transfered;
-  transfers_.erase(transfer);
-  if (GetOverlappedResult(file_,
-                          transfer->GetOverlapped(),
-                          &bytes_transfered,
-                          FALSE)) {
-    if (transfer->is_input_ && !device_info_.has_report_id) {
-      // Move one byte forward.
-      --bytes_transfered;
-      memcpy(transfer->target_->data(),
-             transfer->receiving_->data() + 1,
-             bytes_transfered);
-    }
-    transfer->callback_.Run(true, bytes_transfered);
-  } else {
-    transfer->callback_.Run(false, 0);
-  }
-}
-
-void HidConnectionWin::OnTransferCanceled(
-    scoped_refptr<PendingTransfer> transfer) {
-  transfers_.erase(transfer);
-  transfer->callback_.Run(false, 0);
-}
-
-HidConnectionWin::HidConnectionWin(HidDeviceInfo device_info)
-    : HidConnection(device_info),
-      available_(false) {
+HidConnectionWin::HidConnectionWin(const HidDeviceInfo& device_info)
+    : HidConnection(device_info) {
   DCHECK(thread_checker_.CalledOnValidThread());
   file_.Set(CreateFileA(device_info.device_id.c_str(),
                         GENERIC_WRITE | GENERIC_READ,
@@ -111,7 +110,10 @@
                         OPEN_EXISTING,
                         FILE_FLAG_OVERLAPPED,
                         NULL));
-  available_ = file_.IsValid();
+}
+
+bool HidConnectionWin::available() const {
+  return file_.IsValid();
 }
 
 HidConnectionWin::~HidConnectionWin() {
@@ -119,161 +121,168 @@
   CancelIo(file_.Get());
 }
 
-void HidConnectionWin::Read(scoped_refptr<net::IOBuffer> buffer,
-                            size_t size,
+void HidConnectionWin::Read(scoped_refptr<net::IOBufferWithSize> buffer,
                             const HidConnection::IOCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  size_t report_size = device_info_.input_report_size;
-  if (report_size == 0) {
-    // The device does not supoort input reports.
+  if (device_info().input_report_size == 0) {
+    // The device does not support input reports.
     callback.Run(false, 0);
     return;
   }
 
-  if (size + !device_info_.has_report_id < report_size) {
-    // Buffer too short.
+  if (buffer->size() < device_info().input_report_size) {
     callback.Run(false, 0);
     return;
   }
 
-  scoped_refptr<net::IOBuffer> expanded_buffer;
-  if (!device_info_.has_report_id) {
-    ++size;
-    expanded_buffer = new net::IOBuffer(static_cast<int>(size));
-  }
+  // If the device doesn't support report IDs, the caller should not be
+  // expecting one; however, Windows will always expect enough space for one,
+  // so we need to use a buffer with one extra byte of space in this case.
+  scoped_refptr<net::IOBufferWithSize> receive_buffer(buffer);
+  if (!device_info().has_report_id)
+    receive_buffer = new net::IOBufferWithSize(buffer->size() + 1);
 
-  scoped_refptr<PendingTransfer> transfer(
-      new PendingTransfer(this, buffer, expanded_buffer, true, callback));
+  scoped_refptr<PendingHidTransfer> transfer(
+      new PendingHidTransfer(this, buffer, receive_buffer, callback));
   transfers_.insert(transfer);
-  transfer->TakeResultFromWindowsAPI(ReadFile(file_.Get(),
-                                              device_info_.has_report_id ?
-                                                  buffer->data() :
-                                                  expanded_buffer->data(),
-                                              static_cast<DWORD>(size),
-                                              NULL,
-                                              transfer->GetOverlapped()));
+  transfer->TakeResultFromWindowsAPI(
+      ReadFile(file_.Get(),
+               receive_buffer->data(),
+               static_cast<DWORD>(receive_buffer->size()),
+               NULL,
+               transfer->GetOverlapped()));
 }
 
-void HidConnectionWin::Write(scoped_refptr<net::IOBuffer> buffer,
-                             size_t size,
+void HidConnectionWin::Write(uint8_t report_id,
+                             scoped_refptr<net::IOBufferWithSize> buffer,
                              const HidConnection::IOCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  size_t report_size = device_info_.output_report_size;
-  if (report_size == 0) {
-    // The device does not supoort output reports.
+  if (device_info().output_report_size == 0) {
+    // The device does not support output reports.
     callback.Run(false, 0);
     return;
   }
 
-  if (size + !device_info_.has_report_id > report_size) {
-    // Size of report too long.
-    callback.Run(false, 0);
-    return;
-  }
+  // The Windows API always wants either a report ID (if supported) or
+  // zero at the front of every output report.
+  scoped_refptr<net::IOBufferWithSize> output_buffer(buffer);
+  output_buffer = new net::IOBufferWithSize(buffer->size() + 1);
+  output_buffer->data()[0] = report_id;
+  memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
 
-  scoped_refptr<net::IOBuffer> expanded_buffer;
-  if (!device_info_.has_report_id) {
-    expanded_buffer = new net::IOBuffer(
-        static_cast<int>(device_info_.output_report_size));
-    memset(expanded_buffer->data(), 0, device_info_.output_report_size);
-    memcpy(expanded_buffer->data() + 1,
-           buffer->data(),
-           size);
-    size++;
-  }
-
-  scoped_refptr<PendingTransfer> transfer(
-      new PendingTransfer(this, buffer, expanded_buffer, false, callback));
+  scoped_refptr<PendingHidTransfer> transfer(
+      new PendingHidTransfer(this, buffer, NULL, callback));
   transfers_.insert(transfer);
   transfer->TakeResultFromWindowsAPI(
       WriteFile(file_.Get(),
-                device_info_.has_report_id ?
-                    buffer->data() : expanded_buffer->data(),
-                static_cast<DWORD>(device_info_.output_report_size),
+                output_buffer->data(),
+                static_cast<DWORD>(output_buffer->size()),
                 NULL,
                 transfer->GetOverlapped()));
 }
 
-void HidConnectionWin::GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                        size_t size,
-                                        const IOCallback& callback) {
+void HidConnectionWin::GetFeatureReport(
+    uint8_t report_id,
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    const IOCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  size_t report_size = device_info_.feature_report_size;
-  if (report_size == 0) {
-    // The device does not supoort input reports.
+  if (device_info().feature_report_size == 0) {
+    // The device does not support feature reports.
     callback.Run(false, 0);
     return;
   }
 
-  if (size + !device_info_.has_report_id < report_size) {
-    // Buffer too short.
+  if (buffer->size() < device_info().feature_report_size) {
     callback.Run(false, 0);
     return;
   }
 
-  scoped_refptr<net::IOBuffer> expanded_buffer;
-  if (!device_info_.has_report_id) {
-    ++size;
-    expanded_buffer = new net::IOBuffer(static_cast<int>(size));
-  }
+  scoped_refptr<net::IOBufferWithSize> receive_buffer(buffer);
+  if (!device_info().has_report_id)
+    receive_buffer = new net::IOBufferWithSize(buffer->size() + 1);
 
-  scoped_refptr<PendingTransfer> transfer(
-      new PendingTransfer(this, buffer, expanded_buffer, true, callback));
+  // The first byte of the destination buffer is the report ID being requested.
+  receive_buffer->data()[0] = report_id;
+  scoped_refptr<PendingHidTransfer> transfer(
+      new PendingHidTransfer(this, buffer, receive_buffer, callback));
   transfers_.insert(transfer);
   transfer->TakeResultFromWindowsAPI(
       DeviceIoControl(file_.Get(),
                       IOCTL_HID_GET_FEATURE,
                       NULL,
                       0,
-                      device_info_.has_report_id ?
-                          buffer->data() :
-                          expanded_buffer->data(),
-                      static_cast<DWORD>(size),
+                      receive_buffer->data(),
+                      static_cast<DWORD>(receive_buffer->size()),
                       NULL,
                       transfer->GetOverlapped()));
 }
 
-void HidConnectionWin::SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                         size_t size,
-                                         const IOCallback& callback) {
+void HidConnectionWin::SendFeatureReport(
+    uint8_t report_id,
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    const IOCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  size_t report_size = device_info_.feature_report_size;
-  if (report_size == 0) {
-    // The device does not supoort output reports.
+  if (device_info().feature_report_size == 0) {
+    // The device does not support feature reports.
     callback.Run(false, 0);
     return;
   }
 
-  if (size + !device_info_.has_report_id > report_size) {
-    // Size of report too long.
+  if (buffer->size() < device_info().feature_report_size) {
     callback.Run(false, 0);
     return;
   }
 
-  scoped_refptr<net::IOBuffer> expanded_buffer;
-  if (!device_info_.has_report_id) {
-    expanded_buffer = new net::IOBuffer(
-        static_cast<int>(device_info_.feature_report_size));
-    memset(expanded_buffer->data(), 0, device_info_.feature_report_size);
-    memcpy(expanded_buffer->data() + 1,
-           buffer->data(),
-           size);
-    size++;
-  }
+  // The Windows API always wants either a report ID (if supported) or
+  // zero at the front of every output report.
+  scoped_refptr<net::IOBufferWithSize> output_buffer(buffer);
+  output_buffer = new net::IOBufferWithSize(buffer->size() + 1);
+  output_buffer->data()[0] = report_id;
+  memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
 
-  scoped_refptr<PendingTransfer> transfer(
-      new PendingTransfer(this, buffer, expanded_buffer, false, callback));
+  scoped_refptr<PendingHidTransfer> transfer(
+      new PendingHidTransfer(this, buffer, NULL, callback));
   transfer->TakeResultFromWindowsAPI(
       DeviceIoControl(file_.Get(),
                       IOCTL_HID_SET_FEATURE,
-                      device_info_.has_report_id ?
-                          buffer->data() :
-                          expanded_buffer->data(),
-                      static_cast<DWORD>(device_info_.output_report_size),
+                      output_buffer->data(),
+                      static_cast<DWORD>(output_buffer->size()),
                       NULL,
                       0,
                       NULL,
                       transfer->GetOverlapped()));
 }
 
+void HidConnectionWin::OnTransferFinished(
+    scoped_refptr<PendingHidTransfer> transfer) {
+  DWORD bytes_transferred;
+  transfers_.erase(transfer);
+  if (GetOverlappedResult(
+          file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
+    if (bytes_transferred == 0)
+      transfer->callback_.Run(true, 0);
+    // If this is an input transfer and the receive buffer is not the same as
+    // the target buffer, we need to copy the receive buffer into the target
+    // buffer, discarding the first byte. This is because the target buffer's
+    // owner is not expecting a report ID but Windows will always provide one.
+    if (transfer->receive_buffer_ &&
+        transfer->receive_buffer_ != transfer->target_buffer_) {
+      // Move one byte forward.
+      --bytes_transferred;
+      memcpy(transfer->target_buffer_->data(),
+             transfer->receive_buffer_->data() + 1,
+             bytes_transferred);
+    }
+    transfer->callback_.Run(true, bytes_transferred);
+  } else {
+    transfer->callback_.Run(false, 0);
+  }
+}
+
+void HidConnectionWin::OnTransferCanceled(
+    scoped_refptr<PendingHidTransfer> transfer) {
+  transfers_.erase(transfer);
+  transfer->callback_.Run(false, 0);
+}
+
 }  // namespace device
diff --git a/device/hid/hid_connection_win.h b/device/hid/hid_connection_win.h
index 6cbb9e9..263897a 100644
--- a/device/hid/hid_connection_win.h
+++ b/device/hid/hid_connection_win.h
@@ -5,90 +5,51 @@
 #ifndef DEVICE_HID_HID_CONNECTION_WIN_H_
 #define DEVICE_HID_HID_CONNECTION_WIN_H_
 
-#include <set>
 #include <windows.h>
 
+#include <set>
+
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
 #include "base/threading/thread_checker.h"
-#include "base/win/object_watcher.h"
 #include "device/hid/hid_connection.h"
 #include "device/hid/hid_device_info.h"
-#include "net/base/io_buffer.h"
 
 namespace device {
 
+struct PendingHidTransfer;
+
 class HidConnectionWin : public HidConnection {
  public:
-  struct PendingTransfer : public base::RefCounted<PendingTransfer>,
-                           public base::win::ObjectWatcher::Delegate,
-                           public base::MessageLoop::DestructionObserver {
-   public:
-    PendingTransfer(scoped_refptr<HidConnectionWin> conn,
-                    scoped_refptr<net::IOBuffer> target,
-                    scoped_refptr<net::IOBuffer> receiving,
-                    bool is_input,
-                    IOCallback callback);
+  explicit HidConnectionWin(const HidDeviceInfo& device_info);
 
-    void TakeResultFromWindowsAPI(BOOL result);
+  bool available() const;
 
-    OVERLAPPED* GetOverlapped() { return &overlapped_; }
-
-    // Implements base::win::ObjectWatcher::Delegate.
-    virtual void OnObjectSignaled(HANDLE object) OVERRIDE;
-
-    // Implements base::MessageLoop::DestructionObserver
-    virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
-
-
-   private:
-    friend class base::RefCounted<PendingTransfer>;
-    friend class HidConnectionWin;
-
-    virtual ~PendingTransfer();
-
-    scoped_refptr<HidConnectionWin> conn_;
-    bool is_input_;
-    scoped_refptr<net::IOBuffer> target_;
-    scoped_refptr<net::IOBuffer> receiving_;
-    IOCallback callback_;
-    OVERLAPPED overlapped_;
-    base::win::ScopedHandle event_;
-    base::win::ObjectWatcher watcher_;
-
-    DISALLOW_COPY_AND_ASSIGN(PendingTransfer);
-  };
-
-  HidConnectionWin(HidDeviceInfo device_info);
-
-  virtual void Read(scoped_refptr<net::IOBuffer> buffer,
-                    size_t size,
+  virtual void Read(scoped_refptr<net::IOBufferWithSize> buffer,
                     const IOCallback& callback) OVERRIDE;
-  virtual void Write(scoped_refptr<net::IOBuffer> buffer,
-                     size_t size,
+  virtual void Write(uint8_t report_id,
+                     scoped_refptr<net::IOBufferWithSize> buffer,
                      const IOCallback& callback) OVERRIDE;
-  virtual void GetFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                size_t size,
+  virtual void GetFeatureReport(uint8_t report_id,
+                                scoped_refptr<net::IOBufferWithSize> buffer,
                                 const IOCallback& callback) OVERRIDE;
-  virtual void SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
-                                 size_t size,
+  virtual void SendFeatureReport(uint8_t report_id,
+                                 scoped_refptr<net::IOBufferWithSize> buffer,
                                  const IOCallback& callback) OVERRIDE;
 
-  void OnTransferFinished(scoped_refptr<PendingTransfer> transfer);
-  void OnTransferCanceled(scoped_refptr<PendingTransfer> transfer);
-
-  bool available() const { return available_; }
+  void OnTransferFinished(scoped_refptr<PendingHidTransfer> transfer);
+  void OnTransferCanceled(scoped_refptr<PendingHidTransfer> transfer);
 
  private:
   ~HidConnectionWin();
 
   base::win::ScopedHandle file_;
-  std::set<scoped_refptr<PendingTransfer> > transfers_;
+  std::set<scoped_refptr<PendingHidTransfer> > transfers_;
+
+  base::ThreadChecker thread_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(HidConnectionWin);
-
-  bool available_;
 };
 
 }  // namespace device
diff --git a/device/hid/hid_device_info.cc b/device/hid/hid_device_info.cc
index 34ea686..fdc747d 100644
--- a/device/hid/hid_device_info.cc
+++ b/device/hid/hid_device_info.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,8 +6,13 @@
 
 namespace device {
 
+#if !defined(OS_MACOSX)
+const char kInvalidHidDeviceId[] = "";
+#endif
+
 HidDeviceInfo::HidDeviceInfo()
-    : bus_type(kHIDBusTypeUSB),
+    : device_id(kInvalidHidDeviceId),
+      bus_type(kHIDBusTypeUSB),
       vendor_id(0),
       product_id(0),
       input_report_size(0),
diff --git a/device/hid/hid_device_info.h b/device/hid/hid_device_info.h
index b665944..7531042 100644
--- a/device/hid/hid_device_info.h
+++ b/device/hid/hid_device_info.h
@@ -1,13 +1,19 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef DEVICE_HID_HID_DEVICE_INFO_H_
 #define DEVICE_HID_HID_DEVICE_INFO_H_
 
+#include <stdint.h>
+
 #include <string>
 
-#include "base/basictypes.h"
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX)
+#include <IOKit/hid/IOHIDDevice.h>
+#endif
 
 namespace device {
 
@@ -16,22 +22,30 @@
   kHIDBusTypeBluetooth = 1,
 };
 
+#if defined(OS_MACOSX)
+typedef IOHIDDeviceRef HidDeviceId;
+const HidDeviceId kInvalidHidDeviceId = NULL;
+#else
+typedef std::string HidDeviceId;
+extern const char kInvalidHidDeviceId[];
+#endif
+
 struct HidDeviceInfo {
   HidDeviceInfo();
   ~HidDeviceInfo();
 
-  std::string device_id;
+  HidDeviceId device_id;
 
   HidBusType bus_type;
-  uint16 vendor_id;
-  uint16 product_id;
+  uint16_t vendor_id;
+  uint16_t product_id;
 
-  size_t input_report_size;
-  size_t output_report_size;
-  size_t feature_report_size;
+  int input_report_size;
+  int output_report_size;
+  int feature_report_size;
 
-  uint16 usage_page;
-  uint16 usage;
+  uint16_t usage_page;
+  uint16_t usage;
   bool has_report_id;
 
   std::string product_name;
diff --git a/device/hid/hid_service.cc b/device/hid/hid_service.cc
index 973e901..2d0569b 100644
--- a/device/hid/hid_service.cc
+++ b/device/hid/hid_service.cc
@@ -1,18 +1,14 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "device/hid/hid_service.h"
 
-#include <vector>
-
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/memory/scoped_vector.h"
-#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
 #include "base/threading/thread_restrictions.h"
-#include "build/build_config.h"
-#include "device/hid/hid_device_info.h"
 
 #if defined(OS_LINUX)
 #include "device/hid/hid_service_linux.h"
@@ -32,22 +28,6 @@
 
 }  // namespace
 
-HidService::HidService() : initialized_(false) {
-  base::ThreadRestrictions::AssertIOAllowed();
-  DCHECK(thread_checker_.CalledOnValidThread());
-  base::MessageLoop::current()->AddDestructionObserver(this);
-}
-
-HidService::~HidService() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  base::MessageLoop::current()->RemoveDestructionObserver(this);
-}
-
-void HidService::WillDestroyCurrentMessageLoop() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  g_hid_service_ptr.Get().reset(NULL);
-}
-
 void HidService::GetDevices(std::vector<HidDeviceInfo>* devices) {
   DCHECK(thread_checker_.CalledOnValidThread());
   STLClearObject(devices);
@@ -59,7 +39,8 @@
 }
 
 // Fills in the device info struct of the given device_id.
-bool HidService::GetInfo(std::string device_id, HidDeviceInfo* info) const {
+bool HidService::GetDeviceInfo(const HidDeviceId& device_id,
+                               HidDeviceInfo* info) const {
   DeviceMap::const_iterator it = devices_.find(device_id);
   if (it == devices_.end())
     return false;
@@ -67,18 +48,20 @@
   return true;
 }
 
-void HidService::AddDevice(HidDeviceInfo info) {
-  if (!ContainsKey(devices_, info.device_id)) {
-    DCHECK(thread_checker_.CalledOnValidThread());
-    devices_[info.device_id] = info;
-  }
+void HidService::WillDestroyCurrentMessageLoop() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  g_hid_service_ptr.Get().reset(NULL);
 }
 
-void HidService::RemoveDevice(std::string device_id) {
-  if (ContainsKey(devices_, device_id)) {
-    DCHECK(thread_checker_.CalledOnValidThread());
-    devices_.erase(device_id);
-  }
+HidService::HidService() {
+  base::ThreadRestrictions::AssertIOAllowed();
+  DCHECK(thread_checker_.CalledOnValidThread());
+  base::MessageLoop::current()->AddDestructionObserver(this);
+}
+
+HidService::~HidService() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  base::MessageLoop::current()->RemoveDestructionObserver(this);
 }
 
 HidService* HidService::CreateInstance() {
@@ -93,13 +76,23 @@
 #endif
 }
 
-HidService* HidService::GetInstance() {
-  if (!g_hid_service_ptr.Get().get()){
-    scoped_ptr<HidService> service(CreateInstance());
-
-    if (service && service->initialized())
-      g_hid_service_ptr.Get().reset(service.release());
+void HidService::AddDevice(const HidDeviceInfo& info) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!ContainsKey(devices_, info.device_id)) {
+    devices_[info.device_id] = info;
   }
+}
+
+void HidService::RemoveDevice(const HidDeviceId& device_id) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DeviceMap::iterator it = devices_.find(device_id);
+  if (it != devices_.end())
+    devices_.erase(it);
+}
+
+HidService* HidService::GetInstance() {
+  if (!g_hid_service_ptr.Get().get())
+    g_hid_service_ptr.Get().reset(CreateInstance());
   return g_hid_service_ptr.Get().get();
 }
 
diff --git a/device/hid/hid_service.h b/device/hid/hid_service.h
index 2589220..ee0ebb7 100644
--- a/device/hid/hid_service.h
+++ b/device/hid/hid_service.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,25 +9,15 @@
 #include <string>
 #include <vector>
 
-#include "base/basictypes.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
-#include "base/strings/string16.h"
 #include "base/threading/thread_checker.h"
-#include "build/build_config.h"
 #include "device/hid/hid_device_info.h"
 
 namespace device {
 
-namespace {
-
-class HidServiceContainer;
-
-}  // namespace
-
 class HidConnection;
-class HidService;
 
 class HidService : public base::MessageLoop::DestructionObserver {
  public:
@@ -37,40 +27,35 @@
   // Enumerates and returns a list of device identifiers.
   virtual void GetDevices(std::vector<HidDeviceInfo>* devices);
 
-  // Fills in the device info struct of the given device_id.
-  // Returns true if succeed.
-  // Returns false if the device_id is invalid, with info untouched.
-  bool GetInfo(std::string device_id, HidDeviceInfo* info) const;
+  // Fills in a DeviceInfo struct with info for the given device_id.
+  // Returns |true| if successful or |false| if |device_id| is invalid.
+  bool GetDeviceInfo(const HidDeviceId& device_id, HidDeviceInfo* info) const;
 
   virtual scoped_refptr<HidConnection> Connect(
-      std::string platform_device_id) = 0;
+      const HidDeviceId& device_id) = 0;
 
   // Implements base::MessageLoop::DestructionObserver
   virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
 
-  // Gets whether the HidService have been successfully initialized.
-  bool initialized() const { return initialized_; }
-
  protected:
-  friend class HidServiceContainer;
   friend struct base::DefaultDeleter<HidService>;
   friend class HidConnectionTest;
 
+  typedef std::map<HidDeviceId, HidDeviceInfo> DeviceMap;
+
   HidService();
   virtual ~HidService();
 
   static HidService* CreateInstance();
 
-  virtual void AddDevice(HidDeviceInfo info);
-  virtual void RemoveDevice(std::string platform_device_id);
-
-  typedef std::map<std::string, HidDeviceInfo> DeviceMap;
-  DeviceMap devices_;
-
-  bool initialized_;
+  void AddDevice(const HidDeviceInfo& info);
+  void RemoveDevice(const HidDeviceId& device_id);
 
   base::ThreadChecker thread_checker_;
 
+ private:
+  DeviceMap devices_;
+
   DISALLOW_COPY_AND_ASSIGN(HidService);
 };
 
diff --git a/device/hid/hid_service_linux.cc b/device/hid/hid_service_linux.cc
index f02a550..04a3c16 100644
--- a/device/hid/hid_service_linux.cc
+++ b/device/hid/hid_service_linux.cc
@@ -1,22 +1,19 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include <libudev.h>
+#include <stdint.h>
+
 #include <string>
 #include <vector>
 
-#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/callback.h"
 #include "base/logging.h"
-#include "base/memory/scoped_vector.h"
 #include "base/platform_file.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
-#include "base/threading/thread_restrictions.h"
-#include "device/hid/hid_connection.h"
 #include "device/hid/hid_connection_linux.h"
 #include "device/hid/hid_device_info.h"
 #include "device/hid/hid_service_linux.h"
@@ -79,6 +76,37 @@
   Enumerate();
 }
 
+scoped_refptr<HidConnection> HidServiceLinux::Connect(
+    const HidDeviceId& device_id) {
+  HidDeviceInfo device_info;
+  if (!GetDeviceInfo(device_id, &device_info))
+    return NULL;
+
+  ScopedUdevDevicePtr hid_device(
+      udev_device_new_from_syspath(udev_.get(), device_info.device_id.c_str()));
+  if (hid_device) {
+    return new HidConnectionLinux(device_info, hid_device.Pass());
+  }
+  return NULL;
+}
+
+void HidServiceLinux::OnFileCanReadWithoutBlocking(int fd) {
+  DCHECK_EQ(monitor_fd_, fd);
+
+  ScopedUdevDevicePtr dev(udev_monitor_receive_device(monitor_.get()));
+  if (!dev)
+    return;
+
+  std::string action(udev_device_get_action(dev.get()));
+  if (action == kUdevActionAdd) {
+    PlatformAddDevice(dev.get());
+  } else if (action == kUdevActionRemove) {
+    PlatformRemoveDevice(dev.get());
+  }
+}
+
+void HidServiceLinux::OnFileCanWriteWithoutBlocking(int fd) {}
+
 HidServiceLinux::~HidServiceLinux() {
   monitor_watcher_.StopWatchingFileDescriptor();
   close(monitor_fd_);
@@ -110,26 +138,23 @@
     ScopedUdevDevicePtr hid_dev(
         udev_device_new_from_syspath(udev_.get(), udev_list_entry_get_name(i)));
     if (hid_dev) {
-      PlatformDeviceAdd(hid_dev.get());
+      PlatformAddDevice(hid_dev.get());
     }
   }
-
-  initialized_ = true;
 }
 
-void HidServiceLinux::PlatformDeviceAdd(udev_device* device) {
+void HidServiceLinux::PlatformAddDevice(udev_device* device) {
   if (!device)
     return;
 
-  const char* device_id = udev_device_get_syspath(device);
-  if (!device_id)
+  const char* device_path = udev_device_get_syspath(device);
+  if (!device_path)
     return;
 
-
   HidDeviceInfo device_info;
-  device_info.device_id = device_id;
+  device_info.device_id = device_path;
 
-  uint32 int_property = 0;
+  uint32_t int_property = 0;
   const char* str_property = NULL;
 
   const char* hid_id = udev_device_get_property_value(device, kHIDID);
@@ -161,51 +186,12 @@
   AddDevice(device_info);
 }
 
-void HidServiceLinux::PlatformDeviceRemove(udev_device* raw_dev) {
-  // The returned the device is not referenced.
-  udev_device* hid_dev =
-      udev_device_get_parent_with_subsystem_devtype(raw_dev, "hid", NULL);
-
-  if (!hid_dev)
+void HidServiceLinux::PlatformRemoveDevice(udev_device* raw_dev) {
+  const char* device_path = NULL;
+  device_path = udev_device_get_syspath(raw_dev);
+  if (device_path == NULL)
     return;
-
-  const char* device_id = NULL;
-  device_id = udev_device_get_syspath(hid_dev);
-  if (device_id == NULL)
-    return;
-
-  RemoveDevice(device_id);
+  RemoveDevice(device_path);
 }
 
-scoped_refptr<HidConnection> HidServiceLinux::Connect(std::string device_id) {
-  if (!ContainsKey(devices_, device_id))
-    return NULL;
-  ScopedUdevDevicePtr hid_device(
-      udev_device_new_from_syspath(udev_.get(), device_id.c_str()));
-  if (hid_device) {
-    scoped_refptr<HidConnectionLinux> connection =
-        new HidConnectionLinux(devices_[device_id], hid_device.Pass());
-    if (connection->initialized())
-      return connection;
-  }
-  return NULL;
-}
-
-void HidServiceLinux::OnFileCanReadWithoutBlocking(int fd) {
-  DCHECK_EQ(monitor_fd_, fd);
-
-  ScopedUdevDevicePtr dev(udev_monitor_receive_device(monitor_.get()));
-  if (!dev)
-    return;
-
-  std::string action(udev_device_get_action(dev.get()));
-  if (action == kUdevActionAdd) {
-    PlatformDeviceAdd(dev.get());
-  } else if (action == kUdevActionRemove) {
-    PlatformDeviceRemove(dev.get());
-  }
-}
-
-void HidServiceLinux::OnFileCanWriteWithoutBlocking(int fd) {}
-
 } // namespace dev
diff --git a/device/hid/hid_service_linux.h b/device/hid/hid_service_linux.h
index ba58fad..ae730ce 100644
--- a/device/hid/hid_service_linux.h
+++ b/device/hid/hid_service_linux.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,9 @@
 
 #include <libudev.h>
 
+#include <map>
+#include <string>
+
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_pump_libevent.h"
@@ -50,7 +53,8 @@
  public:
   HidServiceLinux();
 
-  virtual scoped_refptr<HidConnection> Connect(std::string device_id) OVERRIDE;
+  virtual scoped_refptr<HidConnection> Connect(const HidDeviceId& device_id)
+      OVERRIDE;
 
   // Implements base::MessagePumpLibevent::Watcher
   virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
@@ -60,8 +64,8 @@
   virtual ~HidServiceLinux();
 
   void Enumerate();
-  void PlatformDeviceAdd(udev_device* device);
-  void PlatformDeviceRemove(udev_device* raw_dev);
+  void PlatformAddDevice(udev_device* device);
+  void PlatformRemoveDevice(udev_device* raw_dev);
 
   scoped_ptr<udev, UdevDeleter> udev_;
   scoped_ptr<udev_monitor, UdevMonitorDeleter> monitor_;
diff --git a/device/hid/hid_service_mac.cc b/device/hid/hid_service_mac.cc
index 12da7cc..3df7e59 100644
--- a/device/hid/hid_service_mac.cc
+++ b/device/hid/hid_service_mac.cc
@@ -1,250 +1,178 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "device/hid/hid_service_mac.h"
 
-#include <map>
-#include <set>
-#include <string>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/mac/foundation_util.h"
-#include "base/memory/scoped_vector.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/threading/thread_restrictions.h"
-#include "device/hid/hid_connection.h"
-#include "device/hid/hid_connection_mac.h"
-#include "device/hid/hid_utils_mac.h"
-#include "net/base/io_buffer.h"
-
 #include <CoreFoundation/CoreFoundation.h>
 #include <IOKit/hid/IOHIDManager.h>
 
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "device/hid/hid_connection_mac.h"
+#include "device/hid/hid_utils_mac.h"
+
 namespace device {
 
 class HidServiceMac;
 
-HidServiceMac::HidServiceMac() : enumeration_runloop_init_(true, false) {
-  base::ThreadRestrictions::AssertIOAllowed();
+namespace {
 
-  hid_manager_ref_.reset(IOHIDManagerCreate(kCFAllocatorDefault,
-                                            kIOHIDOptionsTypeNone));
-  if (!hid_manager_ref_ ||
-      CFGetTypeID(hid_manager_ref_) != IOHIDManagerGetTypeID()) {
+typedef std::vector<IOHIDDeviceRef> HidDeviceList;
+
+HidServiceMac* HidServiceFromContext(void* context) {
+  return static_cast<HidServiceMac*>(context);
+}
+
+// Callback for CFSetApplyFunction as used by EnumerateHidDevices.
+void HidEnumerationBackInserter(const void* value, void* context) {
+  HidDeviceList* devices = static_cast<HidDeviceList*>(context);
+  const IOHIDDeviceRef device =
+      static_cast<IOHIDDeviceRef>(const_cast<void*>(value));
+  devices->push_back(device);
+}
+
+void EnumerateHidDevices(IOHIDManagerRef hid_manager,
+                         HidDeviceList* device_list) {
+  DCHECK(device_list->size() == 0);
+  // Note that our ownership of each copied device is implied.
+  base::ScopedCFTypeRef<CFSetRef> devices(IOHIDManagerCopyDevices(hid_manager));
+  if (devices)
+    CFSetApplyFunction(devices, HidEnumerationBackInserter, device_list);
+}
+
+}  // namespace
+
+HidServiceMac::HidServiceMac() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  message_loop_ = base::MessageLoopProxy::current();
+  DCHECK(message_loop_);
+  hid_manager_.reset(IOHIDManagerCreate(NULL, 0));
+  if (!hid_manager_) {
     LOG(ERROR) << "Failed to initialize HidManager";
     return;
   }
-  CFRetain(hid_manager_ref_);
+  DCHECK(CFGetTypeID(hid_manager_) == IOHIDManagerGetTypeID());
+  IOHIDManagerOpen(hid_manager_, kIOHIDOptionsTypeNone);
+  IOHIDManagerSetDeviceMatching(hid_manager_, NULL);
 
-  // Register for plug/unplug notifications.
-  IOHIDManagerSetDeviceMatching(hid_manager_ref_.get(), NULL);
-  IOHIDManagerRegisterDeviceMatchingCallback(
-      hid_manager_ref_.get(),
-      &HidServiceMac::AddDeviceCallback,
-      this);
-  IOHIDManagerRegisterDeviceRemovalCallback(
-      hid_manager_ref_.get(),
-      &HidServiceMac::RemoveDeviceCallback,
-      this);
-
-  // Blocking operation to enumerate all the pre-existing devices.
+  // Enumerate all the currently known devices.
   Enumerate();
 
-  // Save the owner message loop.
-  message_loop_ = base::MessageLoopProxy::current();
-
-  // Start a thread to monitor HID device changes.
-  // The reason to create a new thread is that by default the only thread in the
-  // browser process having a CFRunLoop is the UI thread. We do not want to
-  // run potentially blocking routines on it.
-  enumeration_runloop_thread_.reset(
-      new base::Thread("HidService Device Enumeration Thread"));
-
-  base::Thread::Options options;
-  options.message_loop_type = base::MessageLoop::TYPE_UI;
-  enumeration_runloop_thread_->StartWithOptions(options);
-  enumeration_runloop_thread_->message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&HidServiceMac::ScheduleRunLoop, base::Unretained(this)));
-
-  enumeration_runloop_init_.Wait();
-  initialized_ = true;
+  // Register for plug/unplug notifications.
+  StartWatchingDevices();
 }
 
 HidServiceMac::~HidServiceMac() {
-  enumeration_runloop_thread_->message_loop()->PostTask(
-        FROM_HERE,
-        base::Bind(&HidServiceMac::UnscheduleRunLoop, base::Unretained(this)));
-
-  enumeration_runloop_thread_->Stop();
+  StopWatchingDevices();
 }
 
-void HidServiceMac::ScheduleRunLoop() {
-  enumeration_runloop_ = CFRunLoopGetCurrent();
-
+void HidServiceMac::StartWatchingDevices() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  IOHIDManagerRegisterDeviceMatchingCallback(
+      hid_manager_, &AddDeviceCallback, this);
+  IOHIDManagerRegisterDeviceRemovalCallback(
+      hid_manager_, &RemoveDeviceCallback, this);
   IOHIDManagerScheduleWithRunLoop(
-      hid_manager_ref_,
-      enumeration_runloop_,
-      kCFRunLoopDefaultMode);
-
-  IOHIDManagerOpen(hid_manager_ref_, kIOHIDOptionsTypeNone);
-
-  enumeration_runloop_init_.Signal();
+      hid_manager_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
 }
 
-void HidServiceMac::UnscheduleRunLoop() {
+void HidServiceMac::StopWatchingDevices() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!hid_manager_)
+    return;
   IOHIDManagerUnscheduleFromRunLoop(
-        hid_manager_ref_,
-        enumeration_runloop_,
-        kCFRunLoopDefaultMode);
-
-  IOHIDManagerClose(hid_manager_ref_, kIOHIDOptionsTypeNone);
-
-}
-
-HidServiceMac* HidServiceMac::InstanceFromContext(void* context) {
-  return reinterpret_cast<HidServiceMac*>(context);
+      hid_manager_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
+  IOHIDManagerClose(hid_manager_, kIOHIDOptionsTypeNone);
 }
 
 void HidServiceMac::AddDeviceCallback(void* context,
                                       IOReturn result,
                                       void* sender,
-                                      IOHIDDeviceRef ref) {
-  HidServiceMac* service = InstanceFromContext(context);
-
-  // Takes ownership of ref. Will be released inside PlatformDeviceAdd.
-  CFRetain(ref);
-  service->message_loop_->PostTask(
-      FROM_HERE,
-      base::Bind(&HidServiceMac::PlatformAddDevice,
-                 base::Unretained(service),
-                 base::Unretained(ref)));
+                                      IOHIDDeviceRef hid_device) {
+  DCHECK(CFRunLoopGetMain() == CFRunLoopGetCurrent());
+  // Claim ownership of the device.
+  CFRetain(hid_device);
+  HidServiceMac* service = HidServiceFromContext(context);
+  service->message_loop_->PostTask(FROM_HERE,
+                                   base::Bind(&HidServiceMac::PlatformAddDevice,
+                                              base::Unretained(service),
+                                              base::Unretained(hid_device)));
 }
 
 void HidServiceMac::RemoveDeviceCallback(void* context,
                                          IOReturn result,
                                          void* sender,
-                                         IOHIDDeviceRef ref) {
-  HidServiceMac* service = InstanceFromContext(context);
-
-  // Takes ownership of ref. Will be released inside PlatformDeviceRemove.
-  CFRetain(ref);
+                                         IOHIDDeviceRef hid_device) {
+  DCHECK(CFRunLoopGetMain() == CFRunLoopGetCurrent());
+  HidServiceMac* service = HidServiceFromContext(context);
   service->message_loop_->PostTask(
       FROM_HERE,
       base::Bind(&HidServiceMac::PlatformRemoveDevice,
                  base::Unretained(service),
-                 base::Unretained(ref)));
-}
-
-IOHIDDeviceRef HidServiceMac::FindDevice(std::string id) {
-  base::ScopedCFTypeRef<CFSetRef> devices(
-      IOHIDManagerCopyDevices(hid_manager_ref_));
-  CFIndex count = CFSetGetCount(devices);
-  scoped_ptr<IOHIDDeviceRef[]> device_refs(new IOHIDDeviceRef[count]);
-  CFSetGetValues(devices, (const void **)(device_refs.get()));
-
-  for (CFIndex i = 0; i < count; i++) {
-    int32_t int_property = 0;
-    if (GetHidIntProperty(device_refs[i], CFSTR(kIOHIDLocationIDKey),
-                       &int_property)) {
-      if (id == base::HexEncode(&int_property, sizeof(int_property))) {
-        return device_refs[i];
-      }
-    }
-  }
-
-  return NULL;
+                 base::Unretained(hid_device)));
 }
 
 void HidServiceMac::Enumerate() {
-  base::ScopedCFTypeRef<CFSetRef> devices(
-      IOHIDManagerCopyDevices(hid_manager_ref_));
-  CFIndex count = CFSetGetCount(devices);
-  scoped_ptr<IOHIDDeviceRef[]> device_refs(new IOHIDDeviceRef[count]);
-  CFSetGetValues(devices, (const void **)(device_refs.get()));
-
-  for (CFIndex i = 0; i < count; i++) {
-    // Takes ownership. Will be released inside PlatformDeviceAdd.
-    CFRetain(device_refs[i]);
-    PlatformAddDevice(device_refs[i]);
+  DCHECK(thread_checker_.CalledOnValidThread());
+  HidDeviceList devices;
+  EnumerateHidDevices(hid_manager_, &devices);
+  for (HidDeviceList::const_iterator iter = devices.begin();
+       iter != devices.end();
+       ++iter) {
+    IOHIDDeviceRef hid_device = *iter;
+    PlatformAddDevice(hid_device);
   }
 }
 
-void HidServiceMac::PlatformAddDevice(IOHIDDeviceRef raw_ref) {
-  HidDeviceInfo device;
-  int32_t int_property = 0;
-  std::string str_property;
+void HidServiceMac::PlatformAddDevice(IOHIDDeviceRef hid_device) {
+  // Note that our ownership of hid_device is implied if calling this method.
+  // It is balanced in PlatformRemoveDevice.
+  DCHECK(thread_checker_.CalledOnValidThread());
 
-  // Auto-release.
-  base::ScopedCFTypeRef<IOHIDDeviceRef> ref(raw_ref);
-
-  // Unique identifier for HID device.
-  if (GetHidIntProperty(ref, CFSTR(kIOHIDLocationIDKey), &int_property)) {
-    device.device_id = base::HexEncode(&int_property, sizeof(int_property));
-  } else {
-    // Not an available device.
-    return;
-  }
-
-  if (GetHidIntProperty(ref, CFSTR(kIOHIDVendorIDKey), &int_property)) {
-    device.vendor_id = int_property;
-  }
-  if (GetHidIntProperty(ref, CFSTR(kIOHIDProductIDKey), &int_property)) {
-    device.product_id = int_property;
-  }
-  if (GetHidIntProperty(ref, CFSTR(kIOHIDPrimaryUsageKey), &int_property)) {
-    device.usage = int_property;
-  }
-  if (GetHidIntProperty(ref, CFSTR(kIOHIDPrimaryUsagePageKey), &int_property)) {
-    device.usage_page = int_property;
-  }
-  if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxInputReportSizeKey),
-                        &int_property)) {
-    device.input_report_size = int_property;
-  }
-  if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxOutputReportSizeKey),
-                        &int_property)) {
-    device.output_report_size = int_property;
-  }
-  if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxFeatureReportSizeKey),
-                     &int_property)) {
-    device.feature_report_size = int_property;
-  }
-  if (GetHidStringProperty(ref, CFSTR(kIOHIDProductKey), &str_property)) {
-    device.product_name = str_property;
-  }
-  if (GetHidStringProperty(ref, CFSTR(kIOHIDSerialNumberKey), &str_property)) {
-    device.serial_number = str_property;
-  }
-  HidService::AddDevice(device);
+  HidDeviceInfo device_info;
+  device_info.device_id = hid_device;
+  device_info.vendor_id =
+      GetHidIntProperty(hid_device, CFSTR(kIOHIDVendorIDKey));
+  device_info.product_id =
+      GetHidIntProperty(hid_device, CFSTR(kIOHIDProductIDKey));
+  device_info.usage =
+      GetHidIntProperty(hid_device, CFSTR(kIOHIDPrimaryUsageKey));
+  device_info.usage_page =
+      GetHidIntProperty(hid_device, CFSTR(kIOHIDPrimaryUsagePageKey));
+  device_info.input_report_size =
+      GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxInputReportSizeKey));
+  device_info.output_report_size =
+      GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxOutputReportSizeKey));
+  device_info.feature_report_size =
+      GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxFeatureReportSizeKey));
+  device_info.product_name =
+      GetHidStringProperty(hid_device, CFSTR(kIOHIDProductKey));
+  device_info.serial_number =
+      GetHidStringProperty(hid_device, CFSTR(kIOHIDSerialNumberKey));
+  AddDevice(device_info);
 }
 
-void HidServiceMac::PlatformRemoveDevice(IOHIDDeviceRef raw_ref) {
-  std::string device_id;
-  int32_t int_property = 0;
-  // Auto-release.
-  base::ScopedCFTypeRef<IOHIDDeviceRef> ref(raw_ref);
-  if (!GetHidIntProperty(ref, CFSTR(kIOHIDLocationIDKey), &int_property)) {
-    return;
-  }
-  device_id = base::HexEncode(&int_property, sizeof(int_property));
-  HidService::RemoveDevice(device_id);
+void HidServiceMac::PlatformRemoveDevice(IOHIDDeviceRef hid_device) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  RemoveDevice(hid_device);
+  CFRelease(hid_device);
 }
 
-scoped_refptr<HidConnection>
-HidServiceMac::Connect(std::string device_id) {
-  if (!ContainsKey(devices_, device_id))
+scoped_refptr<HidConnection> HidServiceMac::Connect(
+    const HidDeviceId& device_id) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  HidDeviceInfo device_info;
+  if (!GetDeviceInfo(device_id, &device_info))
     return NULL;
-
-  IOHIDDeviceRef ref = FindDevice(device_id);
-  if (ref == NULL)
-    return NULL;
-  return scoped_refptr<HidConnection>(
-      new HidConnectionMac(this, devices_[device_id], ref));
+  return scoped_refptr<HidConnection>(new HidConnectionMac(device_info));
 }
 
 }  // namespace device
diff --git a/device/hid/hid_service_mac.h b/device/hid/hid_service_mac.h
index 9351d1e..7c43234 100644
--- a/device/hid/hid_service_mac.h
+++ b/device/hid/hid_service_mac.h
@@ -1,77 +1,60 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef DEVICE_HID_HID_SERVICE_MAC_H_
 #define DEVICE_HID_HID_SERVICE_MAC_H_
 
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/mac/foundation_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/singleton.h"
-#include "base/message_loop/message_loop.h"
-#include "base/strings/string16.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_checker.h"
-#include "build/build_config.h"
-#include "device/hid/hid_device_info.h"
-#include "device/hid/hid_service.h"
-
 #include <CoreFoundation/CoreFoundation.h>
 #include <IOKit/hid/IOHIDManager.h>
 
+#include <string>
+
+#include "base/mac/foundation_util.h"
+#include "base/memory/ref_counted.h"
+#include "device/hid/hid_service.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
 namespace device {
 
 class HidConnection;
-class HidService;
 
 class HidServiceMac : public HidService {
  public:
   HidServiceMac();
 
-  virtual scoped_refptr<HidConnection> Connect(std::string device_id) OVERRIDE;
+  virtual scoped_refptr<HidConnection> Connect(const HidDeviceId& device_id)
+      OVERRIDE;
 
  private:
   virtual ~HidServiceMac();
 
-  void ScheduleRunLoop();
-  void UnscheduleRunLoop();
+  void StartWatchingDevices();
+  void StopWatchingDevices();
 
   // Device changing callbacks.
   static void AddDeviceCallback(void* context,
                                 IOReturn result,
                                 void* sender,
-                                IOHIDDeviceRef ref);
+                                IOHIDDeviceRef hid_device);
   static void RemoveDeviceCallback(void* context,
                                    IOReturn result,
                                    void* sender,
-                                   IOHIDDeviceRef ref);
-  static HidServiceMac* InstanceFromContext(void* context);
-
-  IOHIDDeviceRef FindDevice(std::string id);
+                                   IOHIDDeviceRef hid_device);
 
   void Enumerate();
 
-  void PlatformAddDevice(IOHIDDeviceRef ref);
-  void PlatformRemoveDevice(IOHIDDeviceRef ref);
-
-  // The message loop this object belongs to.
-  scoped_refptr<base::MessageLoopProxy> message_loop_;
+  void PlatformAddDevice(IOHIDDeviceRef hid_device);
+  void PlatformRemoveDevice(IOHIDDeviceRef hid_device);
 
   // Platform HID Manager
-  base::ScopedCFTypeRef<IOHIDManagerRef> hid_manager_ref_;
+  base::ScopedCFTypeRef<IOHIDManagerRef> hid_manager_;
 
-  // Enumeration thread.
-  scoped_ptr<base::Thread> enumeration_runloop_thread_;
-  CFRunLoopRef enumeration_runloop_;
-  base::WaitableEvent enumeration_runloop_init_;
-
-  bool available_;
+  // The message loop for the thread on which this service was created.
+  scoped_refptr<base::MessageLoopProxy> message_loop_;
 
   DISALLOW_COPY_AND_ASSIGN(HidServiceMac);
 };
diff --git a/device/hid/hid_service_unittest.cc b/device/hid/hid_service_unittest.cc
index 1fa1c0a..0b5043b 100644
--- a/device/hid/hid_service_unittest.cc
+++ b/device/hid/hid_service_unittest.cc
@@ -1,16 +1,11 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <string>
 #include <vector>
 
-#include "base/bind.h"
-#include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
-#include "device/hid/hid_connection.h"
 #include "device/hid/hid_service.h"
-#include "net/base/io_buffer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace device {
@@ -22,11 +17,10 @@
 
   std::vector<HidDeviceInfo> devices;
   service->GetDevices(&devices);
-
   for (std::vector<HidDeviceInfo>::iterator it = devices.begin();
       it != devices.end();
       ++it) {
-    ASSERT_TRUE(!it->device_id.empty());
+    ASSERT_TRUE(it->device_id != kInvalidHidDeviceId);
   }
 }
 
diff --git a/device/hid/hid_service_win.cc b/device/hid/hid_service_win.cc
index cf9db15..27f4499 100644
--- a/device/hid/hid_service_win.cc
+++ b/device/hid/hid_service_win.cc
@@ -1,20 +1,15 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "device/hid/hid_service_win.h"
 
 #include <cstdlib>
-#include <string>
 
-#include "base/callback_helpers.h"
-#include "base/lazy_instance.h"
-#include "base/memory/scoped_ptr.h"
 #include "base/stl_util.h"
 #include "base/strings/sys_string_conversions.h"
-#include "device/hid/hid_connection.h"
 #include "device/hid/hid_connection_win.h"
-#include "device/hid/hid_service.h"
+#include "device/hid/hid_device_info.h"
 #include "net/base/io_buffer.h"
 
 #if defined(OS_WIN)
@@ -49,11 +44,12 @@
 }  // namespace
 
 HidServiceWin::HidServiceWin() {
-  initialized_ = Enumerate();
+  Enumerate();
 }
+
 HidServiceWin::~HidServiceWin() {}
 
-bool HidServiceWin::Enumerate() {
+void HidServiceWin::Enumerate() {
   BOOL res;
   HDEVINFO device_info_set;
   SP_DEVINFO_DATA devinfo_data;
@@ -70,15 +66,15 @@
       DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
 
   if (device_info_set == INVALID_HANDLE_VALUE)
-    return false;
+    return;
 
   for (int device_index = 0;
-      SetupDiEnumDeviceInterfaces(device_info_set,
-                                  NULL,
-                                  &GUID_DEVINTERFACE_HID,
-                                  device_index,
-                                  &device_interface_data);
-      device_index++) {
+       SetupDiEnumDeviceInterfaces(device_info_set,
+                                   NULL,
+                                   &GUID_DEVINTERFACE_HID,
+                                   device_index,
+                                   &device_interface_data);
+       ++device_index) {
     DWORD required_size = 0;
 
     // Determime the required size of detail struct.
@@ -89,9 +85,9 @@
                                      &required_size,
                                      NULL);
 
-    scoped_ptr_malloc<SP_DEVICE_INTERFACE_DETAIL_DATA_A>
+    scoped_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA_A, base::FreeDeleter>
         device_interface_detail_data(
-            reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_A*>(
+            static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_A*>(
                 malloc(required_size)));
     device_interface_detail_data->cbSize =
         sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
@@ -131,7 +127,7 @@
                                                 sizeof(driver_name) - 1,
                                                 NULL);
         if (res) {
-          // Found the drive.
+          // Found the driver.
           break;
         }
       }
@@ -142,11 +138,9 @@
 
     PlatformAddDevice(device_interface_detail_data->DevicePath);
   }
-
-  return true;
 }
 
-void HidServiceWin::PlatformAddDevice(std::string device_path) {
+void HidServiceWin::PlatformAddDevice(const std::string& device_path) {
   HidDeviceInfo device_info;
   device_info.device_id = device_path;
 
@@ -195,6 +189,17 @@
       if (HidP_GetValueCaps(HidP_Input, &value_caps[0], &value_caps_length,
                             preparsed_data) == HIDP_STATUS_SUCCESS) {
         device_info.has_report_id = (value_caps[0].ReportID != 0);
+        // If report IDs are supported, adjust all the expected report sizes
+        // down by one byte. This is because Windows will always provide sizes
+        // which assume the presence of a report ID.
+        if (device_info.has_report_id) {
+          if (device_info.input_report_size > 0)
+            device_info.input_report_size -= 1;
+          if (device_info.output_report_size > 0)
+            device_info.output_report_size -= 1;
+          if (device_info.feature_report_size > 0)
+            device_info.feature_report_size -= 1;
+        }
       }
     }
     HidD_FreePreparsedData(preparsed_data);
@@ -214,11 +219,11 @@
     device_info.product_name = base::SysWideToUTF8(str_property);
   }
 
-  HidService::AddDevice(device_info);
+  AddDevice(device_info);
 }
 
-void HidServiceWin::PlatformRemoveDevice(std::string device_path) {
-  HidService::RemoveDevice(device_path);
+void HidServiceWin::PlatformRemoveDevice(const std::string& device_path) {
+  RemoveDevice(device_path);
 }
 
 void HidServiceWin::GetDevices(std::vector<HidDeviceInfo>* devices) {
@@ -226,10 +231,12 @@
   HidService::GetDevices(devices);
 }
 
-scoped_refptr<HidConnection> HidServiceWin::Connect(std::string device_id) {
-  if (!ContainsKey(devices_, device_id)) return NULL;
-  scoped_refptr<HidConnectionWin> connection(
-      new HidConnectionWin(devices_[device_id]));
+scoped_refptr<HidConnection> HidServiceWin::Connect(
+    const HidDeviceId& device_id) {
+  HidDeviceInfo device_info;
+  if (!GetDeviceInfo(device_id, &device_info))
+    return NULL;
+  scoped_refptr<HidConnectionWin> connection(new HidConnectionWin(device_info));
   if (!connection->available()) {
     LOG_GETLASTERROR(ERROR) << "Failed to open device.";
     return NULL;
diff --git a/device/hid/hid_service_win.h b/device/hid/hid_service_win.h
index 428420e..8f18765 100644
--- a/device/hid/hid_service_win.h
+++ b/device/hid/hid_service_win.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,18 +6,13 @@
 #define DEVICE_HID_HID_SERVICE_WIN_H_
 
 #include <map>
-#include <string>
-#include <vector>
 
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
 #include "device/hid/hid_device_info.h"
 #include "device/hid/hid_service.h"
 
 namespace device {
 
 class HidConnection;
-class HidService;
 
 class HidServiceWin : public HidService {
  public:
@@ -25,14 +20,15 @@
 
   virtual void GetDevices(std::vector<HidDeviceInfo>* devices) OVERRIDE;
 
-  virtual scoped_refptr<HidConnection> Connect(std::string device_id) OVERRIDE;
+  virtual scoped_refptr<HidConnection> Connect(const HidDeviceId& device_id)
+      OVERRIDE;
 
  private:
   virtual ~HidServiceWin();
 
-  bool Enumerate();
-  void PlatformAddDevice(std::string device_path);
-  void PlatformRemoveDevice(std::string device_path);
+  void Enumerate();
+  void PlatformAddDevice(const std::string& device_path);
+  void PlatformRemoveDevice(const std::string& device_path);
 
   DISALLOW_COPY_AND_ASSIGN(HidServiceWin);
 };
diff --git a/device/hid/hid_utils_mac.cc b/device/hid/hid_utils_mac.cc
index 588041a..46605d8 100644
--- a/device/hid/hid_utils_mac.cc
+++ b/device/hid/hid_utils_mac.cc
@@ -1,32 +1,38 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "device/hid/hid_utils_mac.h"
 
-#include "base/basictypes.h"
-#include "base/logging.h"
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
 
-#if defined(OS_MACOSX)
-#include <CoreFoundation/CoreFoundation.h>
-#include <IOKit/hid/IOHIDManager.h>
-#endif
-
 namespace device {
 
-bool GetHidIntProperty(IOHIDDeviceRef device,
-                       CFStringRef key,
-                       int32_t* result) {
+int32_t GetHidIntProperty(IOHIDDeviceRef device, CFStringRef key) {
+  int32_t value;
+  if (TryGetHidIntProperty(device, key, &value))
+    return value;
+  return 0;
+}
+
+std::string GetHidStringProperty(IOHIDDeviceRef device, CFStringRef key) {
+  std::string value;
+  TryGetHidStringProperty(device, key, &value);
+  return value;
+}
+
+bool TryGetHidIntProperty(IOHIDDeviceRef device,
+                          CFStringRef key,
+                          int32_t* result) {
   CFNumberRef ref = base::mac::CFCast<CFNumberRef>(
       IOHIDDeviceGetProperty(device, key));
   return ref && CFNumberGetValue(ref, kCFNumberSInt32Type, result);
 }
 
-bool GetHidStringProperty(IOHIDDeviceRef device,
-                          CFStringRef key,
-                          std::string* result) {
+bool TryGetHidStringProperty(IOHIDDeviceRef device,
+                             CFStringRef key,
+                             std::string* result) {
   CFStringRef ref = base::mac::CFCast<CFStringRef>(
       IOHIDDeviceGetProperty(device, key));
   if (!ref) {
diff --git a/device/hid/hid_utils_mac.h b/device/hid/hid_utils_mac.h
index f1349ae..e9b2524 100644
--- a/device/hid/hid_utils_mac.h
+++ b/device/hid/hid_utils_mac.h
@@ -1,22 +1,29 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef DEVICE_HID_HID_UTILS_MAC_H_
 #define DEVICE_HID_HID_UTILS_MAC_H_
 
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-
+#include <CoreFoundation/CoreFoundation.h>
 #include <IOKit/hid/IOHIDManager.h>
+#include <stdint.h>
+
+#include <string>
 
 namespace device {
 
-bool GetHidIntProperty(IOHIDDeviceRef device, CFStringRef key, int32_t* result);
+int32_t GetHidIntProperty(IOHIDDeviceRef device, CFStringRef key);
 
-bool GetHidStringProperty(IOHIDDeviceRef device,
-                       CFStringRef key,
-                       std::string* result);
+std::string GetHidStringProperty(IOHIDDeviceRef device, CFStringRef key);
+
+bool TryGetHidIntProperty(IOHIDDeviceRef device,
+                          CFStringRef key,
+                          int32_t* result);
+
+bool TryGetHidStringProperty(IOHIDDeviceRef device,
+                             CFStringRef key,
+                             std::string* result);
 
 }  // namespace device
 
diff --git a/device/media_transfer_protocol/media_transfer_protocol_daemon_client.cc b/device/media_transfer_protocol/media_transfer_protocol_daemon_client.cc
index 41ac37e..4edfc60 100644
--- a/device/media_transfer_protocol/media_transfer_protocol_daemon_client.cc
+++ b/device/media_transfer_protocol/media_transfer_protocol_daemon_client.cc
@@ -19,6 +19,7 @@
 namespace {
 
 const char kInvalidResponseMsg[] = "Invalid Response: ";
+uint32 kMaxChunkSize = 1024*1024;  // D-Bus has message size limits.
 
 // The MediaTransferProtocolDaemonClient implementation.
 class MediaTransferProtocolDaemonClientImpl
@@ -140,6 +141,7 @@
       uint32 bytes_to_read,
       const ReadFileCallback& callback,
       const ErrorCallback& error_callback) OVERRIDE {
+    DCHECK_LE(bytes_to_read, kMaxChunkSize);
     dbus::MethodCall method_call(mtpd::kMtpdInterface,
                                  mtpd::kReadFileChunkByPath);
     dbus::MessageWriter writer(&method_call);
@@ -162,6 +164,7 @@
                                  uint32 bytes_to_read,
                                  const ReadFileCallback& callback,
                                  const ErrorCallback& error_callback) OVERRIDE {
+    DCHECK_LE(bytes_to_read, kMaxChunkSize);
     dbus::MethodCall method_call(mtpd::kMtpdInterface,
                                  mtpd::kReadFileChunkById);
     dbus::MessageWriter writer(&method_call);
@@ -349,7 +352,7 @@
       return;
     }
 
-    uint8* data_bytes = NULL;
+    const uint8* data_bytes = NULL;
     size_t data_length = 0;
     dbus::MessageReader reader(response);
     if (!reader.PopArrayOfBytes(&data_bytes, &data_length)) {
diff --git a/device/nfc/OWNERS b/device/nfc/OWNERS
new file mode 100644
index 0000000..7bf8537
--- /dev/null
+++ b/device/nfc/OWNERS
@@ -0,0 +1,2 @@
+keybuk@chromium.org
+armansito@chromium.org
diff --git a/device/serial/serial_device_enumerator_win.cc b/device/serial/serial_device_enumerator_win.cc
index e3bde29..a45a609 100644
--- a/device/serial/serial_device_enumerator_win.cc
+++ b/device/serial/serial_device_enumerator_win.cc
@@ -10,6 +10,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/win/registry.h"
 
 namespace device {
@@ -34,7 +35,7 @@
   for (; iter_key.Valid(); ++iter_key) {
     base::string16 value(iter_key.Value());
     linked_ptr<SerialDeviceInfo> info(new SerialDeviceInfo);
-    info->path = WideToASCII(value);
+    info->path = base::UTF16ToASCII(value);
     devices->push_back(info);
   }
 }