From c4ad6de6f81d155e19df976d4ad1c4b449da4611 Mon Sep 17 00:00:00 2001 From: Hai Shang Date: Mon, 3 Oct 2022 19:33:55 -0700 Subject: [PATCH] add scan function in presence BLE PiperOrigin-RevId: 478666843 --- internal/platform/BUILD | 2 + internal/platform/implementation/BUILD | 2 +- presence/implementation/mediums/BUILD | 34 ++++- presence/implementation/mediums/ble.h | 54 ++++++- presence/implementation/mediums/ble_test.cc | 155 ++++++++++++++++++++ presence/implementation/mediums/mediums.h | 5 +- 6 files changed, 243 insertions(+), 9 deletions(-) create mode 100644 presence/implementation/mediums/ble_test.cc diff --git a/internal/platform/BUILD b/internal/platform/BUILD index a28c7632..6b4c0422 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -220,6 +220,7 @@ cc_library( "//connections:__subpackages__", "//internal/platform:__pkg__", "//internal/platform/implementation:__subpackages__", + "//presence:__subpackages__", ], deps = [ ":base", @@ -341,6 +342,7 @@ cc_library( "//internal/platform/implementation/ios:__subpackages__", "//internal/platform/implementation/windows:__subpackages__", "//location/nearby/cpp/sharing:__subpackages__", + "//presence:__subpackages__", ], deps = [ ":base", diff --git a/internal/platform/implementation/BUILD b/internal/platform/implementation/BUILD index c69760af..f28f9005 100644 --- a/internal/platform/implementation/BUILD +++ b/internal/platform/implementation/BUILD @@ -70,7 +70,7 @@ cc_library( "//internal/platform:__pkg__", "//internal/platform/implementation:__subpackages__", "//presence:__pkg__", - "//presence/implementation:__pkg__", + "//presence/implementation:__subpackages__", ], deps = [ "//connections/implementation/proto:offline_wire_formats_cc_proto", diff --git a/presence/implementation/mediums/BUILD b/presence/implementation/mediums/BUILD index 3993ad82..792d2658 100644 --- a/presence/implementation/mediums/BUILD +++ b/presence/implementation/mediums/BUILD @@ -15,7 +15,8 @@ licenses(["notice"]) cc_library( name = "mediums", - srcs = [], + srcs = [ + ], hdrs = [ "ble.h", "mediums.h", @@ -23,5 +24,34 @@ cc_library( visibility = [ "//presence/implementation:__subpackages__", ], - deps = ["//internal/platform:comm"], + deps = [ + "//internal/platform:comm", + "//internal/platform:uuid", + "//internal/platform/implementation:comm", + "//presence:types", + ], +) + +cc_test( + name = "mediums_test", + size = "small", + srcs = [ + "ble_test.cc", + ], + shard_count = 16, + deps = [ + ":mediums", + "//internal/platform:base", + "//internal/platform:comm", + "//internal/platform:test_util", + "//internal/platform:types", + "//internal/platform:uuid", + "//internal/platform/implementation:comm", + "//internal/platform/implementation/g3", # build_cleaner: keep + "//presence:types", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest_main", + ], ) diff --git a/presence/implementation/mediums/ble.h b/presence/implementation/mediums/ble.h index 1a14fcbe..c26d9603 100644 --- a/presence/implementation/mediums/ble.h +++ b/presence/implementation/mediums/ble.h @@ -15,24 +15,70 @@ #ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_BLE_H_ #define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_BLE_H_ -#include "internal/platform/ble_v2.h" +#include + +#include "internal/platform/bluetooth_adapter.h" +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/uuid.h" +#include "presence/power_mode.h" +#include "presence/scan_request.h" namespace nearby { namespace presence { +/** Presence advertisement service data uuid. */ +ABSL_CONST_INIT const location::nearby::Uuid kPresenceServiceUuid( + 0x0000fcf100001000, 0x800000805f9b34fb); + +using ScanningSession = + ::location::nearby::api::ble_v2::BleMedium::ScanningSession; +using ScanningCallback = + ::location::nearby::api::ble_v2::BleMedium::ScanningCallback; +using ::location::nearby::api::ble_v2::TxPowerLevel; + /* * This Ble class utilizes platform/ble_v2 BleV2Medium, provides ble functions * for presence logic layer to invoke. * This class would have states like if ble is available or not, if it's doing * broadcast/scan. */ +template +// since we are using template for test, then we need to keep functions defs in +// the header, more details: go/cstyle#Self_contained_Headers. class Ble { public: - Ble() = default; + explicit Ble(location::nearby::BluetoothAdapter& bluetooth_adapter) + : adapter_(bluetooth_adapter), + medium_(std::make_unique(bluetooth_adapter)) {} ~Ble() = default; - // TODO(hais): add expected function set. + + bool IsAvailable() const { return medium_->IsValid(); } + + std::unique_ptr StartScanning(ScanRequest scan_request, + ScanningCallback callback) { + return medium_->StartScanning( + kPresenceServiceUuid, + ConvertPowerModeToPowerLevel(scan_request.power_mode), callback); + } + private: - location::nearby::BleV2Medium medium_; + friend class BleTest; + location::nearby::BluetoothAdapter& adapter_; + std::unique_ptr medium_; + + TxPowerLevel ConvertPowerModeToPowerLevel(PowerMode power_mode) { + switch (power_mode) { + case PowerMode::kNoPower: + return TxPowerLevel::kUnknown; + case PowerMode::kLowPower: + return TxPowerLevel::kLow; + case PowerMode::kBalanced: + return TxPowerLevel::kMedium; + case PowerMode::kLowLatency: + return TxPowerLevel::kHigh; + } + return TxPowerLevel::kUnknown; + } }; } // namespace presence diff --git a/presence/implementation/mediums/ble_test.cc b/presence/implementation/mediums/ble_test.cc new file mode 100644 index 00000000..ea2f354d --- /dev/null +++ b/presence/implementation/mediums/ble_test.cc @@ -0,0 +1,155 @@ +// 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/mediums/ble.h" + +#include +#include +#include + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "internal/platform/ble_v2.h" +#include "internal/platform/bluetooth_adapter.h" +#include "internal/platform/count_down_latch.h" +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/medium_environment.h" +#include "internal/platform/uuid.h" +#include "presence/data_element.h" +#include "presence/scan_request.h" + +namespace nearby { +namespace presence { + +using FeatureFlags = location::nearby::FeatureFlags::Flags; +using BleOperationStatus = location::nearby::api::ble_v2::BleOperationStatus; +using BleV2MediumStatus = + location::nearby::MediumEnvironment::BleV2MediumStatus; +using ScanningSession = + location::nearby::api::ble_v2::BleMedium::ScanningSession; +using TxPowerLevel = location::nearby::api::ble_v2::TxPowerLevel; +using ScanningCallback = + location::nearby::api::ble_v2::BleMedium::ScanningCallback; +using Uuid = location::nearby::Uuid; + +constexpr FeatureFlags kTestCases[] = { + FeatureFlags{}, +}; + +class BleTest : public testing::TestWithParam { + public: + class MockBleMedium { + public: + explicit MockBleMedium(location::nearby::BluetoothAdapter& adapter){} + + MOCK_METHOD((std::unique_ptr), StartScanning, + (const Uuid& service_uuid, TxPowerLevel tx_power_level, + ScanningCallback callback)); + }; + constexpr static absl::Duration kWaitDuration = absl::Milliseconds(1000); + + std::string account_name_ = "Test-Name"; + constexpr static PowerMode kPowerMode = PowerMode::kBalanced; + std::vector identity_types_ = { + nearby::internal::IdentityType::IDENTITY_TYPE_TRUSTED, + }; + std::vector extended_properties_ = { + DataElement{DataElement::kTxPowerFieldType, "-10"}}; + std::vector > + filters_ = {PresenceScanFilter{ + .scan_type = ScanType::kPresenceScan, + .extended_properties = extended_properties_, + }}; + constexpr static bool kUseBle = true; + constexpr static ScanType kScanType = ScanType::kPresenceScan; + constexpr static bool kScanOnlyWhenScreenOn = true; + + ScanRequest scan_request_ = { + .account_name = account_name_, + .identity_types = identity_types_, + .scan_filters = filters_, + .use_ble = kUseBle, + .scan_type = kScanType, + .power_mode = kPowerMode, + .scan_only_when_screen_on = kScanOnlyWhenScreenOn, + }; + + protected: + BleTest() { env_.Stop(); } + absl::optional GetBleStatus( + const Ble& ble) { + return env_.GetBleV2MediumStatus(*ble.medium_->GetImpl()); + } + MockBleMedium* GetMedium(const Ble& ble) { + return ble.medium_.get(); + } + location::nearby::MediumEnvironment& env_{ + location::nearby::MediumEnvironment::Instance()}; +}; + +INSTANTIATE_TEST_SUITE_P(ParametrisedBleTest, BleTest, + ::testing::ValuesIn(kTestCases)); + +// Using MediumEnvironment to verify the start&stop StartScanning callback flows +// are working as intended. +TEST_P(BleTest, CanStartThenStopScanning) { + env_.Start(); + ::location::nearby::BluetoothAdapter adapter; + Ble ble(adapter); + + ScanRequest scan_request{ + .power_mode = PowerMode::kBalanced, + }; + ScanningCallback scanning_callback; + location::nearby::CountDownLatch started_scanning_latch(1); + + std::unique_ptr scannning_session = ble.StartScanning( + scan_request, + ScanningCallback{ + .start_scanning_result = + [&started_scanning_latch](BleOperationStatus status) { + if (status == BleOperationStatus::kSucceeded) { + started_scanning_latch.CountDown(); + } + }, + }); + + EXPECT_TRUE(started_scanning_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(GetBleStatus(ble).has_value() && + GetBleStatus(ble).value().is_scanning == true); + BleOperationStatus stop_scanning_status = scannning_session->stop_scanning(); + EXPECT_EQ(BleOperationStatus::kSucceeded, stop_scanning_status); + EXPECT_TRUE(GetBleStatus(ble).has_value() && + GetBleStatus(ble).value().is_scanning == false); + env_.Stop(); +} + +// Using MockBleMedium to verify StartScanning is using the expected parameters +// with underneath BleMedium. +TEST_P(BleTest, VerifyStartScanning) { + env_.Start(); + ::location::nearby::BluetoothAdapter adapter; + Ble ble(adapter); + EXPECT_CALL(*GetMedium(ble), StartScanning(kPresenceServiceUuid, + TxPowerLevel::kMedium, testing::_)) + .Times(1); + + std::unique_ptr scanning_session = + ble.StartScanning(scan_request_, ScanningCallback{}); + env_.Stop(); +} + +} // namespace presence +} // namespace nearby diff --git a/presence/implementation/mediums/mediums.h b/presence/implementation/mediums/mediums.h index c3f3d840..96ba42e5 100644 --- a/presence/implementation/mediums/mediums.h +++ b/presence/implementation/mediums/mediums.h @@ -15,6 +15,7 @@ #ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_MEDIUMS_H_ #define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_MEDIUMS_H_ +#include "internal/platform/ble_v2.h" #include "presence/implementation/mediums/ble.h" namespace nearby { @@ -30,10 +31,10 @@ class Mediums { ~Mediums() = default; // Returns a handle to the Ble medium. - Ble& GetBle(); + Ble& GetBle(); private: - Ble ble_; + Ble ble_; }; } // namespace presence