diff --git a/connections/implementation/mediums/BUILD b/connections/implementation/mediums/BUILD index 95c21cb1..cf4770c8 100644 --- a/connections/implementation/mediums/BUILD +++ b/connections/implementation/mediums/BUILD @@ -90,6 +90,7 @@ cc_test( size = "small", srcs = [ "ble_test.cc", + "ble_v2_test.cc", "bluetooth_classic_test.cc", "bluetooth_radio_test.cc", "lost_entity_tracker_test.cc", diff --git a/connections/implementation/mediums/ble_v2.cc b/connections/implementation/mediums/ble_v2.cc index f22a93ff..c933cb2b 100644 --- a/connections/implementation/mediums/ble_v2.cc +++ b/connections/implementation/mediums/ble_v2.cc @@ -17,11 +17,15 @@ #include #include +#include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" #include "connections/implementation/mediums/ble_v2/ble_advertisement.h" #include "connections/implementation/mediums/ble_v2/ble_advertisement_header.h" #include "connections/implementation/mediums/ble_v2/bloom_filter.h" +#include "connections/implementation/mediums/bluetooth_radio.h" #include "connections/implementation/mediums/utils.h" +#include "internal/platform/logging.h" +#include "internal/platform/mutex_lock.h" #include "internal/platform/prng.h" namespace location { @@ -30,7 +34,13 @@ namespace connections { namespace { +using ::location::nearby::api::ble_v2::BleAdvertisementData; +using ::location::nearby::api::ble_v2::PowerMode; + +constexpr int kMaxAdvertisementLength = 512; constexpr int kDummyServiceIdLength = 128; +constexpr absl::string_view kCopresenceServiceUuid = + "0000FEF3-0000-1000-8000-00805F9B34FB"; ByteArray GenerateAdvertisementHash(const ByteArray& advertisement_bytes) { return Utils::Sha256Hash( @@ -38,11 +48,201 @@ ByteArray GenerateAdvertisementHash(const ByteArray& advertisement_bytes) { mediums::BleAdvertisementHeader::kAdvertisementHashLength); } +ByteArray GenerateHash(const std::string& source, size_t size) { + return Utils::Sha256Hash(source, size); +} + +ByteArray GenerateDeviceToken() { + return Utils::Sha256Hash(std::to_string(Prng().NextUint32()), + mediums::BleAdvertisement::kDeviceTokenLength); +} + } // namespace BleV2::BleV2(BluetoothRadio& radio) : radio_(radio), adapter_(radio_.GetBluetoothAdapter()) {} +bool BleV2::IsAvailable() const { + MutexLock lock(&mutex_); + + return IsAvailableLocked(); +} + +// TODO(edwinwu): Break down the function. +bool BleV2::StartAdvertising( + const std::string& service_id, const ByteArray& advertisement_bytes, + PowerLevel power_level, + const std::string& fast_advertisement_service_uuid) { + MutexLock lock(&mutex_); + + if (advertisement_bytes.Empty()) { + NEARBY_LOGS(INFO) + << "Refusing to turn on BLE advertising. Empty advertisement data."; + return false; + } + + if (advertisement_bytes.size() > kMaxAdvertisementLength) { + NEARBY_LOG(INFO, + "Refusing to start BLE advertising because the advertisement " + "was too long. Expected at most %d bytes but received %d.", + kMaxAdvertisementLength, advertisement_bytes.size()); + return false; + } + + if (IsAdvertisingLocked(service_id)) { + NEARBY_LOGS(INFO) + << "Failed to BLE advertise because we're already advertising."; + return false; + } + + if (!radio_.IsEnabled()) { + NEARBY_LOGS(INFO) + << "Can't start BLE scanning because Bluetooth was never turned on"; + return false; + } + + if (!IsAvailableLocked()) { + NEARBY_LOGS(INFO) << "Can't turn on BLE advertising. BLE is not available."; + return false; + } + + // Wrap the connections advertisement to the medium advertisement. + const bool is_fast_advertisement = !fast_advertisement_service_uuid.empty(); + ByteArray service_id_hash{GenerateHash( + service_id, mediums::BleAdvertisement::kServiceIdHashLength)}; + ByteArray medium_advertisement_bytes{mediums::BleAdvertisement{ + mediums::BleAdvertisement::Version::kV2, + mediums::BleAdvertisement::SocketVersion::kV2, + /* service_id_hash= */ + is_fast_advertisement ? ByteArray{} : service_id_hash, + advertisement_bytes, GenerateDeviceToken()}}; + if (medium_advertisement_bytes.Empty()) { + NEARBY_LOGS(INFO) << "Failed to BLE advertise because we could not wrap a " + "connection advertisement to medium advertisement."; + return false; + } + + // Stop any existing advertisement GATT servers. We don't stop it in + // StopAdvertising() to avoid GATT issues with BLE sockets. + if (IsAdvertisementGattServerRunning()) { + StopAdvertisementGattServer(); + } + + // Assemble AdvertisingData and ScanResponseData. + BleAdvertisementData advertising_data; + BleAdvertisementData scan_response_data; + if (is_fast_advertisement) { + advertising_data.service_data.insert( + {fast_advertisement_service_uuid, medium_advertisement_bytes}); + scan_response_data.service_uuids.insert(fast_advertisement_service_uuid); + } else { + // Start a GATT server to deliver the full advertisement data. If fail to + // advertise the header, we must shut this down before the method returns. + if (!StartAdvertisementGattServer(service_id, medium_advertisement_bytes)) { + NEARBY_LOGS(INFO) << "Failed to BLE advertise because the " + "advertisement GATT server failed to start."; + return false; + } + + ByteArray advertisement_header_bytes(CreateAdvertisementHeader()); + if (advertisement_header_bytes.Empty()) { + NEARBY_LOGS(INFO) << "Failed to BLE advertise because we could not " + "create an advertisement header."; + // Failed to start BLE advertising, so stop the advertisement GATT + // server. + StopAdvertisementGattServer(); + return false; + } + + advertising_data.is_connectable = true; + advertising_data.tx_power_level = + BleAdvertisementData::kUnspecifiedTxPowerLevel; + + scan_response_data.is_connectable = true; + scan_response_data.tx_power_level = + BleAdvertisementData::kUnspecifiedTxPowerLevel; + scan_response_data.service_uuids.insert( + std::string(kCopresenceServiceUuid)); + scan_response_data.service_data.insert( + {std::string(kCopresenceServiceUuid), advertisement_header_bytes}); + } + + // Convert power_mode from power_level. + PowerMode power_mode = PowerMode::kUnknown; + switch (power_level) { + case PowerLevel::kHighPower: + power_mode = PowerMode::kHigh; + break; + case PowerLevel::kLowPower: + // Medium power is about the size of a conference room. Any lower power + // it won't be visible at a distance. + power_mode = PowerMode::kMedium; + break; + default: + power_mode = PowerMode::kUnknown; + } + + if (!medium_.StartAdvertising(advertising_data, scan_response_data, + power_mode)) { + NEARBY_LOGS(ERROR) + << "Failed to turn on BLE advertising with advertisement bytes=" + << absl::BytesToHexString(advertisement_bytes.data()) + << ", size=" << advertisement_bytes.size() + << ", fast advertisement service uuid=" + << fast_advertisement_service_uuid; + + // If BLE advertising was not successful, stop the advertisement GATT + // server. + StopAdvertisementGattServer(); + return false; + } + + NEARBY_LOGS(INFO) << "Started BLE advertising with advertisement bytes=" + << absl::BytesToHexString(advertisement_bytes.data()) + << " for service_id=" << service_id; + advertising_info_.Add(service_id); + return true; +} + +bool BleV2::StopAdvertising(const std::string& service_id) { + MutexLock lock(&mutex_); + + if (!IsAdvertisingLocked(service_id)) { + NEARBY_LOGS(INFO) << "Can't turn off BLE advertising; it is already off"; + return false; + } + + NEARBY_LOGS(INFO) << "Turned off BLE advertising with service id=" + << service_id; + bool can_stop = medium_.StopAdvertising(); + // Reset our bundle of advertising state to mark that we're no longer + // advertising. + advertising_info_.Remove(service_id); + return can_stop; +} + +bool BleV2::IsAdvertising(const std::string& service_id) const { + MutexLock lock(&mutex_); + + return IsAdvertisingLocked(service_id); +} + +bool BleV2::IsAvailableLocked() const { return medium_.IsValid(); } + +bool BleV2::IsAdvertisingLocked(const std::string& service_id) const { + return advertising_info_.Existed(service_id); +} + +// TODO(b/213835576): Implement GattServer +bool BleV2::IsAdvertisementGattServerRunning() { return false; } + +bool BleV2::StartAdvertisementGattServer(const std::string& service_id, + const ByteArray& advertisement) { + return false; +} + +bool BleV2::StopAdvertisementGattServer() { return false; } + ByteArray BleV2::CreateAdvertisementHeader() { // Create a randomized dummy service id to anonymize the header with. ByteArray dummy_service_id_bytes = @@ -63,10 +263,8 @@ ByteArray BleV2::CreateAdvertisementHeader() { // Compute the next hash according to the algorithm in // https://source.corp.google.com/piper///depot/google3/java/com/google/android/gmscore/integ/modules/nearby/src/com/google/android/gms/nearby/mediums/bluetooth/BluetoothLowEnergy.java;rcl=428397891;l=1043 - ByteArray previous_advertisement_hash = advertisement_hash; - std::string advertisement_bodies = - absl::StrCat(previous_advertisement_hash.AsStringView(), - gatt_advertisement.AsStringView()); + std::string advertisement_bodies = absl::StrCat( + advertisement_hash.AsStringView(), gatt_advertisement.AsStringView()); advertisement_hash = GenerateAdvertisementHash(ByteArray(std::move(advertisement_bodies))); diff --git a/connections/implementation/mediums/ble_v2.h b/connections/implementation/mediums/ble_v2.h index 2edc01b4..f5838bbf 100644 --- a/connections/implementation/mediums/ble_v2.h +++ b/connections/implementation/mediums/ble_v2.h @@ -20,27 +20,82 @@ #include #include "absl/container/flat_hash_map.h" +#include "absl/strings/string_view.h" +#include "connections/advertising_options.h" #include "connections/implementation/mediums/bluetooth_radio.h" +#include "internal/platform/ble_v2.h" #include "internal/platform/byte_array.h" #include "internal/platform/mutex.h" +#include "internal/platform/mutex_lock.h" namespace location { namespace nearby { namespace connections { -// TODO(b/213691253): Add BleV2 medium tests after more functions are ready. -// Provides the operations that can be performed on the Bluetooth Low Energy -// (BLE) medium. -class BleV2 { +// Provides the operations that can be performed on the Bluetooth Low Energy +// (BLE) medium. +class BleV2 final { public: explicit BleV2(BluetoothRadio& bluetooth_radio); + // Returns true, if BLE communications are supported by a platform. + bool IsAvailable() const ABSL_LOCKS_EXCLUDED(mutex_); + + // Starts BLE advertising, delivering additional information if the platform + // supports it. + bool StartAdvertising(const std::string& service_id, + const ByteArray& advertisement_bytes, + PowerLevel power_level, + const std::string& fast_advertisement_service_uuid) + ABSL_LOCKS_EXCLUDED(mutex_); + + // Disables BLE advertising. + bool StopAdvertising(const std::string& service_id) + ABSL_LOCKS_EXCLUDED(mutex_); + + bool IsAdvertising(const std::string& service_id) const + ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns true if this object owns a valid platform implementation. + bool IsMediumValid() const ABSL_LOCKS_EXCLUDED(mutex_) { + MutexLock lock(&mutex_); + return medium_.IsValid(); + } + private: + struct AdvertisingInfo { + bool Empty() const { return service_ids.empty(); } + void Clear() { service_ids.clear(); } + void Add(const std::string& service_id) { service_ids.insert(service_id); } + void Remove(const std::string& service_id) { + service_ids.erase(service_id); + } + bool Existed(const std::string& service_id) const { + return service_ids.contains(service_id); + } + + absl::flat_hash_set service_ids; + }; + + // Same as IsAvailable(), but must be called with `mutex_` held. + bool IsAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Same as IsAdvertising(), but must be called with `mutex_` held. + bool IsAdvertisingLocked(const std::string& service_id) const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + bool IsAdvertisementGattServerRunning(); + bool StartAdvertisementGattServer(const std::string& service_id, + const ByteArray& advertisement); + bool StopAdvertisementGattServer(); + ByteArray CreateAdvertisementHeader() ABSL_SHARED_LOCKS_REQUIRED(mutex_); mutable Mutex mutex_; BluetoothRadio& radio_ ABSL_GUARDED_BY(mutex_); BluetoothAdapter& adapter_ ABSL_GUARDED_BY(mutex_); + BleV2Medium medium_ ABSL_GUARDED_BY(mutex_){adapter_}; + AdvertisingInfo advertising_info_ ABSL_GUARDED_BY(mutex_); absl::flat_hash_map> gatt_advertisements_ ABSL_GUARDED_BY(mutex_); }; diff --git a/connections/implementation/mediums/ble_v2_test.cc b/connections/implementation/mediums/ble_v2_test.cc new file mode 100644 index 00000000..7202a61b --- /dev/null +++ b/connections/implementation/mediums/ble_v2_test.cc @@ -0,0 +1,78 @@ +// Copyright 2022 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 "connections/implementation/mediums/ble_v2.h" + +#include + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "connections/implementation/mediums/bluetooth_radio.h" +#include "internal/platform/ble.h" +#include "internal/platform/count_down_latch.h" +#include "internal/platform/logging.h" +#include "internal/platform/medium_environment.h" + +namespace location { +namespace nearby { +namespace connections { +namespace { + +constexpr absl::string_view kServiceID{"com.google.location.nearby.apps.test"}; +constexpr absl::string_view kAdvertisementString{"\x0a\x0b\x0c\x0d"}; +constexpr absl::string_view kFastAdvertisementServiceUuid{"\xf3\xfe"}; + +class BleV2Test : public testing::Test { + protected: + BleV2Test() { env_.Stop(); } + + MediumEnvironment& env_{MediumEnvironment::Instance()}; +}; + +TEST_F(BleV2Test, CanConstructValidObject) { + env_.Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + BleV2 ble_a{radio_a}; + BleV2 ble_b{radio_b}; + + EXPECT_TRUE(ble_a.IsMediumValid()); + EXPECT_TRUE(ble_a.IsAvailable()); + EXPECT_TRUE(ble_b.IsMediumValid()); + EXPECT_TRUE(ble_b.IsAvailable()); + EXPECT_NE(&radio_a.GetBluetoothAdapter(), &radio_b.GetBluetoothAdapter()); + env_.Stop(); +} + +TEST_F(BleV2Test, CanStartAdvertising) { + env_.Start(); + BluetoothRadio radio; + BleV2 ble{radio}; + radio.Enable(); + std::string service_id(kServiceID); + ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + std::string fast_advertisement_service_uuid(kFastAdvertisementServiceUuid); + PowerLevel power_level = PowerLevel::kHighPower; + + EXPECT_TRUE(ble.StartAdvertising(service_id, advertisement_bytes, power_level, + fast_advertisement_service_uuid)); + EXPECT_TRUE(ble.StopAdvertising(service_id)); + env_.Stop(); +} + +} // namespace +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/internal/platform/BUILD b/internal/platform/BUILD index 667e1b6c..5f47fe4f 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -304,12 +304,14 @@ cc_library( name = "comm", srcs = [ "ble.cc", + "ble_v2.cc", "bluetooth_classic.cc", "file.cc", "wifi_lan.cc", ], hdrs = [ "ble.h", + "ble_v2.h", "bluetooth_adapter.h", "bluetooth_classic.h", "wifi_lan.h", @@ -342,6 +344,7 @@ cc_test( "atomic_boolean_test.cc", "atomic_reference_test.cc", "ble_test.cc", + "ble_v2_test.cc", "bluetooth_adapter_test.cc", "bluetooth_classic_test.cc", "cancelable_alarm_test.cc", diff --git a/internal/platform/ble_v2.cc b/internal/platform/ble_v2.cc new file mode 100644 index 00000000..9b10d6dd --- /dev/null +++ b/internal/platform/ble_v2.cc @@ -0,0 +1,36 @@ +// Copyright 2022 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_v2.h" + +#include "internal/platform/logging.h" +#include "internal/platform/mutex_lock.h" + +namespace location { +namespace nearby { + +using ::location::nearby::api::ble_v2::BleAdvertisementData; +using ::location::nearby::api::ble_v2::PowerMode; + +bool BleV2Medium::StartAdvertising( + const BleAdvertisementData& advertising_data, + const BleAdvertisementData& scan_response_data, PowerMode power_mode) { + return impl_->StartAdvertising(advertising_data, scan_response_data, + power_mode); +} + +bool BleV2Medium::StopAdvertising() { return impl_->StopAdvertising(); } + +} // namespace nearby +} // namespace location diff --git a/internal/platform/ble_v2.h b/internal/platform/ble_v2.h new file mode 100644 index 00000000..e87bd4f2 --- /dev/null +++ b/internal/platform/ble_v2.h @@ -0,0 +1,59 @@ +// Copyright 2022 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. + +#ifndef PLATFORM_PUBLIC_BLE_V2_H_ +#define PLATFORM_PUBLIC_BLE_V2_H_ + +#include "absl/container/flat_hash_map.h" +#include "internal/platform/bluetooth_adapter.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/cancellation_flag.h" +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/implementation/platform.h" +#include "internal/platform/input_stream.h" +#include "internal/platform/mutex.h" +#include "internal/platform/output_stream.h" + +namespace location { +namespace nearby { + +// Container of operations that can be performed over the BLE medium. +class BleV2Medium final { + public: + explicit BleV2Medium(BluetoothAdapter& adapter) + : impl_( + api::ImplementationPlatform::CreateBleV2Medium(adapter.GetImpl())), + adapter_(adapter) {} + + // Returns true once the BLE advertising has been initiated. + bool StartAdvertising( + const api::ble_v2::BleAdvertisementData& advertising_data, + const api::ble_v2::BleAdvertisementData& scan_response_data, + api::ble_v2::PowerMode power_mode); + bool StopAdvertising(); + + bool IsValid() const { return impl_ != nullptr; } + + api::ble_v2::BleMedium& GetImpl() const { return *impl_; } + + private: + Mutex mutex_; + std::unique_ptr impl_; + BluetoothAdapter& adapter_; +}; + +} // namespace nearby +} // namespace location + +#endif // PLATFORM_PUBLIC_BLE_V2_H_ diff --git a/internal/platform/ble_v2_test.cc b/internal/platform/ble_v2_test.cc new file mode 100644 index 00000000..aff0db34 --- /dev/null +++ b/internal/platform/ble_v2_test.cc @@ -0,0 +1,76 @@ +// 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_v2.h" + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "internal/platform/medium_environment.h" + +namespace location { +namespace nearby { +namespace { + +using ::location::nearby::api::ble_v2::BleAdvertisementData; +using ::location::nearby::api::ble_v2::PowerMode; + +constexpr absl::string_view kServiceID{"com.google.location.nearby.apps.test"}; +constexpr absl::string_view kAdvertisementString{"\x0a\x0b\x0c\x0d"}; +constexpr absl::string_view kFastAdvertisementServiceUuid{"\xf3\xfe"}; +constexpr PowerMode kPowerMode(PowerMode::kHigh); + +class BleV2MediumTest : public testing::Test { + protected: + BleV2MediumTest() { env_.Stop(); } + + MediumEnvironment& env_{MediumEnvironment::Instance()}; +}; + +TEST_F(BleV2MediumTest, ConstructorDestructorWorks) { + env_.Start(); + 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()); + 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(BleV2MediumTest, CanStartAdvertising) { + env_.Start(); + BluetoothAdapter adapter_; + BleV2Medium ble{adapter_}; + std::string service_id(kServiceID); + ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + std::string fast_advertisement_service_uuid(kFastAdvertisementServiceUuid); + + BleAdvertisementData advertising_data; + BleAdvertisementData scan_response_data; + + ble.StartAdvertising(advertising_data, scan_response_data, kPowerMode); + + EXPECT_TRUE(ble.StopAdvertising()); + env_.Stop(); +} + +} // namespace +} // namespace nearby +} // namespace location diff --git a/internal/platform/implementation/g3/BUILD b/internal/platform/implementation/g3/BUILD index 7d239231..eddc4915 100644 --- a/internal/platform/implementation/g3/BUILD +++ b/internal/platform/implementation/g3/BUILD @@ -52,12 +52,14 @@ cc_library( testonly = True, srcs = [ "ble.cc", + "ble_v2.cc", "bluetooth_adapter.cc", "bluetooth_classic.cc", "wifi_lan.cc", ], hdrs = [ "ble.h", + "ble_v2.h", "bluetooth_adapter.h", "bluetooth_classic.h", "wifi_lan.h", diff --git a/internal/platform/implementation/g3/ble_v2.cc b/internal/platform/implementation/g3/ble_v2.cc new file mode 100644 index 00000000..5b993b6a --- /dev/null +++ b/internal/platform/implementation/g3/ble_v2.cc @@ -0,0 +1,93 @@ +// Copyright 2022 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/implementation/g3/ble_v2.h" + +#include +#include +#include + +#include "absl/synchronization/mutex.h" +#include "internal/platform/implementation/shared/count_down_latch.h" +#include "internal/platform/logging.h" +#include "internal/platform/medium_environment.h" + +namespace location { +namespace nearby { +namespace g3 { + +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; + +BleV2Medium::BleV2Medium(api::BluetoothAdapter& adapter) + : adapter_(static_cast(&adapter)) { + auto& env = MediumEnvironment::Instance(); + env.RegisterBleV2Medium(*this); +} + +BleV2Medium::~BleV2Medium() { + auto& env = MediumEnvironment::Instance(); + env.UnregisterBleV2Medium(*this); +} + +bool BleV2Medium::StartAdvertising( + const BleAdvertisementData& advertising_data, + const BleAdvertisementData& scan_response_data, PowerMode power_mode) { + return true; +} + +bool BleV2Medium::StopAdvertising() { + NEARBY_LOGS(INFO) << "G3 Ble StopAdvertising"; + return true; +} + +bool BleV2Medium::StartScanning(const std::vector& service_uuids, + PowerMode power_mode, + ScanCallback scan_callback) { + return false; +} + +bool BleV2Medium::StopScanning() { return false; } + +std::unique_ptr BleV2Medium::StartGattServer( + ServerGattConnectionCallback callback) { + return nullptr; +} + +bool BleV2Medium::StartListeningForIncomingBleSockets( + const api::ble_v2::ServerBleSocketLifeCycleCallback& callback) { + return false; +} + +void BleV2Medium::StopListeningForIncomingBleSockets() {} + +std::unique_ptr BleV2Medium::ConnectToGattServer( + api::ble_v2::BlePeripheral& peripheral, Mtu mtu, PowerMode power_mode, + api::ble_v2::ClientGattConnectionCallback callback) { + return nullptr; +} + +std::unique_ptr BleV2Medium::EstablishBleSocket( + api::ble_v2::BlePeripheral* peripheral, + const BleSocketLifeCycleCallback& callback) { + return nullptr; +} + +} // namespace g3 +} // namespace nearby +} // namespace location diff --git a/internal/platform/implementation/g3/ble_v2.h b/internal/platform/implementation/g3/ble_v2.h new file mode 100644 index 00000000..68b8b18a --- /dev/null +++ b/internal/platform/implementation/g3/ble_v2.h @@ -0,0 +1,94 @@ +// Copyright 2022 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. + +#ifndef PLATFORM_IMPL_G3_BLE_V2_H_ +#define PLATFORM_IMPL_G3_BLE_V2_H_ + +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/escaping.h" +#include "absl/synchronization/mutex.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/implementation/g3/bluetooth_adapter.h" +#include "internal/platform/implementation/g3/bluetooth_classic.h" +#include "internal/platform/implementation/g3/multi_thread_executor.h" +#include "internal/platform/implementation/g3/pipe.h" +#include "internal/platform/input_stream.h" +#include "internal/platform/output_stream.h" + +namespace location { +namespace nearby { +namespace g3 { + +// TODO(b/213691253): Add g3 BleV2 medium tests after more functions are ready. +// Container of operations that can be performed over the BLE medium. +class BleV2Medium : public api::ble_v2::BleMedium { + public: + explicit BleV2Medium(api::BluetoothAdapter& adapter); + ~BleV2Medium() override; + + // Returns true once the Ble advertising has been initiated. + bool StartAdvertising( + const api::ble_v2::BleAdvertisementData& advertising_data, + const api::ble_v2::BleAdvertisementData& scan_response_data, + api::ble_v2::PowerMode power_mode) override ABSL_LOCKS_EXCLUDED(mutex_); + bool StopAdvertising() override ABSL_LOCKS_EXCLUDED(mutex_); + + bool StartScanning(const std::vector& service_uuids, + api::ble_v2::PowerMode power_mode, + ScanCallback callback) override + ABSL_LOCKS_EXCLUDED(mutex_); + bool StopScanning() override ABSL_LOCKS_EXCLUDED(mutex_); + std::unique_ptr StartGattServer( + api::ble_v2::ServerGattConnectionCallback callback) override + ABSL_LOCKS_EXCLUDED(mutex_); + bool StartListeningForIncomingBleSockets( + const api::ble_v2::ServerBleSocketLifeCycleCallback& callback) override + ABSL_LOCKS_EXCLUDED(mutex_); + void StopListeningForIncomingBleSockets() override + ABSL_LOCKS_EXCLUDED(mutex_); + std::unique_ptr ConnectToGattServer( + api::ble_v2::BlePeripheral& peripheral, Mtu mtu, + api::ble_v2::PowerMode power_mode, + api::ble_v2::ClientGattConnectionCallback callback) override + ABSL_LOCKS_EXCLUDED(mutex_); + std::unique_ptr EstablishBleSocket( + api::ble_v2::BlePeripheral* peripheral, + const api::ble_v2::BleSocketLifeCycleCallback& callback) override + ABSL_LOCKS_EXCLUDED(mutex_); + + BluetoothAdapter& GetAdapter() { return *adapter_; } + + private: + struct AdvertisingInfo { + bool Empty() const { return service_id.empty(); } + void Clear() { service_id.clear(); } + + std::string service_id; + }; + + absl::Mutex mutex_; + BluetoothAdapter* adapter_; // Our device adapter; read-only. + AdvertisingInfo advertising_info_ ABSL_GUARDED_BY(mutex_); +}; + +} // namespace g3 +} // namespace nearby +} // namespace location + +#endif // PLATFORM_IMPL_G3_BLE_V2_H_ diff --git a/internal/platform/implementation/g3/platform.cc b/internal/platform/implementation/g3/platform.cc index ed937acd..10139675 100644 --- a/internal/platform/implementation/g3/platform.cc +++ b/internal/platform/implementation/g3/platform.cc @@ -23,15 +23,14 @@ #include "absl/time/time.h" #include "internal/platform/implementation/atomic_boolean.h" #include "internal/platform/implementation/atomic_reference.h" -#include "internal/platform/implementation/ble_v2.h" #include "internal/platform/implementation/bluetooth_adapter.h" #include "internal/platform/implementation/bluetooth_classic.h" #include "internal/platform/implementation/condition_variable.h" -#include "internal/platform/implementation/shared/count_down_latch.h" #include "internal/platform/implementation/log_message.h" #include "internal/platform/implementation/mutex.h" #include "internal/platform/implementation/scheduled_executor.h" #include "internal/platform/implementation/server_sync.h" +#include "internal/platform/implementation/shared/count_down_latch.h" #include "internal/platform/implementation/submittable_executor.h" #ifndef NO_WEBRTC #include "internal/platform/implementation/g3/webrtc.h" @@ -40,6 +39,7 @@ #include "internal/platform/implementation/g3/atomic_boolean.h" #include "internal/platform/implementation/g3/atomic_reference.h" #include "internal/platform/implementation/g3/ble.h" +#include "internal/platform/implementation/g3/ble_v2.h" #include "internal/platform/implementation/g3/bluetooth_adapter.h" #include "internal/platform/implementation/g3/bluetooth_classic.h" #include "internal/platform/implementation/g3/condition_variable.h" @@ -70,37 +70,37 @@ int GetCurrentTid() { std::unique_ptr ImplementationPlatform::CreateSingleThreadExecutor() { - return absl::make_unique(); + return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreateMultiThreadExecutor(int max_concurrency) { - return absl::make_unique(max_concurrency); + return std::make_unique(max_concurrency); } std::unique_ptr ImplementationPlatform::CreateScheduledExecutor() { - return absl::make_unique(); + return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreateAtomicUint32( std::uint32_t value) { - return absl::make_unique(value); + return std::make_unique(value); } std::unique_ptr ImplementationPlatform::CreateBluetoothAdapter() { - return absl::make_unique(); + return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreateCountDownLatch( std::int32_t count) { - return absl::make_unique(count); + return std::make_unique(count); } std::unique_ptr ImplementationPlatform::CreateAtomicBoolean( bool initial_value) { - return absl::make_unique(initial_value); + return std::make_unique(initial_value); } std::unique_ptr ImplementationPlatform::CreateInputFile( @@ -116,23 +116,23 @@ std::unique_ptr ImplementationPlatform::CreateOutputFile( std::unique_ptr ImplementationPlatform::CreateLogMessage( const char* file, int line, LogMessage::Severity severity) { - return absl::make_unique(file, line, severity); + return std::make_unique(file, line, severity); } std::unique_ptr ImplementationPlatform::CreateBluetoothClassicMedium( api::BluetoothAdapter& adapter) { - return absl::make_unique(adapter); + return std::make_unique(adapter); } std::unique_ptr ImplementationPlatform::CreateBleMedium( api::BluetoothAdapter& adapter) { - return absl::make_unique(adapter); + return std::make_unique(adapter); } -std::unique_ptr ImplementationPlatform::CreateBleV2Medium( - api::BluetoothAdapter& adapter) { - return std::unique_ptr(); +std::unique_ptr +ImplementationPlatform::CreateBleV2Medium(api::BluetoothAdapter& adapter) { + return std::make_unique(adapter); } std::unique_ptr @@ -145,13 +145,13 @@ std::unique_ptr ImplementationPlatform::CreateWifiMedium() { } std::unique_ptr ImplementationPlatform::CreateWifiLanMedium() { - return absl::make_unique(); + return std::make_unique(); } #ifndef NO_WEBRTC std::unique_ptr ImplementationPlatform::CreateWebRtcMedium() { if (MediumEnvironment::Instance().GetEnvironmentConfig().webrtc_enabled) { - return absl::make_unique(); + return std::make_unique(); } else { return nullptr; } @@ -160,9 +160,9 @@ std::unique_ptr ImplementationPlatform::CreateWebRtcMedium() { std::unique_ptr ImplementationPlatform::CreateMutex(Mutex::Mode mode) { if (mode == Mutex::Mode::kRecursive) - return absl::make_unique(); + return std::make_unique(); else - return absl::make_unique(mode == Mutex::Mode::kRegular); + return std::make_unique(mode == Mutex::Mode::kRegular); } std::unique_ptr diff --git a/internal/platform/implementation/ios/Source/Internal/platform.mm b/internal/platform/implementation/ios/Source/Internal/platform.mm index a7366bb9..ada045ce 100644 --- a/internal/platform/implementation/ios/Source/Internal/platform.mm +++ b/internal/platform/implementation/ios/Source/Internal/platform.mm @@ -129,8 +129,8 @@ std::unique_ptr ImplementationPlatform::CreateBleMedium(api::Bluetoot return nullptr; } -std::unique_ptr ImplementationPlatform::CreateBleV2Medium( - api::BluetoothAdapter& adapter) { +std::unique_ptr +ImplementationPlatform::CreateBleV2Medium(api::BluetoothAdapter& adapter) { return nullptr; } diff --git a/internal/platform/medium_environment.cc b/internal/platform/medium_environment.cc index 5d9f619e..104dac01 100644 --- a/internal/platform/medium_environment.cc +++ b/internal/platform/medium_environment.cc @@ -59,6 +59,7 @@ void MediumEnvironment::Reset() { bluetooth_adapters_.clear(); bluetooth_mediums_.clear(); ble_mediums_.clear(); + ble_v2_mediums_.clear(); #ifndef NO_WEBRTC webrtc_signaling_message_callback_.clear(); webrtc_signaling_complete_callback_.clear(); @@ -477,6 +478,31 @@ void MediumEnvironment::CallBleAcceptedConnectionCallback( info.accepted_connection_callback.accepted_cb(socket, service_id); }); } + +void MediumEnvironment::RegisterBleV2Medium(api::ble_v2::BleMedium& medium) { + if (!enabled_) return; + RunOnMediumEnvironmentThread([this, &medium]() { + ble_v2_mediums_.insert({&medium, BleV2MediumContext{}}); + NEARBY_LOGS(INFO) << "Registered: medium:" << &medium; + }); +} + +// TODO(b/213691253): Add g3 BleV2 medium tests after more functions are ready. +void MediumEnvironment::UpdateBleV2MediumForAdvertising( + api::ble_v2::BleMedium& medium, api::ble_v2::BlePeripheral& peripheral, + const std::string& service_id, bool fast_advertisement, bool enabled) { + if (!enabled_) return; +} + +void MediumEnvironment::UnregisterBleV2Medium(api::ble_v2::BleMedium& medium) { + if (!enabled_) return; + RunOnMediumEnvironmentThread([this, &medium]() { + auto item = ble_v2_mediums_.extract(&medium); + if (item.empty()) return; + NEARBY_LOGS(INFO) << "Unregistered Ble medium"; + }); +} + #ifndef NO_WEBRTC void MediumEnvironment::RegisterWebRtcSignalingMessenger( absl::string_view self_id, OnSignalingMessageCallback message_callback, diff --git a/internal/platform/medium_environment.h b/internal/platform/medium_environment.h index 609e9595..ef703939 100644 --- a/internal/platform/medium_environment.h +++ b/internal/platform/medium_environment.h @@ -21,6 +21,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" #include "internal/platform/implementation/ble.h" +#include "internal/platform/implementation/ble_v2.h" #include "internal/platform/implementation/bluetooth_adapter.h" #include "internal/platform/implementation/bluetooth_classic.h" #ifndef NO_WEBRTC @@ -204,6 +205,21 @@ class MediumEnvironment { api::BleSocket& socket, const std::string& service_id); + // Adds medium-related info to allow for scanning/advertising to work. + // This provides acccess to this medium from other mediums, when protocol + // expects they should communicate. + void RegisterBleV2Medium(api::ble_v2::BleMedium& medium); + + // Updates advertising info to indicate the current medium is exposing + // advertising event. + void UpdateBleV2MediumForAdvertising(api::ble_v2::BleMedium& medium, + api::ble_v2::BlePeripheral& peripheral, + const std::string& service_id, + bool fast_advertisement, bool enabled); + + // Removes medium-related info. This should correspond to device power off. + void UnregisterBleV2Medium(api::ble_v2::BleMedium& mediumum); + // Adds medium-related info to allow for discovery/advertising to work. // This provides acccess to this medium from other mediums, when protocol // expects they should communicate. @@ -255,6 +271,9 @@ class MediumEnvironment { bool fast_advertisement = false; }; + struct BleV2MediumContext { + }; + struct WifiLanMediumContext { // advertising service type vs NsdServiceInfo map. absl::flat_hash_map advertising_services; @@ -303,6 +322,10 @@ class MediumEnvironment { bluetooth_mediums_; absl::flat_hash_map ble_mediums_; + + absl::flat_hash_map + ble_v2_mediums_; + #ifndef NO_WEBRTC // Maps peer id to callback for receiving signaling messages. absl::flat_hash_map @@ -312,6 +335,7 @@ class MediumEnvironment { absl::flat_hash_map webrtc_signaling_complete_callback_; #endif + absl::flat_hash_map wifi_lan_mediums_;