service/hal: Add per-client Scan interface

Added a new per-client scan function to hal::BluetoothGattInterface.
The intention here is to push most of the per-client reference counting,
scan settings and filter coalescence below the HAL. This CL does this
first inside the Bluetooth daemon's HAL wrappers in a way that
represents what the future HAL scan API might look like.

This implements a basic reference counting scheme to share the global
controller scan session among different clients.

Bug: 25744656
Change-Id: I20c5cfc291be70d72576ebee014cc13544d5a299
diff --git a/service/hal/bluetooth_gatt_interface.cpp b/service/hal/bluetooth_gatt_interface.cpp
index 4ad6304..107d9ee 100644
--- a/service/hal/bluetooth_gatt_interface.cpp
+++ b/service/hal/bluetooth_gatt_interface.cpp
@@ -670,5 +670,53 @@
   g_interface = test_instance;
 }
 
+bt_status_t BluetoothGattInterface::StartScan(int client_id) {
+  lock_guard<mutex> lock(scan_clients_lock_);
+
+  // Scan already initiated for this client.
+  if (scan_client_set_.find(client_id) != scan_client_set_.end()) {
+    // Assume starting scan multiple times is not error, but warn user.
+    LOG(WARNING) << "Scan already initiated for client";
+    return BT_STATUS_SUCCESS;
+  }
+
+  // If this is the first scan client, then make a call into the stack. We
+  // only do this when the reference count changes to or from 0.
+  if (scan_client_set_.empty()) {
+    bt_status_t status = GetClientHALInterface()->scan(true);
+    if (status != BT_STATUS_SUCCESS) {
+      LOG(ERROR) << "HAL call to scan failed";
+      return status;
+    }
+  }
+
+  scan_client_set_.insert(client_id);
+
+  return BT_STATUS_SUCCESS;
+}
+
+bt_status_t BluetoothGattInterface::StopScan(int client_id) {
+  lock_guard<mutex> lock(scan_clients_lock_);
+
+  // Scan not initiated for this client.
+  auto iter = scan_client_set_.find(client_id);
+  if (iter == scan_client_set_.end()) {
+    // Assume stopping scan multiple times is not error, but warn user.
+    LOG(WARNING) << "Scan already stopped or not initiated for client";
+    return BT_STATUS_SUCCESS;
+  }
+
+  if (scan_client_set_.size() == 1) {
+    bt_status_t status = GetClientHALInterface()->scan(false);
+    if (status != BT_STATUS_SUCCESS) {
+      LOG(ERROR) << "HAL call to stop scan failed";
+      return status;
+    }
+  }
+
+  scan_client_set_.erase(iter);
+  return BT_STATUS_SUCCESS;
+}
+
 }  // namespace hal
 }  // namespace bluetooth
diff --git a/service/hal/bluetooth_gatt_interface.h b/service/hal/bluetooth_gatt_interface.h
index e8d7561..9b2bea6 100644
--- a/service/hal/bluetooth_gatt_interface.h
+++ b/service/hal/bluetooth_gatt_interface.h
@@ -16,6 +16,10 @@
 
 #pragma once
 
+#include <mutex>
+#include <unordered_set>
+#include <vector>
+
 #include <base/macros.h>
 #include <hardware/bluetooth.h>
 #include <hardware/bt_gatt.h>
@@ -226,11 +230,21 @@
   // structure.
   virtual const btgatt_server_interface_t* GetServerHALInterface() const = 0;
 
+  // Initiates a regular BLE device scan. This is called internally from each
+  // LowEnergyClient. This function synchronizes the scan requests and maintains
+  // an internal reference count for each scan client that is interested.
+  bt_status_t StartScan(int client_id);
+  bt_status_t StopScan(int client_id);
+
  protected:
   BluetoothGattInterface() = default;
   virtual ~BluetoothGattInterface() = default;
 
  private:
+  // Used to keep a reference count for the different BLE scan clients.
+  std::mutex scan_clients_lock_;
+  std::unordered_set<int> scan_client_set_;
+
   DISALLOW_COPY_AND_ASSIGN(BluetoothGattInterface);
 };
 
