Merge "Add Connect and Disconnect methods to LowEnergyClient"
diff --git a/service/ipc/binder/bluetooth_low_energy_binder_server.cpp b/service/ipc/binder/bluetooth_low_energy_binder_server.cpp
index f20e9c4..04c8450 100644
--- a/service/ipc/binder/bluetooth_low_energy_binder_server.cpp
+++ b/service/ipc/binder/bluetooth_low_energy_binder_server.cpp
@@ -168,6 +168,12 @@
return true;
}
+void BluetoothLowEnergyBinderServer::OnConnectionState(
+ bluetooth::LowEnergyClient* client, int status,
+ const char* address, bool connected) {
+ VLOG(2) << __func__ << " address: " << address << " connected: " << connected;
+}
+
void BluetoothLowEnergyBinderServer::OnScanResult(
bluetooth::LowEnergyClient* client,
const bluetooth::ScanResult& result) {
diff --git a/service/ipc/binder/bluetooth_low_energy_binder_server.h b/service/ipc/binder/bluetooth_low_energy_binder_server.h
index 666fb77..ed4a476 100644
--- a/service/ipc/binder/bluetooth_low_energy_binder_server.h
+++ b/service/ipc/binder/bluetooth_low_energy_binder_server.h
@@ -60,6 +60,8 @@
bool StopMultiAdvertising(int client_id) override;
// bluetooth::LowEnergyClient::Delegate overrides:
+ void OnConnectionState(bluetooth::LowEnergyClient* client, int status,
+ const char* address, bool connected) override;
void OnScanResult(bluetooth::LowEnergyClient* client,
const bluetooth::ScanResult& result) override;
diff --git a/service/low_energy_client.cpp b/service/low_energy_client.cpp
index b3f9d64..eb46d4b 100644
--- a/service/low_energy_client.cpp
+++ b/service/low_energy_client.cpp
@@ -19,6 +19,7 @@
#include <base/logging.h>
#include "service/adapter.h"
+#include "service/common/bluetooth/util/address_helper.h"
#include "service/logging_helpers.h"
#include "stack/include/bt_types.h"
#include "stack/include/hcidefs.h"
@@ -107,7 +108,7 @@
bool ProcessServiceData(const uint8_t* data,
uint8_t uuid_len,
- HALAdvertiseData* out_data){
+ HALAdvertiseData* out_data) {
size_t field_len = data[0];
// Minimum packet size should be equal to the uuid length + 1 to include
@@ -142,7 +143,7 @@
// through uuid field
VLOG(1) << "More than one UUID entry not allowed";
return false;
- } // else do nothing as UUID is already properly assigned
+ } // else do nothing as UUID is already properly assigned
// Use + uuid_len + 2 here in order to skip over a
// uuid contained in the beggining of the field
@@ -334,6 +335,49 @@
StopScan();
}
+bool LowEnergyClient::Connect(std::string address, bool is_direct) {
+ VLOG(2) << __func__ << "Address: " << address << " is_direct: " << is_direct;
+
+ bt_bdaddr_t bda;
+ util::BdAddrFromString(address, &bda);
+
+ bt_status_t status = hal::BluetoothGattInterface::Get()->
+ GetClientHALInterface()->connect(client_id_, &bda, is_direct,
+ BT_TRANSPORT_LE);
+ if (status != BT_STATUS_SUCCESS) {
+ LOG(ERROR) << "HAL call to connect failed";
+ return false;
+ }
+
+ return true;
+}
+
+bool LowEnergyClient::Disconnect(std::string address) {
+ VLOG(2) << __func__ << "Address: " << address;
+
+ bt_bdaddr_t bda;
+ util::BdAddrFromString(address, &bda);
+
+ std::map<const bt_bdaddr_t, int>::iterator conn_id;
+ {
+ lock_guard<mutex> lock(connection_fields_lock_);
+ conn_id = connection_ids_.find(bda);
+ if (conn_id == connection_ids_.end()) {
+ LOG(WARNING) << "Can't disconnect, no existing connection to " << address;
+ return false;
+ }
+ }
+
+ bt_status_t status = hal::BluetoothGattInterface::Get()->
+ GetClientHALInterface()->disconnect(client_id_, &bda, conn_id->second);
+ if (status != BT_STATUS_SUCCESS) {
+ LOG(ERROR) << "HAL call to disconnect failed";
+ return false;
+ }
+
+ return true;
+}
+
void LowEnergyClient::SetDelegate(Delegate* delegate) {
lock_guard<mutex> lock(delegate_mutex_);
delegate_ = delegate;
@@ -513,6 +557,46 @@
delegate_->OnScanResult(this, result);
}
+void LowEnergyClient::ConnectCallback(
+ hal::BluetoothGattInterface* gatt_iface, int conn_id, int status,
+ int client_id, const bt_bdaddr_t& bda) {
+ if (client_id != client_id_)
+ return;
+
+ VLOG(1) << __func__ << "client_id: " << client_id << " status: " << status;
+
+ {
+ lock_guard<mutex> lock(connection_fields_lock_);
+ auto success = connection_ids_.emplace(bda, conn_id);
+ if (!success.second) {
+ LOG(ERROR) << __func__ << " Insertion into connection_ids_ failed!";
+ }
+ }
+
+ if (delegate_)
+ delegate_->OnConnectionState(this, status, BtAddrString(&bda).c_str(),
+ true);
+}
+
+void LowEnergyClient::DisconnectCallback(
+ hal::BluetoothGattInterface* gatt_iface, int conn_id, int status,
+ int client_id, const bt_bdaddr_t& bda) {
+ if (client_id != client_id_)
+ return;
+
+ VLOG(1) << __func__ << " client_id: " << client_id << " status: " << status;
+ {
+ lock_guard<mutex> lock(connection_fields_lock_);
+ if (!connection_ids_.erase(bda)) {
+ LOG(ERROR) << __func__ << " Erasing from connection_ids_ failed!";
+ }
+ }
+
+ if (delegate_)
+ delegate_->OnConnectionState(this, status, BtAddrString(&bda).c_str(),
+ false);
+}
+
void LowEnergyClient::MultiAdvEnableCallback(
hal::BluetoothGattInterface* gatt_iface,
int client_id, int status) {
diff --git a/service/low_energy_client.h b/service/low_energy_client.h
index c1e787d..700ad9c 100644
--- a/service/low_energy_client.h
+++ b/service/low_energy_client.h
@@ -35,6 +35,12 @@
namespace bluetooth {
+struct ConnComparator {
+ bool operator()(const bt_bdaddr_t& a, const bt_bdaddr_t& b) const {
+ return memcmp(&a, &b, sizeof(bt_bdaddr_t)) < 0;
+ }
+};
+
class Adapter;
// A LowEnergyClient represents an application's handle to perform various
@@ -55,6 +61,10 @@
virtual void OnScanResult(LowEnergyClient* client,
const ScanResult& scan_result) = 0;
+ // Called asynchronously to notify the delegate of connection state change
+ virtual void OnConnectionState(LowEnergyClient* client, int status,
+ const char* address, bool connected) = 0;
+
private:
DISALLOW_COPY_AND_ASSIGN(Delegate);
};
@@ -70,6 +80,15 @@
// Callback type used to return the result of asynchronous operations below.
using StatusCallback = std::function<void(BLEStatus)>;
+ // Initiates a BLE connection do device with address |address|. If
+ // |is_direct| is set, use direct connect procedure. Return true on success
+ //, false otherwise.
+ bool Connect(std::string address, bool is_direct);
+
+ // Disconnect from previously connected BLE device with address |address|.
+ // Return true on success, false otherwise.
+ bool Disconnect(std::string address);
+
// Initiates a BLE device scan for this client using the given |settings| and
// |filters|. See the documentation for ScanSettings and ScanFilter for how
// these parameters can be configured. Return true on success, false
@@ -102,7 +121,7 @@
// Returns the current advertising settings.
const AdvertiseSettings& advertise_settings() const {
- return advertise_settings_;
+ return advertise_settings_;
}
// Returns the current scan settings.
@@ -123,6 +142,13 @@
void ScanResultCallback(
hal::BluetoothGattInterface* gatt_iface,
const bt_bdaddr_t& bda, int rssi, uint8_t* adv_data) override;
+
+ void ConnectCallback(
+ hal::BluetoothGattInterface* gatt_iface, int conn_id, int status,
+ int client_id, const bt_bdaddr_t& bda) override;
+ void DisconnectCallback(
+ hal::BluetoothGattInterface* gatt_iface, int conn_id, int status,
+ int client_id, const bt_bdaddr_t& bda) override;
void MultiAdvEnableCallback(
hal::BluetoothGattInterface* gatt_iface,
int client_id, int status) override;
@@ -190,6 +216,12 @@
std::mutex delegate_mutex_;
Delegate* delegate_;
+ // Protects device connection related members below.
+ std::mutex connection_fields_lock_;
+
+ // Maps bluetooth address to connection id
+ std::map<const bt_bdaddr_t, int, ConnComparator> connection_ids_;
+
DISALLOW_COPY_AND_ASSIGN(LowEnergyClient);
};
diff --git a/service/test/low_energy_client_unittest.cpp b/service/test/low_energy_client_unittest.cpp
index 6f99d3f..70bbd2a 100644
--- a/service/test/low_energy_client_unittest.cpp
+++ b/service/test/low_energy_client_unittest.cpp
@@ -27,6 +27,9 @@
using ::testing::_;
using ::testing::Return;
+using ::testing::Pointee;
+using ::testing::DoAll;
+using ::testing::Invoke;
namespace bluetooth {
namespace {
@@ -74,7 +77,7 @@
class TestDelegate : public LowEnergyClient::Delegate {
public:
- TestDelegate() : scan_result_count_(0) {
+ TestDelegate() : scan_result_count_(0), connection_state_count_(0) {
}
~TestDelegate() override = default;
@@ -82,6 +85,14 @@
int scan_result_count() const { return scan_result_count_; }
const ScanResult& last_scan_result() const { return last_scan_result_; }
+ int connection_state_count() const { return connection_state_count_; }
+
+ void OnConnectionState(LowEnergyClient* client, int status,
+ const char* address, bool connected) {
+ ASSERT_TRUE(client);
+ connection_state_count_++;
+ }
+
void OnScanResult(LowEnergyClient* client, const ScanResult& scan_result) {
ASSERT_TRUE(client);
scan_result_count_++;
@@ -92,6 +103,8 @@
int scan_result_count_;
ScanResult last_scan_result_;
+ int connection_state_count_;
+
DISALLOW_COPY_AND_ASSIGN(TestDelegate);
};
@@ -248,7 +261,7 @@
ASSERT_FALSE(le_client_->IsStoppingAdvertising());
}
- void AdvertiseDataTestHelper(AdvertiseData data, std::function<void(BLEStatus)> callback){
+ void AdvertiseDataTestHelper(AdvertiseData data, std::function<void(BLEStatus)> callback) {
AdvertiseSettings settings;
EXPECT_TRUE(le_client_->StartAdvertising(
settings, data, AdvertiseData(), callback));
@@ -835,7 +848,7 @@
EXPECT_EQ(service_data, adv_handler->service_data());
EXPECT_EQ(uuid_32bit_canonical, adv_handler->uuid_data());
- //Service data and UUID where the UUID for dont match, should fail
+ // Service data and UUID where the UUID for dont match, should fail
EXPECT_TRUE(le_client_->StartAdvertising(
settings, service_uuid_mismatch, AdvertiseData(), callback));
fake_hal_gatt_iface_->NotifyMultiAdvEnableCallback(
@@ -856,7 +869,7 @@
// Adapter is not enabled.
EXPECT_FALSE(le_client_->StartScan(settings, filters));
- //TODO(jpawlowski): add tests checking settings and filter parsing when
+ // TODO(jpawlowski): add tests checking settings and filter parsing when
// implemented
// These should succeed and result in a HAL call
@@ -937,5 +950,60 @@
le_client_->SetDelegate(nullptr);
}
+MATCHER_P(BitEq, x, std::string(negation ? "isn't" : "is") +
+ " bitwise equal to " + ::testing::PrintToString(x)) {
+ static_assert(sizeof(x) == sizeof(arg), "Size mismatch");
+ return std::memcmp(&arg, &x, sizeof(x)) == 0;
+}
+
+TEST_F(LowEnergyClientPostRegisterTest, Connect) {
+ const bt_bdaddr_t kTestAddress = {
+ { 0x01, 0x02, 0x03, 0x0A, 0x0B, 0x0C }
+ };
+ const char kTestAddressStr[] = "01:02:03:0A:0B:0C";
+ const bool kTestDirect = false;
+ const int connId = 12;
+
+ TestDelegate delegate;
+ le_client_->SetDelegate(&delegate);
+
+ // TODO(jpawlowski): NotifyConnectCallback should be called after returning
+ // success, fix it when it becomes important.
+ // These should succeed and result in a HAL call
+ EXPECT_CALL(*mock_handler_, Connect(le_client_->GetInstanceId(),
+ Pointee(BitEq(kTestAddress)), kTestDirect, BT_TRANSPORT_LE))
+ .Times(1)
+ .WillOnce(DoAll(
+ Invoke([&](int client_id, const bt_bdaddr_t *bd_addr, bool is_direct,
+ int transport){
+ fake_hal_gatt_iface_->NotifyConnectCallback(connId, BT_STATUS_SUCCESS,
+ client_id, *bd_addr);
+ }),
+ Return(BT_STATUS_SUCCESS)));
+
+ EXPECT_TRUE(le_client_->Connect(kTestAddressStr, kTestDirect));
+ EXPECT_EQ(1, delegate.connection_state_count());
+
+ // TODO(jpawlowski): same as above
+ // These should succeed and result in a HAL call
+ EXPECT_CALL(*mock_handler_, Disconnect(le_client_->GetInstanceId(),
+ Pointee(BitEq(kTestAddress)), connId))
+ .Times(1)
+ .WillOnce(DoAll(
+ Invoke([&](int client_id, const bt_bdaddr_t *bd_addr, int connId){
+ fake_hal_gatt_iface_->NotifyDisconnectCallback(connId,
+ BT_STATUS_SUCCESS,
+ client_id, *bd_addr);
+ }),
+ Return(BT_STATUS_SUCCESS)));
+
+ EXPECT_TRUE(le_client_->Disconnect(kTestAddressStr));
+ EXPECT_EQ(2, delegate.connection_state_count());
+
+ le_client_->SetDelegate(nullptr);
+ ::testing::Mock::VerifyAndClearExpectations(mock_handler_.get());
+}
+
+
} // namespace
} // namespace bluetooth