[BLE Refactor] Implements GattClient for sender's device to fetch Gatt advertisement.

PiperOrigin-RevId: 448400837
This commit is contained in:
edwinwu
2022-05-12 20:29:30 -07:00
committed by Copybara-Service
parent 73c0ce9c76
commit 080e964a4c
14 changed files with 843 additions and 291 deletions
+79 -16
View File
@@ -77,6 +77,8 @@ bool BleV2::IsAvailable() const {
}
// TODO(edwinwu): Break down the function.
// TODO(b/229927044): Use bool: is_fast_advertisement, not
// fast_advertisement_service_uuid.
bool BleV2::StartAdvertising(
const std::string& service_id, const ByteArray& advertisement_bytes,
PowerLevel power_level,
@@ -273,6 +275,7 @@ bool BleV2::IsAdvertising(const std::string& service_id) const {
return IsAdvertisingLocked(service_id);
}
// TODO(b/229927044): Remove param: fast_advertisement_service_uuid.
bool BleV2::StartScanning(const std::string& service_id, PowerLevel power_level,
DiscoveredPeripheralCallback callback,
const std::string& fast_advertisement_service_uuid) {
@@ -314,13 +317,27 @@ bool BleV2::StartScanning(const std::string& service_id, PowerLevel power_level,
return true;
}
// Check if scan has been activated, if yes, no need to notify client
// to scan again.
if (!scanned_service_ids_.empty()) {
scanned_service_ids_.insert(service_id);
NEARBY_LOGS(INFO) << "Turned on BLE scanning with service id=" << service_id
<< " without start client scanning";
return true;
}
scanned_service_ids_.insert(service_id);
// TODO(b/213835576): We should re-start scanning once the power level is
// changed.
std::vector<std::string> service_uuids{
std::string(mediums::bleutils::kCopresenceServiceUuid)};
std::vector<std::string> scanning_service_uuids;
if (!fast_advertisement_service_uuid.empty()) {
scanning_service_uuids.push_back(fast_advertisement_service_uuid);
} else {
scanning_service_uuids.push_back(
std::string(mediums::bleutils::kCopresenceServiceUuid));
}
if (!medium_.StartScanning(
service_uuids, PowerLevelToPowerMode(power_level),
scanning_service_uuids, PowerLevelToPowerMode(power_level),
{
.advertisement_found_cb =
[this](BleV2Peripheral peripheral,
@@ -428,18 +445,8 @@ bool BleV2::StartAdvertisementGattServerLocked(
return false;
}
std::unique_ptr<GattServer> gatt_server = medium_.StartGattServer({
.characteristic_subscription_cb =
[](const ServerGattConnection& connection,
const GattCharacteristic& characteristic) {
// TODO(b/213835576): Impl or remove.
},
.characteristic_unsubscription_cb =
[](const ServerGattConnection& connection,
const GattCharacteristic& characteristic) {
// TODO(b/213835576): Impl or remove.
},
});
std::unique_ptr<GattServer> gatt_server =
medium_.StartGattServer(/*ServerGattConnectionCallback=*/{});
if (!gatt_server || !gatt_server->IsValid()) {
NEARBY_LOGS(INFO) << "Unable to start an advertisement GATT server.";
return false;
@@ -508,7 +515,63 @@ void BleV2::ProcessFetchGattAdvertisementsRequest(
return;
}
// TODO(edwinwu): Attempt to connect and read some GATT characteristics.
// Connect to a GATT server, reads advertisement data, and then disconnect
// from the GATT server.
bool read_success = true;
std::unique_ptr<GattClient> gatt_client = medium_.ConnectToGattServer(
std::move(peripheral), PowerLevelToPowerMode(PowerLevel::kHighPower),
/*ClientGattConnectionCallback=*/{});
if (!gatt_client || !gatt_client->IsValid()) {
advertisement_read_result.RecordLastReadStatus(false);
return;
}
// Always use kCopresenceServiceUuid for service uuid.
std::string service_uuid =
std::string(mediums::bleutils::kCopresenceServiceUuid);
if (!gatt_client->DiscoverService(service_uuid)) {
NEARBY_LOGS(WARNING) << "GATT client can't discover service.";
advertisement_read_result.RecordLastReadStatus(false);
return;
}
// Read all advertisements from all slots that we haven't read from yet.
for (int slot = 0; slot < num_slots; ++slot) {
// Make sure we haven't already read this advertisement before.
if (advertisement_read_result.HasAdvertisement(slot)) {
continue;
}
// Make sure the characteristic even exists for this slot number. If
// the characteristic doesn't exist, we shouldn't count the fetch as a
// failure because there's nothing we could've done about a
// non-existed characteristic.
auto gatt_characteristic = gatt_client->GetCharacteristic(
std::string(mediums::bleutils::kCopresenceServiceUuid),
mediums::bleutils::GenerateAdvertisementUuid(slot));
if (!gatt_characteristic.has_value()) {
continue;
}
// Read advertisement data from the characteristic associated with this
// slot.
auto characteristic_byte =
gatt_client->ReadCharacteristic(gatt_characteristic.value());
if (characteristic_byte.has_value()) {
advertisement_read_result.AddAdvertisement(slot, *characteristic_byte);
NEARBY_LOGS(VERBOSE) << "Successfully read advertisement at slot="
<< slot;
} else {
NEARBY_LOGS(WARNING) << "Can't read advertisement for slot=" << slot;
read_success = false;
}
// Whether or not we succeeded with this slot, we should try reading the
// other slots to get as many advertisements as possible before
// returning a success or failure.
}
gatt_client->Disconnect();
advertisement_read_result.RecordLastReadStatus(read_success);
}
bool BleV2::StopAdvertisementGattServerLocked() {
@@ -42,8 +42,6 @@ namespace connections {
// (BLE) medium.
class BleV2 final {
public:
using ServerGattConnectionCallback =
BleV2Medium::ServerGattConnectionCallback;
using DiscoveredPeripheralCallback = mediums::DiscoveredPeripheralCallback;
static constexpr absl::Duration kPeripheralLostTimeout = absl::Seconds(3);
+191 -77
View File
@@ -28,11 +28,11 @@ namespace connections {
namespace {
constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000);
constexpr absl::string_view kServiceIDA{
"com.google.location.nearby.apps.test.a"};
constexpr absl::string_view kServiceIDB{
"com.google.location.nearby.apps.test.b"};
constexpr absl::string_view kAdvertisementString{"\x0a\x0b\x0c\x0d"};
constexpr absl::string_view kServiceIDA =
"com.google.location.nearby.apps.test.a";
constexpr absl::string_view kServiceIDB =
"com.google.location.nearby.apps.test.b";
constexpr absl::string_view kAdvertisementString = "\x0a\x0b\x0c\x0d";
constexpr absl::string_view kFastAdvertisementServiceUuid =
"0000FE2C-0000-1000-8000-00805F9B34FB";
@@ -47,8 +47,8 @@ TEST_F(BleV2Test, CanConstructValidObject) {
env_.Start();
BluetoothRadio radio_a;
BluetoothRadio radio_b;
BleV2 ble_a{radio_a};
BleV2 ble_b{radio_b};
BleV2 ble_a(radio_a);
BleV2 ble_b(radio_b);
EXPECT_TRUE(ble_a.IsMediumValid());
EXPECT_TRUE(ble_a.IsAvailable());
@@ -58,58 +58,15 @@ TEST_F(BleV2Test, CanConstructValidObject) {
env_.Stop();
}
TEST_F(BleV2Test, CanStartAdvertising) {
env_.Start();
BluetoothRadio radio;
BleV2 ble{radio};
radio.Enable();
ByteArray advertisement_bytes{std::string(kAdvertisementString)};
EXPECT_TRUE(ble.StartAdvertising(std::string(kServiceIDA),
advertisement_bytes, PowerLevel::kHighPower,
/*fast_advertisement_service_uuid=*/""));
// Can't advertise twice for the same service_id.
EXPECT_FALSE(ble.StartAdvertising(std::string(kServiceIDA),
advertisement_bytes, PowerLevel::kHighPower,
/*fast_advertisement_service_uuid=*/""));
EXPECT_TRUE(ble.StopAdvertising(std::string(kServiceIDA)));
env_.Stop();
}
TEST_F(BleV2Test, CanStartScanning) {
env_.Start();
BluetoothRadio radio;
BleV2 ble{radio};
radio.Enable();
EXPECT_TRUE(ble.StartScanning(
std::string(kServiceIDA), PowerLevel::kHighPower,
mediums::DiscoveredPeripheralCallback{
.peripheral_discovered_cb =
[](BleV2Peripheral peripheral, const std::string& service_id,
const ByteArray& advertisement_bytes,
bool fast_advertisement) {
// nothing to do for now
},
.peripheral_lost_cb =
[](BleV2Peripheral peripheral, const std::string& service_id) {
// nothing to do for now
},
},
/*fast_advertisement_service_uuid=*/""));
EXPECT_TRUE(ble.StopScanning(std::string(kServiceIDA)));
env_.Stop();
}
TEST_F(BleV2Test, CanStartFastAdvertising) {
env_.Start();
BluetoothRadio radio_a;
BluetoothRadio radio_b;
BleV2 ble_a{radio_a};
BleV2 ble_b{radio_b};
BleV2 ble_a(radio_a);
BleV2 ble_b(radio_b);
radio_a.Enable();
radio_b.Enable();
ByteArray advertisement_bytes{std::string(kAdvertisementString)};
ByteArray advertisement_bytes((std::string(kAdvertisementString)));
CountDownLatch found_latch(1);
ble_b.StartScanning(
@@ -123,10 +80,6 @@ TEST_F(BleV2Test, CanStartFastAdvertising) {
EXPECT_TRUE(fast_advertisement);
found_latch.CountDown();
},
.peripheral_lost_cb =
[](BleV2Peripheral peripheral, const std::string& service_id) {
// nothing to do for now
},
},
std::string(kFastAdvertisementServiceUuid));
@@ -143,11 +96,11 @@ TEST_F(BleV2Test, CanStartFastScanning) {
env_.Start();
BluetoothRadio radio_a;
BluetoothRadio radio_b;
BleV2 ble_a{radio_a};
BleV2 ble_b{radio_b};
BleV2 ble_a(radio_a);
BleV2 ble_b(radio_b);
radio_a.Enable();
radio_b.Enable();
ByteArray advertisement_bytes{std::string(kAdvertisementString)};
ByteArray advertisement_bytes((std::string(kAdvertisementString)));
CountDownLatch found_latch(1);
ble_b.StartAdvertising(std::string(kServiceIDA), advertisement_bytes,
@@ -165,10 +118,6 @@ TEST_F(BleV2Test, CanStartFastScanning) {
EXPECT_TRUE(fast_advertisement);
found_latch.CountDown();
},
.peripheral_lost_cb =
[](BleV2Peripheral peripheral, const std::string& service_id) {
// nothing to do for now
},
},
std::string(kFastAdvertisementServiceUuid)));
@@ -178,10 +127,79 @@ TEST_F(BleV2Test, CanStartFastScanning) {
env_.Stop();
}
TEST_F(BleV2Test, CanStartAdvertising) {
env_.Start();
BluetoothRadio radio_a;
BluetoothRadio radio_b;
BleV2 ble_a(radio_a);
BleV2 ble_b(radio_b);
radio_a.Enable();
radio_b.Enable();
ByteArray advertisement_bytes((std::string(kAdvertisementString)));
CountDownLatch found_latch(1);
ble_b.StartScanning(
std::string(kServiceIDA), PowerLevel::kHighPower,
mediums::DiscoveredPeripheralCallback{
.peripheral_discovered_cb =
[&found_latch](BleV2Peripheral peripheral,
const std::string& service_id,
const ByteArray& advertisement_bytes,
bool fast_advertisement) {
EXPECT_FALSE(fast_advertisement);
found_latch.CountDown();
},
},
/*fast_advertisement_service_uuid=*/"");
EXPECT_TRUE(ble_a.StartAdvertising(
std::string(kServiceIDA), advertisement_bytes, PowerLevel::kHighPower,
/*fast_advertisement_service_uuid=*/""));
EXPECT_TRUE(found_latch.Await(kWaitDuration).result());
EXPECT_TRUE(ble_a.StopAdvertising(std::string(kServiceIDA)));
ble_b.StopScanning(std::string(kServiceIDA));
env_.Stop();
}
TEST_F(BleV2Test, CanStartScanning) {
env_.Start();
BluetoothRadio radio_a;
BluetoothRadio radio_b;
BleV2 ble_a(radio_a);
BleV2 ble_b(radio_b);
radio_a.Enable();
radio_b.Enable();
ByteArray advertisement_bytes((std::string(kAdvertisementString)));
CountDownLatch found_latch(1);
ble_b.StartAdvertising(std::string(kServiceIDA), advertisement_bytes,
PowerLevel::kHighPower,
/*fast_advertisement_service_uuid=*/"");
EXPECT_TRUE(ble_a.StartScanning(
std::string(kServiceIDA), PowerLevel::kHighPower,
mediums::DiscoveredPeripheralCallback{
.peripheral_discovered_cb =
[&found_latch](BleV2Peripheral peripheral,
const std::string& service_id,
const ByteArray& advertisement_bytes,
bool fast_advertisement) {
EXPECT_FALSE(fast_advertisement);
found_latch.CountDown();
},
},
/*fast_advertisement_service_uuid=*/""));
EXPECT_TRUE(found_latch.Await(kWaitDuration).result());
ble_b.StopAdvertising(std::string(kServiceIDA));
EXPECT_TRUE(ble_a.StopScanning(std::string(kServiceIDA)));
env_.Stop();
}
TEST_F(BleV2Test, CanStartStopMultipleScanningWithDifferentServiceIds) {
env_.Start();
BluetoothRadio radio;
BleV2 ble{radio};
BleV2 ble(radio);
radio.Enable();
EXPECT_TRUE(ble.StartScanning(std::string(kServiceIDA),
@@ -207,12 +225,12 @@ TEST_F(BleV2Test, DestructWorksForStartAdvertisingAndScanningWithoutStop) {
env_.Start();
BluetoothRadio radio_a;
BluetoothRadio radio_b;
BleV2 ble_a{radio_a};
BleV2 ble_b{radio_b};
BleV2 ble_a(radio_a);
BleV2 ble_b(radio_b);
radio_a.Enable();
radio_b.Enable();
ByteArray advertisement_bytes{std::string(kAdvertisementString)};
ByteArray advertisement_bytes((std::string(kAdvertisementString)));
// Device A starts advertising with service IDA and IDB.
EXPECT_TRUE(ble_a.StartAdvertising(
@@ -234,15 +252,15 @@ TEST_F(BleV2Test, DestructWorksForStartAdvertisingAndScanningWithoutStop) {
env_.Stop();
}
TEST_F(BleV2Test, StartScanningDiscoverAndLostPeripheral) {
TEST_F(BleV2Test, StartFastScanningDiscoverAndLostPeripheral) {
env_.Start();
BluetoothRadio radio_a;
BluetoothRadio radio_b;
BleV2 ble_a{radio_a};
BleV2 ble_b{radio_b};
BleV2 ble_a(radio_a);
BleV2 ble_b(radio_b);
radio_a.Enable();
radio_b.Enable();
ByteArray advertisement_bytes{std::string(kAdvertisementString)};
ByteArray advertisement_bytes((std::string(kAdvertisementString)));
CountDownLatch found_latch(1);
CountDownLatch lost_latch(1);
@@ -273,7 +291,7 @@ TEST_F(BleV2Test, StartScanningDiscoverAndLostPeripheral) {
ble_b.StopAdvertising(std::string(kServiceIDA));
// Wait for a while (2 times delay) to let the alaram occur twice and
// Wait for a while (2 times delay) to let the alarm occur twice and
// `ProcessLostGattAdvertisements` twice to lost periperal.
SystemClock::Sleep(BleV2::kPeripheralLostTimeout * 2);
@@ -283,15 +301,16 @@ TEST_F(BleV2Test, StartScanningDiscoverAndLostPeripheral) {
env_.Stop();
}
TEST_F(BleV2Test, StartScanningDiscoverButNoPeripheralLostAfterStopScanning) {
TEST_F(BleV2Test,
StartFastScanningDiscoverButNoPeripheralLostAfterStopScanning) {
env_.Start();
BluetoothRadio radio_a;
BluetoothRadio radio_b;
BleV2 ble_a{radio_a};
BleV2 ble_b{radio_b};
BleV2 ble_a(radio_a);
BleV2 ble_b(radio_b);
radio_a.Enable();
radio_b.Enable();
ByteArray advertisement_bytes{std::string(kAdvertisementString)};
ByteArray advertisement_bytes((std::string(kAdvertisementString)));
CountDownLatch found_latch(1);
CountDownLatch lost_latch(1);
@@ -330,6 +349,101 @@ TEST_F(BleV2Test, StartScanningDiscoverButNoPeripheralLostAfterStopScanning) {
env_.Stop();
}
TEST_F(BleV2Test, StartScanningDiscoverAndLostPeripheral) {
env_.Start();
BluetoothRadio radio_a;
BluetoothRadio radio_b;
BleV2 ble_a(radio_a);
BleV2 ble_b(radio_b);
radio_a.Enable();
radio_b.Enable();
ByteArray advertisement_bytes((std::string(kAdvertisementString)));
CountDownLatch found_latch(1);
CountDownLatch lost_latch(1);
ble_b.StartAdvertising(std::string(kServiceIDA), advertisement_bytes,
PowerLevel::kHighPower,
/*fast_advertisement_service_uuid=*/"");
ble_a.StartScanning(
std::string(kServiceIDA), PowerLevel::kHighPower,
mediums::DiscoveredPeripheralCallback{
.peripheral_discovered_cb =
[&found_latch](BleV2Peripheral peripheral,
const std::string& service_id,
const ByteArray& advertisement_bytes,
bool fast_advertisement) {
EXPECT_FALSE(fast_advertisement);
found_latch.CountDown();
},
.peripheral_lost_cb =
[&lost_latch](BleV2Peripheral peripheral,
const std::string& service_id) {
lost_latch.CountDown();
},
},
/*fast_advertisement_service_uuid=*/"");
EXPECT_TRUE(found_latch.Await(kWaitDuration).result());
ble_b.StopAdvertising(std::string(kServiceIDA));
// Wait for a while (2 times delay) to let the alarm occur twice and
// `ProcessLostGattAdvertisements` twice to lost periperal.
SystemClock::Sleep(BleV2::kPeripheralLostTimeout * 2);
EXPECT_TRUE(lost_latch.Await(kWaitDuration).result());
ble_a.StopScanning(std::string(kServiceIDA));
env_.Stop();
}
TEST_F(BleV2Test, StartScanningDiscoverButNoPeripheralLostAfterStopScanning) {
env_.Start();
BluetoothRadio radio_a;
BluetoothRadio radio_b;
BleV2 ble_a(radio_a);
BleV2 ble_b(radio_b);
radio_a.Enable();
radio_b.Enable();
ByteArray advertisement_bytes((std::string(kAdvertisementString)));
CountDownLatch found_latch(1);
CountDownLatch lost_latch(1);
ble_b.StartAdvertising(std::string(kServiceIDA), advertisement_bytes,
PowerLevel::kHighPower, "");
ble_a.StartScanning(
std::string(kServiceIDA), PowerLevel::kHighPower,
mediums::DiscoveredPeripheralCallback{
.peripheral_discovered_cb =
[&found_latch](BleV2Peripheral peripheral,
const std::string& service_id,
const ByteArray& advertisement_bytes,
bool fast_advertisement) {
EXPECT_FALSE(fast_advertisement);
found_latch.CountDown();
},
.peripheral_lost_cb =
[&lost_latch](BleV2Peripheral peripheral,
const std::string& service_id) {
lost_latch.CountDown();
},
},
/*fast_advertisement_service_uuid=*/"");
EXPECT_TRUE(found_latch.Await(kWaitDuration).result());
ble_b.StopAdvertising(std::string(kServiceIDA));
ble_a.StopScanning(std::string(kServiceIDA));
// Don't receive lost peripheral callback because we have stopped scanning and
// cancelled the alarm.
EXPECT_FALSE(lost_latch.Await(kWaitDuration).result());
env_.Stop();
}
} // namespace
} // namespace connections
} // namespace nearby
+1
View File
@@ -340,6 +340,7 @@ cc_library(
"//internal/platform/implementation:platform",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:optional",
],
)
+26 -9
View File
@@ -15,6 +15,7 @@
#include "internal/platform/ble_v2.h"
#include <memory>
#include <string>
#include <utility>
#include "internal/platform/bluetooth_adapter.h"
@@ -102,25 +103,41 @@ std::unique_ptr<GattServer> BleV2Medium::StartGattServer(
std::unique_ptr<api::ble_v2::GattServer> api_gatt_server =
impl_->StartGattServer({
.characteristic_subscription_cb =
[this](api::ble_v2::ServerGattConnection& connection,
const GattCharacteristic& characteristic) {
[this](const GattCharacteristic& characteristic) {
MutexLock lock(&mutex_);
ServerGattConnection server_gatt_connection(&connection);
server_gatt_connection_callback_.characteristic_subscription_cb(
server_gatt_connection, characteristic);
characteristic);
},
.characteristic_unsubscription_cb =
[this](api::ble_v2::ServerGattConnection& connection,
const GattCharacteristic& characteristic) {
[this](const GattCharacteristic& characteristic) {
MutexLock lock(&mutex_);
ServerGattConnection server_gatt_connection(&connection);
server_gatt_connection_callback_
.characteristic_unsubscription_cb(server_gatt_connection,
characteristic);
.characteristic_unsubscription_cb(characteristic);
},
});
return std::make_unique<GattServer>(std::move(api_gatt_server));
}
std::unique_ptr<GattClient> BleV2Medium::ConnectToGattServer(
BleV2Peripheral peripheral, PowerMode power_mode,
ClientGattConnectionCallback callback) {
{
MutexLock lock(&mutex_);
client_gatt_connection_callback_ = std::move(callback);
}
std::unique_ptr<api::ble_v2::GattClient> api_gatt_client =
impl_->ConnectToGattServer(
peripheral.GetImpl(), power_mode,
{
.disconnected_cb =
[this]() {
MutexLock lock(&mutex_);
client_gatt_connection_callback_.disconnected_cb();
},
});
return std::make_unique<GattClient>(std::move(api_gatt_client));
}
} // namespace nearby
} // namespace location
+66 -31
View File
@@ -16,9 +16,11 @@
#define PLATFORM_PUBLIC_BLE_V2_H_
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include "absl/types/optional.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/implementation/ble_v2.h"
@@ -27,38 +29,18 @@
namespace location {
namespace nearby {
// Opaque wrapper over a ServerGattConnection.
class ServerGattConnection final {
public:
ServerGattConnection() = default;
explicit ServerGattConnection(
api::ble_v2::ServerGattConnection* server_gatt_connection)
: impl_(server_gatt_connection) {}
bool SendCharacteristic(const api::ble_v2::GattCharacteristic& characteristic,
const ByteArray& value) {
return impl_->SendCharacteristic(characteristic, value);
}
api::ble_v2::ServerGattConnection* GetImpl() { return impl_; }
bool IsValid() const { return impl_ != nullptr; }
private:
api::ble_v2::ServerGattConnection* impl_ = nullptr;
};
// Opaque wrapper over a GattServer.
// Move only, disallow copy.
//
// Note that some of the methods return absl::optional instead
// of std::optional, because iOS platform is still in C++14.
class GattServer final {
public:
GattServer() = default;
explicit GattServer(std::unique_ptr<api::ble_v2::GattServer> gatt_server)
: impl_(std::move(gatt_server)) {}
GattServer(GattServer&&) = default;
GattServer& operator=(GattServer&&) = default;
~GattServer() { Stop(); }
// NOLINTNEXTLINE(google3-legacy-absl-backports)
absl::optional<api::ble_v2::GattCharacteristic> CreateCharacteristic(
const std::string& service_uuid, const std::string& characteristic_uuid,
const std::vector<api::ble_v2::GattCharacteristic::Permission>&
@@ -89,6 +71,47 @@ class GattServer final {
std::unique_ptr<api::ble_v2::GattServer> impl_;
};
// Opaque wrapper for a GattClient.
//
// Note that some of the methods return absl::optional instead
// of std::optional, because iOS platform is still in C++14.
class GattClient final {
public:
explicit GattClient(
std::unique_ptr<api::ble_v2::GattClient> client_gatt_connection)
: impl_(std::move(client_gatt_connection)) {}
bool DiscoverService(const std::string& service_uuid) {
return impl_->DiscoverService(service_uuid);
}
// TODO(edwinwu): Change std::string to Uuid.
// NOLINTNEXTLINE(google3-legacy-absl-backports)
absl::optional<api::ble_v2::GattCharacteristic> GetCharacteristic(
const std::string& service_uuid, const std::string& characteristic_uuid) {
return impl_->GetCharacteristic(service_uuid, characteristic_uuid);
}
// NOLINTNEXTLINE(google3-legacy-absl-backports)
absl::optional<ByteArray> ReadCharacteristic(
api::ble_v2::GattCharacteristic& characteristic) {
return impl_->ReadCharacteristic(characteristic);
}
void Disconnect() { impl_->Disconnect(); }
// Returns true if a client_gatt_connection is usable. If this method returns
// false, it is not safe to call any other method.
bool IsValid() const { return impl_ != nullptr; }
// Returns reference to platform implementation.
// This is used to communicate with platform code, and for debugging purposes.
api::ble_v2::GattClient* GetImpl() { return impl_.get(); }
private:
std::unique_ptr<api::ble_v2::GattClient> impl_;
};
// Container of operations that can be performed over the BLE medium.
class BleV2Medium final {
public:
@@ -107,16 +130,19 @@ class BleV2Medium final {
advertisement_found_cb = location::nearby::DefaultCallback<
BleV2Peripheral, const api::ble_v2::BleAdvertisementData&>();
};
struct ServerGattConnectionCallback {
std::function<void(ServerGattConnection& connection,
const api::ble_v2::GattCharacteristic& characteristic)>
std::function<void(const api::ble_v2::GattCharacteristic& characteristic)>
characteristic_subscription_cb = location::nearby::DefaultCallback<
ServerGattConnection&, const api::ble_v2::GattCharacteristic&>();
std::function<void(ServerGattConnection& connection,
const api::ble_v2::GattCharacteristic& characteristic)>
const api::ble_v2::GattCharacteristic&>();
std::function<void(const api::ble_v2::GattCharacteristic& characteristic)>
characteristic_unsubscription_cb = location::nearby::DefaultCallback<
ServerGattConnection&, const api::ble_v2::GattCharacteristic&>();
const api::ble_v2::GattCharacteristic&>();
};
// TODO(b/231318879): Remove this wrapper callback and use impl callback if
// there is only disconnect function here in the end.
struct ClientGattConnectionCallback {
std::function<void()> disconnected_cb =
location::nearby::DefaultCallback<>();
};
explicit BleV2Medium(BluetoothAdapter& adapter)
@@ -136,9 +162,16 @@ class BleV2Medium final {
api::ble_v2::PowerMode power_mode, ScanCallback callback);
bool StopScanning();
// Starts Gatt Server for waiting to client connection.
std::unique_ptr<GattServer> StartGattServer(
ServerGattConnectionCallback callback);
// Returns a new GattClient connection to a gatt server.
// There is only one instance of GattServer can run at a time.
std::unique_ptr<GattClient> ConnectToGattServer(
BleV2Peripheral peripheral, api::ble_v2::PowerMode power_mode,
ClientGattConnectionCallback callback);
bool IsValid() const { return impl_ != nullptr; }
api::ble_v2::BleMedium* GetImpl() const { return impl_.get(); }
@@ -149,6 +182,8 @@ class BleV2Medium final {
BluetoothAdapter& adapter_;
ServerGattConnectionCallback server_gatt_connection_callback_
ABSL_GUARDED_BY(mutex_);
ClientGattConnectionCallback client_gatt_connection_callback_
ABSL_GUARDED_BY(mutex_);
absl::flat_hash_set<api::ble_v2::BlePeripheral*> peripherals_
ABSL_GUARDED_BY(mutex_);
ScanCallback scan_callback_ ABSL_GUARDED_BY(mutex_);
+205 -59
View File
@@ -17,7 +17,11 @@
#include <memory>
#include <string>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/medium_environment.h"
namespace location {
@@ -27,12 +31,27 @@ namespace {
using ::location::nearby::api::ble_v2::BleAdvertisementData;
using ::location::nearby::api::ble_v2::GattCharacteristic;
using ::location::nearby::api::ble_v2::PowerMode;
using ::testing::Optional;
constexpr absl::string_view kAdvertisementString{"\x0a\x0b\x0c\x0d"};
constexpr absl::string_view kCopresenceServiceUuid{"F3FE"};
constexpr absl::string_view kFastAdvertisementServiceUuid{"FAST"};
constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000);
constexpr absl::string_view kAdvertisementString = "\x0a\x0b\x0c\x0d";
constexpr absl::string_view kCopresenceServiceUuid = "F3FE";
constexpr absl::string_view kFastAdvertisementServiceUuid = "FE2C";
constexpr PowerMode kPowerMode(PowerMode::kHigh);
// A stub BlePeripheral implementation.
class BlePeripheralStub : public api::ble_v2::BlePeripheral {
public:
explicit BlePeripheralStub(absl::string_view mac_address) {
mac_address_ = mac_address;
}
std::string GetAddress() const override { return mac_address_; }
private:
std::string mac_address_;
};
class BleV2MediumTest : public testing::Test {
protected:
BleV2MediumTest() { env_.Stop(); }
@@ -42,10 +61,10 @@ class BleV2MediumTest : public testing::Test {
TEST_F(BleV2MediumTest, ConstructorDestructorWorks) {
env_.Start();
BluetoothAdapter adapter_a_;
BluetoothAdapter adapter_b_;
BleV2Medium ble_a{adapter_a_};
BleV2Medium ble_b{adapter_b_};
BluetoothAdapter adapter_a;
BluetoothAdapter adapter_b;
BleV2Medium ble_a(adapter_a);
BleV2Medium ble_b(adapter_b);
// Make sure we can create functional mediums.
ASSERT_TRUE(ble_a.IsValid());
@@ -56,75 +75,162 @@ TEST_F(BleV2MediumTest, ConstructorDestructorWorks) {
env_.Stop();
}
TEST_F(BleV2MediumTest, CanStartFastAdvertising) {
TEST_F(BleV2MediumTest, CanStartFastScanningAndFastAdvertising) {
env_.Start();
BluetoothAdapter adapter_;
BleV2Medium ble{adapter_};
ByteArray advertisement_bytes{std::string(kAdvertisementString)};
std::string fast_advertisement_service_uuid(kFastAdvertisementServiceUuid);
BluetoothAdapter adapter_a;
BluetoothAdapter adapter_b;
BleV2Medium ble_a(adapter_a);
BleV2Medium ble_b(adapter_b);
CountDownLatch found_latch(1);
BleAdvertisementData advertising_data;
advertising_data.is_connectable = true;
advertising_data.tx_power_level =
BleAdvertisementData::kUnspecifiedTxPowerLevel;
advertising_data.service_uuids.insert(fast_advertisement_service_uuid);
EXPECT_TRUE(ble_a.StartScanning(
{std::string(kFastAdvertisementServiceUuid)}, kPowerMode,
{
.advertisement_found_cb =
[&found_latch](BleV2Peripheral peripheral,
const BleAdvertisementData& advertisement_data) {
found_latch.CountDown();
},
}));
// Assemble fast advertising and scan response data.
BleAdvertisementData advertising_data;
advertising_data.service_uuids.insert(
std::string(kFastAdvertisementServiceUuid));
BleAdvertisementData scan_response_data;
scan_response_data.is_connectable = true;
scan_response_data.tx_power_level =
BleAdvertisementData::kUnspecifiedTxPowerLevel;
scan_response_data.service_data.insert(
{fast_advertisement_service_uuid, advertisement_bytes});
{std::string(kFastAdvertisementServiceUuid),
ByteArray(std::string(kAdvertisementString))});
EXPECT_TRUE(
ble.StartAdvertising(advertising_data, scan_response_data, kPowerMode));
EXPECT_TRUE(ble.StopAdvertising());
ble_b.StartAdvertising(advertising_data, scan_response_data, kPowerMode));
EXPECT_TRUE(found_latch.Await(kWaitDuration).result());
EXPECT_TRUE(ble_a.StopScanning());
EXPECT_TRUE(ble_b.StopAdvertising());
env_.Stop();
}
TEST_F(BleV2MediumTest, CanStartAdvertising) {
TEST_F(BleV2MediumTest, CanStartScanningAndAdvertising) {
env_.Start();
BluetoothAdapter adapter_;
BleV2Medium ble{adapter_};
ByteArray advertisement_header_bytes{std::string(kAdvertisementString)};
std::string fast_advertisement_service_uuid(kFastAdvertisementServiceUuid);
BluetoothAdapter adapter_a;
BluetoothAdapter adapter_b;
BleV2Medium ble_a(adapter_a);
BleV2Medium ble_b(adapter_b);
CountDownLatch found_latch(1);
EXPECT_TRUE(ble_a.StartScanning(
{std::string(kCopresenceServiceUuid)}, kPowerMode,
{
.advertisement_found_cb =
[&found_latch](BleV2Peripheral peripheral,
const BleAdvertisementData& advertisement_data) {
found_latch.CountDown();
},
}));
// Assemble regular advertising and scan response data.
BleAdvertisementData advertising_data;
advertising_data.is_connectable = true;
advertising_data.tx_power_level =
BleAdvertisementData::kUnspecifiedTxPowerLevel;
BleAdvertisementData advertising_data = {};
BleAdvertisementData scan_response_data;
scan_response_data.is_connectable = true;
scan_response_data.tx_power_level =
BleAdvertisementData::kUnspecifiedTxPowerLevel;
scan_response_data.service_uuids.insert(fast_advertisement_service_uuid);
scan_response_data.service_uuids.insert(std::string(kCopresenceServiceUuid));
scan_response_data.service_data.insert(
{fast_advertisement_service_uuid, advertisement_header_bytes});
{std::string(kCopresenceServiceUuid),
ByteArray(std::string(kAdvertisementString))});
EXPECT_TRUE(
ble.StartAdvertising(advertising_data, scan_response_data, kPowerMode));
ble_b.StartAdvertising(advertising_data, scan_response_data, kPowerMode));
EXPECT_TRUE(found_latch.Await(kWaitDuration).result());
EXPECT_TRUE(ble_a.StopScanning());
EXPECT_TRUE(ble_b.StopAdvertising());
env_.Stop();
}
EXPECT_TRUE(ble.StopAdvertising());
TEST_F(BleV2MediumTest,
CanStartFastAdvertisingButRegularScanningFailToFoundAdvertisement) {
env_.Start();
BluetoothAdapter adapter_a;
BluetoothAdapter adapter_b;
BleV2Medium ble_a(adapter_a);
BleV2Medium ble_b(adapter_b);
CountDownLatch found_latch(1);
EXPECT_TRUE(ble_a.StartScanning(
{std::string(kCopresenceServiceUuid)}, kPowerMode,
{
.advertisement_found_cb =
[&found_latch](BleV2Peripheral peripheral,
const BleAdvertisementData& advertisement_data) {
found_latch.CountDown();
},
}));
// Assemble fast advertising and scan response data.
BleAdvertisementData advertising_data;
advertising_data.service_uuids.insert(
std::string(kFastAdvertisementServiceUuid));
BleAdvertisementData scan_response_data;
scan_response_data.service_data.insert(
{std::string(kFastAdvertisementServiceUuid),
ByteArray(std::string(kAdvertisementString))});
EXPECT_TRUE(
ble_b.StartAdvertising(advertising_data, scan_response_data, kPowerMode));
// Fail to found the advertiement.
EXPECT_FALSE(found_latch.Await(kWaitDuration).result());
EXPECT_TRUE(ble_a.StopScanning());
EXPECT_TRUE(ble_b.StopAdvertising());
env_.Stop();
}
TEST_F(BleV2MediumTest,
CanStartAdvertisingButFastScanningFailToFoundAdvertisement) {
env_.Start();
BluetoothAdapter adapter_a;
BluetoothAdapter adapter_b;
BleV2Medium ble_a(adapter_a);
BleV2Medium ble_b(adapter_b);
CountDownLatch found_latch(1);
EXPECT_TRUE(ble_a.StartScanning(
{std::string(kFastAdvertisementServiceUuid)}, kPowerMode,
{
.advertisement_found_cb =
[&found_latch](BleV2Peripheral peripheral,
const BleAdvertisementData& advertisement_data) {
found_latch.CountDown();
},
}));
// Assemble regular advertising and scan response data.
BleAdvertisementData advertising_data = {};
BleAdvertisementData scan_response_data;
scan_response_data.service_uuids.insert(std::string(kCopresenceServiceUuid));
scan_response_data.service_data.insert(
{std::string(kCopresenceServiceUuid),
ByteArray(std::string(kAdvertisementString))});
EXPECT_TRUE(
ble_b.StartAdvertising(advertising_data, scan_response_data, kPowerMode));
// Fail to found the advertiement.
EXPECT_FALSE(found_latch.Await(kWaitDuration).result());
EXPECT_TRUE(ble_a.StopScanning());
EXPECT_TRUE(ble_b.StopAdvertising());
env_.Stop();
}
TEST_F(BleV2MediumTest, CanStartGattServer) {
env_.Start();
BluetoothAdapter adapter_;
BleV2Medium ble{adapter_};
BluetoothAdapter adapter;
BleV2Medium ble{adapter};
std::string characteristic_uuid = "characteristic_uuid";
std::unique_ptr<GattServer> gatt_server = ble.StartGattServer({});
std::unique_ptr<GattServer> gatt_server =
ble.StartGattServer(/*ServerGattConnectionCallback=*/{});
ASSERT_NE(gatt_server, nullptr);
std::vector<GattCharacteristic::Permission> permissions{
std::vector<GattCharacteristic::Permission> permissions = {
GattCharacteristic::Permission::kRead};
std::vector<GattCharacteristic::Property> properties{
std::vector<GattCharacteristic::Property> properties = {
GattCharacteristic::Property::kRead};
absl::optional<GattCharacteristic> gatt_characteristic =
gatt_server->CreateCharacteristic(std::string(kCopresenceServiceUuid),
@@ -143,21 +249,61 @@ TEST_F(BleV2MediumTest, CanStartGattServer) {
env_.Stop();
}
TEST_F(BleV2MediumTest, CanStartScanning) {
TEST_F(BleV2MediumTest, GattClientConnectToGattServerWorks) {
env_.Start();
BluetoothAdapter adapter_;
BleV2Medium ble{adapter_};
BluetoothAdapter adapter_a;
BluetoothAdapter adapter_b;
BleV2Medium ble_a(adapter_a);
BleV2Medium ble_b(adapter_b);
std::string characteristic_uuid = "characteristic_uuid";
EXPECT_TRUE(ble.StartScanning(
{std::string(kCopresenceServiceUuid)}, kPowerMode,
{
.advertisement_found_cb =
[](BleV2Peripheral peripheral,
BleAdvertisementData advertisement_data) {
// nothing to do for now
},
}));
EXPECT_TRUE(ble.StopScanning());
// Start GattServer
std::unique_ptr<GattServer> gatt_server =
ble_a.StartGattServer(/*ServerGattConnectionCallback=*/{});
ASSERT_NE(gatt_server, nullptr);
std::vector<GattCharacteristic::Permission> permissions = {
GattCharacteristic::Permission::kRead};
std::vector<GattCharacteristic::Property> properties = {
GattCharacteristic::Property::kRead};
// Add characteristic and its value.
// NOLINTNEXTLINE(google3-legacy-absl-backports)
absl::optional<GattCharacteristic> server_characteristic =
gatt_server->CreateCharacteristic(std::string(kCopresenceServiceUuid),
characteristic_uuid, permissions,
properties);
ASSERT_TRUE(server_characteristic.has_value());
ByteArray server_value("any");
EXPECT_TRUE(
gatt_server->UpdateCharacteristic(*server_characteristic, server_value));
// Start GattClient
auto ble_peripheral =
std::make_unique<BlePeripheralStub>(/*mac_address=*/"ABCD");
std::unique_ptr<GattClient> gatt_client = ble_b.ConnectToGattServer(
BleV2Peripheral(ble_peripheral.get()), kPowerMode,
/*ClientGattConnectionCallback=*/{});
ASSERT_NE(gatt_client, nullptr);
// Discover service.
EXPECT_TRUE(
gatt_client->DiscoverService(std::string(kCopresenceServiceUuid)));
// Discover characteristic.
// NOLINTNEXTLINE(google3-legacy-absl-backports)
absl::optional<GattCharacteristic> client_characteristic =
gatt_client->GetCharacteristic(std::string(kCopresenceServiceUuid),
characteristic_uuid);
ASSERT_TRUE(client_characteristic.has_value());
// Can read the characteristic value.
EXPECT_THAT(gatt_client->ReadCharacteristic(*client_characteristic),
Optional(server_value));
gatt_client->Disconnect();
gatt_server->Stop();
env_.Stop();
}
+30 -61
View File
@@ -16,6 +16,7 @@
#define PLATFORM_API_BLE_V2_H_
#include <cstdint>
#include <functional>
#include <limits>
#include <map>
#include <memory>
@@ -25,7 +26,6 @@
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/input_stream.h"
@@ -111,35 +111,32 @@ struct GattCharacteristic {
};
std::string uuid;
std::string servie_uuid;
std::string service_uuid;
// Hashable
template <typename H>
friend H AbslHashValue(H h, const GattCharacteristic& s) {
return H::combine(std::move(h), s.uuid, s.servie_uuid);
return H::combine(std::move(h), s.uuid, s.service_uuid);
}
bool operator==(const GattCharacteristic& rhs) const {
return this->uuid == rhs.uuid && this->servie_uuid == rhs.servie_uuid;
return this->uuid == rhs.uuid && this->service_uuid == rhs.service_uuid;
}
};
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt
//
// Representation of a client GATT connection to a remote GATT server.
class ClientGattConnection {
class GattClient {
public:
virtual ~ClientGattConnection() = default;
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#getDevice()
virtual BlePeripheral& GetPeripheral() = 0;
virtual ~GattClient() = default;
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#discoverServices()
//
// Discovers all available services and characteristics on this connection.
// Discovers available service and characteristics on this connection.
// Returns whether or not discovery finished successfully.
//
// This function should block until discovery has finished.
virtual bool DiscoverServices() = 0;
virtual bool DiscoverService(const std::string& service_uuid) = 0;
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#getService(java.util.UUID)
// https://developer.android.com/reference/android/bluetooth/BluetoothGattService.html#getCharacteristic(java.util.UUID)
@@ -172,49 +169,6 @@ class ClientGattConnection {
virtual void Disconnect() = 0;
};
// https://developer.android.com/reference/android/bluetooth/BluetoothGattServer
//
// Representation of a server GATT connection to a remote GATT client.
class ServerGattConnection {
public:
virtual ~ServerGattConnection() = default;
// https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#setValue(byte[])
// https://developer.android.com/reference/android/bluetooth/BluetoothGattServer.html#notifyCharacteristicChanged(android.bluetooth.BluetoothDevice,%20android.bluetooth.BluetoothGattCharacteristic,%20boolean)
//
// Sends a notification (via indication) to the client that a characteristic
// has changed with the given value. Returns whether or not it was
// successful.
//
// The value sent does not have to reflect the locally stored characteristic
// value. To update the local value, call GattServer::UpdateCharacteristic.
virtual bool SendCharacteristic(const GattCharacteristic& characteristic,
const ByteArray& value) = 0;
};
// Callback for asynchronous events on the client side of a GATT connection.
struct ClientGattConnectionCallback {
public:
// Called when the client is disconnected from the GATT server.
std::function<void(ClientGattConnection& connection)> disconnected_cb =
DefaultCallback<ClientGattConnection&>();
};
// Callback for asynchronous events on the server side of a GATT connection.
struct ServerGattConnectionCallback {
// Called when a remote peripheral connected to us and subscribed to one of
// our characteristics.
std::function<void(ServerGattConnection& connection,
const GattCharacteristic& characteristic)>
characteristic_subscription_cb;
// Called when a remote peripheral unsubscribed from one of our
// characteristics.
std::function<void(ServerGattConnection& connection,
const GattCharacteristic& characteristic)>
characteristic_unsubscription_cb;
};
// https://developer.android.com/reference/android/bluetooth/BluetoothGattServer
//
// Representation of a BLE GATT server.
@@ -251,6 +205,26 @@ class GattServer {
virtual void Stop() = 0;
};
// Callback for asynchronous events on the client side of a GATT connection.
struct ClientGattConnectionCallback {
public:
// Called when the client is disconnected from the GATT server.
std::function<void()> disconnected_cb = DefaultCallback<>();
};
// Callback for asynchronous events on the server side of a GATT connection.
struct ServerGattConnectionCallback {
// Called when a remote peripheral connected to us and subscribed to one of
// our characteristics.
std::function<void(const GattCharacteristic& characteristic)>
characteristic_subscription_cb;
// Called when a remote peripheral unsubscribed from one of our
// characteristics.
std::function<void(const GattCharacteristic& characteristic)>
characteristic_unsubscription_cb;
};
class BleSocket {
public:
virtual ~BleSocket() {}
@@ -295,8 +269,6 @@ class ServerBleSocketLifeCycleCallback : public BleSocketLifeCycleCallback {
// for all BLE and GATT related operations.
class BleMedium {
public:
using Mtu = uint32_t;
virtual ~BleMedium() = default;
// https://developer.android.com/reference/android/bluetooth/le/BluetoothLeAdvertiser.html#startAdvertising(android.bluetooth.le.AdvertiseSettings,%20android.bluetooth.le.AdvertiseData,%20android.bluetooth.le.AdvertiseData,%20android.bluetooth.le.AdvertiseCallback)
@@ -375,16 +347,13 @@ class BleMedium {
// Connects to a GATT server and negotiates the specified connection
// parameters. Returns nullptr upon error.
//
// Both connection interval and MTU can be negotiated on a best-effort
// basis.
//
// Power mode should be interpreted in the following way:
// HIGH:
// - Connection interval = ~11.25ms - 15ms
// LOW:
// - Connection interval = ~100ms - 125ms
virtual std::unique_ptr<ClientGattConnection> ConnectToGattServer(
BlePeripheral& peripheral, Mtu mtu, PowerMode power_mode,
virtual std::unique_ptr<GattClient> ConnectToGattServer(
BlePeripheral& peripheral, PowerMode power_mode,
ClientGattConnectionCallback callback) = 0;
// Establishes a BLE socket to the specified remote peripheral. Returns
+106 -10
View File
@@ -16,12 +16,13 @@
#include <iostream>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "absl/strings/escaping.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/shared/count_down_latch.h"
#include "internal/platform/logging.h"
#include "internal/platform/medium_environment.h"
@@ -34,9 +35,7 @@ namespace {
using ::location::nearby::api::ble_v2::BleAdvertisementData;
using ::location::nearby::api::ble_v2::BleSocket;
using ::location::nearby::api::ble_v2::BleSocketLifeCycleCallback;
using ::location::nearby::api::ble_v2::ClientGattConnection;
using ::location::nearby::api::ble_v2::PowerMode;
using ::location::nearby::api::ble_v2::ServerGattConnectionCallback;
std::string PowerModeToName(PowerMode power_mode) {
switch (power_mode) {
@@ -96,7 +95,6 @@ bool BleV2Medium::StartAdvertising(
bool BleV2Medium::StopAdvertising() {
NEARBY_LOGS(INFO) << "G3 Ble StopAdvertising";
absl::MutexLock lock(&mutex_);
advertisement_byte_ = {};
BleAdvertisementData empty_advertisement_data = {};
MediumEnvironment::Instance().UpdateBleV2MediumForAdvertising(
@@ -111,7 +109,7 @@ bool BleV2Medium::StartScanning(const std::vector<std::string>& service_uuids,
absl::MutexLock lock(&mutex_);
MediumEnvironment::Instance().UpdateBleV2MediumForScanning(
true, std::move(callback), *this);
/*enabled=*/true, service_uuids.front(), std::move(callback), *this);
return true;
}
@@ -119,12 +117,14 @@ bool BleV2Medium::StopScanning() {
NEARBY_LOGS(INFO) << "G3 Ble StopScanning";
absl::MutexLock lock(&mutex_);
MediumEnvironment::Instance().UpdateBleV2MediumForScanning(false, {}, *this);
MediumEnvironment::Instance().UpdateBleV2MediumForScanning(
/*enabled=*/false,
/*service_uuid=*/{}, /*callback=*/{}, *this);
return true;
}
std::unique_ptr<api::ble_v2::GattServer> BleV2Medium::StartGattServer(
ServerGattConnectionCallback callback) {
api::ble_v2::ServerGattConnectionCallback callback) {
return std::make_unique<GattServer>();
}
@@ -135,10 +135,10 @@ bool BleV2Medium::StartListeningForIncomingBleSockets(
void BleV2Medium::StopListeningForIncomingBleSockets() {}
std::unique_ptr<ClientGattConnection> BleV2Medium::ConnectToGattServer(
api::ble_v2::BlePeripheral& peripheral, Mtu mtu, PowerMode power_mode,
std::unique_ptr<api::ble_v2::GattClient> BleV2Medium::ConnectToGattServer(
api::ble_v2::BlePeripheral& peripheral, PowerMode power_mode,
api::ble_v2::ClientGattConnectionCallback callback) {
return nullptr;
return std::make_unique<GattClient>();
}
std::unique_ptr<BleSocket> BleV2Medium::EstablishBleSocket(
@@ -147,6 +147,102 @@ std::unique_ptr<BleSocket> BleV2Medium::EstablishBleSocket(
return nullptr;
}
std::optional<api::ble_v2::GattCharacteristic>
BleV2Medium::GattServer::CreateCharacteristic(
absl::string_view service_uuid, absl::string_view characteristic_uuid,
const std::vector<api::ble_v2::GattCharacteristic::Permission>& permissions,
const std::vector<api::ble_v2::GattCharacteristic::Property>& properties) {
api::ble_v2::GattCharacteristic characteristic = {
.uuid = std::string(characteristic_uuid),
.service_uuid = std::string(service_uuid)};
return characteristic;
}
bool BleV2Medium::GattServer::UpdateCharacteristic(
const api::ble_v2::GattCharacteristic& characteristic,
const location::nearby::ByteArray& value) {
NEARBY_LOGS(INFO)
<< "G3 Ble GattServer UpdateCharacteristic, characteristic=("
<< characteristic.service_uuid << "," << characteristic.uuid
<< "), value = " << absl::BytesToHexString(value.data());
MediumEnvironment::Instance().InsertBleV2MediumGattCharacteristics(
characteristic, value);
return true;
}
void BleV2Medium::GattServer::Stop() {
NEARBY_LOGS(INFO) << "G3 Ble GattServer Stop";
MediumEnvironment::Instance().ClearBleV2MediumGattCharacteristics();
}
bool BleV2Medium::GattClient::DiscoverService(const std::string& service_uuid) {
absl::MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "G3 Ble GattClient DiscoverService, service_uuid"
<< service_uuid;
if (!is_connection_alive_) {
return false;
}
// Search if the service exists.
return MediumEnvironment::Instance().ContainsBleV2MediumGattCharacteristics(
service_uuid, "");
}
std::optional<api::ble_v2::GattCharacteristic>
BleV2Medium::GattClient::GetCharacteristic(
absl::string_view service_uuid, absl::string_view characteristic_uuid) {
absl::MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "G3 Ble GattClient GetCharacteristic, service_uuid="
<< service_uuid
<< ", characteristic_uuid=" << characteristic_uuid;
if (!is_connection_alive_) {
return std::nullopt;
}
// Search gatt_characteristic by uuid and if found return the
// gatt_characteristic.
api::ble_v2::GattCharacteristic characteristic = {};
if (MediumEnvironment::Instance().ContainsBleV2MediumGattCharacteristics(
service_uuid, characteristic_uuid)) {
characteristic = {.uuid = std::string(characteristic_uuid),
.service_uuid = std::string(service_uuid)};
}
NEARBY_LOGS(INFO)
<< "G3 Ble GattClient GetCharacteristic, found characteristic=("
<< characteristic.service_uuid << "," << characteristic.uuid << ")";
return characteristic;
}
std::optional<ByteArray> BleV2Medium::GattClient::ReadCharacteristic(
const api::ble_v2::GattCharacteristic& characteristic) {
absl::MutexLock lock(&mutex_);
if (!is_connection_alive_) {
return std::nullopt;
}
ByteArray value =
MediumEnvironment::Instance().ReadBleV2MediumGattCharacteristics(
characteristic);
NEARBY_LOGS(INFO) << "G3 Ble ReadCharacteristic, characteristic=("
<< characteristic.service_uuid << "," << characteristic.uuid
<< "), value = " << absl::BytesToHexString(value.data());
return std::move(value);
}
bool BleV2Medium::GattClient::WriteCharacteristic(
const api::ble_v2::GattCharacteristic& characteristic,
const ByteArray& value) {
// No op.
return false;
}
void BleV2Medium::GattClient::Disconnect() {
absl::MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "G3 Ble GattClient Disconnect";
is_connection_alive_ = false;
}
} // namespace g3
} // namespace nearby
} // namespace location
+33 -17
View File
@@ -55,9 +55,8 @@ class BleV2Medium : public api::ble_v2::BleMedium {
ABSL_LOCKS_EXCLUDED(mutex_);
void StopListeningForIncomingBleSockets() override
ABSL_LOCKS_EXCLUDED(mutex_);
std::unique_ptr<api::ble_v2::ClientGattConnection> ConnectToGattServer(
api::ble_v2::BlePeripheral& peripheral, Mtu mtu,
api::ble_v2::PowerMode power_mode,
std::unique_ptr<api::ble_v2::GattClient> ConnectToGattServer(
api::ble_v2::BlePeripheral& peripheral, api::ble_v2::PowerMode power_mode,
api::ble_v2::ClientGattConnectionCallback callback) override
ABSL_LOCKS_EXCLUDED(mutex_);
std::unique_ptr<api::ble_v2::BleSocket> EstablishBleSocket(
@@ -68,6 +67,7 @@ class BleV2Medium : public api::ble_v2::BleMedium {
BluetoothAdapter& GetAdapter() { return *adapter_; }
private:
// A concrete implemenation for GattServer.
class GattServer : public api::ble_v2::GattServer {
public:
std::optional<api::ble_v2::GattCharacteristic> CreateCharacteristic(
@@ -75,28 +75,44 @@ class BleV2Medium : public api::ble_v2::BleMedium {
const std::vector<api::ble_v2::GattCharacteristic::Permission>&
permissions,
const std::vector<api::ble_v2::GattCharacteristic::Property>&
properties) override {
api::ble_v2::GattCharacteristic characteristic = {
.uuid = std::string(characteristic_uuid),
.servie_uuid = std::string(service_uuid)};
return characteristic;
}
properties) override;
bool UpdateCharacteristic(
const api::ble_v2::GattCharacteristic& characteristic,
const location::nearby::ByteArray& value) override {
// No action for now.
return true;
}
const location::nearby::ByteArray& value) override;
void Stop() override {
// No action for now.
}
void Stop() override;
};
// A concrete implemenation for GattClient.
class GattClient : public api::ble_v2::GattClient {
public:
bool DiscoverService(const std::string& service_uuid) override;
std::optional<api::ble_v2::GattCharacteristic> GetCharacteristic(
absl::string_view service_uuid,
absl::string_view characteristic_uuid) override;
std::optional<ByteArray> ReadCharacteristic(
const api::ble_v2::GattCharacteristic& characteristic) override;
bool WriteCharacteristic(
const api::ble_v2::GattCharacteristic& characteristic,
const ByteArray& value) override;
void Disconnect() override;
private:
absl::Mutex mutex_;
// A flag to indicate the gatt connection alive or not. If it is
// disconnected/*false*/, the instance needs to be created again to bring it
// alive.
bool is_connection_alive_ ABSL_GUARDED_BY(mutex_) = true;
};
absl::Mutex mutex_;
BluetoothAdapter* adapter_; // Our device adapter; read-only.
ByteArray advertisement_byte_ ABSL_GUARDED_BY(mutex_);
};
} // namespace g3
@@ -35,7 +35,7 @@ namespace {
using ::location::nearby::api::ble_v2::BleAdvertisementData;
using ::location::nearby::api::ble_v2::BleSocket;
using ::location::nearby::api::ble_v2::BleSocketLifeCycleCallback;
using ::location::nearby::api::ble_v2::ClientGattConnection;
using ::location::nearby::api::ble_v2::GattClient;
using ::location::nearby::api::ble_v2::PowerMode;
using ::location::nearby::api::ble_v2::ServerGattConnectionCallback;
using ::winrt::Windows::Devices::Bluetooth::BluetoothError;
@@ -289,8 +289,8 @@ bool BleV2Medium::StartListeningForIncomingBleSockets(
void BleV2Medium::StopListeningForIncomingBleSockets() {}
std::unique_ptr<ClientGattConnection> BleV2Medium::ConnectToGattServer(
api::ble_v2::BlePeripheral& peripheral, Mtu mtu, PowerMode power_mode,
std::unique_ptr<GattClient> BleV2Medium::ConnectToGattServer(
api::ble_v2::BlePeripheral& peripheral, PowerMode power_mode,
api::ble_v2::ClientGattConnectionCallback callback) {
return nullptr;
}
@@ -66,9 +66,8 @@ class BleV2Medium : public api::ble_v2::BleMedium {
ABSL_LOCKS_EXCLUDED(mutex_);
void StopListeningForIncomingBleSockets() override
ABSL_LOCKS_EXCLUDED(mutex_);
std::unique_ptr<api::ble_v2::ClientGattConnection> ConnectToGattServer(
api::ble_v2::BlePeripheral& peripheral, Mtu mtu,
api::ble_v2::PowerMode power_mode,
std::unique_ptr<api::ble_v2::GattClient> ConnectToGattServer(
api::ble_v2::BlePeripheral& peripheral, api::ble_v2::PowerMode power_mode,
api::ble_v2::ClientGattConnectionCallback callback) override
ABSL_LOCKS_EXCLUDED(mutex_);
std::unique_ptr<api::ble_v2::BleSocket> EstablishBleSocket(
+74 -2
View File
@@ -539,6 +539,9 @@ void MediumEnvironment::UpdateBleV2MediumForAdvertising(
BleV2MediumContext& remote_context = medium_info.second;
// Do not send notification to the same medium.
if (remote_medium == &medium) continue;
if (!context.advertisement_data.service_uuids.contains(
remote_context.scanning_service_uuid))
continue;
NEARBY_LOGS(INFO)
<< "G3 UpdateBleV2MediumForAdvertising, found other medium="
<< remote_medium << ", remote_medium_context=" << &remote_context
@@ -552,10 +555,12 @@ void MediumEnvironment::UpdateBleV2MediumForAdvertising(
}
void MediumEnvironment::UpdateBleV2MediumForScanning(
bool enabled, BleScanCallback callback, api::ble_v2::BleMedium& medium) {
bool enabled, const std::string& scanning_service_uuid,
BleScanCallback callback, api::ble_v2::BleMedium& medium) {
if (!enabled_) return;
RunOnMediumEnvironmentThread(
[this, &medium, callback = std::move(callback), enabled]() {
[this, &medium, scanning_service_uuid = scanning_service_uuid,
callback = std::move(callback), enabled]() {
auto it = ble_v2_mediums_.find(&medium);
if (it == ble_v2_mediums_.end()) {
NEARBY_LOGS(INFO)
@@ -565,6 +570,7 @@ void MediumEnvironment::UpdateBleV2MediumForScanning(
}
BleV2MediumContext& context = it->second;
context.scan_callback = std::move(callback);
context.scanning_service_uuid = scanning_service_uuid;
NEARBY_LOGS(INFO) << "G3 UpdateBleV2MediumForScanning: this=" << this
<< ", medium=" << &medium
<< ", medium_context=" << &context
@@ -577,10 +583,14 @@ void MediumEnvironment::UpdateBleV2MediumForScanning(
// medium.
if (remote_medium == &medium || !remote_context.advertising)
continue;
if (!remote_context.advertisement_data.service_uuids.contains(
context.scanning_service_uuid))
continue;
NEARBY_LOGS(INFO)
<< "G3 UpdateBleV2MediumForScanning, found other medium="
<< remote_medium
<< ", remote_medium_context=" << &remote_context
<< ", scanning_service_uuid=" << scanning_service_uuid
<< ". Ready to call OnBleV2PeripheralStateChanged.";
OnBleV2PeripheralStateChanged(enabled, context,
remote_context.advertisement_data,
@@ -590,6 +600,68 @@ void MediumEnvironment::UpdateBleV2MediumForScanning(
});
}
void MediumEnvironment::InsertBleV2MediumGattCharacteristics(
const api::ble_v2::GattCharacteristic& characteristic,
const ByteArray& gatt_advertisement_byte) {
if (!enabled_) return;
CountDownLatch latch(1);
RunOnMediumEnvironmentThread(
[this, &latch, &characteristic, &gatt_advertisement_byte]() {
gatt_advertisement_bytes_[characteristic] = gatt_advertisement_byte;
latch.CountDown();
});
latch.Await();
}
bool MediumEnvironment::ContainsBleV2MediumGattCharacteristics(
absl::string_view service_uuid, absl::string_view characteristic_uuid) {
if (!enabled_) return false;
bool found_characteristic = false;
CountDownLatch latch(1);
RunOnMediumEnvironmentThread([this, &latch, &service_uuid,
&characteristic_uuid, &found_characteristic]() {
for (const auto& item : gatt_advertisement_bytes_) {
if (service_uuid == item.first.service_uuid) {
if (characteristic_uuid.empty()) {
// Found the service uuid and no need to search characteristic
// uuid.
found_characteristic = true;
break;
}
if (characteristic_uuid == item.first.uuid) {
found_characteristic = true;
break;
}
}
}
latch.CountDown();
});
latch.Await();
return found_characteristic;
}
ByteArray MediumEnvironment::ReadBleV2MediumGattCharacteristics(
const api::ble_v2::GattCharacteristic& characteristic) {
if (!enabled_) return {};
ByteArray gatt_advertisement_byte = {};
CountDownLatch latch(1);
RunOnMediumEnvironmentThread(
[this, &latch, &characteristic, &gatt_advertisement_byte]() {
auto it = gatt_advertisement_bytes_.find(characteristic);
if (it != gatt_advertisement_bytes_.end()) {
gatt_advertisement_byte = it->second;
}
latch.CountDown();
});
latch.Await();
return gatt_advertisement_byte;
}
void MediumEnvironment::ClearBleV2MediumGattCharacteristics() {
if (!enabled_) return;
RunOnMediumEnvironmentThread([this]() { gatt_advertisement_bytes_.clear(); });
}
void MediumEnvironment::UnregisterBleV2Medium(api::ble_v2::BleMedium& medium) {
if (!enabled_) return;
RunOnMediumEnvironmentThread([this, &medium]() {
+27 -1
View File
@@ -230,9 +230,31 @@ class MediumEnvironment {
// This should be called when discoverable state changes.
// The `callback` argument should be non-empty if `enabled` is true or empty
// if `enabled` is false.
void UpdateBleV2MediumForScanning(bool enabled, BleScanCallback callback,
void UpdateBleV2MediumForScanning(bool enabled,
const std::string& scanning_service_uuid,
BleScanCallback callback,
api::ble_v2::BleMedium& medium);
// Inserts the BLE GATT characteristic and its value BleAdvertisement byte
// array.
void InsertBleV2MediumGattCharacteristics(
const api::ble_v2::GattCharacteristic& characteristic,
const ByteArray& gatt_advertisement_byte);
// Check if `service_uuid` and `characteristic_uuid` exists in the map.
//
// `characteristic_uuid` can be empty and to check `service_uuid` only.
bool ContainsBleV2MediumGattCharacteristics(
absl::string_view service_uuid, absl::string_view characteristic_uuid);
// Reads the BLE GATT characteristic value. If the GATT characteristic is not
// existed, return empty byte array.
ByteArray ReadBleV2MediumGattCharacteristics(
const api::ble_v2::GattCharacteristic& characteristic);
// Clears the map `gatt_advertisement_bytes_`.
void ClearBleV2MediumGattCharacteristics();
// Removes medium-related info. This should correspond to device power off.
void UnregisterBleV2Medium(api::ble_v2::BleMedium& mediumum);
@@ -311,6 +333,7 @@ class MediumEnvironment {
BleScanCallback scan_callback = {};
api::ble_v2::BlePeripheral* ble_peripheral = nullptr;
api::ble_v2::BleAdvertisementData advertisement_data;
std::string scanning_service_uuid = {};
bool advertising = false;
};
@@ -377,6 +400,9 @@ class MediumEnvironment {
absl::flat_hash_map<api::BleMedium*, BleMediumContext> ble_mediums_;
absl::flat_hash_map<api::ble_v2::BleMedium*, BleV2MediumContext>
ble_v2_mediums_;
absl::flat_hash_map<api::ble_v2::GattCharacteristic,
location::nearby::ByteArray>
gatt_advertisement_bytes_;
#ifndef NO_WEBRTC
// Maps peer id to callback for receiving signaling messages.