diff --git a/service/hal/fake_bluetooth_gatt_interface.cpp b/service/hal/fake_bluetooth_gatt_interface.cpp
index 41183aa..c29bb1b 100644
--- a/service/hal/fake_bluetooth_gatt_interface.cpp
+++ b/service/hal/fake_bluetooth_gatt_interface.cpp
@@ -40,6 +40,13 @@
   return BT_STATUS_FAIL;
 }
 
+bt_status_t FakeScan(bool start) {
+  if (g_client_handler)
+    return g_client_handler->Scan(start);
+
+  return BT_STATUS_FAIL;
+}
+
 bt_status_t FakeMultiAdvEnable(
     int client_if, int min_interval, int max_interval, int adv_type,
     int chnl_map, int tx_power, int timeout_s) {
@@ -152,7 +159,7 @@
 btgatt_client_interface_t fake_btgattc_iface = {
   FakeRegisterClient,
   FakeUnregisterClient,
-  nullptr,  // scan
+  FakeScan,
   nullptr,  // connect
   nullptr,  // disconnect
   nullptr,  // listen
diff --git a/service/hal/fake_bluetooth_gatt_interface.h b/service/hal/fake_bluetooth_gatt_interface.h
index 03f25f9..cdcb794 100644
--- a/service/hal/fake_bluetooth_gatt_interface.h
+++ b/service/hal/fake_bluetooth_gatt_interface.h
@@ -35,6 +35,9 @@
 
     virtual bt_status_t RegisterClient(bt_uuid_t* app_uuid) = 0;
     virtual bt_status_t UnregisterClient(int client_if) = 0;
+
+    virtual bt_status_t Scan(bool start) = 0;
+
     virtual bt_status_t MultiAdvEnable(
         int client_if, int min_interval, int max_interval, int adv_type,
         int chnl_map, int tx_power, int timeout_s) = 0;
@@ -136,6 +139,7 @@
   std::shared_ptr<TestClientHandler> client_handler_;
   std::shared_ptr<TestServerHandler> server_handler_;
 
+
   DISALLOW_COPY_AND_ASSIGN(FakeBluetoothGattInterface);
 };
 
diff --git a/service/test/gatt_client_unittest.cpp b/service/test/gatt_client_unittest.cpp
index 0df3132..167609c 100644
--- a/service/test/gatt_client_unittest.cpp
+++ b/service/test/gatt_client_unittest.cpp
@@ -35,6 +35,7 @@
 
   MOCK_METHOD1(RegisterClient, bt_status_t(bt_uuid_t*));
   MOCK_METHOD1(UnregisterClient, bt_status_t(int));
+  MOCK_METHOD1(Scan, bt_status_t(bool));
 
   // Stub implementations for uninteresting TestClientHandler methods:
   bt_status_t MultiAdvEnable(int, int, int, int, int, int, int) override {
@@ -170,5 +171,23 @@
   EXPECT_EQ(uuid1, cb_uuid);
 }
 
+TEST_F(GattClientTest, StartStopScan) {
+  EXPECT_CALL(*mock_handler_, Scan(true))
+      .Times(1)
+      .WillOnce(Return(BT_STATUS_SUCCESS));
+
+  EXPECT_CALL(*mock_handler_, Scan(false))
+      .Times(1)
+      .WillOnce(Return(BT_STATUS_SUCCESS));
+
+  for(int i=0; i<5; i++)
+    hal::BluetoothGattInterface::Get()->StartScan(i);
+
+  for(int i=0; i<5; i++)
+    hal::BluetoothGattInterface::Get()->StopScan(i);
+
+  testing::Mock::VerifyAndClearExpectations(mock_handler_.get());
+}
+
 }  // namespace
 }  // namespace bluetooth
diff --git a/service/test/low_energy_client_unittest.cpp b/service/test/low_energy_client_unittest.cpp
index da57d8b..a2c9121 100644
--- a/service/test/low_energy_client_unittest.cpp
+++ b/service/test/low_energy_client_unittest.cpp
@@ -37,6 +37,7 @@
 
   MOCK_METHOD1(RegisterClient, bt_status_t(bt_uuid_t*));
   MOCK_METHOD1(UnregisterClient, bt_status_t(int));
+  MOCK_METHOD1(Scan, bt_status_t(bool));
   MOCK_METHOD7(MultiAdvEnable, bt_status_t(int, int, int, int, int, int, int));
   MOCK_METHOD10(
       MultiAdvSetInstDataMock,