Exposing a new easyUnlock.setupConnectionGetDeviceAddress JS API.

This CL exposes a new JS API to find the bluetooth address for the
remote device corresponding to a given connection id.

This is necesssary as the bluetooth address of a device changes after
it's paired (but it's updated in the underlying
BluetoothLowEnergyConnection object).

BUG=528277

Review URL: https://codereview.chromium.org/1370133005

Cr-Commit-Position: refs/heads/master@{#351549}
diff --git a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
index 2b927349..369c231 100644
--- a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
+++ b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
@@ -1039,7 +1039,12 @@
           make_scoped_ptr(new base::DefaultTickClock()))) {}
 
 EasyUnlockPrivateFindSetupConnectionFunction::
-    ~EasyUnlockPrivateFindSetupConnectionFunction() {}
+    ~EasyUnlockPrivateFindSetupConnectionFunction() {
+  // |connection_finder_| has a raw pointer to |bluetooth_throttler_|, so it
+  // should be destroyed first.
+  connection_finder_.reset();
+  bluetooth_throttler_.reset();
+}
 
 void EasyUnlockPrivateFindSetupConnectionFunction::
     OnConnectionFinderTimedOut() {
@@ -1141,4 +1146,27 @@
   return true;
 }
 
+EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction::
+    EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction() {}
+
+EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction::
+    ~EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction() {}
+
+bool EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction::RunSync() {
+  scoped_ptr<easy_unlock_private::SetupConnectionGetDeviceAddress::Params>
+      params =
+          easy_unlock_private::SetupConnectionGetDeviceAddress::Params::Create(
+              *args_);
+  EXTENSION_FUNCTION_VALIDATE(params);
+  std::string device_address =
+      GetConnectionManager(browser_context())
+          ->GetDeviceAddress(extension(), params->connection_id);
+  results_ =
+      easy_unlock_private::SetupConnectionGetDeviceAddress::Results::Create(
+          device_address);
+  if (device_address.empty())
+    SetError("Invalid connectionId.");
+  return true;
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h
index 31c15ca..347c6606 100644
--- a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h
+++ b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h
@@ -535,6 +535,24 @@
   DISALLOW_COPY_AND_ASSIGN(EasyUnlockPrivateSetupConnectionSendFunction);
 };
 
+class EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction
+    : public SyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION(
+      "easyUnlockPrivate.setupConnectionGetDeviceAddress",
+      EASYUNLOCKPRIVATE_SETUPCONNECTIONGETDEVICEADDRESS)
+  EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction();
+
+ private:
+  ~EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction() override;
+
+  // SyncExtensionFunction:
+  bool RunSync() override;
+
+  DISALLOW_COPY_AND_ASSIGN(
+      EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction);
+};
+
 }  // namespace extensions
 
 #endif  // CHROME_BROWSER_EXTENSIONS_API_EASY_UNLOCK_PRIVATE_EASY_UNLOCK_PRIVATE_API_H_
diff --git a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.cc b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.cc
index 9003855f..878e6ab6 100644
--- a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.cc
+++ b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.cc
@@ -70,6 +70,15 @@
   return api::easy_unlock_private::CONNECTION_STATUS_NONE;
 }
 
+std::string EasyUnlockPrivateConnectionManager::GetDeviceAddress(
+    const Extension* extension,
+    int connection_id) const {
+  Connection* connection = GetConnection(extension->id(), connection_id);
+  if (!connection)
+    return std::string();
+  return connection->GetDeviceAddress();
+}
+
 bool EasyUnlockPrivateConnectionManager::Disconnect(const Extension* extension,
                                                     int connection_id) {
   Connection* connection = GetConnection(extension->id(), connection_id);
diff --git a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.h b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.h
index 3d2f7a61..a0ca893a 100644
--- a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.h
+++ b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.h
@@ -52,6 +52,11 @@
                    int connection_id,
                    const std::string& payload);
 
