// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "internal/platform/ble.h" #include #include #include #include #include #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" #include "absl/status/status.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/byte_array.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/feature_flags.h" #include "internal/platform/implementation/ble.h" #include "internal/platform/mac_address.h" #include "internal/platform/medium_environment.h" #include "internal/platform/single_thread_executor.h" #include "internal/platform/uuid.h" namespace nearby { namespace { using FeatureFlags = FeatureFlags::Flags; constexpr FeatureFlags kTestCases[] = { FeatureFlags{ .enable_cancellation_flag = true, }, FeatureFlags{ .enable_cancellation_flag = false, }, }; using ::nearby::api::ble::BleAdvertisementData; using ::nearby::api::ble::GattCharacteristic; using ::nearby::api::ble::TxPowerLevel; using ::testing::Optional; constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000); constexpr absl::string_view kAdvertisementString = "\x0a\x0b\x0c\x0d"; constexpr absl::string_view kAdvertisementHeaderString = "\x0x\x0y\x0z"; constexpr TxPowerLevel kTxPowerLevel(TxPowerLevel::kHigh); constexpr absl::string_view kServiceIDA{ "com.google.location.nearby.apps.test.a"}; class BleMediumTest : public ::testing::TestWithParam { protected: MediumEnvironment& env_{MediumEnvironment::Instance()}; }; TEST_P(BleMediumTest, CanConnectToService) { FeatureFlags feature_flags = GetParam(); env_.SetFeatureFlags(feature_flags); env_.Start(); BluetoothAdapter adapter_a_; BluetoothAdapter adapter_b_; BleMedium ble_a(adapter_a_); BleMedium ble_b(adapter_b_); Uuid service_uuid(1234, 5678); std::string service_id(kServiceIDA); ByteArray advertisement_bytes{std::string(kAdvertisementString)}; CountDownLatch found_latch(1); CountDownLatch lost_latch(1); BleServerSocket server_socket = ble_b.OpenServerSocket(service_id); EXPECT_TRUE(server_socket.IsValid()); // Assemble regular advertisement data BleAdvertisementData advertising_data; advertising_data.is_extended_advertisement = false; advertising_data.service_data = {{service_uuid, advertisement_bytes}}; (ble_b.StartAdvertising(advertising_data, {.tx_power_level = kTxPowerLevel, .is_connectable = true})); BlePeripheral discovered_peripheral; ble_a.StartScanning( service_uuid, kTxPowerLevel, { .advertisement_found_cb = [&found_latch, &discovered_peripheral]( BlePeripheral peripheral, const BleAdvertisementData& advertisement_data) { discovered_peripheral = std::move(peripheral); found_latch.CountDown(); }, }); EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); BleSocket socket_a; BleSocket socket_b; EXPECT_FALSE(socket_a.IsValid()); EXPECT_FALSE(socket_b.IsValid()); { CancellationFlag flag; SingleThreadExecutor server_executor; SingleThreadExecutor client_executor; client_executor.Execute( [&ble_a, &socket_a, &service_id, discovered_peripheral = std::move(discovered_peripheral), &server_socket, &flag]() { socket_a = ble_a.Connect(service_id, kTxPowerLevel, discovered_peripheral, &flag); if (!socket_a.IsValid()) { server_socket.Close(); } }); server_executor.Execute([&socket_b, &server_socket]() { socket_b = server_socket.Accept(); if (!socket_b.IsValid()) { server_socket.Close(); } }); } EXPECT_TRUE(socket_a.IsValid()); EXPECT_TRUE(socket_b.IsValid()); server_socket.Close(); env_.Stop(); } TEST_P(BleMediumTest, CanConnectToServiceWithMultipleServices) { FeatureFlags feature_flags = GetParam(); env_.SetFeatureFlags(feature_flags); env_.Start(); BluetoothAdapter adapter_a_; BluetoothAdapter adapter_b_; BleMedium ble_a(adapter_a_); BleMedium ble_b(adapter_b_); Uuid service_uuid(1234, 5678); std::string service_id(kServiceIDA); ByteArray advertisement_bytes{std::string(kAdvertisementString)}; CountDownLatch found_latch(1); CountDownLatch lost_latch(1); BleServerSocket server_socket = ble_b.OpenServerSocket(service_id); EXPECT_TRUE(server_socket.IsValid()); // Assemble regular advertisement data BleAdvertisementData advertising_data; advertising_data.is_extended_advertisement = false; advertising_data.service_data = {{service_uuid, advertisement_bytes}}; (ble_b.StartAdvertising(advertising_data, {.tx_power_level = kTxPowerLevel, .is_connectable = true})); BlePeripheral discovered_peripheral; ble_a.StartMultipleServicesScanning( std::vector{service_uuid}, kTxPowerLevel, { .advertisement_found_cb = [&found_latch, &discovered_peripheral]( BlePeripheral peripheral, const BleAdvertisementData& advertisement_data) { discovered_peripheral = std::move(peripheral); found_latch.CountDown(); }, }); EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); BleSocket socket_a; BleSocket socket_b; EXPECT_FALSE(socket_a.IsValid()); EXPECT_FALSE(socket_b.IsValid()); { CancellationFlag flag; SingleThreadExecutor server_executor; SingleThreadExecutor client_executor; client_executor.Execute( [&ble_a, &socket_a, &service_id, discovered_peripheral = std::move(discovered_peripheral), &server_socket, &flag]() { socket_a = ble_a.Connect(service_id, kTxPowerLevel, discovered_peripheral, &flag); if (!socket_a.IsValid()) { server_socket.Close(); } }); server_executor.Execute([&socket_b, &server_socket]() { socket_b = server_socket.Accept(); if (!socket_b.IsValid()) { server_socket.Close(); } }); } EXPECT_TRUE(socket_a.IsValid()); EXPECT_TRUE(socket_b.IsValid()); server_socket.Close(); env_.Stop(); } TEST_P(BleMediumTest, CanDiscoverMultipleServices) { FeatureFlags feature_flags = GetParam(); env_.SetFeatureFlags(feature_flags); env_.Start(); BluetoothAdapter adapter_a_; BluetoothAdapter adapter_b_; BluetoothAdapter adapter_c_; BleMedium ble_a(adapter_a_); BleMedium ble_b(adapter_b_); BleMedium ble_c(adapter_c_); Uuid service_uuid_a(1234, 5678); Uuid service_uuid_b(1234, 5679); std::string service_id(kServiceIDA); ByteArray advertisement_bytes{std::string(kAdvertisementString)}; CountDownLatch found_latch(1); // Start advertising on adapter a BleAdvertisementData advertising_data_a; advertising_data_a.is_extended_advertisement = false; advertising_data_a.service_data = {{service_uuid_a, advertisement_bytes}}; (ble_a.StartAdvertising(advertising_data_a, {.tx_power_level = kTxPowerLevel, .is_connectable = true})); // Start advertising on adapter b BleAdvertisementData advertising_data_b; advertising_data_b.is_extended_advertisement = false; advertising_data_b.service_data = {{service_uuid_b, advertisement_bytes}}; (ble_b.StartAdvertising(advertising_data_b, {.tx_power_level = kTxPowerLevel, .is_connectable = true})); // Discover both services on adapter c bool found_service_a = false; bool found_service_b = false; ble_c.StartMultipleServicesScanning( std::vector{service_uuid_a, service_uuid_b}, kTxPowerLevel, {.advertisement_found_cb = [&found_latch, &found_service_a, &found_service_b, &service_uuid_a, &service_uuid_b](BlePeripheral peripheral, const BleAdvertisementData& advertisement_data) { if (advertisement_data.service_data.contains(service_uuid_a)) { found_service_a = true; } if (advertisement_data.service_data.contains(service_uuid_b)) { found_service_b = true; } if (found_service_a && found_service_b) { found_latch.CountDown(); } }}); EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); env_.Stop(); } TEST_P(BleMediumTest, CanCancelConnect) { FeatureFlags feature_flags = GetParam(); env_.SetFeatureFlags(feature_flags); env_.Start(); BluetoothAdapter adapter_a_; BluetoothAdapter adapter_b_; BleMedium ble_a(adapter_a_); BleMedium ble_b(adapter_b_); Uuid service_uuid(1234, 5678); std::string service_id(kServiceIDA); ByteArray advertisement_bytes((std::string(kAdvertisementString))); CountDownLatch found_latch(1); CountDownLatch lost_latch(1); BleServerSocket server_socket = ble_b.OpenServerSocket(service_id); EXPECT_TRUE(server_socket.IsValid()); // Assemble regular advertisement data. BleAdvertisementData advertising_data; advertising_data.is_extended_advertisement = false; advertising_data.service_data = {{service_uuid, advertisement_bytes}}; (ble_b.StartAdvertising(advertising_data, {.tx_power_level = kTxPowerLevel, .is_connectable = true})); BlePeripheral discovered_peripheral; ble_a.StartScanning( service_uuid, kTxPowerLevel, { .advertisement_found_cb = [&found_latch, &discovered_peripheral]( BlePeripheral peripheral, const BleAdvertisementData& advertisement_data) { discovered_peripheral = std::move(peripheral); found_latch.CountDown(); }, }); EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); BleSocket socket_a; BleSocket socket_b; EXPECT_FALSE(socket_a.IsValid()); EXPECT_FALSE(socket_b.IsValid()); { CancellationFlag flag(true); SingleThreadExecutor server_executor; SingleThreadExecutor client_executor; client_executor.Execute( [&ble_a, &socket_a, &service_id, discovered_peripheral = std::move(discovered_peripheral), &server_socket, &flag]() { socket_a = ble_a.Connect(service_id, kTxPowerLevel, discovered_peripheral, &flag); if (!socket_a.IsValid()) { server_socket.Close(); } }); server_executor.Execute([&socket_b, &server_socket]() { socket_b = server_socket.Accept(); if (!socket_b.IsValid()) { server_socket.Close(); } }); } // If FeatureFlag is disabled, Cancelled is false as no-op. if (!feature_flags.enable_cancellation_flag) { EXPECT_TRUE(socket_a.IsValid()); EXPECT_TRUE(socket_b.IsValid()); } else { EXPECT_FALSE(socket_a.IsValid()); EXPECT_FALSE(socket_b.IsValid()); } server_socket.Close(); env_.Stop(); } INSTANTIATE_TEST_SUITE_P(ParametrisedBleMediumTest, BleMediumTest, ::testing::ValuesIn(kTestCases)); TEST_F(BleMediumTest, ConstructorDestructorWorks) { env_.Start(); BluetoothAdapter adapter_a; BluetoothAdapter adapter_b; BleMedium ble_a(adapter_a); BleMedium ble_b(adapter_b); // Make sure we can create functional mediums. ASSERT_TRUE(ble_a.IsValid()); ASSERT_TRUE(ble_b.IsValid()); // Make sure we can create 2 distinct mediums. EXPECT_NE(ble_a.GetImpl(), ble_b.GetImpl()); env_.Stop(); } TEST_F(BleMediumTest, CanStartFastScanningAndFastAdvertising) { env_.Start(); BluetoothAdapter adapter_a; BluetoothAdapter adapter_b; BleMedium ble_a(adapter_a); BleMedium ble_b(adapter_b); Uuid service_uuid(1234, 5678); ByteArray advertisement_bytes((std::string(kAdvertisementString))); CountDownLatch found_latch(1); EXPECT_TRUE(ble_a.StartScanning( service_uuid, kTxPowerLevel, { .advertisement_found_cb = [&found_latch](BlePeripheral peripheral, const BleAdvertisementData& advertisement_data) { found_latch.CountDown(); }, })); // Fail to start extended advertisement due to g3 Ble medium does not support. BleAdvertisementData advertising_data; advertising_data.is_extended_advertisement = true; advertising_data.service_data.insert({service_uuid, advertisement_bytes}); EXPECT_FALSE(ble_b.StartAdvertising( advertising_data, {.tx_power_level = kTxPowerLevel, .is_connectable = true})); // Succeed to start regular advertisement. advertising_data.is_extended_advertisement = false; advertising_data.service_data = {{service_uuid, advertisement_bytes}}; EXPECT_TRUE(ble_b.StartAdvertising( advertising_data, {.tx_power_level = kTxPowerLevel, .is_connectable = true})); EXPECT_TRUE(env_.GetBleMediumStatus(*ble_a.GetImpl()).value().is_scanning); EXPECT_TRUE(env_.GetBleMediumStatus(*ble_b.GetImpl()).value().is_advertising); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_TRUE(ble_a.StopScanning()); EXPECT_TRUE(ble_b.StopAdvertising()); EXPECT_FALSE(env_.GetBleMediumStatus(*ble_a.GetImpl()).value().is_scanning); EXPECT_FALSE( env_.GetBleMediumStatus(*ble_b.GetImpl()).value().is_advertising); env_.Stop(); } TEST_F(BleMediumTest, CanStartScanningAndAdvertising) { env_.Start(); BluetoothAdapter adapter_a; BluetoothAdapter adapter_b; BleMedium ble_a(adapter_a); BleMedium ble_b(adapter_b); Uuid service_uuid(1234, 5678); ByteArray advertisement_bytes{std::string(kAdvertisementString)}; ByteArray advertisement_header_bytes{std::string(kAdvertisementHeaderString)}; CountDownLatch found_latch(1); EXPECT_TRUE(ble_a.StartScanning( service_uuid, kTxPowerLevel, { .advertisement_found_cb = [&found_latch](BlePeripheral peripheral, const BleAdvertisementData& advertisement_data) { found_latch.CountDown(); }, })); // Fail to start extended advertisement due to g3 Ble medium does not support. BleAdvertisementData advertising_data; advertising_data.is_extended_advertisement = true; advertising_data.service_data.insert({service_uuid, advertisement_bytes}); EXPECT_FALSE(ble_b.StartAdvertising( advertising_data, {.tx_power_level = kTxPowerLevel, .is_connectable = true})); // Succeed to start regular advertisement. advertising_data.is_extended_advertisement = false; advertising_data.service_data = {{service_uuid, advertisement_header_bytes}}; EXPECT_TRUE(ble_b.StartAdvertising( advertising_data, {.tx_power_level = kTxPowerLevel, .is_connectable = true})); EXPECT_TRUE(env_.GetBleMediumStatus(*ble_a.GetImpl()).value().is_scanning); EXPECT_TRUE(env_.GetBleMediumStatus(*ble_b.GetImpl()).value().is_advertising); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_TRUE(ble_a.StopScanning()); EXPECT_TRUE(ble_b.StopAdvertising()); EXPECT_FALSE(env_.GetBleMediumStatus(*ble_a.GetImpl()).value().is_scanning); EXPECT_FALSE( env_.GetBleMediumStatus(*ble_b.GetImpl()).value().is_advertising); env_.UnregisterBleMedium(*ble_a.GetImpl()); env_.UnregisterBleMedium(*ble_b.GetImpl()); EXPECT_EQ(env_.GetBleMediumStatus(*ble_a.GetImpl()), std::nullopt); EXPECT_EQ(env_.GetBleMediumStatus(*ble_b.GetImpl()), std::nullopt); env_.Stop(); } TEST_F(BleMediumTest, StartThenStopAsyncScanning) { env_.Start(); BluetoothAdapter adapter_a; BleMedium ble_a(adapter_a); Uuid service_uuid(1234, 5678); CountDownLatch found_latch_a(1); std::unique_ptr scanning_session_a = ble_a.StartScanning( service_uuid, kTxPowerLevel, api::ble::BleMedium::ScanningCallback{ .advertisement_found_cb = [&](api::ble::BlePeripheral::UniqueId peripheral_id, BleAdvertisementData advertisement_data) -> void { found_latch_a.CountDown(); }, }); EXPECT_TRUE(env_.GetBleMediumStatus(*ble_a.GetImpl()).value().is_scanning); EXPECT_OK(scanning_session_a->stop_scanning()); EXPECT_FALSE(env_.GetBleMediumStatus(*ble_a.GetImpl()).value().is_scanning); env_.Stop(); } TEST_F(BleMediumTest, CanStartMultipleAsyncScanning) { env_.Start(); BluetoothAdapter adapter_a; BluetoothAdapter adapter_b; BleMedium ble_a(adapter_a); BleMedium ble_b(adapter_b); Uuid service_uuid(1234, 5678); CountDownLatch found_latch_a(1); CountDownLatch found_latch_b(1); std::unique_ptr scanning_session_a = ble_a.StartScanning( service_uuid, kTxPowerLevel, api::ble::BleMedium::ScanningCallback{ .advertisement_found_cb = [&](api::ble::BlePeripheral::UniqueId peripheral_id, BleAdvertisementData advertisement_data) -> void { found_latch_a.CountDown(); }, }); std::unique_ptr scanning_session_b = ble_b.StartScanning( service_uuid, kTxPowerLevel, api::ble::BleMedium::ScanningCallback{ .advertisement_found_cb = [&](api::ble::BlePeripheral::UniqueId peripheral_id, BleAdvertisementData advertisement_data) -> void { found_latch_b.CountDown(); }, }); EXPECT_TRUE(env_.GetBleMediumStatus(*ble_a.GetImpl()).value().is_scanning); EXPECT_TRUE(env_.GetBleMediumStatus(*ble_b.GetImpl()).value().is_scanning); EXPECT_OK(scanning_session_a->stop_scanning()); EXPECT_OK(scanning_session_b->stop_scanning()); EXPECT_FALSE(env_.GetBleMediumStatus(*ble_a.GetImpl()).value().is_scanning); EXPECT_FALSE(env_.GetBleMediumStatus(*ble_b.GetImpl()).value().is_scanning); env_.Stop(); } TEST_F(BleMediumTest, CanStartAsyncScanningAndAdvertising) { env_.Start(); BluetoothAdapter adapter_a; BluetoothAdapter adapter_b; BleMedium ble_a(adapter_a); BleMedium ble_b(adapter_b); Uuid service_uuid(1234, 5678); ByteArray advertisement_bytes{std::string(kAdvertisementString)}; ByteArray advertisement_header_bytes{std::string(kAdvertisementHeaderString)}; CountDownLatch found_latch(1); std::unique_ptr scanning_session = ble_a.StartScanning( service_uuid, kTxPowerLevel, api::ble::BleMedium::ScanningCallback{ .advertisement_found_cb = [&](api::ble::BlePeripheral::UniqueId peripheral_id, BleAdvertisementData advertisement_data) -> void { found_latch.CountDown(); }, }); // Succeed to start regular advertisement. BleAdvertisementData advertising_data; advertising_data.is_extended_advertisement = false; advertising_data.service_data = {{service_uuid, advertisement_header_bytes}}; EXPECT_TRUE(ble_b.StartAdvertising( advertising_data, {.tx_power_level = kTxPowerLevel, .is_connectable = true})); EXPECT_TRUE(env_.GetBleMediumStatus(*ble_a.GetImpl()).value().is_scanning); EXPECT_TRUE(env_.GetBleMediumStatus(*ble_b.GetImpl()).value().is_advertising); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_OK(scanning_session->stop_scanning()); EXPECT_TRUE(ble_b.StopAdvertising()); EXPECT_FALSE(env_.GetBleMediumStatus(*ble_a.GetImpl()).value().is_scanning); EXPECT_FALSE( env_.GetBleMediumStatus(*ble_b.GetImpl()).value().is_advertising); env_.UnregisterBleMedium(*ble_a.GetImpl()); env_.UnregisterBleMedium(*ble_b.GetImpl()); EXPECT_EQ(env_.GetBleMediumStatus(*ble_a.GetImpl()), std::nullopt); EXPECT_EQ(env_.GetBleMediumStatus(*ble_b.GetImpl()), std::nullopt); env_.Stop(); } TEST_F(BleMediumTest, CanStartGattServer) { env_.Start(); BluetoothAdapter adapter; BleMedium ble(adapter); Uuid service_uuid(1234, 5678); Uuid characteristic_uuid(5678, 1234); std::unique_ptr gatt_server = ble.StartGattServer(/*ServerGattConnectionCallback=*/{}); ASSERT_NE(gatt_server, nullptr); GattCharacteristic::Permission permission = GattCharacteristic::Permission::kRead; GattCharacteristic::Property property = GattCharacteristic::Property::kRead; // NOLINTNEXTLINE(google3-legacy-absl-backports) std::optional gatt_characteristic = gatt_server->CreateCharacteristic(service_uuid, characteristic_uuid, permission, property); ASSERT_TRUE(gatt_characteristic.has_value()); ByteArray any_byte("any"); EXPECT_TRUE( gatt_server->UpdateCharacteristic(gatt_characteristic.value(), any_byte)); gatt_server->Stop(); env_.Stop(); } TEST_F(BleMediumTest, GattClientConnectToGattServerWorks) { env_.Start(); BluetoothAdapter adapter_a; BluetoothAdapter adapter_b; BleMedium ble_a(adapter_a); BleMedium ble_b(adapter_b); Uuid service_uuid(1234, 5678); Uuid characteristic_uuid(5678, 1234); // Start GattServer std::unique_ptr gatt_server = ble_a.StartGattServer(/*ServerGattConnectionCallback=*/{}); ASSERT_NE(gatt_server, nullptr); GattCharacteristic::Permission permissions = GattCharacteristic::Permission::kRead; GattCharacteristic::Property properties = GattCharacteristic::Property::kRead; // Add characteristic and its value. // NOLINTNEXTLINE(google3-legacy-absl-backports) std::optional server_characteristic = gatt_server->CreateCharacteristic(service_uuid, 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 MacAddress mac_address = adapter_a.GetAddress(); EXPECT_TRUE(mac_address.IsSet()); std::unique_ptr gatt_client = ble_b.ConnectToGattServer( BlePeripheral(ble_b, mac_address.address()), kTxPowerLevel, /*ClientGattConnectionCallback=*/{}); ASSERT_NE(gatt_client, nullptr); // Discover service and characteristics. EXPECT_TRUE(gatt_client->DiscoverServiceAndCharacteristics( service_uuid, {characteristic_uuid})); // NOLINTNEXTLINE(google3-legacy-absl-backports) std::optional client_characteristic = gatt_client->GetCharacteristic(service_uuid, characteristic_uuid); ASSERT_TRUE(client_characteristic.has_value()); // Can read the characteristic value. EXPECT_THAT(gatt_client->ReadCharacteristic(*client_characteristic), Optional(server_value.string_data())); gatt_client->Disconnect(); gatt_server->Stop(); env_.Stop(); } TEST_F(BleMediumTest, GattClientConnectToStoppedGattServerFails) { env_.Start(); BluetoothAdapter adapter_a; BluetoothAdapter adapter_b; BleMedium ble_a(adapter_a); BleMedium ble_b(adapter_b); std::unique_ptr gatt_server = ble_a.StartGattServer(/*ServerGattConnectionCallback=*/{}); ASSERT_NE(gatt_server, nullptr); MacAddress mac_address = adapter_a.GetAddress(); gatt_server->Stop(); std::unique_ptr gatt_client = ble_b.ConnectToGattServer( BlePeripheral(ble_b, mac_address.address()), kTxPowerLevel, /*ClientGattConnectionCallback=*/{}); ASSERT_NE(gatt_client, nullptr); EXPECT_FALSE(gatt_client->IsValid()); env_.Stop(); } TEST_F(BleMediumTest, GattClientNotifiedWhenServerDisconnects) { env_.Start(); BluetoothAdapter adapter_a; BluetoothAdapter adapter_b; BleMedium ble_a(adapter_a); BleMedium ble_b(adapter_b); std::unique_ptr gatt_server = ble_a.StartGattServer(/*ServerGattConnectionCallback=*/{}); ASSERT_NE(gatt_server, nullptr); CountDownLatch disconnected_latch(1); // Start GattClient MacAddress mac_address = adapter_a.GetAddress(); std::unique_ptr gatt_client = ble_b.ConnectToGattServer( BlePeripheral(ble_b, mac_address.address()), kTxPowerLevel, /*ClientGattConnectionCallback=*/{.disconnected_cb = [&]() { disconnected_latch.CountDown(); }}); ASSERT_NE(gatt_client, nullptr); gatt_server->Stop(); disconnected_latch.Await(); env_.Stop(); } TEST_F(BleMediumTest, GattClientOperatiosOnCharacteristic) { env_.Start(); BluetoothAdapter adapter_a; BluetoothAdapter adapter_b; BleMedium ble_a(adapter_a); BleMedium ble_b(adapter_b); Uuid service_uuid(1234, 5678); Uuid characteristic_uuid(5678, 1234); std::string written_data; // Start GattServer. std::unique_ptr gatt_server = ble_a.StartGattServer(/*ServerGattConnectionCallback=*/{ .on_characteristic_write_cb = [&](const api::ble::BlePeripheral::UniqueId remote_device_id, const api::ble::GattCharacteristic& characteristic, int offset, absl::string_view data, api::ble::ServerGattConnectionCallback::WriteValueCallback callback) { written_data = data; callback(absl::OkStatus()); }}); ASSERT_NE(gatt_server, nullptr); // Start GattClient. MacAddress mac_address = adapter_a.GetAddress(); std::unique_ptr gatt_client = ble_b.ConnectToGattServer( BlePeripheral(ble_b, mac_address.address()), kTxPowerLevel, /*ClientGattConnectionCallback=*/{}); ASSERT_NE(gatt_client, nullptr); // Can't not discover service and characteristic. EXPECT_FALSE(gatt_client->DiscoverServiceAndCharacteristics( service_uuid, {characteristic_uuid})); // Add characteristic and its value. GattCharacteristic::Permission permissions = GattCharacteristic::Permission::kRead; GattCharacteristic::Property properties = GattCharacteristic::Property::kRead; std::optional server_characteristic = gatt_server->CreateCharacteristic(service_uuid, characteristic_uuid, permissions, properties); ASSERT_TRUE(server_characteristic.has_value()); ByteArray server_value("any"); EXPECT_TRUE( gatt_server->UpdateCharacteristic(*server_characteristic, server_value)); // Can discover service and characteristics. EXPECT_TRUE(gatt_client->DiscoverServiceAndCharacteristics( service_uuid, {characteristic_uuid})); // Can get Characteristic. std::optional client_characteristic = gatt_client->GetCharacteristic(service_uuid, characteristic_uuid); ASSERT_TRUE(client_characteristic.has_value()); // Can read the characteristic value. EXPECT_THAT(gatt_client->ReadCharacteristic(*client_characteristic), Optional(std::string("any"))); // Can write the characteristic value. EXPECT_TRUE(gatt_client->WriteCharacteristic( *client_characteristic, "hello", api::ble::GattClient::WriteType::kWithResponse)); EXPECT_EQ(written_data, "hello"); gatt_client->Disconnect(); // Failed to write/read characteristic value as gatt is disconnected. EXPECT_FALSE(gatt_client->WriteCharacteristic( *client_characteristic, "any", api::ble::GattClient::WriteType::kWithResponse)); EXPECT_THAT(gatt_client->ReadCharacteristic(*client_characteristic), std::nullopt); gatt_server->Stop(); env_.Stop(); } } // namespace } // namespace nearby