From a11733988f8e2bc7b2cb3cedf3cdd531a58048ed Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Fri, 18 Nov 2022 16:15:20 -0800 Subject: [PATCH] Implement start advertising in service controller. PiperOrigin-RevId: 489584407 --- internal/platform/medium_environment.cc | 1 + presence/data_types.h | 2 +- presence/implementation/BUILD | 16 +++ .../implementation/service_controller_impl.cc | 61 +++++++- .../implementation/service_controller_impl.h | 3 + .../service_controller_impl_test.cc | 136 ++++++++++++++++++ 6 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 presence/implementation/service_controller_impl_test.cc diff --git a/internal/platform/medium_environment.cc b/internal/platform/medium_environment.cc index 4906c927..638a283f 100644 --- a/internal/platform/medium_environment.cc +++ b/internal/platform/medium_environment.cc @@ -726,6 +726,7 @@ MediumEnvironment::GetBleV2MediumStatus(const api::ble_v2::BleMedium& medium) { return; } BleV2MediumContext context = it->second; + result = BleV2MediumStatus{.is_advertising = context.advertising, .is_scanning = context.scanning}; latch.CountDown(); diff --git a/presence/data_types.h b/presence/data_types.h index 572926bc..3ebf9315 100644 --- a/presence/data_types.h +++ b/presence/data_types.h @@ -83,7 +83,7 @@ struct BroadcastSession { // Nearby library would provide the implementation of this callback in // runtime. Assiging with a default value NotImplemented to surface potential // issue where library failed to provide the implementation. - std::function stop_broadcast_callback = []() { + absl::AnyInvocable stop_broadcast_callback = []() { return Status{Status::Value::kNotImplemented}; }; }; diff --git a/presence/implementation/BUILD b/presence/implementation/BUILD index 2f416b09..f388d0b6 100644 --- a/presence/implementation/BUILD +++ b/presence/implementation/BUILD @@ -135,6 +135,22 @@ cc_test( ], ) +cc_test( + name = "service_controller_impl_test", + size = "small", + srcs = ["service_controller_impl_test.cc"], + deps = [ + ":internal", + "//internal/platform:base", + "//internal/platform:test_util", + "//internal/platform:types", + "//internal/platform/implementation/g3", # build_cleaner: keep + "//internal/proto:credential_cc_proto", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "encryption_test", size = "small", diff --git a/presence/implementation/service_controller_impl.cc b/presence/implementation/service_controller_impl.cc index 0875df75..ec14f99c 100644 --- a/presence/implementation/service_controller_impl.cc +++ b/presence/implementation/service_controller_impl.cc @@ -15,18 +15,75 @@ #include "presence/implementation/service_controller_impl.h" #include +#include +#include + +#include "absl/status/statusor.h" +#include "internal/platform/implementation/ble_v2.h" +#include "presence/implementation/advertisement_factory.h" +#include "presence/implementation/base_broadcast_request.h" +#include "presence/implementation/mediums/advertisement_data.h" +#include "presence/status.h" namespace nearby { namespace presence { +namespace { + +using ::location::nearby::api::ble_v2::BleOperationStatus; +using AdvertisingCallback = + ::location::nearby::api::ble_v2::BleMedium::AdvertisingCallback; +using AdvertisingSession = + ::location::nearby::api::ble_v2::BleMedium::AdvertisingSession; + +Status ConvertBleStatus(BleOperationStatus status) { + return status == BleOperationStatus::kSucceeded + ? Status{Status::Value::kSuccess} + : Status{Status::Value::kError}; +} + +} // namespace std::unique_ptr ServiceControllerImpl::StartScan( ScanRequest scan_request, ScanCallback callback) { return scan_manager_.StartScan(scan_request, callback); } + std::unique_ptr ServiceControllerImpl::StartBroadcast( BroadcastRequest broadcast_request, BroadcastCallback callback) { - callback.start_broadcast_cb({Status::Value::kError}); - return nullptr; + absl::StatusOr request = + BaseBroadcastRequest::Create(broadcast_request); + if (!request.ok()) { + NEARBY_LOGS(WARNING) << "Invalid broadcast request, reason: " + << request.status(); + callback.start_broadcast_cb(Status{Status::Value::kError}); + return nullptr; + } + absl::StatusOr advertisement = + AdvertisementFactory(&credential_manager_).CreateAdvertisement(*request); + if (!advertisement.ok()) { + NEARBY_LOGS(WARNING) << "Can't create advertisement, reason: " + << advertisement.status(); + callback.start_broadcast_cb(Status{Status::Value::kError}); + return nullptr; + } + std::unique_ptr session = + mediums_.GetBle().StartAdvertising( + *advertisement, broadcast_request.power_mode, + AdvertisingCallback{.start_advertising_result = + [callback](BleOperationStatus status) { + callback.start_broadcast_cb( + ConvertBleStatus(status)); + }}); + if (!session) { + NEARBY_LOGS(WARNING) << "Failed to start broadcasting"; + callback.start_broadcast_cb(Status{Status::Value::kError}); + return nullptr; + } + + return std::make_unique(BroadcastSession{ + .stop_broadcast_callback = [session = std::move(session)]() { + return ConvertBleStatus(session->stop_advertising()); + }}); } } // namespace presence diff --git a/presence/implementation/service_controller_impl.h b/presence/implementation/service_controller_impl.h index 6e67abb5..19039160 100644 --- a/presence/implementation/service_controller_impl.h +++ b/presence/implementation/service_controller_impl.h @@ -39,6 +39,9 @@ class ServiceControllerImpl : public ServiceController { std::unique_ptr StartBroadcast( BroadcastRequest broadcast_request, BroadcastCallback callback) override; + // Gives tests access to mediums. + Mediums& GetMediums() { return mediums_; } + private: Mediums mediums_; // NOLINT: further impl will use it. CredentialManagerImpl diff --git a/presence/implementation/service_controller_impl_test.cc b/presence/implementation/service_controller_impl_test.cc new file mode 100644 index 00000000..68e239e4 --- /dev/null +++ b/presence/implementation/service_controller_impl_test.cc @@ -0,0 +1,136 @@ +// 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 "presence/implementation/service_controller_impl.h" + +#include +#include + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "internal/platform/feature_flags.h" +#include "internal/platform/future.h" +#include "internal/platform/medium_environment.h" +#include "internal/proto/credential.pb.h" + +namespace nearby { +namespace presence { +namespace { + +using FeatureFlags = location::nearby::FeatureFlags::Flags; +using internal::IdentityType; +using location::nearby::MediumEnvironment; + +constexpr FeatureFlags kTestCases[] = { + FeatureFlags{}, +}; + +constexpr absl::string_view kAccountName = "Test account"; +constexpr int8_t kTxPower = 30; + +BroadcastRequest CreateBroadcastRequest(IdentityType identity) { + PresenceBroadcast::BroadcastSection section = { + .identity = identity, + .extended_properties = {DataElement( + DataElement(ActionBit::kActiveUnlockAction))}, + .account_name = std::string(kAccountName)}; + PresenceBroadcast presence_request = {.sections = {section}}; + BroadcastRequest request = {.tx_power = kTxPower, + .variant = presence_request}; + return request; +} + +class MediumEnvironmentStarter { + public: + MediumEnvironmentStarter() { MediumEnvironment::Instance().Start(); } + ~MediumEnvironmentStarter() { MediumEnvironment::Instance().Stop(); } +}; + +class ServiceControllerImplTest : public testing::TestWithParam { + protected: + // The medium environment must be initialized (started) before the service + // controller. + MediumEnvironmentStarter env_; + ServiceControllerImpl service_controller_; + location::nearby::Future start_broadcast_status_; + BroadcastCallback broadcast_callback_{ + .start_broadcast_cb = [this](Status status) { + start_broadcast_status_.Set(status); + }}; +}; + +INSTANTIATE_TEST_SUITE_P(ParametrisedServiceControllerImplTest, + ServiceControllerImplTest, + testing::ValuesIn(kTestCases)); + +TEST_P(ServiceControllerImplTest, StartBroadcastPublicIdentity) { + std::unique_ptr session = + service_controller_.StartBroadcast( + CreateBroadcastRequest(internal::IDENTITY_TYPE_PUBLIC), + broadcast_callback_); + + EXPECT_TRUE(session); + EXPECT_TRUE(start_broadcast_status_.Get().ok()); + EXPECT_EQ(start_broadcast_status_.Get().GetResult(), + Status{Status::Value::kSuccess}); + EXPECT_TRUE(MediumEnvironment::Instance() + .GetBleV2MediumStatus( + *service_controller_.GetMediums().GetBle().GetImpl()) + ->is_advertising); +} + +TEST_P(ServiceControllerImplTest, StartAndStopBroadcast) { + std::unique_ptr session = + service_controller_.StartBroadcast( + CreateBroadcastRequest(internal::IDENTITY_TYPE_PUBLIC), + broadcast_callback_); + + ASSERT_TRUE(session); + EXPECT_EQ(session->stop_broadcast_callback(), + Status{Status::Value::kSuccess}); + MediumEnvironment::Instance().Sync(); + EXPECT_FALSE(MediumEnvironment::Instance() + .GetBleV2MediumStatus( + *service_controller_.GetMediums().GetBle().GetImpl()) + ->is_advertising); +} + +TEST_P(ServiceControllerImplTest, StartBroadcastInvalidRequestFails) { + std::unique_ptr session = + service_controller_.StartBroadcast(BroadcastRequest{}, + broadcast_callback_); + + EXPECT_FALSE(session); + EXPECT_TRUE(start_broadcast_status_.Get().ok()); + EXPECT_EQ(start_broadcast_status_.Get().GetResult(), + Status{Status::Value::kError}); +} + +TEST_P(ServiceControllerImplTest, StartBroadcastPrivateIdentityFails) { + // TODO(b/256249404): Support private identity. + std::unique_ptr session = + service_controller_.StartBroadcast( + CreateBroadcastRequest(internal::IDENTITY_TYPE_PRIVATE), + broadcast_callback_); + + EXPECT_FALSE(session); + EXPECT_TRUE(start_broadcast_status_.Get().ok()); + EXPECT_EQ(start_broadcast_status_.Get().GetResult(), + Status{Status::Value::kError}); +} + +} // namespace +} // namespace presence +} // namespace nearby