+  // Returns the Bluetooth address of the device connected with a given
+  // |connection_id|, and an empty string if |connection_id| was not found.
+  std::string GetDeviceAddress(const Extension* extension,
+                               int connection_id) const;
+
   // proximity_auth::ConnectionObserver:
   void OnConnectionStatusChanged(
       proximity_auth::Connection* connection,
diff --git a/chrome/common/extensions/api/easy_unlock_private.idl b/chrome/common/extensions/api/easy_unlock_private.idl
index 1b1b9ce9..0359bd8 100644
--- a/chrome/common/extensions/api/easy_unlock_private.idl
+++ b/chrome/common/extensions/api/easy_unlock_private.idl
@@ -246,6 +246,12 @@
   // |status|: The status of the connection with |connection_id|.
   callback SetupConnectionStatusCallback = void(ConnectionStatus status);
 
+  // Callback for the |setupConnectionGetDeviceAddress()| method.
+  // |deviceAddress|: The bluetooth address of the connection with 
+  // |connectionId|.
+  callback SetupConnectionGetDeviceAddressCallback = void(
+      DOMString deviceAddress);
+
   interface Functions {
     // Gets localized strings required to render the API.
     //
@@ -420,6 +426,10 @@
     static void setupConnectionSend(long connectionId,
                                     ArrayBuffer data,
                                     optional EmptyCallback callback);
+
+    // Gets the Bluetooth address of the connection with |connectionId|
+    static void setupConnectionGetDeviceAddress(long connectionId,
+        SetupConnectionGetDeviceAddressCallback callback);
   };
 
   interface Events {
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection.cc b/components/proximity_auth/ble/bluetooth_low_energy_connection.cc
index d193acf..52af29b 100644
--- a/components/proximity_auth/ble/bluetooth_low_energy_connection.cc
+++ b/components/proximity_auth/ble/bluetooth_low_energy_connection.cc
@@ -219,12 +219,12 @@
                                                  BluetoothDevice* device) {
   DCHECK(device);
   if (sub_status() == SubStatus::DISCONNECTED ||
-      device->GetAddress() != GetRemoteDeviceAddress())
+      device->GetAddress() != GetDeviceAddress())
     return;
 
   if (sub_status() != SubStatus::WAITING_GATT_CONNECTION &&
       !device->IsConnected()) {
-    PA_LOG(INFO) << "GATT connection dropped " << GetRemoteDeviceAddress()
+    PA_LOG(INFO) << "GATT connection dropped " << GetDeviceAddress()
                  << "\ndevice connected: " << device->IsConnected()
                  << "\ngatt connection: "
                  << (gatt_connection_ ? gatt_connection_->IsConnected()
@@ -237,10 +237,10 @@
                                                  BluetoothDevice* device) {
   DCHECK(device);
   if (sub_status_ == SubStatus::DISCONNECTED ||
-      device->GetAddress() != GetRemoteDeviceAddress())
+      device->GetAddress() != GetDeviceAddress())
     return;
 
-  PA_LOG(INFO) << "Device removed " << GetRemoteDeviceAddress();
+  PA_LOG(INFO) << "Device removed " << GetDeviceAddress();
   Disconnect();
 }
 
@@ -343,6 +343,9 @@
                << " created.";
   PrintTimeElapsed();
 
+  // Informing |bluetooth_trottler_| a new connection was established.
+  bluetooth_throttler_->OnConnection(this);
+
   gatt_connection_ = gatt_connection.Pass();
   SetSubStatus(SubStatus::WAITING_CHARACTERISTICS);
   characteristic_finder_.reset(CreateCharacteristicsFinder(
@@ -350,9 +353,6 @@
                  weak_ptr_factory_.GetWeakPtr()),
       base::Bind(&BluetoothLowEnergyConnection::OnCharacteristicsFinderError,
                  weak_ptr_factory_.GetWeakPtr())));
-
-  // Informing |bluetooth_trottler_| a new connection was established.
-  bluetooth_throttler_->OnConnection(this);
 }
 
 BluetoothLowEnergyCharacteristicsFinder*
@@ -544,7 +544,7 @@
   PA_LOG(INFO) << "Time elapsed: " << base::TimeTicks::Now() - start_time_;
 }
 
-std::string BluetoothLowEnergyConnection::GetRemoteDeviceAddress() {
+std::string BluetoothLowEnergyConnection::GetDeviceAddress() {
   // When the remote device is connected we should rely on the address given by
   // |gatt_connection_|. As the device address may change if the device is
   // paired. The address in |gatt_connection_| is automatically updated in this
@@ -555,15 +555,15 @@
 
 BluetoothDevice* BluetoothLowEnergyConnection::GetRemoteDevice() {
   // It's not possible to simply use
-  // |adapter_->GetDevice(GetRemoteDeviceAddress())| to find the device with MAC
-  // address |GetRemoteDeviceAddress()|. For paired devices,
+  // |adapter_->GetDevice(GetDeviceAddress())| to find the device with MAC
+  // address |GetDeviceAddress()|. For paired devices,
   // BluetoothAdapter::GetDevice(XXX) searches for the temporary MAC address
-  // XXX, whereas |GetRemoteDeviceAddress()| is the real MAC address. This is a
+  // XXX, whereas |GetDeviceAddress()| is the real MAC address. This is a
   // bug in the way device::BluetoothAdapter is storing the devices (see
   // crbug.com/497841).
   std::vector<BluetoothDevice*> devices = adapter_->GetDevices();
   for (const auto& device : devices) {
-    if (device->GetAddress() == GetRemoteDeviceAddress())
+    if (device->GetAddress() == GetDeviceAddress())
       return device;
   }
 
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection.h b/components/proximity_auth/ble/bluetooth_low_energy_connection.h
index d4d0ad8f..b316f641 100644
--- a/components/proximity_auth/ble/bluetooth_low_energy_connection.h
+++ b/components/proximity_auth/ble/bluetooth_low_energy_connection.h
@@ -96,6 +96,7 @@
   // proximity_auth::Connection:
   void Connect() override;
   void Disconnect() override;
+  std::string GetDeviceAddress() override;
 
  protected:
   // Exposed for testing.
@@ -224,9 +225,6 @@
   // Prints the time elapsed since |Connect()| was called.
   void PrintTimeElapsed();
 
-  // Returns the Bluetooth address of the remote device.
-  std::string GetRemoteDeviceAddress();
-
   // Returns the device corresponding to |remote_device_address_|.
   device::BluetoothDevice* GetRemoteDevice();
 
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.cc b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.cc
index ade408ff..bf50796 100644
--- a/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.cc
+++ b/components/proximity_auth/ble/bluetooth_low_energy_connection_finder.cc
@@ -269,7 +269,8 @@
     // If we invoke the callback now, the callback function may install its own
     // observer to |connection_|. Because we are in the ConnectionObserver
     // callstack, this new observer will receive this connection event.
-    // Therefore, we need to invoke the callback asynchronously.
+    // Therefore, we need to invoke the callback or restart discovery
+    // asynchronously.
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::Bind(&BluetoothLowEnergyConnectionFinder::InvokeCallbackAsync,
diff --git a/components/proximity_auth/connection.cc b/components/proximity_auth/connection.cc
index 437be60..85249bb8 100644
--- a/components/proximity_auth/connection.cc
+++ b/components/proximity_auth/connection.cc
@@ -46,6 +46,10 @@
   observers_.RemoveObserver(observer);
 }
 
+std::string Connection::GetDeviceAddress() {
+  return remote_device_.bluetooth_address;
+}
+
 void Connection::SetStatus(Status status) {
   if (status_ == status)
     return;
diff --git a/components/proximity_auth/connection.h b/components/proximity_auth/connection.h
index a79ce188..532e03d9 100644
--- a/components/proximity_auth/connection.h
+++ b/components/proximity_auth/connection.h
@@ -54,6 +54,9 @@
   // Disconnects from the remote device.
   virtual void Disconnect() = 0;
 
+  // The bluetooth address of the connected device.
+  virtual std::string GetDeviceAddress();
+
   Status status() const { return status_; }
 
  protected:
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 1e8bd72..3c918e2 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1150,6 +1150,7 @@
   EASYUNLOCKPRIVATE_SETUPCONNECTIONDISCONNECT,
   EASYUNLOCKPRIVATE_SETUPCONNECTIONSEND,
   DATAREDUCTIONPROXY_GETDATAUSAGE,
+  EASYUNLOCKPRIVATE_SETUPCONNECTIONGETDEVICEADDRESS,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 00a33bd..af2e22e 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -57919,6 +57919,7 @@
   <int value="1087" label="EASYUNLOCKPRIVATE_SETUPCONNECTIONDISCONNECT"/>
   <int value="1088" label="EASYUNLOCKPRIVATE_SETUPCONNECTIONSEND"/>
   <int value="1089" label="DATAREDUCTIONPROXY_GETDATAUSAGE"/>
+  <int value="1090" label="EASYUNLOCKPRIVATE_SETUPCONNECTIONGETDEVICEADDRESS"/>
 </enum>
 
 <enum name="ExtensionInstallCause" type="int">