diff --git a/.bazelrc b/.bazelrc index eee8fe04..ad54b2bd 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,4 +1,10 @@ build --action_env=BAZEL_CXXOPTS=-"std=c++20" --repo_env=BAZEL_CONLYOPTS="-std=gnu17" +build --action_env=CC=/usr/bin/clang +build --action_env=CXX=/usr/bin/clang++ +build --check_visibility=false --spawn_strategy=standalone --verbose_failures +build --cxxopt=-std=c++20 +build --cxxopt='-fvisibility-inlines-hidden' +build --host_cxxopt=-std=c++20 # Definition of --config=memcheck build:memcheck --strip=never --test_timeout=3600 common --enable_bzlmod diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index cafc5b01..dbc820b6 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -69,14 +69,8 @@ jobs: - name: Build Connections run: | bazel build \ - --check_visibility=false \ - --spawn_strategy=standalone \ - --cxxopt=-std=c++20 \ - --host_cxxopt=-std=c++20 \ --@com_google_protobuf//bazel/toolchains:prefer_prebuilt_protoc=true \ --copt='-DGITHUB_BUILD' \ - --copt='-fvisibility=hidden' \ - --cxxopt='-fvisibility-inlines-hidden' \ //connections:core # - name: Build Presence @@ -92,27 +86,21 @@ jobs: # --cxxopt='-fvisibility-inlines-hidden' \ # //presence - - name: Build Nearby Sharing Linux shared API + - name: Build Nearby Sharing Linux run: | bazel build \ - --check_visibility=false \ - --spawn_strategy=standalone \ - --cxxopt=-std=c++20 \ - --host_cxxopt=-std=c++20 \ --@com_google_protobuf//bazel/toolchains:prefer_prebuilt_protoc=true \ --copt='-DGITHUB_BUILD' \ - --copt='-fvisibility=hidden' \ - --cxxopt='-fvisibility-inlines-hidden' \ - //sharing/linux:nearby_sharing_api_shared + //sharing/linux:nearby_sharing_cli - build-rust-linux: - name: Build Rust on Linux - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - submodules: recursive + #build-rust-linux: + # name: Build Rust on Linux + # runs-on: ubuntu-latest + # steps: + # - name: Checkout + # uses: actions/checkout@v6 + # with: + # submodules: recursive - - name: Build FPP - run: cargo build --manifest-path presence/fpp/fpp/Cargo.toml + # - name: Build FPP + # run: cargo build --manifest-path presence/fpp/fpp/Cargo.toml diff --git a/sharing/proto/analytics/BUILD b/BUILD.bazel similarity index 57% rename from sharing/proto/analytics/BUILD rename to BUILD.bazel index 3a8e26d8..82473d02 100644 --- a/sharing/proto/analytics/BUILD +++ b/BUILD.bazel @@ -1,4 +1,4 @@ -# Copyright 2022 Google LLC +# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,25 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("@com_google_protobuf//bazel:cc_proto_library.bzl", "cc_proto_library") -load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library") - -licenses(["notice"]) +load("@bazel_skylib//rules:common_settings.bzl", "bool_flag") package(default_visibility = ["//visibility:public"]) -proto_library( - name = "sharing_log_proto", - srcs = [ - "nearby_sharing_log.proto", - ], - deps = [ - "//proto:sharing_enums_proto", - "@com_google_protobuf//:duration_proto", - ], +licenses(["notice"]) + +bool_flag( + name = "enable_webrtc", + build_setting_default = False, ) -cc_proto_library( - name = "sharing_log_cc_proto", - deps = [":sharing_log_proto"], +config_setting( + name = "webrtc_enabled", + flag_values = {":enable_webrtc": "True"}, ) diff --git a/MODULE.bazel b/MODULE.bazel index a465e98b..5bfd7f6e 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -11,6 +11,10 @@ bazel_dep(name = "googletest", version = "1.17.0.bcr.2", repo_name = "com_google bazel_dep(name = "boringssl", version = "0.20251124.0") bazel_dep(name = "rules_foreign_cc", version = "0.15.1") +# for linux TUI +# Should update to 7.0.0 +bazel_dep(name = "ftxui", version = "6.1.9") + git_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") @@ -190,10 +194,11 @@ cmake( "CMAKE_C_STANDARD": "17", "CMAKE_C_STANDARD_REQUIRED": "ON", "CMAKE_C_EXTENSIONS": "ON", + "CMAKE_INSTALL_LIBDIR": "lib", }, env = { # CMake still needs pkg-config to pass its internal configuration checks for systemd - "PKG_CONFIG_PATH": "/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig", + "PKG_CONFIG_PATH": "/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/lib64/pkgconfig:/usr/share/pkgconfig", }, # Link the systemd dependency you defined in your WORKSPACE deps = ["@libsystemd//:libsystemd"], diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index a4169fee..382d442e 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -63,6 +63,8 @@ "https://bcr.bazel.build/modules/boringssl/0.20251124.0/source.json": "2f325a76db6c6e14c4db923a26aa9a4bfbbd03e61b9256311f460c18bfba6013", "https://bcr.bazel.build/modules/buildozer/8.5.1/MODULE.bazel": "a35d9561b3fc5b18797c330793e99e3b834a473d5fbd3d7d7634aafc9bdb6f8f", "https://bcr.bazel.build/modules/buildozer/8.5.1/source.json": "e3386e6ff4529f2442800dee47ad28d3e6487f36a1f75ae39ae56c70f0cd2fbd", + "https://bcr.bazel.build/modules/ftxui/6.1.9/MODULE.bazel": "cc9326c084f535dab294bc7aaea0be08ceedd7623c421e09e7ab16f921f4fcb7", + "https://bcr.bazel.build/modules/ftxui/6.1.9/source.json": "401772aaf4b1dc988ffe03a8280e94927bd4e318b98724ba309f41000c41283e", "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", @@ -152,8 +154,8 @@ "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", "https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2", - "https://bcr.bazel.build/modules/rules_java/9.1.0/MODULE.bazel": "ee63f27e36a3fada80342869361182f120a9819c74320e8e65b1e04ba0cd7a9d", - "https://bcr.bazel.build/modules/rules_java/9.1.0/source.json": "da589573c1dee2c9ac4a568b301269a2e8191110ff0345c1a959fa7ea6c4dfd6", + "https://bcr.bazel.build/modules/rules_java/9.0.3/MODULE.bazel": "1f98ed015f7e744a745e0df6e898a7c5e83562d6b759dfd475c76456dda5ccea", + "https://bcr.bazel.build/modules/rules_java/9.0.3/source.json": "b038c0c07e12e658135bbc32cc1a2ded6e33785105c9d41958014c592de4593e", "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", @@ -220,7 +222,7 @@ "moduleExtensions": { "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { - "bzlTransitiveDigest": "Ga4z8lQy1YQ5rAMy+dOl0dqcCEBnYNCXku8x3YQmDZI=", + "bzlTransitiveDigest": "ABI1D/sbS1ovwaW/kHDoj8nnXjQ0oKU9fzmzEG4iT8o=", "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", "recordedInputs": [ "REPO_MAPPING:rules_kotlin+,bazel_tools bazel_tools" diff --git a/Package.swift b/Package.swift index 6d3d5326..d96c9aa4 100644 --- a/Package.swift +++ b/Package.swift @@ -92,7 +92,7 @@ let package = Package( .target( name: "protobuf-utf8", dependencies: [ - .product(name: "abseil", package: "abseil-cpp-SwiftPM"), + .product(name: "abseil", package: "abseil-cpp-SwiftPM") ], path: "third_party/protobuf/third_party/utf8_range", sources: [ @@ -102,13 +102,13 @@ let package = Package( ], publicHeadersPath: ".", cSettings: [ - .headerSearchPath("./"), + .headerSearchPath("./") ], ), .target( name: "protobuf", dependencies: [ - "protobuf-utf8", + "protobuf-utf8" ], path: "third_party/protobuf/src", exclude: [ @@ -282,11 +282,11 @@ let package = Package( "google/protobuf/io/zero_copy_sink_test.cc", ], sources: [ - "google/protobuf", + "google/protobuf" ], publicHeadersPath: ".", cSettings: [ - .headerSearchPath("./"), + .headerSearchPath("./") ] ), .target( @@ -321,7 +321,6 @@ let package = Package( "connections/implementation/flags/BUILD", "connections/implementation/mediums/advertisements/BUILD", "connections/implementation/mediums/ble/BUILD", - "connections/implementation/mediums/multiplex/BUILD", "connections/implementation/mediums/BUILD", "connections/implementation/BUILD", "connections/implementation/fuzzers", @@ -346,7 +345,6 @@ let package = Package( "internal/platform/implementation/apple/Mediums/WiFiCommon/BUILD", "internal/platform/implementation/BUILD", "internal/platform/BUILD", - "internal/analytics/BUILD", "internal/flags/BUILD", "internal/network/BUILD", "internal/rpc/BUILD", @@ -369,16 +367,12 @@ let package = Package( "connections/implementation/payload_manager_test.cc", "connections/implementation/offline_frames_validator_test.cc", "connections/implementation/service_controller_router_test.cc", - "connections/implementation/bluetooth_bwu_test.cc", - "connections/implementation/wifi_direct_bwu_test.cc", - "connections/implementation/wifi_hotspot_bwu_test.cc", - "connections/implementation/wifi_lan_bwu_handler_test.cc", - "connections/implementation/analytics/analytics_recorder_test.cc", - "connections/implementation/analytics/throughput_recorder_test.cc", + "connections/implementation/analytics/analytics_recorder_impl_test.cc", "connections/implementation/mediums/advertisements/data_element_test.cc", "connections/implementation/mediums/advertisements/dct_advertisement_test.cc", "connections/implementation/mediums/advertisements/advertisement_util_test.cc", "connections/implementation/mediums/awdl_test.cc", + "connections/implementation/mediums/awdl_bwu_handler_test.cc", "connections/implementation/mediums/ble_test.cc", "connections/implementation/mediums/ble/bloom_filter_test.cc", "connections/implementation/mediums/ble/ble_l2cap_packet_test.cc", @@ -391,18 +385,19 @@ let package = Package( "connections/implementation/mediums/ble/discovered_peripheral_tracker_test.cc", "connections/implementation/mediums/ble/instant_on_lost_advertisement_test.cc", "connections/implementation/mediums/ble/instant_on_lost_manager_test.cc", - "connections/implementation/mediums/multiplex/multiplex_frames_test.cc", - "connections/implementation/mediums/multiplex/multiplex_socket_test.cc", - "connections/implementation/mediums/multiplex/multiplex_output_stream_test.cc", - "connections/implementation/mediums/webrtc_peer_id_test.cc", - "connections/implementation/mediums/wifi_lan_test.cc", - "connections/implementation/mediums/bluetooth_classic_test.cc", "connections/implementation/mediums/ble_test.cc", - "connections/implementation/mediums/webrtc_test.cc", - "connections/implementation/mediums/lost_entity_tracker_test.cc", + "connections/implementation/mediums/bluetooth_bwu_handler_test.cc", + "connections/implementation/mediums/bluetooth_classic_test.cc", "connections/implementation/mediums/bluetooth_radio_test.cc", + "connections/implementation/mediums/lost_entity_tracker_test.cc", + "connections/implementation/mediums/webrtc_peer_id_test.cc", + "connections/implementation/mediums/webrtc_test.cc", + "connections/implementation/mediums/wifi_direct_bwu_handler_test.cc", "connections/implementation/mediums/wifi_direct_test.cc", + "connections/implementation/mediums/wifi_hotspot_bwu_handler_test.cc", "connections/implementation/mediums/wifi_hotspot_test.cc", + "connections/implementation/mediums/wifi_lan_bwu_handler_test.cc", + "connections/implementation/mediums/wifi_lan_test.cc", "connections/implementation/mediums/wifi_test.cc", "connections/implementation/endpoint_channel_manager_test.cc", "connections/implementation/bwu_manager_test.cc", @@ -437,13 +432,13 @@ let package = Package( "internal/encoding/base85_test.cc", "internal/data/leveldb_data_set_test.cc", "internal/flags/nearby_flags_test.cc", - "internal/proto/analytics/connections_log_test.cc", "internal/platform/feature_flags_test.cc", "internal/platform/file_test.cc", "internal/platform/cancelable_alarm_test.cc", "internal/platform/crypto_test.cc", "internal/platform/byte_array_test.cc", "internal/platform/credential_storage_impl_test.cc", + "internal/platform/implementation/device_info_test.cc", "internal/platform/implementation/g3/awdl_test.cc", "internal/platform/implementation/g3/ble_test.cc", "internal/platform/input_stream_test.cc", @@ -478,6 +473,7 @@ let package = Package( "internal/platform/implementation/apple/mutex_test.cc", "internal/platform/implementation/apple/atomic_boolean_test.cc", "internal/platform/implementation/apple/atomic_uint32_test.cc", + "internal/platform/implementation/apple/webrtc_platform.mm", "internal/platform/implementation/shared/file_test.cc", "internal/platform/implementation/wifi_utils_test.cc", "internal/platform/atomic_boolean_test.cc", @@ -500,7 +496,6 @@ let package = Package( "internal/network/http_client_impl_test.cc", "internal/network/http_status_code_test.cc", "internal/test/fake_clock_test.cc", - "internal/test/fake_webrtc.cc", "internal/test/fake_timer_test.cc", "internal/test/fake_device_info_test.cc", "internal/test/fake_task_runner_test.cc", @@ -522,16 +517,9 @@ let package = Package( "proto", "internal/data/leveldb_data_set_test.proto", // webrtc - "connections/implementation/webrtc_bwu_handler.cc", - "connections/implementation/webrtc_endpoint_channel.cc", - "connections/implementation/mediums/webrtc.cc", - "connections/implementation/mediums/webrtc_peer_id.cc", - "connections/implementation/mediums/webrtc", - "internal/platform/tachyon_express_signaling_messenger.cc", - "internal/platform/tachyon_express_signaling_messenger.h", "internal/platform/implementation/apple/webrtc.h", "internal/platform/implementation/apple/webrtc.mm", - // This breaks the build, but seems to work fine without it? + // Only used in tests "internal/platform/medium_environment.cc", ], sources: [ @@ -564,6 +552,7 @@ let package = Package( .headerSearchPath("./"), .headerSearchPath("compiled_proto/"), .define("NO_WEBRTC"), + .define("NC_OSS_BUILD"), ] ), .target( diff --git a/README.md b/README.md index 3265eece..1b1c8e1f 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,13 @@ https://github.com/user-attachments/assets/048afa1e-40a4-4351-a859-c81b642fc6e3 ## What -This repo consists of the entirety of google's open source nearby library. Currently, it is seperated into 3 sections. +This repo consists of the entirety of google's open source nearby library. Currently, it is seperated into 3 (now 2) sections. - Sharing - Connections -- ~Presence~ ( Build fails for some reason. Haven't had time to investigate. Maybe something minor. Check the validate.yaml for build steps and see why its faiing ) +- ~Presence~ ( *Was removed by google from their repo. RIP :(* ) Linux specific implementation and compatibility shims are provided for building **Sharing** and **Connections**. -Nearby presence may or may not build. ## Why This repo could've been a PR on the official repo. All I've done is implement the platform abstraction layer google has provided for a linux specific environment. @@ -34,10 +33,8 @@ Moreover, all I've wanted was seamless file sharing between my android devices a it accomplishes that goal perfectly. This repo wasn't created out of any altruistic goals or out of the goodness of my heart. I had a problem. I solved it. Simple as that. -## How -### How to use -Proper documentation is coming I swear. University's getting pretty hectic so docs is on the backburner. The wiki has some more information but it's nowhere near a proper documentation. It has some good brief -overviews and where generally everything is located if you're thinking about contributing. +## Documentation +Docs is on the backburner for now If you want any clarification on anything, feel free to open an issue. I'll get back to you ASAP. @@ -78,80 +75,45 @@ sudo dnf install -y \ Currently there are no prebuilt shared library or headers. You'll have to build them yourself ### How to build -~Check the [wiki](https://github.com/kidfromjupiter/nearby/wiki/Development-Environment-and-Building)~ -Wiki isn't built yet. Best place to consult would be the Github actions and workflows. -### How to contribute -~Check the [wiki](https://github.com/kidfromjupiter/nearby/wiki/Development-Environment-and-Building)~ +Best place to consult would be the Github actions and workflows. + ## TODO -> **Development is paused until my next break (around mid-June.)** +### WIP + +- [ ] **Transition from QT to a TUI** + + We're moving away from a GUI application to TUI application. On top of this, I've made a nearby sharing daemon with socket based IPC so that even if anyone wants to create a GUI application, it is trivial without them needing to link with nearby sharing libraries. + + This change was done to decouple the UI from the library itself since the previous implementation was difficult to work with + --- -### 🔴 P0 — Critical / Core Functionality - -> Issues that break core functionality. These should be addressed immediately. - -- **Merge latest upstream changes** - - -- **Bluetooth classic bandwidth** +### BUGS +- [ ] **Bluetooth classic bandwidth** File transfer on bluetooth classic is painfully slow. Bandwidth close to 20KB/s. ~May be a regression issue after bluetooth socket refactor~. May be an issue with sending back acknowledgements. Issue is present on pre-refactor versions. ~Look into Multiplexing maybe~ Multiplexing did not fix it : (? -- **~Linux → Android~ file sharing is unreliable** after the newest Android Quick Share updates. - Investigate why and fix it. ~Possibly related to proprietary certificate changes~. Should probably add unit tests and integration tests for each medium. Everything is so fucking buggy it makes me wanna rip my fucking eyes out. Might be related to recent upstream changes. I could probably properly test linux -> linux bidirectional sharing. Will need to simulate a lot of hardware stuff though. Since there's no stable reference platform to write automatic tests against writing linux to linux tests could be like clown to clown communication - - image - - -- **QR code scanning does not work.** - Likely related to the Linux → Android sharing issue above. I did get it working once in a very old build. So it shouldn't impossible. Unless google changed something - ---- - -### 🟠 P1 — Important Annoyances - -> Problems that are not fully blocking, but noticeably affect usability. - -- **Investigate why Bluetooth connection requests pairing.** +- [ ] **Investigate why Bluetooth connection requests pairing.** + Both the L2CAP socket and Bluetooth profile should be unauthenticated. -- **Handle existing files when receiving.** +- [ ] **Handle existing files when receiving.** + Currently, files are not overwritten if they already exist. Decide whether to overwrite, rename, or skip. ---- - -### 🟡 P2 — Quality of Life / Cleanup - -> Improvements that would make the project cleaner, smoother, or easier to maintain. - -- **Bluetooth Classic transfer progress issue.** +- [ ] **Bluetooth Classic transfer progress issue.** + When transferring Android → Linux, Android shows 100% transferred but still says `Sending...`, while Linux lags behind. Could be a bottleneck or Android-side issue. -- **Add tests for basically everything.** -- **Clean up `implementation/linux`.** - Linux-specific implementation files are currently all in one directory. This matches the other platforms, but creates visual bloat. -- **Document basically everything.** - This would be a large project on its own. -- **Resolve random crashes in the Quick Share application.** --- -### 🔵 P3 — New Features +### New Features -> Non-essential features and future improvements. - -- **Support fast initiation.** -- Upstream has been slowly adding webrtc support. Should we support it? - - -## Apologies -I may have done things in *incredibly* stupid and overcomplicated ways. It doesn't certainly help that this was the way I decided to learn C++. Blessed be my naive soul. I also do not have much experience working with such -enormous codebases. - -If you see any such stupidities, feel free to berate me in the most shameless of manners in an issue. I look forward to learning how to do it the proper way and to improve my atrocious code quality. +- [ ] Upstream has been slowly adding webrtc support. Should we support it? ## Special thanks diff --git a/compiled_proto/connections/implementation/proto/offline_wire_formats.pb.cc b/compiled_proto/connections/implementation/proto/offline_wire_formats.pb.cc index 5c907238..2da3e87d 100644 --- a/compiled_proto/connections/implementation/proto/offline_wire_formats.pb.cc +++ b/compiled_proto/connections/implementation/proto/offline_wire_formats.pb.cc @@ -562,6 +562,9 @@ inline constexpr BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCred pin_( &::google::protobuf::internal::fixed_address_empty_string, ::_pbi::ConstantInitialized()), + device_name_( + &::google::protobuf::internal::fixed_address_empty_string, + ::_pbi::ConstantInitialized()), port_{0}, frequency_{0} {} @@ -2056,44 +2059,47 @@ bool AutoReconnectFrame_EventType_Parse(::absl::string_view name, AutoReconnectF return success; } PROTOBUF_CONSTINIT const uint32_t MediumMetadata_WifiDirectAuthType_internal_data_[] = { - 196608u, 0u, }; + 262144u, 0u, }; static ::google::protobuf::internal::ExplicitlyConstructed<::std::string> - MediumMetadata_WifiDirectAuthType_strings[3] = {}; + MediumMetadata_WifiDirectAuthType_strings[4] = {}; static const char MediumMetadata_WifiDirectAuthType_names[] = { "WIFI_DIRECT_TYPE_UNKNOWN" + "WIFI_DIRECT_WITH_DEVICE_NAME" "WIFI_DIRECT_WITH_PASSWORD" "WIFI_DIRECT_WITH_PIN" }; static const ::google::protobuf::internal::EnumEntry MediumMetadata_WifiDirectAuthType_entries[] = { {{&MediumMetadata_WifiDirectAuthType_names[0], 24}, 0}, - {{&MediumMetadata_WifiDirectAuthType_names[24], 25}, 1}, - {{&MediumMetadata_WifiDirectAuthType_names[49], 20}, 2}, + {{&MediumMetadata_WifiDirectAuthType_names[24], 28}, 3}, + {{&MediumMetadata_WifiDirectAuthType_names[52], 25}, 1}, + {{&MediumMetadata_WifiDirectAuthType_names[77], 20}, 2}, }; static const int MediumMetadata_WifiDirectAuthType_entries_by_number[] = { 0, // 0 -> WIFI_DIRECT_TYPE_UNKNOWN - 1, // 1 -> WIFI_DIRECT_WITH_PASSWORD - 2, // 2 -> WIFI_DIRECT_WITH_PIN + 2, // 1 -> WIFI_DIRECT_WITH_PASSWORD + 3, // 2 -> WIFI_DIRECT_WITH_PIN + 1, // 3 -> WIFI_DIRECT_WITH_DEVICE_NAME }; const ::std::string& MediumMetadata_WifiDirectAuthType_Name(MediumMetadata_WifiDirectAuthType value) { static const bool kDummy = ::google::protobuf::internal::InitializeEnumStrings( - MediumMetadata_WifiDirectAuthType_entries, MediumMetadata_WifiDirectAuthType_entries_by_number, 3, + MediumMetadata_WifiDirectAuthType_entries, MediumMetadata_WifiDirectAuthType_entries_by_number, 4, MediumMetadata_WifiDirectAuthType_strings); (void)kDummy; int idx = ::google::protobuf::internal::LookUpEnumName(MediumMetadata_WifiDirectAuthType_entries, MediumMetadata_WifiDirectAuthType_entries_by_number, - 3, value); + 4, value); return idx == -1 ? ::google::protobuf::internal::GetEmptyString() : MediumMetadata_WifiDirectAuthType_strings[idx].get(); } bool MediumMetadata_WifiDirectAuthType_Parse(::absl::string_view name, MediumMetadata_WifiDirectAuthType* PROTOBUF_NONNULL value) { int int_value; bool success = ::google::protobuf::internal::LookUpEnumValue( - MediumMetadata_WifiDirectAuthType_entries, 3, name, &int_value); + MediumMetadata_WifiDirectAuthType_entries, 4, name, &int_value); if (success) { *value = static_cast(int_value); } @@ -7696,7 +7702,8 @@ PROTOBUF_NDEBUG_INLINE BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDire gateway_(arena, from.gateway_, _i_give_permission_to_break_this_code_default_gateway_), ip_v6_address_(arena, from.ip_v6_address_), service_name_(arena, from.service_name_), - pin_(arena, from.pin_) {} + pin_(arena, from.pin_), + device_name_(arena, from.device_name_) {} BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials( ::google::protobuf::Arena* PROTOBUF_NULLABLE arena, @@ -7730,7 +7737,8 @@ PROTOBUF_NDEBUG_INLINE BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDire gateway_(arena, Impl_::_i_give_permission_to_break_this_code_default_gateway_), ip_v6_address_(arena), service_name_(arena), - pin_(arena) {} + pin_(arena), + device_name_(arena) {} inline void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::SharedCtor(::_pb::Arena* PROTOBUF_NULLABLE arena) { new (&_impl_) Impl_(internal_visibility(), arena); @@ -7758,6 +7766,7 @@ inline void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentia this_._impl_.ip_v6_address_.Destroy(); this_._impl_.service_name_.Destroy(); this_._impl_.pin_.Destroy(); + this_._impl_.device_name_.Destroy(); this_._impl_.~Impl_(); } @@ -7801,16 +7810,16 @@ BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::GetClass return BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials_class_data_.base(); } PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 -const ::_pbi::TcParseTable<3, 8, 0, 0, 2> +const ::_pbi::TcParseTable<4, 9, 0, 0, 2> BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::_table_ = { { PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_._has_bits_), 0, // no _extensions_ - 8, 56, // max_field_number, fast_idx_mask + 9, 120, // max_field_number, fast_idx_mask offsetof(decltype(_table_), field_lookup_table), - 4294967040, // skipmap + 4294966784, // skipmap offsetof(decltype(_table_), field_entries), - 8, // num_field_entries + 9, // num_field_entries 0, // num_aux_entries offsetof(decltype(_table_), field_names), // no aux_entries BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials_class_data_.base(), @@ -7820,10 +7829,7 @@ BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::_table_ ::_pbi::TcParser::GetTable<::location::nearby::connections::BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials>(), // to_prefetch #endif // PROTOBUF_PREFETCH_PARSE_TABLE }, {{ - // optional string pin = 8; - {::_pbi::TcParser::FastBS1, - {66, 5, 0, - PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.pin_)}}, + {::_pbi::TcParser::MiniParse, {}}, // optional string ssid = 1; {::_pbi::TcParser::FastBS1, {10, 0, 0, @@ -7834,11 +7840,11 @@ BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::_table_ PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.password_)}}, // optional int32 port = 3; {::_pbi::TcParser::FastV32S1, - {24, 6, 0, + {24, 7, 0, PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.port_)}}, // optional int32 frequency = 4; {::_pbi::TcParser::FastV32S1, - {32, 7, 0, + {32, 8, 0, PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.frequency_)}}, // optional string gateway = 5 [default = "0.0.0.0"]; {::_pbi::TcParser::FastBS1, @@ -7848,10 +7854,24 @@ BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::_table_ {::_pbi::TcParser::FastBS1, {50, 3, 0, PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.ip_v6_address_)}}, - // optional string service_name = 7; + // optional string service_name = 7 [deprecated = true]; {::_pbi::TcParser::FastBS1, {58, 4, 0, PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.service_name_)}}, + // optional string pin = 8; + {::_pbi::TcParser::FastBS1, + {66, 5, 0, + PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.pin_)}}, + // optional string device_name = 9; + {::_pbi::TcParser::FastBS1, + {74, 6, 0, + PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.device_name_)}}, + {::_pbi::TcParser::MiniParse, {}}, + {::_pbi::TcParser::MiniParse, {}}, + {::_pbi::TcParser::MiniParse, {}}, + {::_pbi::TcParser::MiniParse, {}}, + {::_pbi::TcParser::MiniParse, {}}, + {::_pbi::TcParser::MiniParse, {}}, }}, {{ 65535, 65535 }}, {{ @@ -7860,17 +7880,19 @@ BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::_table_ // optional string password = 2; {PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.password_), _Internal::kHasBitsOffset + 1, 0, (0 | ::_fl::kFcOptional | ::_fl::kBytes | ::_fl::kRepAString)}, // optional int32 port = 3; - {PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.port_), _Internal::kHasBitsOffset + 6, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)}, + {PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.port_), _Internal::kHasBitsOffset + 7, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)}, // optional int32 frequency = 4; - {PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.frequency_), _Internal::kHasBitsOffset + 7, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)}, + {PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.frequency_), _Internal::kHasBitsOffset + 8, 0, (0 | ::_fl::kFcOptional | ::_fl::kInt32)}, // optional string gateway = 5 [default = "0.0.0.0"]; {PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.gateway_), _Internal::kHasBitsOffset + 2, 0, (0 | ::_fl::kFcOptional | ::_fl::kBytes | ::_fl::kRepAString)}, // optional bytes ip_v6_address = 6; {PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.ip_v6_address_), _Internal::kHasBitsOffset + 3, 0, (0 | ::_fl::kFcOptional | ::_fl::kBytes | ::_fl::kRepAString)}, - // optional string service_name = 7; + // optional string service_name = 7 [deprecated = true]; {PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.service_name_), _Internal::kHasBitsOffset + 4, 0, (0 | ::_fl::kFcOptional | ::_fl::kBytes | ::_fl::kRepAString)}, // optional string pin = 8; {PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.pin_), _Internal::kHasBitsOffset + 5, 0, (0 | ::_fl::kFcOptional | ::_fl::kBytes | ::_fl::kRepAString)}, + // optional string device_name = 9; + {PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.device_name_), _Internal::kHasBitsOffset + 6, 0, (0 | ::_fl::kFcOptional | ::_fl::kBytes | ::_fl::kRepAString)}, }}, // no aux_entries {{ @@ -7884,7 +7906,7 @@ PROTOBUF_NOINLINE void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDire (void) cached_has_bits; cached_has_bits = _impl_._has_bits_[0]; - if (BatchCheckHasBit(cached_has_bits, 0x0000003fU)) { + if (BatchCheckHasBit(cached_has_bits, 0x0000007fU)) { if (CheckHasBit(cached_has_bits, 0x00000001U)) { _impl_.ssid_.ClearNonDefaultToEmpty(); } @@ -7903,12 +7925,12 @@ PROTOBUF_NOINLINE void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDire if (CheckHasBit(cached_has_bits, 0x00000020U)) { _impl_.pin_.ClearNonDefaultToEmpty(); } + if (CheckHasBit(cached_has_bits, 0x00000040U)) { + _impl_.device_name_.ClearNonDefaultToEmpty(); + } } - if (BatchCheckHasBit(cached_has_bits, 0x000000c0U)) { - ::memset(&_impl_.port_, 0, static_cast<::size_t>( - reinterpret_cast(&_impl_.frequency_) - - reinterpret_cast(&_impl_.port_)) + sizeof(_impl_.frequency_)); - } + _impl_.port_ = 0; + _impl_.frequency_ = 0; _impl_._has_bits_.Clear(); _internal_metadata_.Clear<::std::string>(); } @@ -7945,14 +7967,14 @@ PROTOBUF_NOINLINE void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDire } // optional int32 port = 3; - if (CheckHasBit(cached_has_bits, 0x00000040U)) { + if (CheckHasBit(cached_has_bits, 0x00000080U)) { target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArrayWithField<3>( stream, this_._internal_port(), target); } // optional int32 frequency = 4; - if (CheckHasBit(cached_has_bits, 0x00000080U)) { + if (CheckHasBit(cached_has_bits, 0x00000100U)) { target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArrayWithField<4>( stream, this_._internal_frequency(), target); @@ -7970,7 +7992,7 @@ PROTOBUF_NOINLINE void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDire target = stream->WriteBytesMaybeAliased(6, _s, target); } - // optional string service_name = 7; + // optional string service_name = 7 [deprecated = true]; if (CheckHasBit(cached_has_bits, 0x00000010U)) { const ::std::string& _s = this_._internal_service_name(); target = stream->WriteStringMaybeAliased(7, _s, target); @@ -7982,6 +8004,12 @@ PROTOBUF_NOINLINE void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDire target = stream->WriteStringMaybeAliased(8, _s, target); } + // optional string device_name = 9; + if (CheckHasBit(cached_has_bits, 0x00000040U)) { + const ::std::string& _s = this_._internal_device_name(); + target = stream->WriteStringMaybeAliased(9, _s, target); + } + if (ABSL_PREDICT_FALSE(this_._internal_metadata_.have_unknown_fields())) { target = stream->WriteRaw( this_._internal_metadata_.unknown_fields<::std::string>(::google::protobuf::internal::GetEmptyString).data(), @@ -8028,7 +8056,7 @@ PROTOBUF_NOINLINE void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDire total_size += 1 + ::google::protobuf::internal::WireFormatLite::BytesSize( this_._internal_ip_v6_address()); } - // optional string service_name = 7; + // optional string service_name = 7 [deprecated = true]; if (CheckHasBit(cached_has_bits, 0x00000010U)) { total_size += 1 + ::google::protobuf::internal::WireFormatLite::StringSize( this_._internal_service_name()); @@ -8038,13 +8066,20 @@ PROTOBUF_NOINLINE void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDire total_size += 1 + ::google::protobuf::internal::WireFormatLite::StringSize( this_._internal_pin()); } - // optional int32 port = 3; + // optional string device_name = 9; if (CheckHasBit(cached_has_bits, 0x00000040U)) { + total_size += 1 + ::google::protobuf::internal::WireFormatLite::StringSize( + this_._internal_device_name()); + } + // optional int32 port = 3; + if (CheckHasBit(cached_has_bits, 0x00000080U)) { total_size += ::_pbi::WireFormatLite::Int32SizePlusOne( this_._internal_port()); } + } + { // optional int32 frequency = 4; - if (CheckHasBit(cached_has_bits, 0x00000080U)) { + if (CheckHasBit(cached_has_bits, 0x00000100U)) { total_size += ::_pbi::WireFormatLite::Int32SizePlusOne( this_._internal_frequency()); } @@ -8090,12 +8125,15 @@ void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::Mer _this->_internal_set_pin(from._internal_pin()); } if (CheckHasBit(cached_has_bits, 0x00000040U)) { - _this->_impl_.port_ = from._impl_.port_; + _this->_internal_set_device_name(from._internal_device_name()); } if (CheckHasBit(cached_has_bits, 0x00000080U)) { - _this->_impl_.frequency_ = from._impl_.frequency_; + _this->_impl_.port_ = from._impl_.port_; } } + if (CheckHasBit(cached_has_bits, 0x00000100U)) { + _this->_impl_.frequency_ = from._impl_.frequency_; + } _this->_impl_._has_bits_[0] |= cached_has_bits; _this->_internal_metadata_.MergeFrom<::std::string>( from._internal_metadata_); @@ -8121,6 +8159,7 @@ void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::Int ::_pbi::ArenaStringPtr::InternalSwap(&_impl_.ip_v6_address_, &other->_impl_.ip_v6_address_, arena); ::_pbi::ArenaStringPtr::InternalSwap(&_impl_.service_name_, &other->_impl_.service_name_, arena); ::_pbi::ArenaStringPtr::InternalSwap(&_impl_.pin_, &other->_impl_.pin_, arena); + ::_pbi::ArenaStringPtr::InternalSwap(&_impl_.device_name_, &other->_impl_.device_name_, arena); ::google::protobuf::internal::memswap< PROTOBUF_FIELD_OFFSET(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials, _impl_.frequency_) + sizeof(BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::_impl_.frequency_) @@ -13302,7 +13341,7 @@ MediumMetadata::_table_ = { PROTOBUF_FIELD_OFFSET(MediumMetadata, _impl_.medium_role_)}}, // repeated .location.nearby.connections.MediumMetadata.WifiDirectAuthType supported_wifi_direct_auth_types = 13 [packed = true]; {::_pbi::TcParser::FastEr0P1, - {106, 0, 2, + {106, 0, 3, PROTOBUF_FIELD_OFFSET(MediumMetadata, _impl_.supported_wifi_direct_auth_types_)}}, {::_pbi::TcParser::MiniParse, {}}, {::_pbi::TcParser::MiniParse, {}}, @@ -13343,7 +13382,7 @@ MediumMetadata::_table_ = { {::_pbi::TcParser::GetTable<::location::nearby::connections::WifiAwareUsableChannels>()}, {::_pbi::TcParser::GetTable<::location::nearby::connections::WifiHotspotStaUsableChannels>()}, {::_pbi::TcParser::GetTable<::location::nearby::connections::MediumRole>()}, - {0, 2}, + {0, 3}, }}, {{ }}, diff --git a/compiled_proto/connections/implementation/proto/offline_wire_formats.pb.h b/compiled_proto/connections/implementation/proto/offline_wire_formats.pb.h index 9c8cf9cb..4441f499 100644 --- a/compiled_proto/connections/implementation/proto/offline_wire_formats.pb.h +++ b/compiled_proto/connections/implementation/proto/offline_wire_formats.pb.h @@ -722,18 +722,19 @@ bool AutoReconnectFrame_EventType_Parse( enum MediumMetadata_WifiDirectAuthType : int { MediumMetadata_WifiDirectAuthType_WIFI_DIRECT_TYPE_UNKNOWN = 0, MediumMetadata_WifiDirectAuthType_WIFI_DIRECT_WITH_PASSWORD = 1, - MediumMetadata_WifiDirectAuthType_WIFI_DIRECT_WITH_PIN = 2, + MediumMetadata_WifiDirectAuthType_WIFI_DIRECT_WITH_PIN [[deprecated]] = 2, + MediumMetadata_WifiDirectAuthType_WIFI_DIRECT_WITH_DEVICE_NAME = 3, }; extern const uint32_t MediumMetadata_WifiDirectAuthType_internal_data_[]; inline constexpr MediumMetadata_WifiDirectAuthType MediumMetadata_WifiDirectAuthType_WifiDirectAuthType_MIN = static_cast(0); inline constexpr MediumMetadata_WifiDirectAuthType MediumMetadata_WifiDirectAuthType_WifiDirectAuthType_MAX = - static_cast(2); + static_cast(3); inline bool MediumMetadata_WifiDirectAuthType_IsValid(int value) { - return 0 <= value && value <= 2; + return 0 <= value && value <= 3; } -inline constexpr int MediumMetadata_WifiDirectAuthType_WifiDirectAuthType_ARRAYSIZE = 2 + 1; +inline constexpr int MediumMetadata_WifiDirectAuthType_WifiDirectAuthType_ARRAYSIZE = 3 + 1; const ::std::string& MediumMetadata_WifiDirectAuthType_Name(MediumMetadata_WifiDirectAuthType value); template const ::std::string& MediumMetadata_WifiDirectAuthType_Name(T value) { @@ -4914,6 +4915,7 @@ class BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials fin kIpV6AddressFieldNumber = 6, kServiceNameFieldNumber = 7, kPinFieldNumber = 8, + kDeviceNameFieldNumber = 9, kPortFieldNumber = 3, kFrequencyFieldNumber = 4, }; @@ -4981,15 +4983,15 @@ class BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials fin ::std::string* PROTOBUF_NONNULL _internal_mutable_ip_v6_address(); public: - // optional string service_name = 7; - bool has_service_name() const; - void clear_service_name() ; - const ::std::string& service_name() const; + // optional string service_name = 7 [deprecated = true]; + [[deprecated]] bool has_service_name() const; + [[deprecated]] void clear_service_name() ; + [[deprecated]] const ::std::string& service_name() const; template - void set_service_name(Arg_&& arg, Args_... args); - ::std::string* PROTOBUF_NONNULL mutable_service_name(); - [[nodiscard]] ::std::string* PROTOBUF_NULLABLE release_service_name(); - void set_allocated_service_name(::std::string* PROTOBUF_NULLABLE value); + [[deprecated]] void set_service_name(Arg_&& arg, Args_... args); + [[deprecated]] ::std::string* PROTOBUF_NONNULL mutable_service_name(); + [[deprecated]] [[nodiscard]] ::std::string* PROTOBUF_NULLABLE release_service_name(); + [[deprecated]] void set_allocated_service_name(::std::string* PROTOBUF_NULLABLE value); private: const ::std::string& _internal_service_name() const; @@ -5012,6 +5014,22 @@ class BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials fin PROTOBUF_ALWAYS_INLINE void _internal_set_pin(const ::std::string& value); ::std::string* PROTOBUF_NONNULL _internal_mutable_pin(); + public: + // optional string device_name = 9; + bool has_device_name() const; + void clear_device_name() ; + const ::std::string& device_name() const; + template + void set_device_name(Arg_&& arg, Args_... args); + ::std::string* PROTOBUF_NONNULL mutable_device_name(); + [[nodiscard]] ::std::string* PROTOBUF_NULLABLE release_device_name(); + void set_allocated_device_name(::std::string* PROTOBUF_NULLABLE value); + + private: + const ::std::string& _internal_device_name() const; + PROTOBUF_ALWAYS_INLINE void _internal_set_device_name(const ::std::string& value); + ::std::string* PROTOBUF_NONNULL _internal_mutable_device_name(); + public: // optional int32 port = 3; bool has_port() const; @@ -5039,7 +5057,7 @@ class BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials fin private: class _Internal; friend class ::google::protobuf::internal::TcParser; - static const ::google::protobuf::internal::TcParseTable<3, 8, + static const ::google::protobuf::internal::TcParseTable<4, 9, 0, 0, 2> _table_; @@ -5068,6 +5086,7 @@ class BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials fin ::google::protobuf::internal::ArenaStringPtr ip_v6_address_; ::google::protobuf::internal::ArenaStringPtr service_name_; ::google::protobuf::internal::ArenaStringPtr pin_; + ::google::protobuf::internal::ArenaStringPtr device_name_; ::int32_t port_; ::int32_t frequency_; PROTOBUF_TSAN_DECLARE_MEMBER @@ -7679,7 +7698,8 @@ class MediumMetadata final : public ::google::protobuf::MessageLite using WifiDirectAuthType = MediumMetadata_WifiDirectAuthType; static constexpr WifiDirectAuthType WIFI_DIRECT_TYPE_UNKNOWN = MediumMetadata_WifiDirectAuthType_WIFI_DIRECT_TYPE_UNKNOWN; static constexpr WifiDirectAuthType WIFI_DIRECT_WITH_PASSWORD = MediumMetadata_WifiDirectAuthType_WIFI_DIRECT_WITH_PASSWORD; - static constexpr WifiDirectAuthType WIFI_DIRECT_WITH_PIN = MediumMetadata_WifiDirectAuthType_WIFI_DIRECT_WITH_PIN; + [[deprecated]] static constexpr WifiDirectAuthType WIFI_DIRECT_WITH_PIN = MediumMetadata_WifiDirectAuthType_WIFI_DIRECT_WITH_PIN; + static constexpr WifiDirectAuthType WIFI_DIRECT_WITH_DEVICE_NAME = MediumMetadata_WifiDirectAuthType_WIFI_DIRECT_WITH_DEVICE_NAME; static inline bool WifiDirectAuthType_IsValid(int value) { return MediumMetadata_WifiDirectAuthType_IsValid(value); } @@ -15574,14 +15594,14 @@ inline void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentia // optional int32 port = 3; inline bool BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::has_port() const { - bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000040U); + bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000080U); return value; } inline void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::clear_port() { ::google::protobuf::internal::TSanWrite(&_impl_); _impl_.port_ = 0; ClearHasBit(_impl_._has_bits_[0], - 0x00000040U); + 0x00000080U); } inline ::int32_t BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::port() const { // @@protoc_insertion_point(field_get:location.nearby.connections.BandwidthUpgradeNegotiationFrame.UpgradePathInfo.WifiDirectCredentials.port) @@ -15589,7 +15609,7 @@ inline ::int32_t BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCred } inline void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::set_port(::int32_t value) { _internal_set_port(value); - SetHasBit(_impl_._has_bits_[0], 0x00000040U); + SetHasBit(_impl_._has_bits_[0], 0x00000080U); // @@protoc_insertion_point(field_set:location.nearby.connections.BandwidthUpgradeNegotiationFrame.UpgradePathInfo.WifiDirectCredentials.port) } inline ::int32_t BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::_internal_port() const { @@ -15603,14 +15623,14 @@ inline void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentia // optional int32 frequency = 4; inline bool BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::has_frequency() const { - bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000080U); + bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000100U); return value; } inline void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::clear_frequency() { ::google::protobuf::internal::TSanWrite(&_impl_); _impl_.frequency_ = 0; ClearHasBit(_impl_._has_bits_[0], - 0x00000080U); + 0x00000100U); } inline ::int32_t BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::frequency() const { // @@protoc_insertion_point(field_get:location.nearby.connections.BandwidthUpgradeNegotiationFrame.UpgradePathInfo.WifiDirectCredentials.frequency) @@ -15618,7 +15638,7 @@ inline ::int32_t BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCred } inline void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::set_frequency(::int32_t value) { _internal_set_frequency(value); - SetHasBit(_impl_._has_bits_[0], 0x00000080U); + SetHasBit(_impl_._has_bits_[0], 0x00000100U); // @@protoc_insertion_point(field_set:location.nearby.connections.BandwidthUpgradeNegotiationFrame.UpgradePathInfo.WifiDirectCredentials.frequency) } inline ::int32_t BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::_internal_frequency() const { @@ -15764,7 +15784,7 @@ inline void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentia // @@protoc_insertion_point(field_set_allocated:location.nearby.connections.BandwidthUpgradeNegotiationFrame.UpgradePathInfo.WifiDirectCredentials.ip_v6_address) } -// optional string service_name = 7; +// optional string service_name = 7 [deprecated = true]; inline bool BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::has_service_name() const { bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000010U); return value; @@ -15833,6 +15853,75 @@ inline void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentia // @@protoc_insertion_point(field_set_allocated:location.nearby.connections.BandwidthUpgradeNegotiationFrame.UpgradePathInfo.WifiDirectCredentials.service_name) } +// optional string device_name = 9; +inline bool BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::has_device_name() const { + bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000040U); + return value; +} +inline void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::clear_device_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + _impl_.device_name_.ClearToEmpty(); + ClearHasBit(_impl_._has_bits_[0], + 0x00000040U); +} +inline const ::std::string& BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::device_name() const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + // @@protoc_insertion_point(field_get:location.nearby.connections.BandwidthUpgradeNegotiationFrame.UpgradePathInfo.WifiDirectCredentials.device_name) + return _internal_device_name(); +} +template +PROTOBUF_ALWAYS_INLINE void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::set_device_name(Arg_&& arg, Args_... args) { + ::google::protobuf::internal::TSanWrite(&_impl_); + SetHasBit(_impl_._has_bits_[0], 0x00000040U); + _impl_.device_name_.Set(static_cast(arg), args..., GetArena()); + // @@protoc_insertion_point(field_set:location.nearby.connections.BandwidthUpgradeNegotiationFrame.UpgradePathInfo.WifiDirectCredentials.device_name) +} +inline ::std::string* PROTOBUF_NONNULL BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::mutable_device_name() + ABSL_ATTRIBUTE_LIFETIME_BOUND { + SetHasBit(_impl_._has_bits_[0], 0x00000040U); + ::std::string* _s = _internal_mutable_device_name(); + // @@protoc_insertion_point(field_mutable:location.nearby.connections.BandwidthUpgradeNegotiationFrame.UpgradePathInfo.WifiDirectCredentials.device_name) + return _s; +} +inline const ::std::string& BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::_internal_device_name() const { + ::google::protobuf::internal::TSanRead(&_impl_); + return _impl_.device_name_.Get(); +} +inline void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::_internal_set_device_name(const ::std::string& value) { + ::google::protobuf::internal::TSanWrite(&_impl_); + _impl_.device_name_.Set(value, GetArena()); +} +inline ::std::string* PROTOBUF_NONNULL BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::_internal_mutable_device_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.device_name_.Mutable( GetArena()); +} +inline ::std::string* PROTOBUF_NULLABLE BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::release_device_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:location.nearby.connections.BandwidthUpgradeNegotiationFrame.UpgradePathInfo.WifiDirectCredentials.device_name) + if (!CheckHasBit(_impl_._has_bits_[0], 0x00000040U)) { + return nullptr; + } + ClearHasBit(_impl_._has_bits_[0], 0x00000040U); + auto* released = _impl_.device_name_.Release(); + if (::google::protobuf::internal::DebugHardenForceCopyDefaultString()) { + _impl_.device_name_.Set("", GetArena()); + } + return released; +} +inline void BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::set_allocated_device_name(::std::string* PROTOBUF_NULLABLE value) { + ::google::protobuf::internal::TSanWrite(&_impl_); + if (value != nullptr) { + SetHasBit(_impl_._has_bits_[0], 0x00000040U); + } else { + ClearHasBit(_impl_._has_bits_[0], 0x00000040U); + } + _impl_.device_name_.SetAllocated(value, GetArena()); + if (::google::protobuf::internal::DebugHardenForceCopyDefaultString() && _impl_.device_name_.IsDefault()) { + _impl_.device_name_.Set("", GetArena()); + } + // @@protoc_insertion_point(field_set_allocated:location.nearby.connections.BandwidthUpgradeNegotiationFrame.UpgradePathInfo.WifiDirectCredentials.device_name) +} + // optional string pin = 8; inline bool BandwidthUpgradeNegotiationFrame_UpgradePathInfo_WifiDirectCredentials::has_pin() const { bool value = CheckHasBit(_impl_._has_bits_[0], 0x00000020U); diff --git a/compiled_proto/proto/connections_enums.pb.cc b/compiled_proto/proto/connections_enums.pb.cc index bf00c0c3..cd26feb2 100644 --- a/compiled_proto/proto/connections_enums.pb.cc +++ b/compiled_proto/proto/connections_enums.pb.cc @@ -263,44 +263,47 @@ bool Medium_Parse(::absl::string_view name, Medium* PROTOBUF_NONNULL value) { return success; } PROTOBUF_CONSTINIT const uint32_t WifiDirectAuthType_internal_data_[] = { - 196608u, 0u, }; + 262144u, 0u, }; static ::google::protobuf::internal::ExplicitlyConstructed<::std::string> - WifiDirectAuthType_strings[3] = {}; + WifiDirectAuthType_strings[4] = {}; static const char WifiDirectAuthType_names[] = { "WIFI_DIRECT_TYPE_UNKNOWN" + "WIFI_DIRECT_WITH_DEVICE_NAME" "WIFI_DIRECT_WITH_PASSWORD" "WIFI_DIRECT_WITH_PIN" }; static const ::google::protobuf::internal::EnumEntry WifiDirectAuthType_entries[] = { {{&WifiDirectAuthType_names[0], 24}, 0}, - {{&WifiDirectAuthType_names[24], 25}, 1}, - {{&WifiDirectAuthType_names[49], 20}, 2}, + {{&WifiDirectAuthType_names[24], 28}, 3}, + {{&WifiDirectAuthType_names[52], 25}, 1}, + {{&WifiDirectAuthType_names[77], 20}, 2}, }; static const int WifiDirectAuthType_entries_by_number[] = { 0, // 0 -> WIFI_DIRECT_TYPE_UNKNOWN - 1, // 1 -> WIFI_DIRECT_WITH_PASSWORD - 2, // 2 -> WIFI_DIRECT_WITH_PIN + 2, // 1 -> WIFI_DIRECT_WITH_PASSWORD + 3, // 2 -> WIFI_DIRECT_WITH_PIN + 1, // 3 -> WIFI_DIRECT_WITH_DEVICE_NAME }; const ::std::string& WifiDirectAuthType_Name(WifiDirectAuthType value) { static const bool kDummy = ::google::protobuf::internal::InitializeEnumStrings( - WifiDirectAuthType_entries, WifiDirectAuthType_entries_by_number, 3, + WifiDirectAuthType_entries, WifiDirectAuthType_entries_by_number, 4, WifiDirectAuthType_strings); (void)kDummy; int idx = ::google::protobuf::internal::LookUpEnumName(WifiDirectAuthType_entries, WifiDirectAuthType_entries_by_number, - 3, value); + 4, value); return idx == -1 ? ::google::protobuf::internal::GetEmptyString() : WifiDirectAuthType_strings[idx].get(); } bool WifiDirectAuthType_Parse(::absl::string_view name, WifiDirectAuthType* PROTOBUF_NONNULL value) { int int_value; bool success = ::google::protobuf::internal::LookUpEnumValue( - WifiDirectAuthType_entries, 3, name, &int_value); + WifiDirectAuthType_entries, 4, name, &int_value); if (success) { *value = static_cast(int_value); } @@ -1352,9 +1355,9 @@ bool OperationResultCategory_Parse(::absl::string_view name, OperationResultCate return success; } PROTOBUF_CONSTINIT const uint32_t OperationResultCode_internal_data_[] = { - 131072u, 5024u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 4294705152u, 4095u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 1984u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 4227858432u, 4294967295u, 1023u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 4294950912u, 2097151u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 524284u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 4290772992u, 31u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 4294966272u, 4294967295u, 4294967295u, 33554431u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 4294705152u, 4294967295u, 4294967295u, 4294967295u, 7u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 2147483584u, }; + 131072u, 5088u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 4294705152u, 4095u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 1984u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 4227858432u, 4294967295u, 1023u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 4294950912u, 2097151u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 524284u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 4290772992u, 31u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 4294966272u, 4294967295u, 4294967295u, 33554431u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 4294705152u, 4294967295u, 4294967295u, 4294967295u, 7u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 4294967232u, 4294967295u, 15u, }; static ::google::protobuf::internal::ExplicitlyConstructed<::std::string> - OperationResultCode_strings[401] = {}; + OperationResultCode_strings[438] = {}; static const char OperationResultCode_names[] = { "CLIENT_ALREADY_CONNECTED_TO_ENDPOINT" @@ -1537,6 +1540,7 @@ static const char OperationResultCode_names[] = { "DCT_ERROR_BLE_DISABLED" "DCT_ERROR_BLE_SCAN_FAILED" "DCT_ERROR_CAPABILITY_MISMATCH" + "DCT_ERROR_CHECKIN_FAILURE" "DCT_ERROR_CONTROL_MESSAGE_EXCHANGE" "DCT_ERROR_ESTABLISHED_CONNECTION_LOST" "DCT_ERROR_HIGH_SPEED_MEDIUM_UNAVAILABLE" @@ -1545,14 +1549,50 @@ static const char OperationResultCode_names[] = { "DCT_ERROR_KEEPALIVE_TIMEOUT" "DCT_ERROR_L2CAP_CLIENT_FAILED" "DCT_ERROR_L2CAP_SERVER_FAILED" + "DCT_ERROR_LOCAL_ATTESTATION_PLAY_INTEGRITY_UNAVAILABLE" + "DCT_ERROR_LOCAL_ATTESTATION_TIMEOUT" "DCT_ERROR_MDNS_DISCOVERY_TIMEOUT" "DCT_ERROR_MDNS_REGISTER_SERVICE" + "DCT_ERROR_PARALLEL_ATTESTATION_TIMEOUT" + "DCT_ERROR_REMOTE_ATTESTATION_APPLE_INTEGRITY_UNAVAILABLE" + "DCT_ERROR_REMOTE_ATTESTATION_HASH_TOO_SHORT" + "DCT_ERROR_REMOTE_ATTESTATION_NULL_PACKET" + "DCT_ERROR_REMOTE_ATTESTATION_STATUS_NOT_AVAILABLE" + "DCT_ERROR_REMOTE_ATTESTATION_TIMEOUT" + "DCT_ERROR_REMOTE_CAPABILITY_MISMATCH" + "DCT_ERROR_REMOTE_CONTROL_MESSAGE_EXCHANGE" + "DCT_ERROR_REMOTE_HIGH_SPEED_MEDIUM_UNAVAILABLE" + "DCT_ERROR_REMOTE_MDNS_DISCOVERY_TIMEOUT" + "DCT_ERROR_REMOTE_MDNS_REGISTER_SERVICE" + "DCT_ERROR_REMOTE_REQUEST_FAILED" + "DCT_ERROR_REMOTE_RESPONSE_FAILED" + "DCT_ERROR_REMOTE_SERVICE_CANCELLED" + "DCT_ERROR_REMOTE_UNVERIFIED_INTEGRITY" + "DCT_ERROR_REMOTE_UPGRADE_HIGH_SPEED_MEDIUM_FAILED" + "DCT_ERROR_REMOTE_USER_CANCELLED" + "DCT_ERROR_REMOTE_WIFI_CREDENTIAL_TRANSFER" + "DCT_ERROR_REMOTE_WIFI_DISABLED" + "DCT_ERROR_REMOTE_WIFI_DISCONNECTED" + "DCT_ERROR_REMOTE_WIFI_INTERNET_CONNECTION" "DCT_ERROR_REQUEST_FAILED" "DCT_ERROR_RESPONSE_FAILED" "DCT_ERROR_SERVICE_CANCELLED" "DCT_ERROR_SUBSEQUENT_TLS_SPAKE" "DCT_ERROR_UNVERIFIED_INTEGRITY" "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED" + "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_CONNECTION" + "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_HOST_NETWORK_NOT_AVAILABLE" + "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_HOST_NOT_STARTED" + "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_INTERRUPTED" + "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_LOW_SPEED" + "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_MDNS_DISCOVERY_NOT_STARTED" + "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_MEDIUM_NEGOTIATION" + "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_NO_INCOMING_HTTP_CONNECTION" + "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_NO_MEDIUM" + "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_USB_NETWORK_NOT_STARTED" + "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_USB_NOT_HOST" + "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_USB_NOT_PLUGGED" + "DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_USB_NO_CONNECTED_DEVICE" "DCT_ERROR_USER_CANCELLED" "DCT_ERROR_WIFI_CREDENTIAL_TRANSFER" "DCT_ERROR_WIFI_DISABLED" @@ -1941,232 +1981,269 @@ static const ::google::protobuf::internal::EnumEntry OperationResultCode_entries {{&OperationResultCode_names[8024], 22}, 5000}, {{&OperationResultCode_names[8046], 25}, 5002}, {{&OperationResultCode_names[8071], 29}, 5012}, - {{&OperationResultCode_names[8100], 34}, 5011}, - {{&OperationResultCode_names[8134], 37}, 5020}, - {{&OperationResultCode_names[8171], 39}, 5013}, - {{&OperationResultCode_names[8210], 28}, 5024}, - {{&OperationResultCode_names[8238], 27}, 5007}, - {{&OperationResultCode_names[8265], 27}, 5019}, - {{&OperationResultCode_names[8292], 29}, 5004}, - {{&OperationResultCode_names[8321], 29}, 5003}, - {{&OperationResultCode_names[8350], 32}, 5005}, - {{&OperationResultCode_names[8382], 31}, 5006}, - {{&OperationResultCode_names[8413], 24}, 5009}, - {{&OperationResultCode_names[8437], 25}, 5010}, - {{&OperationResultCode_names[8462], 27}, 5022}, - {{&OperationResultCode_names[8489], 30}, 5008}, - {{&OperationResultCode_names[8519], 30}, 5023}, - {{&OperationResultCode_names[8549], 42}, 5018}, - {{&OperationResultCode_names[8591], 24}, 5021}, - {{&OperationResultCode_names[8615], 34}, 5016}, - {{&OperationResultCode_names[8649], 23}, 5014}, - {{&OperationResultCode_names[8672], 27}, 5015}, - {{&OperationResultCode_names[8699], 34}, 5017}, - {{&OperationResultCode_names[8733], 14}, 1}, - {{&OperationResultCode_names[8747], 14}, 0}, - {{&OperationResultCode_names[8761], 46}, 1000}, - {{&OperationResultCode_names[8807], 39}, 1001}, - {{&OperationResultCode_names[8846], 30}, 1002}, - {{&OperationResultCode_names[8876], 36}, 1003}, - {{&OperationResultCode_names[8912], 35}, 1004}, - {{&OperationResultCode_names[8947], 27}, 3005}, - {{&OperationResultCode_names[8974], 33}, 3006}, - {{&OperationResultCode_names[9007], 26}, 3007}, - {{&OperationResultCode_names[9033], 27}, 3009}, - {{&OperationResultCode_names[9060], 27}, 3013}, - {{&OperationResultCode_names[9087], 27}, 3014}, - {{&OperationResultCode_names[9114], 31}, 3008}, - {{&OperationResultCode_names[9145], 34}, 3012}, - {{&OperationResultCode_names[9179], 35}, 3010}, - {{&OperationResultCode_names[9214], 36}, 3011}, - {{&OperationResultCode_names[9250], 21}, 3000}, - {{&OperationResultCode_names[9271], 21}, 3001}, - {{&OperationResultCode_names[9292], 21}, 3002}, - {{&OperationResultCode_names[9313], 24}, 3003}, - {{&OperationResultCode_names[9337], 29}, 3004}, - {{&OperationResultCode_names[9366], 51}, 1534}, - {{&OperationResultCode_names[9417], 60}, 1535}, - {{&OperationResultCode_names[9477], 37}, 1547}, - {{&OperationResultCode_names[9514], 47}, 1515}, - {{&OperationResultCode_names[9561], 36}, 1505}, - {{&OperationResultCode_names[9597], 42}, 1507}, - {{&OperationResultCode_names[9639], 40}, 1546}, - {{&OperationResultCode_names[9679], 46}, 1516}, - {{&OperationResultCode_names[9725], 45}, 1501}, - {{&OperationResultCode_names[9770], 45}, 1541}, - {{&OperationResultCode_names[9815], 38}, 1506}, - {{&OperationResultCode_names[9853], 30}, 1544}, - {{&OperationResultCode_names[9883], 47}, 1517}, - {{&OperationResultCode_names[9930], 36}, 1513}, - {{&OperationResultCode_names[9966], 54}, 1532}, - {{&OperationResultCode_names[10020], 49}, 1503}, - {{&OperationResultCode_names[10069], 52}, 1504}, - {{&OperationResultCode_names[10121], 37}, 1543}, - {{&OperationResultCode_names[10158], 47}, 1518}, - {{&OperationResultCode_names[10205], 36}, 1512}, - {{&OperationResultCode_names[10241], 36}, 1542}, - {{&OperationResultCode_names[10277], 30}, 1545}, - {{&OperationResultCode_names[10307], 60}, 1536}, - {{&OperationResultCode_names[10367], 43}, 1533}, - {{&OperationResultCode_names[10410], 38}, 1502}, - {{&OperationResultCode_names[10448], 39}, 1539}, - {{&OperationResultCode_names[10487], 37}, 1540}, - {{&OperationResultCode_names[10524], 41}, 1537}, - {{&OperationResultCode_names[10565], 55}, 1526}, - {{&OperationResultCode_names[10620], 54}, 1530}, - {{&OperationResultCode_names[10674], 57}, 1527}, - {{&OperationResultCode_names[10731], 55}, 1529}, - {{&OperationResultCode_names[10786], 55}, 1531}, - {{&OperationResultCode_names[10841], 59}, 1528}, - {{&OperationResultCode_names[10900], 47}, 1519}, - {{&OperationResultCode_names[10947], 36}, 1514}, - {{&OperationResultCode_names[10983], 51}, 1520}, - {{&OperationResultCode_names[11034], 40}, 1508}, - {{&OperationResultCode_names[11074], 38}, 1538}, - {{&OperationResultCode_names[11112], 54}, 1521}, - {{&OperationResultCode_names[11166], 43}, 1509}, - {{&OperationResultCode_names[11209], 52}, 1500}, - {{&OperationResultCode_names[11261], 55}, 1523}, - {{&OperationResultCode_names[11316], 44}, 1511}, - {{&OperationResultCode_names[11360], 57}, 1525}, - {{&OperationResultCode_names[11417], 56}, 1522}, - {{&OperationResultCode_names[11473], 45}, 1510}, - {{&OperationResultCode_names[11518], 58}, 1524}, - {{&OperationResultCode_names[11576], 38}, 2503}, - {{&OperationResultCode_names[11614], 51}, 2514}, - {{&OperationResultCode_names[11665], 41}, 2500}, - {{&OperationResultCode_names[11706], 59}, 2510}, - {{&OperationResultCode_names[11765], 37}, 2505}, - {{&OperationResultCode_names[11802], 40}, 2504}, - {{&OperationResultCode_names[11842], 33}, 2501}, - {{&OperationResultCode_names[11875], 48}, 2513}, - {{&OperationResultCode_names[11923], 52}, 2511}, - {{&OperationResultCode_names[11975], 38}, 2515}, - {{&OperationResultCode_names[12013], 55}, 2512}, - {{&OperationResultCode_names[12068], 45}, 2506}, - {{&OperationResultCode_names[12113], 46}, 2507}, - {{&OperationResultCode_names[12159], 56}, 2502}, - {{&OperationResultCode_names[12215], 47}, 2509}, - {{&OperationResultCode_names[12262], 43}, 2508}, - {{&OperationResultCode_names[12305], 31}, 2516}, - {{&OperationResultCode_names[12336], 29}, 4568}, - {{&OperationResultCode_names[12365], 38}, 4611}, - {{&OperationResultCode_names[12403], 45}, 4612}, - {{&OperationResultCode_names[12448], 60}, 4609}, - {{&OperationResultCode_names[12508], 45}, 4500}, - {{&OperationResultCode_names[12553], 37}, 4571}, - {{&OperationResultCode_names[12590], 44}, 4504}, - {{&OperationResultCode_names[12634], 42}, 4572}, - {{&OperationResultCode_names[12676], 49}, 4515}, - {{&OperationResultCode_names[12725], 29}, 4518}, - {{&OperationResultCode_names[12754], 30}, 4579}, - {{&OperationResultCode_names[12784], 38}, 4536}, - {{&OperationResultCode_names[12822], 43}, 4570}, - {{&OperationResultCode_names[12865], 36}, 4578}, - {{&OperationResultCode_names[12901], 48}, 4501}, - {{&OperationResultCode_names[12949], 44}, 4585}, - {{&OperationResultCode_names[12993], 35}, 4592}, - {{&OperationResultCode_names[13028], 43}, 4506}, - {{&OperationResultCode_names[13071], 35}, 4530}, - {{&OperationResultCode_names[13106], 23}, 4520}, - {{&OperationResultCode_names[13129], 37}, 4538}, - {{&OperationResultCode_names[13166], 41}, 4563}, - {{&OperationResultCode_names[13207], 37}, 4602}, - {{&OperationResultCode_names[13244], 31}, 4610}, - {{&OperationResultCode_names[13275], 38}, 4606}, - {{&OperationResultCode_names[13313], 37}, 4591}, - {{&OperationResultCode_names[13350], 25}, 4567}, - {{&OperationResultCode_names[13375], 27}, 4605}, - {{&OperationResultCode_names[13402], 32}, 4503}, - {{&OperationResultCode_names[13434], 35}, 4514}, - {{&OperationResultCode_names[13469], 48}, 4588}, - {{&OperationResultCode_names[13517], 45}, 4552}, - {{&OperationResultCode_names[13562], 40}, 4532}, - {{&OperationResultCode_names[13602], 40}, 4535}, - {{&OperationResultCode_names[13642], 48}, 4547}, - {{&OperationResultCode_names[13690], 60}, 4556}, - {{&OperationResultCode_names[13750], 56}, 4558}, - {{&OperationResultCode_names[13806], 60}, 4557}, - {{&OperationResultCode_names[13866], 56}, 4553}, - {{&OperationResultCode_names[13922], 52}, 4555}, - {{&OperationResultCode_names[13974], 56}, 4554}, - {{&OperationResultCode_names[14030], 43}, 4559}, - {{&OperationResultCode_names[14073], 43}, 4560}, - {{&OperationResultCode_names[14116], 37}, 4561}, - {{&OperationResultCode_names[14153], 41}, 4562}, - {{&OperationResultCode_names[14194], 49}, 4586}, - {{&OperationResultCode_names[14243], 46}, 4505}, - {{&OperationResultCode_names[14289], 26}, 4519}, - {{&OperationResultCode_names[14315], 40}, 4537}, - {{&OperationResultCode_names[14355], 29}, 4566}, - {{&OperationResultCode_names[14384], 44}, 4507}, - {{&OperationResultCode_names[14428], 36}, 4531}, - {{&OperationResultCode_names[14464], 24}, 4525}, - {{&OperationResultCode_names[14488], 38}, 4539}, - {{&OperationResultCode_names[14526], 41}, 4593}, - {{&OperationResultCode_names[14567], 28}, 4594}, - {{&OperationResultCode_names[14595], 42}, 4564}, - {{&OperationResultCode_names[14637], 30}, 4569}, - {{&OperationResultCode_names[14667], 31}, 4607}, - {{&OperationResultCode_names[14698], 27}, 4587}, - {{&OperationResultCode_names[14725], 37}, 4573}, - {{&OperationResultCode_names[14762], 44}, 4508}, - {{&OperationResultCode_names[14806], 30}, 4577}, - {{&OperationResultCode_names[14836], 24}, 4522}, - {{&OperationResultCode_names[14860], 35}, 4601}, - {{&OperationResultCode_names[14895], 56}, 4608}, - {{&OperationResultCode_names[14951], 29}, 4603}, - {{&OperationResultCode_names[14980], 28}, 4604}, - {{&OperationResultCode_names[15008], 35}, 4590}, - {{&OperationResultCode_names[15043], 37}, 4576}, - {{&OperationResultCode_names[15080], 44}, 4513}, - {{&OperationResultCode_names[15124], 30}, 4582}, - {{&OperationResultCode_names[15154], 24}, 4521}, - {{&OperationResultCode_names[15178], 30}, 4583}, - {{&OperationResultCode_names[15208], 35}, 4502}, - {{&OperationResultCode_names[15243], 48}, 4512}, - {{&OperationResultCode_names[15291], 34}, 4584}, - {{&OperationResultCode_names[15325], 38}, 4589}, - {{&OperationResultCode_names[15363], 28}, 4524}, - {{&OperationResultCode_names[15391], 42}, 4540}, - {{&OperationResultCode_names[15433], 37}, 4599}, - {{&OperationResultCode_names[15470], 44}, 4575}, - {{&OperationResultCode_names[15514], 51}, 4509}, - {{&OperationResultCode_names[15565], 37}, 4581}, - {{&OperationResultCode_names[15602], 31}, 4523}, - {{&OperationResultCode_names[15633], 45}, 4541}, - {{&OperationResultCode_names[15678], 42}, 4600}, - {{&OperationResultCode_names[15720], 52}, 4511}, - {{&OperationResultCode_names[15772], 39}, 4516}, - {{&OperationResultCode_names[15811], 41}, 4533}, - {{&OperationResultCode_names[15852], 32}, 4527}, - {{&OperationResultCode_names[15884], 32}, 4529}, - {{&OperationResultCode_names[15916], 28}, 4528}, - {{&OperationResultCode_names[15944], 46}, 4546}, - {{&OperationResultCode_names[15990], 48}, 4549}, - {{&OperationResultCode_names[16038], 48}, 4551}, - {{&OperationResultCode_names[16086], 51}, 4596}, - {{&OperationResultCode_names[16137], 43}, 4595}, - {{&OperationResultCode_names[16180], 54}, 4545}, - {{&OperationResultCode_names[16234], 54}, 4542}, - {{&OperationResultCode_names[16288], 53}, 4510}, - {{&OperationResultCode_names[16341], 40}, 4517}, - {{&OperationResultCode_names[16381], 52}, 4544}, - {{&OperationResultCode_names[16433], 44}, 4534}, - {{&OperationResultCode_names[16477], 33}, 4526}, - {{&OperationResultCode_names[16510], 49}, 4548}, - {{&OperationResultCode_names[16559], 49}, 4550}, - {{&OperationResultCode_names[16608], 52}, 4598}, - {{&OperationResultCode_names[16660], 44}, 4597}, - {{&OperationResultCode_names[16704], 55}, 4543}, - {{&OperationResultCode_names[16759], 42}, 4574}, - {{&OperationResultCode_names[16801], 35}, 4580}, - {{&OperationResultCode_names[16836], 32}, 4565}, + {{&OperationResultCode_names[8100], 25}, 5025}, + {{&OperationResultCode_names[8125], 34}, 5011}, + {{&OperationResultCode_names[8159], 37}, 5020}, + {{&OperationResultCode_names[8196], 39}, 5013}, + {{&OperationResultCode_names[8235], 28}, 5024}, + {{&OperationResultCode_names[8263], 27}, 5007}, + {{&OperationResultCode_names[8290], 27}, 5019}, + {{&OperationResultCode_names[8317], 29}, 5004}, + {{&OperationResultCode_names[8346], 29}, 5003}, + {{&OperationResultCode_names[8375], 54}, 5031}, + {{&OperationResultCode_names[8429], 35}, 5032}, + {{&OperationResultCode_names[8464], 32}, 5005}, + {{&OperationResultCode_names[8496], 31}, 5006}, + {{&OperationResultCode_names[8527], 38}, 5033}, + {{&OperationResultCode_names[8565], 56}, 5030}, + {{&OperationResultCode_names[8621], 43}, 5029}, + {{&OperationResultCode_names[8664], 40}, 5027}, + {{&OperationResultCode_names[8704], 49}, 5028}, + {{&OperationResultCode_names[8753], 36}, 5026}, + {{&OperationResultCode_names[8789], 36}, 5039}, + {{&OperationResultCode_names[8825], 41}, 5038}, + {{&OperationResultCode_names[8866], 46}, 5040}, + {{&OperationResultCode_names[8912], 39}, 5034}, + {{&OperationResultCode_names[8951], 38}, 5035}, + {{&OperationResultCode_names[8989], 31}, 5036}, + {{&OperationResultCode_names[9020], 32}, 5037}, + {{&OperationResultCode_names[9052], 34}, 5047}, + {{&OperationResultCode_names[9086], 37}, 5048}, + {{&OperationResultCode_names[9123], 49}, 5045}, + {{&OperationResultCode_names[9172], 31}, 5046}, + {{&OperationResultCode_names[9203], 41}, 5043}, + {{&OperationResultCode_names[9244], 30}, 5041}, + {{&OperationResultCode_names[9274], 34}, 5042}, + {{&OperationResultCode_names[9308], 41}, 5044}, + {{&OperationResultCode_names[9349], 24}, 5009}, + {{&OperationResultCode_names[9373], 25}, 5010}, + {{&OperationResultCode_names[9398], 27}, 5022}, + {{&OperationResultCode_names[9425], 30}, 5008}, + {{&OperationResultCode_names[9455], 30}, 5023}, + {{&OperationResultCode_names[9485], 42}, 5018}, + {{&OperationResultCode_names[9527], 53}, 5050}, + {{&OperationResultCode_names[9580], 69}, 5058}, + {{&OperationResultCode_names[9649], 59}, 5057}, + {{&OperationResultCode_names[9708], 54}, 5061}, + {{&OperationResultCode_names[9762], 52}, 5049}, + {{&OperationResultCode_names[9814], 69}, 5053}, + {{&OperationResultCode_names[9883], 61}, 5056}, + {{&OperationResultCode_names[9944], 70}, 5059}, + {{&OperationResultCode_names[10014], 52}, 5054}, + {{&OperationResultCode_names[10066], 66}, 5055}, + {{&OperationResultCode_names[10132], 55}, 5052}, + {{&OperationResultCode_names[10187], 58}, 5051}, + {{&OperationResultCode_names[10245], 66}, 5060}, + {{&OperationResultCode_names[10311], 24}, 5021}, + {{&OperationResultCode_names[10335], 34}, 5016}, + {{&OperationResultCode_names[10369], 23}, 5014}, + {{&OperationResultCode_names[10392], 27}, 5015}, + {{&OperationResultCode_names[10419], 34}, 5017}, + {{&OperationResultCode_names[10453], 14}, 1}, + {{&OperationResultCode_names[10467], 14}, 0}, + {{&OperationResultCode_names[10481], 46}, 1000}, + {{&OperationResultCode_names[10527], 39}, 1001}, + {{&OperationResultCode_names[10566], 30}, 1002}, + {{&OperationResultCode_names[10596], 36}, 1003}, + {{&OperationResultCode_names[10632], 35}, 1004}, + {{&OperationResultCode_names[10667], 27}, 3005}, + {{&OperationResultCode_names[10694], 33}, 3006}, + {{&OperationResultCode_names[10727], 26}, 3007}, + {{&OperationResultCode_names[10753], 27}, 3009}, + {{&OperationResultCode_names[10780], 27}, 3013}, + {{&OperationResultCode_names[10807], 27}, 3014}, + {{&OperationResultCode_names[10834], 31}, 3008}, + {{&OperationResultCode_names[10865], 34}, 3012}, + {{&OperationResultCode_names[10899], 35}, 3010}, + {{&OperationResultCode_names[10934], 36}, 3011}, + {{&OperationResultCode_names[10970], 21}, 3000}, + {{&OperationResultCode_names[10991], 21}, 3001}, + {{&OperationResultCode_names[11012], 21}, 3002}, + {{&OperationResultCode_names[11033], 24}, 3003}, + {{&OperationResultCode_names[11057], 29}, 3004}, + {{&OperationResultCode_names[11086], 51}, 1534}, + {{&OperationResultCode_names[11137], 60}, 1535}, + {{&OperationResultCode_names[11197], 37}, 1547}, + {{&OperationResultCode_names[11234], 47}, 1515}, + {{&OperationResultCode_names[11281], 36}, 1505}, + {{&OperationResultCode_names[11317], 42}, 1507}, + {{&OperationResultCode_names[11359], 40}, 1546}, + {{&OperationResultCode_names[11399], 46}, 1516}, + {{&OperationResultCode_names[11445], 45}, 1501}, + {{&OperationResultCode_names[11490], 45}, 1541}, + {{&OperationResultCode_names[11535], 38}, 1506}, + {{&OperationResultCode_names[11573], 30}, 1544}, + {{&OperationResultCode_names[11603], 47}, 1517}, + {{&OperationResultCode_names[11650], 36}, 1513}, + {{&OperationResultCode_names[11686], 54}, 1532}, + {{&OperationResultCode_names[11740], 49}, 1503}, + {{&OperationResultCode_names[11789], 52}, 1504}, + {{&OperationResultCode_names[11841], 37}, 1543}, + {{&OperationResultCode_names[11878], 47}, 1518}, + {{&OperationResultCode_names[11925], 36}, 1512}, + {{&OperationResultCode_names[11961], 36}, 1542}, + {{&OperationResultCode_names[11997], 30}, 1545}, + {{&OperationResultCode_names[12027], 60}, 1536}, + {{&OperationResultCode_names[12087], 43}, 1533}, + {{&OperationResultCode_names[12130], 38}, 1502}, + {{&OperationResultCode_names[12168], 39}, 1539}, + {{&OperationResultCode_names[12207], 37}, 1540}, + {{&OperationResultCode_names[12244], 41}, 1537}, + {{&OperationResultCode_names[12285], 55}, 1526}, + {{&OperationResultCode_names[12340], 54}, 1530}, + {{&OperationResultCode_names[12394], 57}, 1527}, + {{&OperationResultCode_names[12451], 55}, 1529}, + {{&OperationResultCode_names[12506], 55}, 1531}, + {{&OperationResultCode_names[12561], 59}, 1528}, + {{&OperationResultCode_names[12620], 47}, 1519}, + {{&OperationResultCode_names[12667], 36}, 1514}, + {{&OperationResultCode_names[12703], 51}, 1520}, + {{&OperationResultCode_names[12754], 40}, 1508}, + {{&OperationResultCode_names[12794], 38}, 1538}, + {{&OperationResultCode_names[12832], 54}, 1521}, + {{&OperationResultCode_names[12886], 43}, 1509}, + {{&OperationResultCode_names[12929], 52}, 1500}, + {{&OperationResultCode_names[12981], 55}, 1523}, + {{&OperationResultCode_names[13036], 44}, 1511}, + {{&OperationResultCode_names[13080], 57}, 1525}, + {{&OperationResultCode_names[13137], 56}, 1522}, + {{&OperationResultCode_names[13193], 45}, 1510}, + {{&OperationResultCode_names[13238], 58}, 1524}, + {{&OperationResultCode_names[13296], 38}, 2503}, + {{&OperationResultCode_names[13334], 51}, 2514}, + {{&OperationResultCode_names[13385], 41}, 2500}, + {{&OperationResultCode_names[13426], 59}, 2510}, + {{&OperationResultCode_names[13485], 37}, 2505}, + {{&OperationResultCode_names[13522], 40}, 2504}, + {{&OperationResultCode_names[13562], 33}, 2501}, + {{&OperationResultCode_names[13595], 48}, 2513}, + {{&OperationResultCode_names[13643], 52}, 2511}, + {{&OperationResultCode_names[13695], 38}, 2515}, + {{&OperationResultCode_names[13733], 55}, 2512}, + {{&OperationResultCode_names[13788], 45}, 2506}, + {{&OperationResultCode_names[13833], 46}, 2507}, + {{&OperationResultCode_names[13879], 56}, 2502}, + {{&OperationResultCode_names[13935], 47}, 2509}, + {{&OperationResultCode_names[13982], 43}, 2508}, + {{&OperationResultCode_names[14025], 31}, 2516}, + {{&OperationResultCode_names[14056], 29}, 4568}, + {{&OperationResultCode_names[14085], 38}, 4611}, + {{&OperationResultCode_names[14123], 45}, 4612}, + {{&OperationResultCode_names[14168], 60}, 4609}, + {{&OperationResultCode_names[14228], 45}, 4500}, + {{&OperationResultCode_names[14273], 37}, 4571}, + {{&OperationResultCode_names[14310], 44}, 4504}, + {{&OperationResultCode_names[14354], 42}, 4572}, + {{&OperationResultCode_names[14396], 49}, 4515}, + {{&OperationResultCode_names[14445], 29}, 4518}, + {{&OperationResultCode_names[14474], 30}, 4579}, + {{&OperationResultCode_names[14504], 38}, 4536}, + {{&OperationResultCode_names[14542], 43}, 4570}, + {{&OperationResultCode_names[14585], 36}, 4578}, + {{&OperationResultCode_names[14621], 48}, 4501}, + {{&OperationResultCode_names[14669], 44}, 4585}, + {{&OperationResultCode_names[14713], 35}, 4592}, + {{&OperationResultCode_names[14748], 43}, 4506}, + {{&OperationResultCode_names[14791], 35}, 4530}, + {{&OperationResultCode_names[14826], 23}, 4520}, + {{&OperationResultCode_names[14849], 37}, 4538}, + {{&OperationResultCode_names[14886], 41}, 4563}, + {{&OperationResultCode_names[14927], 37}, 4602}, + {{&OperationResultCode_names[14964], 31}, 4610}, + {{&OperationResultCode_names[14995], 38}, 4606}, + {{&OperationResultCode_names[15033], 37}, 4591}, + {{&OperationResultCode_names[15070], 25}, 4567}, + {{&OperationResultCode_names[15095], 27}, 4605}, + {{&OperationResultCode_names[15122], 32}, 4503}, + {{&OperationResultCode_names[15154], 35}, 4514}, + {{&OperationResultCode_names[15189], 48}, 4588}, + {{&OperationResultCode_names[15237], 45}, 4552}, + {{&OperationResultCode_names[15282], 40}, 4532}, + {{&OperationResultCode_names[15322], 40}, 4535}, + {{&OperationResultCode_names[15362], 48}, 4547}, + {{&OperationResultCode_names[15410], 60}, 4556}, + {{&OperationResultCode_names[15470], 56}, 4558}, + {{&OperationResultCode_names[15526], 60}, 4557}, + {{&OperationResultCode_names[15586], 56}, 4553}, + {{&OperationResultCode_names[15642], 52}, 4555}, + {{&OperationResultCode_names[15694], 56}, 4554}, + {{&OperationResultCode_names[15750], 43}, 4559}, + {{&OperationResultCode_names[15793], 43}, 4560}, + {{&OperationResultCode_names[15836], 37}, 4561}, + {{&OperationResultCode_names[15873], 41}, 4562}, + {{&OperationResultCode_names[15914], 49}, 4586}, + {{&OperationResultCode_names[15963], 46}, 4505}, + {{&OperationResultCode_names[16009], 26}, 4519}, + {{&OperationResultCode_names[16035], 40}, 4537}, + {{&OperationResultCode_names[16075], 29}, 4566}, + {{&OperationResultCode_names[16104], 44}, 4507}, + {{&OperationResultCode_names[16148], 36}, 4531}, + {{&OperationResultCode_names[16184], 24}, 4525}, + {{&OperationResultCode_names[16208], 38}, 4539}, + {{&OperationResultCode_names[16246], 41}, 4593}, + {{&OperationResultCode_names[16287], 28}, 4594}, + {{&OperationResultCode_names[16315], 42}, 4564}, + {{&OperationResultCode_names[16357], 30}, 4569}, + {{&OperationResultCode_names[16387], 31}, 4607}, + {{&OperationResultCode_names[16418], 27}, 4587}, + {{&OperationResultCode_names[16445], 37}, 4573}, + {{&OperationResultCode_names[16482], 44}, 4508}, + {{&OperationResultCode_names[16526], 30}, 4577}, + {{&OperationResultCode_names[16556], 24}, 4522}, + {{&OperationResultCode_names[16580], 35}, 4601}, + {{&OperationResultCode_names[16615], 56}, 4608}, + {{&OperationResultCode_names[16671], 29}, 4603}, + {{&OperationResultCode_names[16700], 28}, 4604}, + {{&OperationResultCode_names[16728], 35}, 4590}, + {{&OperationResultCode_names[16763], 37}, 4576}, + {{&OperationResultCode_names[16800], 44}, 4513}, + {{&OperationResultCode_names[16844], 30}, 4582}, + {{&OperationResultCode_names[16874], 24}, 4521}, + {{&OperationResultCode_names[16898], 30}, 4583}, + {{&OperationResultCode_names[16928], 35}, 4502}, + {{&OperationResultCode_names[16963], 48}, 4512}, + {{&OperationResultCode_names[17011], 34}, 4584}, + {{&OperationResultCode_names[17045], 38}, 4589}, + {{&OperationResultCode_names[17083], 28}, 4524}, + {{&OperationResultCode_names[17111], 42}, 4540}, + {{&OperationResultCode_names[17153], 37}, 4599}, + {{&OperationResultCode_names[17190], 44}, 4575}, + {{&OperationResultCode_names[17234], 51}, 4509}, + {{&OperationResultCode_names[17285], 37}, 4581}, + {{&OperationResultCode_names[17322], 31}, 4523}, + {{&OperationResultCode_names[17353], 45}, 4541}, + {{&OperationResultCode_names[17398], 42}, 4600}, + {{&OperationResultCode_names[17440], 52}, 4511}, + {{&OperationResultCode_names[17492], 39}, 4516}, + {{&OperationResultCode_names[17531], 41}, 4533}, + {{&OperationResultCode_names[17572], 32}, 4527}, + {{&OperationResultCode_names[17604], 32}, 4529}, + {{&OperationResultCode_names[17636], 28}, 4528}, + {{&OperationResultCode_names[17664], 46}, 4546}, + {{&OperationResultCode_names[17710], 48}, 4549}, + {{&OperationResultCode_names[17758], 48}, 4551}, + {{&OperationResultCode_names[17806], 51}, 4596}, + {{&OperationResultCode_names[17857], 43}, 4595}, + {{&OperationResultCode_names[17900], 54}, 4545}, + {{&OperationResultCode_names[17954], 54}, 4542}, + {{&OperationResultCode_names[18008], 53}, 4510}, + {{&OperationResultCode_names[18061], 40}, 4517}, + {{&OperationResultCode_names[18101], 52}, 4544}, + {{&OperationResultCode_names[18153], 44}, 4534}, + {{&OperationResultCode_names[18197], 33}, 4526}, + {{&OperationResultCode_names[18230], 49}, 4548}, + {{&OperationResultCode_names[18279], 49}, 4550}, + {{&OperationResultCode_names[18328], 52}, 4598}, + {{&OperationResultCode_names[18380], 44}, 4597}, + {{&OperationResultCode_names[18424], 55}, 4543}, + {{&OperationResultCode_names[18479], 42}, 4574}, + {{&OperationResultCode_names[18521], 35}, 4580}, + {{&OperationResultCode_names[18556], 32}, 4565}, }; static const int OperationResultCode_entries_by_number[] = { - 202, // 0 -> DETAIL_UNKNOWN - 201, // 1 -> DETAIL_SUCCESS + 239, // 0 -> DETAIL_UNKNOWN + 238, // 1 -> DETAIL_SUCCESS 28, // 500 -> CLIENT_CANCELLATION_REMOTE_IN_CANCELED_STATE 24, // 501 -> CLIENT_CANCELLATION_LOCAL_CANCEL_PAYLOAD 26, // 502 -> CLIENT_CANCELLATION_REMOTE_CANCEL_PAYLOAD @@ -2193,59 +2270,59 @@ static const int OperationResultCode_entries_by_number[] = { 27, // 523 -> CLIENT_CANCELLATION_REMOTE_DISCONNECT 9, // 524 -> CLIENT_CANCELLATION_AWDL_SERVER_SOCKET_CREATION 11, // 525 -> CLIENT_CANCELLATION_CANCEL_AWDL_OUTGOING_CONNECTION - 203, // 1000 -> DEVICE_STATE_ERROR_UNFINISHED_UPGRADE_ATTEMPTS - 204, // 1001 -> DEVICE_STATE_ERROR_USER_HOTSPOT_ENABLED - 205, // 1002 -> DEVICE_STATE_LOCATION_DISABLED - 206, // 1003 -> DEVICE_STATE_RADIO_DISABLING_FAILURE - 207, // 1004 -> DEVICE_STATE_RADIO_ENABLING_FAILURE - 264, // 1500 -> MEDIUM_UNAVAILABLE_WIFI_AWARE_RESOURCE_NOT_AVAILABLE - 231, // 1501 -> MEDIUM_UNAVAILABLE_DIRECT_HOTSPOT_NOT_SUPPORT - 247, // 1502 -> MEDIUM_UNAVAILABLE_SOFT_AP_NOT_SUPPORT - 238, // 1503 -> MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_NOT_SUPPORT - 239, // 1504 -> MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_NOT_SUPPORT_5G - 227, // 1505 -> MEDIUM_UNAVAILABLE_BLE_NOT_AVAILABLE - 233, // 1506 -> MEDIUM_UNAVAILABLE_L2CAP_NOT_AVAILABLE - 228, // 1507 -> MEDIUM_UNAVAILABLE_BLUETOOTH_NOT_AVAILABLE - 260, // 1508 -> MEDIUM_UNAVAILABLE_WEB_RTC_NOT_AVAILABLE - 263, // 1509 -> MEDIUM_UNAVAILABLE_WIFI_AWARE_NOT_AVAILABLE - 269, // 1510 -> MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_NOT_AVAILABLE - 266, // 1511 -> MEDIUM_UNAVAILABLE_WIFI_DIRECT_NOT_AVAILABLE - 242, // 1512 -> MEDIUM_UNAVAILABLE_NFC_NOT_AVAILABLE - 236, // 1513 -> MEDIUM_UNAVAILABLE_LAN_NOT_AVAILABLE - 258, // 1514 -> MEDIUM_UNAVAILABLE_USB_NOT_AVAILABLE - 226, // 1515 -> MEDIUM_UNAVAILABLE_BLE_NC_LOGICAL_NOT_AVAILABLE - 230, // 1516 -> MEDIUM_UNAVAILABLE_BT_NC_LOGICAL_NOT_AVAILABLE - 235, // 1517 -> MEDIUM_UNAVAILABLE_LAN_NC_LOGICAL_NOT_AVAILABLE - 241, // 1518 -> MEDIUM_UNAVAILABLE_NFC_NC_LOGICAL_NOT_AVAILABLE - 257, // 1519 -> MEDIUM_UNAVAILABLE_USB_NC_LOGICAL_NOT_AVAILABLE - 259, // 1520 -> MEDIUM_UNAVAILABLE_WEB_RTC_NC_LOGICAL_NOT_AVAILABLE - 262, // 1521 -> MEDIUM_UNAVAILABLE_WIFI_AWARE_NC_LOGICAL_NOT_AVAILABLE - 268, // 1522 -> MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_NC_LOGICAL_NOT_AVAILABLE - 265, // 1523 -> MEDIUM_UNAVAILABLE_WIFI_DIRECT_NC_LOGICAL_NOT_AVAILABLE - 270, // 1524 -> MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_P2P_RESOURCE_NOT_AVAILABLE - 267, // 1525 -> MEDIUM_UNAVAILABLE_WIFI_DIRECT_P2P_RESOURCE_NOT_AVAILABLE - 251, // 1526 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_BLE_LOW_QUALITY_MEDIUMS - 253, // 1527 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_L2CAP_LOW_QUALITY_MEDIUMS - 256, // 1528 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_WEB_RTC_LOW_QUALITY_MEDIUMS - 254, // 1529 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_LAN_LOW_QUALITY_MEDIUMS - 252, // 1530 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_BT_LOW_QUALITY_MEDIUMS - 255, // 1531 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_USB_LOW_QUALITY_MEDIUMS - 237, // 1532 -> MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_DISRUPTIVE_FALSE - 246, // 1533 -> MEDIUM_UNAVAILABLE_SOFT_AP_DISRUPTIVE_FALSE - 223, // 1534 -> MEDIUM_UNAVAILABLE_ALREADY_HAVE_A_WIFI_DIRECT_GROUP - 224, // 1535 -> MEDIUM_UNAVAILABLE_ALREADY_HOSTING_HOTSPOT_FOR_OTHER_CLIENTS - 245, // 1536 -> MEDIUM_UNAVAILABLE_REJECT_L2CAP_ON_GATT_MULTIPLEX_CONNECTION - 250, // 1537 -> MEDIUM_UNAVAILABLE_UPGRADE_ON_SAME_MEDIUM - 261, // 1538 -> MEDIUM_UNAVAILABLE_WEB_RTC_NO_INTERNET - 248, // 1539 -> MEDIUM_UNAVAILABLE_STA_DISRUPTIVE_FALSE - 249, // 1540 -> MEDIUM_UNAVAILABLE_STA_USER_NOT_ALLOW - 232, // 1541 -> MEDIUM_UNAVAILABLE_DUPLICATE_FAST_ADVERTISING - 243, // 1542 -> MEDIUM_UNAVAILABLE_NSD_NOT_AVAILABLE - 240, // 1543 -> MEDIUM_UNAVAILABLE_MDNS_NOT_AVAILABLE - 234, // 1544 -> MEDIUM_UNAVAILABLE_LAN_BLOCKED - 244, // 1545 -> MEDIUM_UNAVAILABLE_POOR_SIGNAL - 229, // 1546 -> MEDIUM_UNAVAILABLE_BT_MULTIPLEX_DISABLED - 225, // 1547 -> MEDIUM_UNAVAILABLE_AWDL_NOT_AVAILABLE + 240, // 1000 -> DEVICE_STATE_ERROR_UNFINISHED_UPGRADE_ATTEMPTS + 241, // 1001 -> DEVICE_STATE_ERROR_USER_HOTSPOT_ENABLED + 242, // 1002 -> DEVICE_STATE_LOCATION_DISABLED + 243, // 1003 -> DEVICE_STATE_RADIO_DISABLING_FAILURE + 244, // 1004 -> DEVICE_STATE_RADIO_ENABLING_FAILURE + 301, // 1500 -> MEDIUM_UNAVAILABLE_WIFI_AWARE_RESOURCE_NOT_AVAILABLE + 268, // 1501 -> MEDIUM_UNAVAILABLE_DIRECT_HOTSPOT_NOT_SUPPORT + 284, // 1502 -> MEDIUM_UNAVAILABLE_SOFT_AP_NOT_SUPPORT + 275, // 1503 -> MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_NOT_SUPPORT + 276, // 1504 -> MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_NOT_SUPPORT_5G + 264, // 1505 -> MEDIUM_UNAVAILABLE_BLE_NOT_AVAILABLE + 270, // 1506 -> MEDIUM_UNAVAILABLE_L2CAP_NOT_AVAILABLE + 265, // 1507 -> MEDIUM_UNAVAILABLE_BLUETOOTH_NOT_AVAILABLE + 297, // 1508 -> MEDIUM_UNAVAILABLE_WEB_RTC_NOT_AVAILABLE + 300, // 1509 -> MEDIUM_UNAVAILABLE_WIFI_AWARE_NOT_AVAILABLE + 306, // 1510 -> MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_NOT_AVAILABLE + 303, // 1511 -> MEDIUM_UNAVAILABLE_WIFI_DIRECT_NOT_AVAILABLE + 279, // 1512 -> MEDIUM_UNAVAILABLE_NFC_NOT_AVAILABLE + 273, // 1513 -> MEDIUM_UNAVAILABLE_LAN_NOT_AVAILABLE + 295, // 1514 -> MEDIUM_UNAVAILABLE_USB_NOT_AVAILABLE + 263, // 1515 -> MEDIUM_UNAVAILABLE_BLE_NC_LOGICAL_NOT_AVAILABLE + 267, // 1516 -> MEDIUM_UNAVAILABLE_BT_NC_LOGICAL_NOT_AVAILABLE + 272, // 1517 -> MEDIUM_UNAVAILABLE_LAN_NC_LOGICAL_NOT_AVAILABLE + 278, // 1518 -> MEDIUM_UNAVAILABLE_NFC_NC_LOGICAL_NOT_AVAILABLE + 294, // 1519 -> MEDIUM_UNAVAILABLE_USB_NC_LOGICAL_NOT_AVAILABLE + 296, // 1520 -> MEDIUM_UNAVAILABLE_WEB_RTC_NC_LOGICAL_NOT_AVAILABLE + 299, // 1521 -> MEDIUM_UNAVAILABLE_WIFI_AWARE_NC_LOGICAL_NOT_AVAILABLE + 305, // 1522 -> MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_NC_LOGICAL_NOT_AVAILABLE + 302, // 1523 -> MEDIUM_UNAVAILABLE_WIFI_DIRECT_NC_LOGICAL_NOT_AVAILABLE + 307, // 1524 -> MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_P2P_RESOURCE_NOT_AVAILABLE + 304, // 1525 -> MEDIUM_UNAVAILABLE_WIFI_DIRECT_P2P_RESOURCE_NOT_AVAILABLE + 288, // 1526 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_BLE_LOW_QUALITY_MEDIUMS + 290, // 1527 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_L2CAP_LOW_QUALITY_MEDIUMS + 293, // 1528 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_WEB_RTC_LOW_QUALITY_MEDIUMS + 291, // 1529 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_LAN_LOW_QUALITY_MEDIUMS + 289, // 1530 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_BT_LOW_QUALITY_MEDIUMS + 292, // 1531 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_USB_LOW_QUALITY_MEDIUMS + 274, // 1532 -> MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_DISRUPTIVE_FALSE + 283, // 1533 -> MEDIUM_UNAVAILABLE_SOFT_AP_DISRUPTIVE_FALSE + 260, // 1534 -> MEDIUM_UNAVAILABLE_ALREADY_HAVE_A_WIFI_DIRECT_GROUP + 261, // 1535 -> MEDIUM_UNAVAILABLE_ALREADY_HOSTING_HOTSPOT_FOR_OTHER_CLIENTS + 282, // 1536 -> MEDIUM_UNAVAILABLE_REJECT_L2CAP_ON_GATT_MULTIPLEX_CONNECTION + 287, // 1537 -> MEDIUM_UNAVAILABLE_UPGRADE_ON_SAME_MEDIUM + 298, // 1538 -> MEDIUM_UNAVAILABLE_WEB_RTC_NO_INTERNET + 285, // 1539 -> MEDIUM_UNAVAILABLE_STA_DISRUPTIVE_FALSE + 286, // 1540 -> MEDIUM_UNAVAILABLE_STA_USER_NOT_ALLOW + 269, // 1541 -> MEDIUM_UNAVAILABLE_DUPLICATE_FAST_ADVERTISING + 280, // 1542 -> MEDIUM_UNAVAILABLE_NSD_NOT_AVAILABLE + 277, // 1543 -> MEDIUM_UNAVAILABLE_MDNS_NOT_AVAILABLE + 271, // 1544 -> MEDIUM_UNAVAILABLE_LAN_BLOCKED + 281, // 1545 -> MEDIUM_UNAVAILABLE_POOR_SIGNAL + 266, // 1546 -> MEDIUM_UNAVAILABLE_BT_MULTIPLEX_DISABLED + 262, // 1547 -> MEDIUM_UNAVAILABLE_AWDL_NOT_AVAILABLE 60, // 2000 -> CLIENT_WIFI_DIRECT_ALREADY_HOSTING_DIRECT_GROUP_FOR_THIS_CLIENT 61, // 2001 -> CLIENT_WIFI_HOTSPOT_ALREADY_HOSTING_HOTSPOT_FOR_THIS_CLIENT 37, // 2002 -> CLIENT_DUPLICATE_ACCEPTING_BLE_CONNECTION_REQUEST @@ -2285,38 +2362,38 @@ static const int OperationResultCode_entries_by_number[] = { 2, // 2036 -> CLIENT_AWDL_DUPLICATE_ADVERTISING 3, // 2037 -> CLIENT_AWDL_DUPLICATE_DISCOVERING 36, // 2038 -> CLIENT_DUPLICATE_ACCEPTING_AWDL_CONNECTION_REQUEST - 273, // 2500 -> MISCELLEANEOUS_BLUETOOTH_MAC_ADDRESS_NULL - 277, // 2501 -> MISCELLEANEOUS_MOVE_TO_NEW_MEDIUM - 284, // 2502 -> MISCELLEANEOUS_WIFI_HOTSPOT_SOFT_AP_BLOCKED_BY_PROVISION - 271, // 2503 -> MISCELLEANEOUS_BLE_SYSTEM_SERVICE_NULL - 276, // 2504 -> MISCELLEANEOUS_L2CAP_SYSTEM_SERVICE_NULL - 275, // 2505 -> MISCELLEANEOUS_BT_SYSTEM_SERVICE_NULL - 282, // 2506 -> MISCELLEANEOUS_WIFI_AWARE_SYSTEM_SERVICE_NULL - 283, // 2507 -> MISCELLEANEOUS_WIFI_DIRECT_SYSTEM_SERVICE_NULL - 286, // 2508 -> MISCELLEANEOUS_WIFI_LAN_SYSTEM_SERVICE_NULL - 285, // 2509 -> MISCELLEANEOUS_WIFI_HOTSPOT_SYSTEM_SERVICE_NULL - 274, // 2510 -> MISCELLEANEOUS_BT_NOT_ACCEPTING_CONNECTION_FOR_WORK_PROFILE - 279, // 2511 -> MISCELLEANEOUS_WEB_RTC_GET_DROIDGUARD_RESULT_FAILURE - 281, // 2512 -> MISCELLEANEOUS_WEB_RTC_TACHYON_SIGNALING_MESSENGER_NULL - 278, // 2513 -> MISCELLEANEOUS_WEB_RTC_FAILED_TO_RECEIVE_MESSAGE - 272, // 2514 -> MISCELLEANEOUS_BLUETOOTH_CHANGE_DEVICE_NAME_FAILURE - 280, // 2515 -> MISCELLEANEOUS_WEB_RTC_ICE_SERVER_NULL - 287, // 2516 -> MISCELLEANEOUS_WORK_SOURCE_NULL - 218, // 3000 -> IO_FILE_OPENING_ERROR - 219, // 3001 -> IO_FILE_READING_ERROR - 220, // 3002 -> IO_FILE_WRITING_ERROR - 221, // 3003 -> IO_FOLDER_CREATION_ERROR - 222, // 3004 -> IO_STREAM_CREATE_PIPE_FAILURE - 208, // 3005 -> IO_ENDPOINT_IO_ERROR_ON_BLE - 209, // 3006 -> IO_ENDPOINT_IO_ERROR_ON_BLE_L2CAP - 210, // 3007 -> IO_ENDPOINT_IO_ERROR_ON_BT - 214, // 3008 -> IO_ENDPOINT_IO_ERROR_ON_WEB_RTC - 211, // 3009 -> IO_ENDPOINT_IO_ERROR_ON_LAN - 216, // 3010 -> IO_ENDPOINT_IO_ERROR_ON_WIFI_DIRECT - 217, // 3011 -> IO_ENDPOINT_IO_ERROR_ON_WIFI_HOTSPOT - 215, // 3012 -> IO_ENDPOINT_IO_ERROR_ON_WIFI_AWARE - 212, // 3013 -> IO_ENDPOINT_IO_ERROR_ON_NFC - 213, // 3014 -> IO_ENDPOINT_IO_ERROR_ON_USB + 310, // 2500 -> MISCELLEANEOUS_BLUETOOTH_MAC_ADDRESS_NULL + 314, // 2501 -> MISCELLEANEOUS_MOVE_TO_NEW_MEDIUM + 321, // 2502 -> MISCELLEANEOUS_WIFI_HOTSPOT_SOFT_AP_BLOCKED_BY_PROVISION + 308, // 2503 -> MISCELLEANEOUS_BLE_SYSTEM_SERVICE_NULL + 313, // 2504 -> MISCELLEANEOUS_L2CAP_SYSTEM_SERVICE_NULL + 312, // 2505 -> MISCELLEANEOUS_BT_SYSTEM_SERVICE_NULL + 319, // 2506 -> MISCELLEANEOUS_WIFI_AWARE_SYSTEM_SERVICE_NULL + 320, // 2507 -> MISCELLEANEOUS_WIFI_DIRECT_SYSTEM_SERVICE_NULL + 323, // 2508 -> MISCELLEANEOUS_WIFI_LAN_SYSTEM_SERVICE_NULL + 322, // 2509 -> MISCELLEANEOUS_WIFI_HOTSPOT_SYSTEM_SERVICE_NULL + 311, // 2510 -> MISCELLEANEOUS_BT_NOT_ACCEPTING_CONNECTION_FOR_WORK_PROFILE + 316, // 2511 -> MISCELLEANEOUS_WEB_RTC_GET_DROIDGUARD_RESULT_FAILURE + 318, // 2512 -> MISCELLEANEOUS_WEB_RTC_TACHYON_SIGNALING_MESSENGER_NULL + 315, // 2513 -> MISCELLEANEOUS_WEB_RTC_FAILED_TO_RECEIVE_MESSAGE + 309, // 2514 -> MISCELLEANEOUS_BLUETOOTH_CHANGE_DEVICE_NAME_FAILURE + 317, // 2515 -> MISCELLEANEOUS_WEB_RTC_ICE_SERVER_NULL + 324, // 2516 -> MISCELLEANEOUS_WORK_SOURCE_NULL + 255, // 3000 -> IO_FILE_OPENING_ERROR + 256, // 3001 -> IO_FILE_READING_ERROR + 257, // 3002 -> IO_FILE_WRITING_ERROR + 258, // 3003 -> IO_FOLDER_CREATION_ERROR + 259, // 3004 -> IO_STREAM_CREATE_PIPE_FAILURE + 245, // 3005 -> IO_ENDPOINT_IO_ERROR_ON_BLE + 246, // 3006 -> IO_ENDPOINT_IO_ERROR_ON_BLE_L2CAP + 247, // 3007 -> IO_ENDPOINT_IO_ERROR_ON_BT + 251, // 3008 -> IO_ENDPOINT_IO_ERROR_ON_WEB_RTC + 248, // 3009 -> IO_ENDPOINT_IO_ERROR_ON_LAN + 253, // 3010 -> IO_ENDPOINT_IO_ERROR_ON_WIFI_DIRECT + 254, // 3011 -> IO_ENDPOINT_IO_ERROR_ON_WIFI_HOTSPOT + 252, // 3012 -> IO_ENDPOINT_IO_ERROR_ON_WIFI_AWARE + 249, // 3013 -> IO_ENDPOINT_IO_ERROR_ON_NFC + 250, // 3014 -> IO_ENDPOINT_IO_ERROR_ON_USB 135, // 3500 -> CONNECTIVITY_WIFI_AWARE_ATTACH_FAILURE 78, // 3501 -> CONNECTIVITY_BLUETOOTH_DEVICE_OBTAIN_FAILURE 71, // 3502 -> CONNECTIVITY_BLE_CLIENT_SOCKET_CREATION_FAILURE @@ -2428,162 +2505,199 @@ static const int OperationResultCode_entries_by_number[] = { 138, // 3608 -> CONNECTIVITY_WIFI_AWARE_DISCOVER_PEER_NULL_SCREEN_OFF 139, // 3609 -> CONNECTIVITY_WIFI_AWARE_DISCOVER_PEER_NULL_TIMEOUT 144, // 3610 -> CONNECTIVITY_WIFI_AWARE_JOIN_NETWORK_FAILED - 292, // 4500 -> NEARBY_BLE_ADVERTISEMENT_MAPPING_TO_MAC_ERROR - 302, // 4501 -> NEARBY_BLUETOOTH_MAC_ADDRESS_INVALID_FOR_CONNECT - 362, // 4502 -> NEARBY_WEB_RTC_CONNECTION_FLOW_NULL - 316, // 4503 -> NEARBY_GENERIC_CONNECTION_CLOSED - 294, // 4504 -> NEARBY_BLE_ENDPOINT_CHANNEL_CREATION_FAILURE - 334, // 4505 -> NEARBY_L2CAP_ENDPOINT_CHANNEL_CREATION_FAILURE - 305, // 4506 -> NEARBY_BT_ENDPOINT_CHANNEL_CREATION_FAILURE - 338, // 4507 -> NEARBY_LAN_ENDPOINT_CHANNEL_CREATION_FAILURE - 349, // 4508 -> NEARBY_NFC_ENDPOINT_CHANNEL_CREATION_FAILURE - 370, // 4509 -> NEARBY_WIFI_AWARE_ENDPOINT_CHANNEL_CREATION_FAILURE - 388, // 4510 -> NEARBY_WIFI_HOTSPOT_ENDPOINT_CHANNEL_CREATION_FAILURE - 375, // 4511 -> NEARBY_WIFI_DIRECT_ENDPOINT_CHANNEL_CREATION_FAILURE - 363, // 4512 -> NEARBY_WEB_RTC_ENDPOINT_CHANNEL_CREATION_FAILURE - 358, // 4513 -> NEARBY_USB_ENDPOINT_CHANNEL_CREATION_FAILURE - 317, // 4514 -> NEARBY_GENERIC_ENDPOINT_UNENCRYPTED - 296, // 4515 -> NEARBY_BLE_GATT_ADVERTISEMENT_NULL_FOR_CONNECTION - 376, // 4516 -> NEARBY_WIFI_DIRECT_HOST_ON_SRD_CHANNELS - 389, // 4517 -> NEARBY_WIFI_HOTSPOT_HOST_ON_SRD_CHANNELS - 297, // 4518 -> NEARBY_BLE_GATT_NULL_CALLBACK - 335, // 4519 -> NEARBY_L2CAP_NULL_CALLBACK - 307, // 4520 -> NEARBY_BT_NULL_CALLBACK - 360, // 4521 -> NEARBY_USB_NULL_CALLBACK - 351, // 4522 -> NEARBY_NFC_NULL_CALLBACK - 372, // 4523 -> NEARBY_WIFI_AWARE_NULL_CALLBACK - 366, // 4524 -> NEARBY_WEB_RTC_NULL_CALLBACK - 340, // 4525 -> NEARBY_LAN_NULL_CALLBACK - 392, // 4526 -> NEARBY_WIFI_HOTSPOT_NULL_CALLBACK - 378, // 4527 -> NEARBY_WIFI_DIRECT_NULL_CALLBACK - 380, // 4528 -> NEARBY_WIFI_DIRECT_NULL_SSID - 379, // 4529 -> NEARBY_WIFI_DIRECT_NULL_PASSWORD - 306, // 4530 -> NEARBY_BT_MULTIPLEX_SOCKET_DISABLED - 339, // 4531 -> NEARBY_LAN_MULTIPLEX_SOCKET_DISABLED - 320, // 4532 -> NEARBY_GENERIC_NEW_ENDPOINT_CHANNEL_NULL - 377, // 4533 -> NEARBY_WIFI_DIRECT_NO_GROUP_FOR_LISTENING - 391, // 4534 -> NEARBY_WIFI_HOTSPOT_NO_HOTSPOT_FOR_LISTENING - 321, // 4535 -> NEARBY_GENERIC_OLD_ENDPOINT_CHANNEL_NULL - 299, // 4536 -> NEARBY_BLE_OPERATION_REGISTERED_FAILED - 336, // 4537 -> NEARBY_L2CAP_OPERATION_REGISTERED_FAILED - 308, // 4538 -> NEARBY_BT_OPERATION_REGISTERED_FAILED - 341, // 4539 -> NEARBY_LAN_OPERATION_REGISTERED_FAILED - 367, // 4540 -> NEARBY_WEB_RTC_OPERATION_REGISTERED_FAILED - 373, // 4541 -> NEARBY_WIFI_AWARE_OPERATION_REGISTERED_FAILED - 387, // 4542 -> NEARBY_WIFI_HOTSPOT_DIRECT_OPERATION_REGISTERED_FAILED - 397, // 4543 -> NEARBY_WIFI_HOTSPOT_SOFT_AP_OPERATION_REGISTERED_FAILED - 390, // 4544 -> NEARBY_WIFI_HOTSPOT_LOHS_OPERATION_REGISTERED_FAILED - 386, // 4545 -> NEARBY_WIFI_HOTSPOT_CLIENT_OPERATION_REGISTERED_FAILED - 381, // 4546 -> NEARBY_WIFI_DIRECT_OPERATION_REGISTERED_FAILED - 322, // 4547 -> NEARBY_GENERIC_OUTGOING_PAYLOAD_CREATION_FAILURE - 393, // 4548 -> NEARBY_WIFI_HOTSPOT_P2P_NON_DBS_WANT_2G_BUT_AP_5G - 382, // 4549 -> NEARBY_WIFI_DIRECT_P2P_NON_DBS_WANT_2G_BUT_AP_5G - 394, // 4550 -> NEARBY_WIFI_HOTSPOT_P2P_NON_DBS_WANT_5G_BUT_AP_2G - 383, // 4551 -> NEARBY_WIFI_DIRECT_P2P_NON_DBS_WANT_5G_BUT_AP_2G - 319, // 4552 -> NEARBY_GENERIC_INCOMING_PAYLOAD_NOT_DATA_TYPE - 326, // 4553 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_EVENT_TYPE_ERROR - 328, // 4554 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_FRAME_TYPE_ERROR - 327, // 4555 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_FORMAT_ERROR - 323, // 4556 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_EVENT_TYPE_ERROR - 325, // 4557 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_FRAME_TYPE_ERROR - 324, // 4558 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_FORMAT_ERROR - 329, // 4559 -> NEARBY_GENERIC_REMOTE_ENDPOINT_STATUS_ERROR - 330, // 4560 -> NEARBY_GENERIC_REMOTE_REPORT_PAYLOADS_ERROR - 331, // 4561 -> NEARBY_GENERIC_REMOTE_UPGRADE_FAILURE - 332, // 4562 -> NEARBY_GENERIC_SEND_PAYLOAD_EXECUTOR_NULL - 309, // 4563 -> NEARBY_BT_VIRTUAL_SOCKET_CREATION_FAILURE - 344, // 4564 -> NEARBY_LAN_VIRTUAL_SOCKET_CREATION_FAILURE - 400, // 4565 -> NEARBY_WIFI_LAN_IP_ADDRESS_ERROR - 337, // 4566 -> NEARBY_L2CAP_PSM_NOT_POSITIVE - 314, // 4567 -> NEARBY_ENCRYPTION_FAILURE - 288, // 4568 -> NEARBY_AUTHENTICATION_FAILURE - 345, // 4569 -> NEARBY_LAN_VIRTUAL_SOCKET_NULL - 300, // 4570 -> NEARBY_BLUETOOTH_ADVERTISE_TO_BYTES_FAILURE - 293, // 4571 -> NEARBY_BLE_ADVERTISE_TO_BYTES_FAILURE - 295, // 4572 -> NEARBY_BLE_FAST_ADVERTISE_TO_BYTES_FAILURE - 348, // 4573 -> NEARBY_NFC_ADVERTISE_TO_BYTES_FAILURE - 398, // 4574 -> NEARBY_WIFI_LAN_ADVERTISE_TO_BYTES_FAILURE - 369, // 4575 -> NEARBY_WIFI_AWARE_ADVERTISE_TO_BYTES_FAILURE - 357, // 4576 -> NEARBY_USB_ADVERTISE_TO_BYTES_FAILURE - 350, // 4577 -> NEARBY_NFC_INVALID_PCP_OPTIONS - 301, // 4578 -> NEARBY_BLUETOOTH_INVALID_PCP_OPTIONS - 298, // 4579 -> NEARBY_BLE_INVALID_PCP_OPTIONS - 399, // 4580 -> NEARBY_WIFI_LAN_INVALID_PCP_OPTIONS - 371, // 4581 -> NEARBY_WIFI_AWARE_INVALID_PCP_OPTIONS - 359, // 4582 -> NEARBY_USB_INVALID_PCP_OPTIONS - 361, // 4583 -> NEARBY_UWB_INVALID_PCP_OPTIONS - 364, // 4584 -> NEARBY_WEB_RTC_INVALID_PCP_OPTIONS - 303, // 4585 -> NEARBY_BLUETOOTH_NO_CLIENT_REGISTER_FOR_SCAN - 333, // 4586 -> NEARBY_INSTANT_CONNECTION_WRONG_CONNECTIVITY_INFO - 347, // 4587 -> NEARBY_NEED_METHOD_OVERRIDE - 318, // 4588 -> NEARBY_GENERIC_INCOMING_PAYLOAD_CREATION_FAILURE - 365, // 4589 -> NEARBY_WEB_RTC_NO_LISTENING_PEER_FOUND - 356, // 4590 -> NEARBY_UPGRADE_PATH_ON_WRONG_MEDIUM - 313, // 4591 -> NEARBY_CONNECT_TO_ALL_MEDIUMS_FAILURE - 304, // 4592 -> NEARBY_BLUETOOTH_RECONNECT_MAC_NULL - 342, // 4593 -> NEARBY_LAN_RECONNECT_CONNECTION_INFO_NULL - 343, // 4594 -> NEARBY_LAN_RECONNECT_IP_NULL - 385, // 4595 -> NEARBY_WIFI_DIRECT_RECONNECT_META_DATA_NULL - 384, // 4596 -> NEARBY_WIFI_DIRECT_RECONNECT_CONNECT_META_DATA_NULL - 396, // 4597 -> NEARBY_WIFI_HOTSPOT_RECONNECT_META_DATA_NULL - 395, // 4598 -> NEARBY_WIFI_HOTSPOT_RECONNECT_CONNECT_META_DATA_NULL - 368, // 4599 -> NEARBY_WEB_RTC_RECONNECT_PEER_ID_NULL - 374, // 4600 -> NEARBY_WIFI_AWARE_RECONNECT_META_DATA_NULL - 352, // 4601 -> NEARBY_NOT_ADVERTISING_OR_LISTENING - 310, // 4602 -> NEARBY_CAN_NOT_OBTAIN_DEVICE_PROVIDER - 354, // 4603 -> NEARBY_SETUP_STRATEGY_FAILURE - 355, // 4604 -> NEARBY_TX_ADVERTISEMENT_NULL - 315, // 4605 -> NEARBY_ENDPOINT_ID_MISMATCH - 312, // 4606 -> NEARBY_CONNECTIVITY_INFO_NULL_OR_WRONG - 346, // 4607 -> NEARBY_LOCAL_CLIENT_STATE_WRONG - 353, // 4608 -> NEARBY_REMOTE_EXCEPTION_WHEN_PROCESSING_RECEIVED_PAYLOAD - 291, // 4609 -> NEARBY_BAD_FILE_DESCRIPTION_WHEN_PROCESSING_RECEIVED_PAYLOAD - 311, // 4610 -> NEARBY_CONNECTION_LISTENER_NULL - 289, // 4611 -> NEARBY_AWDL_ADVERTISE_TO_BYTES_FAILURE - 290, // 4612 -> NEARBY_AWDL_ENDPOINT_CHANNEL_CREATION_FAILURE + 329, // 4500 -> NEARBY_BLE_ADVERTISEMENT_MAPPING_TO_MAC_ERROR + 339, // 4501 -> NEARBY_BLUETOOTH_MAC_ADDRESS_INVALID_FOR_CONNECT + 399, // 4502 -> NEARBY_WEB_RTC_CONNECTION_FLOW_NULL + 353, // 4503 -> NEARBY_GENERIC_CONNECTION_CLOSED + 331, // 4504 -> NEARBY_BLE_ENDPOINT_CHANNEL_CREATION_FAILURE + 371, // 4505 -> NEARBY_L2CAP_ENDPOINT_CHANNEL_CREATION_FAILURE + 342, // 4506 -> NEARBY_BT_ENDPOINT_CHANNEL_CREATION_FAILURE + 375, // 4507 -> NEARBY_LAN_ENDPOINT_CHANNEL_CREATION_FAILURE + 386, // 4508 -> NEARBY_NFC_ENDPOINT_CHANNEL_CREATION_FAILURE + 407, // 4509 -> NEARBY_WIFI_AWARE_ENDPOINT_CHANNEL_CREATION_FAILURE + 425, // 4510 -> NEARBY_WIFI_HOTSPOT_ENDPOINT_CHANNEL_CREATION_FAILURE + 412, // 4511 -> NEARBY_WIFI_DIRECT_ENDPOINT_CHANNEL_CREATION_FAILURE + 400, // 4512 -> NEARBY_WEB_RTC_ENDPOINT_CHANNEL_CREATION_FAILURE + 395, // 4513 -> NEARBY_USB_ENDPOINT_CHANNEL_CREATION_FAILURE + 354, // 4514 -> NEARBY_GENERIC_ENDPOINT_UNENCRYPTED + 333, // 4515 -> NEARBY_BLE_GATT_ADVERTISEMENT_NULL_FOR_CONNECTION + 413, // 4516 -> NEARBY_WIFI_DIRECT_HOST_ON_SRD_CHANNELS + 426, // 4517 -> NEARBY_WIFI_HOTSPOT_HOST_ON_SRD_CHANNELS + 334, // 4518 -> NEARBY_BLE_GATT_NULL_CALLBACK + 372, // 4519 -> NEARBY_L2CAP_NULL_CALLBACK + 344, // 4520 -> NEARBY_BT_NULL_CALLBACK + 397, // 4521 -> NEARBY_USB_NULL_CALLBACK + 388, // 4522 -> NEARBY_NFC_NULL_CALLBACK + 409, // 4523 -> NEARBY_WIFI_AWARE_NULL_CALLBACK + 403, // 4524 -> NEARBY_WEB_RTC_NULL_CALLBACK + 377, // 4525 -> NEARBY_LAN_NULL_CALLBACK + 429, // 4526 -> NEARBY_WIFI_HOTSPOT_NULL_CALLBACK + 415, // 4527 -> NEARBY_WIFI_DIRECT_NULL_CALLBACK + 417, // 4528 -> NEARBY_WIFI_DIRECT_NULL_SSID + 416, // 4529 -> NEARBY_WIFI_DIRECT_NULL_PASSWORD + 343, // 4530 -> NEARBY_BT_MULTIPLEX_SOCKET_DISABLED + 376, // 4531 -> NEARBY_LAN_MULTIPLEX_SOCKET_DISABLED + 357, // 4532 -> NEARBY_GENERIC_NEW_ENDPOINT_CHANNEL_NULL + 414, // 4533 -> NEARBY_WIFI_DIRECT_NO_GROUP_FOR_LISTENING + 428, // 4534 -> NEARBY_WIFI_HOTSPOT_NO_HOTSPOT_FOR_LISTENING + 358, // 4535 -> NEARBY_GENERIC_OLD_ENDPOINT_CHANNEL_NULL + 336, // 4536 -> NEARBY_BLE_OPERATION_REGISTERED_FAILED + 373, // 4537 -> NEARBY_L2CAP_OPERATION_REGISTERED_FAILED + 345, // 4538 -> NEARBY_BT_OPERATION_REGISTERED_FAILED + 378, // 4539 -> NEARBY_LAN_OPERATION_REGISTERED_FAILED + 404, // 4540 -> NEARBY_WEB_RTC_OPERATION_REGISTERED_FAILED + 410, // 4541 -> NEARBY_WIFI_AWARE_OPERATION_REGISTERED_FAILED + 424, // 4542 -> NEARBY_WIFI_HOTSPOT_DIRECT_OPERATION_REGISTERED_FAILED + 434, // 4543 -> NEARBY_WIFI_HOTSPOT_SOFT_AP_OPERATION_REGISTERED_FAILED + 427, // 4544 -> NEARBY_WIFI_HOTSPOT_LOHS_OPERATION_REGISTERED_FAILED + 423, // 4545 -> NEARBY_WIFI_HOTSPOT_CLIENT_OPERATION_REGISTERED_FAILED + 418, // 4546 -> NEARBY_WIFI_DIRECT_OPERATION_REGISTERED_FAILED + 359, // 4547 -> NEARBY_GENERIC_OUTGOING_PAYLOAD_CREATION_FAILURE + 430, // 4548 -> NEARBY_WIFI_HOTSPOT_P2P_NON_DBS_WANT_2G_BUT_AP_5G + 419, // 4549 -> NEARBY_WIFI_DIRECT_P2P_NON_DBS_WANT_2G_BUT_AP_5G + 431, // 4550 -> NEARBY_WIFI_HOTSPOT_P2P_NON_DBS_WANT_5G_BUT_AP_2G + 420, // 4551 -> NEARBY_WIFI_DIRECT_P2P_NON_DBS_WANT_5G_BUT_AP_2G + 356, // 4552 -> NEARBY_GENERIC_INCOMING_PAYLOAD_NOT_DATA_TYPE + 363, // 4553 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_EVENT_TYPE_ERROR + 365, // 4554 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_FRAME_TYPE_ERROR + 364, // 4555 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_FORMAT_ERROR + 360, // 4556 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_EVENT_TYPE_ERROR + 362, // 4557 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_FRAME_TYPE_ERROR + 361, // 4558 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_FORMAT_ERROR + 366, // 4559 -> NEARBY_GENERIC_REMOTE_ENDPOINT_STATUS_ERROR + 367, // 4560 -> NEARBY_GENERIC_REMOTE_REPORT_PAYLOADS_ERROR + 368, // 4561 -> NEARBY_GENERIC_REMOTE_UPGRADE_FAILURE + 369, // 4562 -> NEARBY_GENERIC_SEND_PAYLOAD_EXECUTOR_NULL + 346, // 4563 -> NEARBY_BT_VIRTUAL_SOCKET_CREATION_FAILURE + 381, // 4564 -> NEARBY_LAN_VIRTUAL_SOCKET_CREATION_FAILURE + 437, // 4565 -> NEARBY_WIFI_LAN_IP_ADDRESS_ERROR + 374, // 4566 -> NEARBY_L2CAP_PSM_NOT_POSITIVE + 351, // 4567 -> NEARBY_ENCRYPTION_FAILURE + 325, // 4568 -> NEARBY_AUTHENTICATION_FAILURE + 382, // 4569 -> NEARBY_LAN_VIRTUAL_SOCKET_NULL + 337, // 4570 -> NEARBY_BLUETOOTH_ADVERTISE_TO_BYTES_FAILURE + 330, // 4571 -> NEARBY_BLE_ADVERTISE_TO_BYTES_FAILURE + 332, // 4572 -> NEARBY_BLE_FAST_ADVERTISE_TO_BYTES_FAILURE + 385, // 4573 -> NEARBY_NFC_ADVERTISE_TO_BYTES_FAILURE + 435, // 4574 -> NEARBY_WIFI_LAN_ADVERTISE_TO_BYTES_FAILURE + 406, // 4575 -> NEARBY_WIFI_AWARE_ADVERTISE_TO_BYTES_FAILURE + 394, // 4576 -> NEARBY_USB_ADVERTISE_TO_BYTES_FAILURE + 387, // 4577 -> NEARBY_NFC_INVALID_PCP_OPTIONS + 338, // 4578 -> NEARBY_BLUETOOTH_INVALID_PCP_OPTIONS + 335, // 4579 -> NEARBY_BLE_INVALID_PCP_OPTIONS + 436, // 4580 -> NEARBY_WIFI_LAN_INVALID_PCP_OPTIONS + 408, // 4581 -> NEARBY_WIFI_AWARE_INVALID_PCP_OPTIONS + 396, // 4582 -> NEARBY_USB_INVALID_PCP_OPTIONS + 398, // 4583 -> NEARBY_UWB_INVALID_PCP_OPTIONS + 401, // 4584 -> NEARBY_WEB_RTC_INVALID_PCP_OPTIONS + 340, // 4585 -> NEARBY_BLUETOOTH_NO_CLIENT_REGISTER_FOR_SCAN + 370, // 4586 -> NEARBY_INSTANT_CONNECTION_WRONG_CONNECTIVITY_INFO + 384, // 4587 -> NEARBY_NEED_METHOD_OVERRIDE + 355, // 4588 -> NEARBY_GENERIC_INCOMING_PAYLOAD_CREATION_FAILURE + 402, // 4589 -> NEARBY_WEB_RTC_NO_LISTENING_PEER_FOUND + 393, // 4590 -> NEARBY_UPGRADE_PATH_ON_WRONG_MEDIUM + 350, // 4591 -> NEARBY_CONNECT_TO_ALL_MEDIUMS_FAILURE + 341, // 4592 -> NEARBY_BLUETOOTH_RECONNECT_MAC_NULL + 379, // 4593 -> NEARBY_LAN_RECONNECT_CONNECTION_INFO_NULL + 380, // 4594 -> NEARBY_LAN_RECONNECT_IP_NULL + 422, // 4595 -> NEARBY_WIFI_DIRECT_RECONNECT_META_DATA_NULL + 421, // 4596 -> NEARBY_WIFI_DIRECT_RECONNECT_CONNECT_META_DATA_NULL + 433, // 4597 -> NEARBY_WIFI_HOTSPOT_RECONNECT_META_DATA_NULL + 432, // 4598 -> NEARBY_WIFI_HOTSPOT_RECONNECT_CONNECT_META_DATA_NULL + 405, // 4599 -> NEARBY_WEB_RTC_RECONNECT_PEER_ID_NULL + 411, // 4600 -> NEARBY_WIFI_AWARE_RECONNECT_META_DATA_NULL + 389, // 4601 -> NEARBY_NOT_ADVERTISING_OR_LISTENING + 347, // 4602 -> NEARBY_CAN_NOT_OBTAIN_DEVICE_PROVIDER + 391, // 4603 -> NEARBY_SETUP_STRATEGY_FAILURE + 392, // 4604 -> NEARBY_TX_ADVERTISEMENT_NULL + 352, // 4605 -> NEARBY_ENDPOINT_ID_MISMATCH + 349, // 4606 -> NEARBY_CONNECTIVITY_INFO_NULL_OR_WRONG + 383, // 4607 -> NEARBY_LOCAL_CLIENT_STATE_WRONG + 390, // 4608 -> NEARBY_REMOTE_EXCEPTION_WHEN_PROCESSING_RECEIVED_PAYLOAD + 328, // 4609 -> NEARBY_BAD_FILE_DESCRIPTION_WHEN_PROCESSING_RECEIVED_PAYLOAD + 348, // 4610 -> NEARBY_CONNECTION_LISTENER_NULL + 326, // 4611 -> NEARBY_AWDL_ADVERTISE_TO_BYTES_FAILURE + 327, // 4612 -> NEARBY_AWDL_ENDPOINT_CHANNEL_CREATION_FAILURE 177, // 5000 -> DCT_ERROR_BLE_DISABLED 176, // 5001 -> DCT_ERROR_BLE_ADV_FAILED 178, // 5002 -> DCT_ERROR_BLE_SCAN_FAILED - 187, // 5003 -> DCT_ERROR_L2CAP_SERVER_FAILED - 186, // 5004 -> DCT_ERROR_L2CAP_CLIENT_FAILED - 188, // 5005 -> DCT_ERROR_MDNS_DISCOVERY_TIMEOUT - 189, // 5006 -> DCT_ERROR_MDNS_REGISTER_SERVICE - 184, // 5007 -> DCT_ERROR_INITIAL_TLS_SPAKE - 193, // 5008 -> DCT_ERROR_SUBSEQUENT_TLS_SPAKE - 190, // 5009 -> DCT_ERROR_REQUEST_FAILED - 191, // 5010 -> DCT_ERROR_RESPONSE_FAILED - 180, // 5011 -> DCT_ERROR_CONTROL_MESSAGE_EXCHANGE + 188, // 5003 -> DCT_ERROR_L2CAP_SERVER_FAILED + 187, // 5004 -> DCT_ERROR_L2CAP_CLIENT_FAILED + 191, // 5005 -> DCT_ERROR_MDNS_DISCOVERY_TIMEOUT + 192, // 5006 -> DCT_ERROR_MDNS_REGISTER_SERVICE + 185, // 5007 -> DCT_ERROR_INITIAL_TLS_SPAKE + 217, // 5008 -> DCT_ERROR_SUBSEQUENT_TLS_SPAKE + 214, // 5009 -> DCT_ERROR_REQUEST_FAILED + 215, // 5010 -> DCT_ERROR_RESPONSE_FAILED + 181, // 5011 -> DCT_ERROR_CONTROL_MESSAGE_EXCHANGE 179, // 5012 -> DCT_ERROR_CAPABILITY_MISMATCH - 182, // 5013 -> DCT_ERROR_HIGH_SPEED_MEDIUM_UNAVAILABLE - 198, // 5014 -> DCT_ERROR_WIFI_DISABLED - 199, // 5015 -> DCT_ERROR_WIFI_DISCONNECTED - 197, // 5016 -> DCT_ERROR_WIFI_CREDENTIAL_TRANSFER - 200, // 5017 -> DCT_ERROR_WIFI_INTERNET_CONNECTION - 195, // 5018 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED - 185, // 5019 -> DCT_ERROR_KEEPALIVE_TIMEOUT - 181, // 5020 -> DCT_ERROR_ESTABLISHED_CONNECTION_LOST - 196, // 5021 -> DCT_ERROR_USER_CANCELLED - 192, // 5022 -> DCT_ERROR_SERVICE_CANCELLED - 194, // 5023 -> DCT_ERROR_UNVERIFIED_INTEGRITY - 183, // 5024 -> DCT_ERROR_HTTP_SERVER_CLOSED + 183, // 5013 -> DCT_ERROR_HIGH_SPEED_MEDIUM_UNAVAILABLE + 235, // 5014 -> DCT_ERROR_WIFI_DISABLED + 236, // 5015 -> DCT_ERROR_WIFI_DISCONNECTED + 234, // 5016 -> DCT_ERROR_WIFI_CREDENTIAL_TRANSFER + 237, // 5017 -> DCT_ERROR_WIFI_INTERNET_CONNECTION + 219, // 5018 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED + 186, // 5019 -> DCT_ERROR_KEEPALIVE_TIMEOUT + 182, // 5020 -> DCT_ERROR_ESTABLISHED_CONNECTION_LOST + 233, // 5021 -> DCT_ERROR_USER_CANCELLED + 216, // 5022 -> DCT_ERROR_SERVICE_CANCELLED + 218, // 5023 -> DCT_ERROR_UNVERIFIED_INTEGRITY + 184, // 5024 -> DCT_ERROR_HTTP_SERVER_CLOSED + 180, // 5025 -> DCT_ERROR_CHECKIN_FAILURE + 198, // 5026 -> DCT_ERROR_REMOTE_ATTESTATION_TIMEOUT + 196, // 5027 -> DCT_ERROR_REMOTE_ATTESTATION_NULL_PACKET + 197, // 5028 -> DCT_ERROR_REMOTE_ATTESTATION_STATUS_NOT_AVAILABLE + 195, // 5029 -> DCT_ERROR_REMOTE_ATTESTATION_HASH_TOO_SHORT + 194, // 5030 -> DCT_ERROR_REMOTE_ATTESTATION_APPLE_INTEGRITY_UNAVAILABLE + 189, // 5031 -> DCT_ERROR_LOCAL_ATTESTATION_PLAY_INTEGRITY_UNAVAILABLE + 190, // 5032 -> DCT_ERROR_LOCAL_ATTESTATION_TIMEOUT + 193, // 5033 -> DCT_ERROR_PARALLEL_ATTESTATION_TIMEOUT + 202, // 5034 -> DCT_ERROR_REMOTE_MDNS_DISCOVERY_TIMEOUT + 203, // 5035 -> DCT_ERROR_REMOTE_MDNS_REGISTER_SERVICE + 204, // 5036 -> DCT_ERROR_REMOTE_REQUEST_FAILED + 205, // 5037 -> DCT_ERROR_REMOTE_RESPONSE_FAILED + 200, // 5038 -> DCT_ERROR_REMOTE_CONTROL_MESSAGE_EXCHANGE + 199, // 5039 -> DCT_ERROR_REMOTE_CAPABILITY_MISMATCH + 201, // 5040 -> DCT_ERROR_REMOTE_HIGH_SPEED_MEDIUM_UNAVAILABLE + 211, // 5041 -> DCT_ERROR_REMOTE_WIFI_DISABLED + 212, // 5042 -> DCT_ERROR_REMOTE_WIFI_DISCONNECTED + 210, // 5043 -> DCT_ERROR_REMOTE_WIFI_CREDENTIAL_TRANSFER + 213, // 5044 -> DCT_ERROR_REMOTE_WIFI_INTERNET_CONNECTION + 208, // 5045 -> DCT_ERROR_REMOTE_UPGRADE_HIGH_SPEED_MEDIUM_FAILED + 209, // 5046 -> DCT_ERROR_REMOTE_USER_CANCELLED + 206, // 5047 -> DCT_ERROR_REMOTE_SERVICE_CANCELLED + 207, // 5048 -> DCT_ERROR_REMOTE_UNVERIFIED_INTEGRITY + 224, // 5049 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_LOW_SPEED + 220, // 5050 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_CONNECTION + 231, // 5051 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_USB_NOT_PLUGGED + 230, // 5052 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_USB_NOT_HOST + 225, // 5053 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_MDNS_DISCOVERY_NOT_STARTED + 228, // 5054 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_NO_MEDIUM + 229, // 5055 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_USB_NETWORK_NOT_STARTED + 226, // 5056 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_MEDIUM_NEGOTIATION + 222, // 5057 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_HOST_NOT_STARTED + 221, // 5058 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_HOST_NETWORK_NOT_AVAILABLE + 227, // 5059 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_NO_INCOMING_HTTP_CONNECTION + 232, // 5060 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_USB_NO_CONNECTED_DEVICE + 223, // 5061 -> DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_INTERRUPTED }; const ::std::string& OperationResultCode_Name(OperationResultCode value) { static const bool kDummy = ::google::protobuf::internal::InitializeEnumStrings( - OperationResultCode_entries, OperationResultCode_entries_by_number, 401, + OperationResultCode_entries, OperationResultCode_entries_by_number, 438, OperationResultCode_strings); (void)kDummy; int idx = ::google::protobuf::internal::LookUpEnumName(OperationResultCode_entries, OperationResultCode_entries_by_number, - 401, value); + 438, value); return idx == -1 ? ::google::protobuf::internal::GetEmptyString() : OperationResultCode_strings[idx].get(); } bool OperationResultCode_Parse(::absl::string_view name, OperationResultCode* PROTOBUF_NONNULL value) { int int_value; bool success = ::google::protobuf::internal::LookUpEnumValue( - OperationResultCode_entries, 401, name, &int_value); + OperationResultCode_entries, 438, name, &int_value); if (success) { *value = static_cast(int_value); } diff --git a/compiled_proto/proto/connections_enums.pb.h b/compiled_proto/proto/connections_enums.pb.h index 0d0dcf74..097d4e90 100644 --- a/compiled_proto/proto/connections_enums.pb.h +++ b/compiled_proto/proto/connections_enums.pb.h @@ -324,18 +324,19 @@ bool Medium_Parse( enum WifiDirectAuthType : int { WIFI_DIRECT_TYPE_UNKNOWN = 0, WIFI_DIRECT_WITH_PASSWORD = 1, - WIFI_DIRECT_WITH_PIN = 2, + WIFI_DIRECT_WITH_PIN [[deprecated]] = 2, + WIFI_DIRECT_WITH_DEVICE_NAME = 3, }; extern const uint32_t WifiDirectAuthType_internal_data_[]; inline constexpr WifiDirectAuthType WifiDirectAuthType_MIN = static_cast(0); inline constexpr WifiDirectAuthType WifiDirectAuthType_MAX = - static_cast(2); + static_cast(3); inline bool WifiDirectAuthType_IsValid(int value) { - return 0 <= value && value <= 2; + return 0 <= value && value <= 3; } -inline constexpr int WifiDirectAuthType_ARRAYSIZE = 2 + 1; +inline constexpr int WifiDirectAuthType_ARRAYSIZE = 3 + 1; const ::std::string& WifiDirectAuthType_Name(WifiDirectAuthType value); template const ::std::string& WifiDirectAuthType_Name(T value) { @@ -1272,17 +1273,54 @@ enum OperationResultCode : int { DCT_ERROR_SERVICE_CANCELLED = 5022, DCT_ERROR_UNVERIFIED_INTEGRITY = 5023, DCT_ERROR_HTTP_SERVER_CLOSED = 5024, + DCT_ERROR_CHECKIN_FAILURE = 5025, + DCT_ERROR_REMOTE_ATTESTATION_TIMEOUT = 5026, + DCT_ERROR_REMOTE_ATTESTATION_NULL_PACKET = 5027, + DCT_ERROR_REMOTE_ATTESTATION_STATUS_NOT_AVAILABLE = 5028, + DCT_ERROR_REMOTE_ATTESTATION_HASH_TOO_SHORT = 5029, + DCT_ERROR_REMOTE_ATTESTATION_APPLE_INTEGRITY_UNAVAILABLE = 5030, + DCT_ERROR_LOCAL_ATTESTATION_PLAY_INTEGRITY_UNAVAILABLE = 5031, + DCT_ERROR_LOCAL_ATTESTATION_TIMEOUT = 5032, + DCT_ERROR_PARALLEL_ATTESTATION_TIMEOUT = 5033, + DCT_ERROR_REMOTE_MDNS_DISCOVERY_TIMEOUT = 5034, + DCT_ERROR_REMOTE_MDNS_REGISTER_SERVICE = 5035, + DCT_ERROR_REMOTE_REQUEST_FAILED = 5036, + DCT_ERROR_REMOTE_RESPONSE_FAILED = 5037, + DCT_ERROR_REMOTE_CONTROL_MESSAGE_EXCHANGE = 5038, + DCT_ERROR_REMOTE_CAPABILITY_MISMATCH = 5039, + DCT_ERROR_REMOTE_HIGH_SPEED_MEDIUM_UNAVAILABLE = 5040, + DCT_ERROR_REMOTE_WIFI_DISABLED = 5041, + DCT_ERROR_REMOTE_WIFI_DISCONNECTED = 5042, + DCT_ERROR_REMOTE_WIFI_CREDENTIAL_TRANSFER = 5043, + DCT_ERROR_REMOTE_WIFI_INTERNET_CONNECTION = 5044, + DCT_ERROR_REMOTE_UPGRADE_HIGH_SPEED_MEDIUM_FAILED = 5045, + DCT_ERROR_REMOTE_USER_CANCELLED = 5046, + DCT_ERROR_REMOTE_SERVICE_CANCELLED = 5047, + DCT_ERROR_REMOTE_UNVERIFIED_INTEGRITY = 5048, + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_LOW_SPEED = 5049, + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_CONNECTION = 5050, + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_USB_NOT_PLUGGED = 5051, + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_USB_NOT_HOST = 5052, + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_MDNS_DISCOVERY_NOT_STARTED = 5053, + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_NO_MEDIUM = 5054, + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_USB_NETWORK_NOT_STARTED = 5055, + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_MEDIUM_NEGOTIATION = 5056, + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_HOST_NOT_STARTED = 5057, + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_HOST_NETWORK_NOT_AVAILABLE = 5058, + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_NO_INCOMING_HTTP_CONNECTION = 5059, + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_USB_NO_CONNECTED_DEVICE = 5060, + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_INTERRUPTED = 5061, }; extern const uint32_t OperationResultCode_internal_data_[]; inline constexpr OperationResultCode OperationResultCode_MIN = static_cast(0); inline constexpr OperationResultCode OperationResultCode_MAX = - static_cast(5024); + static_cast(5061); inline bool OperationResultCode_IsValid(int value) { return ::google::protobuf::internal::ValidateEnum(value, OperationResultCode_internal_data_); } -inline constexpr int OperationResultCode_ARRAYSIZE = 5024 + 1; +inline constexpr int OperationResultCode_ARRAYSIZE = 5061 + 1; const ::std::string& OperationResultCode_Name(OperationResultCode value); template const ::std::string& OperationResultCode_Name(T value) { diff --git a/connections/BUILD b/connections/BUILD index 1bf50743..f7cddcb5 100644 --- a/connections/BUILD +++ b/connections/BUILD @@ -44,9 +44,11 @@ cc_library( ], deps = [ ":core_types", + "//connections/implementation:client_proxy", "//connections/implementation:internal", + "//connections/implementation:service_id_constants", + "//connections/implementation/analytics", "//connections/v3:v3_types", - "//internal/analytics:event_logger", "//internal/interop:device", "//internal/platform:base", "//internal/platform:logging", @@ -95,15 +97,15 @@ cc_library( "//connections/implementation/proto:offline_wire_formats_cc_proto", "//internal/interop:authentication_status", "//internal/platform:base", + "//internal/platform:comm", "//internal/platform:logging", "//internal/platform:mac_address", - "//internal/platform:types", "//internal/platform:util", "//proto:connections_enums_cc_proto", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/random", + "@com_google_absl//absl/strings", "@com_google_absl//absl/time", - "@com_google_absl//absl/types:variant", ], ) @@ -124,6 +126,7 @@ cc_test( "//connections/implementation:internal_test", "//connections/v3:v3_types", "//internal/platform:base", + "//internal/platform:comm", "//internal/platform:logging", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep diff --git a/connections/c/BUILD b/connections/c/BUILD index a9ce6b37..c745e9c9 100644 --- a/connections/c/BUILD +++ b/connections/c/BUILD @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("@rules_apple//apple:apple.bzl", "apple_static_xcframework") +load("@rules_apple//apple:apple_xcframework.bzl", "apple_static_xcframework") load("@rules_apple//apple:macos.bzl", "macos_dylib") load("@rules_cc//cc:cc_library.bzl", "cc_library") load("//third_party/cpptoolchains/portable_llvm/build_defs:windows.bzl", "windows") @@ -51,16 +51,18 @@ cc_library( ":nc_types", "//connections:core", "//connections:core_types", + "//connections/implementation/analytics:analytics_recorder_impl", "//connections/implementation/flags:connections_flags", - "//internal/analytics:event_logger", "//internal/flags:flag_reader", "//internal/flags:nearby_flags", "//internal/platform:base", + "//internal/platform:comm", "//internal/platform:logging", "//internal/platform:mac_address", "//internal/platform:types", - "//internal/proto/analytics:connections_log_cc_proto", - "//sharing/proto/analytics:sharing_log_cc_proto", + "//location/nearby/analytics/cpp/logging:event_logger", + "//location/nearby/analytics/cpp/proto:connections_log_cc_proto", + "//location/nearby/analytics/cpp/proto:sharing_log_cc_proto", "@com_google_absl//absl/base:no_destructor", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/strings", @@ -73,6 +75,7 @@ cc_library( ], "//conditions:default": [], }), + alwayslink = True, ) # iOS only. diff --git a/connections/c/nc.cc b/connections/c/nc.cc index e251f77c..2c975cb9 100644 --- a/connections/c/nc.cc +++ b/connections/c/nc.cc @@ -20,10 +20,16 @@ #include #include #include +#include #include #include #include +#if !defined(NC_OSS_BUILD) +#include "location/nearby/analytics/cpp/logging/event_logger.h" +#include "location/nearby/analytics/cpp/proto/connections_log.pb.h" +#include "location/nearby/analytics/cpp/proto/nearby_sharing_log.pb.h" +#endif // !defined(NC_OSS_BUILD) #include "absl/base/no_destructor.h" #include "absl/container/flat_hash_map.h" #include "absl/strings/str_cat.h" @@ -34,6 +40,9 @@ #include "connections/connection_options.h" #include "connections/core.h" #include "connections/discovery_options.h" +#if !defined(NC_OSS_BUILD) +#include "connections/implementation/analytics/analytics_recorder_impl.h" +#endif // !defined(NC_OSS_BUILD) #include "connections/listeners.h" #include "connections/medium_selector.h" #include "connections/out_of_band_connection_metadata.h" @@ -41,7 +50,6 @@ #include "connections/payload.h" #include "connections/status.h" #include "connections/strategy.h" -#include "internal/analytics/event_logger.h" #include "internal/flags/flag.h" #include "internal/flags/flag_reader.h" #include "internal/flags/nearby_flags.h" @@ -49,8 +57,6 @@ #include "internal/platform/file.h" #include "internal/platform/logging.h" #include "internal/platform/mac_address.h" -#include "internal/proto/analytics/connections_log.pb.h" -#include "sharing/proto/analytics/nearby_sharing_log.pb.h" #if TARGET_OS_IOS #include "internal/platform/implementation/apple/nearby_logger.h" #endif // TARGET_OS_IOS @@ -118,6 +124,7 @@ class FlagReaderWrapper : public nearby::flags::FlagReader { NC_PHENOTYPE_FLAG_READER phenotype_flag_reader_; }; +#if !defined(NC_OSS_BUILD) // This is a bridging class between the C API and the C++ EventLogger interface. class NcEventLogger : public ::nearby::analytics::EventLogger { public: @@ -145,6 +152,10 @@ class NcEventLogger : public ::nearby::analytics::EventLogger { private: const NC_EVENT_LOGGER* event_logger_; }; +#else // !defined(NC_OSS_BUILD) +class NcEventLogger; +#endif // !defined(NC_OSS_BUILD) + } // namespace typedef struct NcContext { @@ -244,6 +255,7 @@ NcContext* GetContext(NC_INSTANCE instance) { return cpp_connection_request_info; } +#if !defined(NC_OSS_BUILD) NC_INSTANCE NcCreateService() { return NcCreateServiceWithEventLogger(nullptr); } @@ -259,12 +271,24 @@ NcCreateServiceWithEventLogger(const NC_EVENT_LOGGER* event_logger) { nc_context.router = new ::nearby::connections::ServiceControllerRouter(); nc_context.event_logger = event_logger == nullptr ? nullptr : new NcEventLogger(event_logger); - nc_context.core = new ::nearby::connections::Core(nc_context.event_logger, - nc_context.router); + nc_context.core = new ::nearby::connections::Core( + std::make_unique<::nearby::analytics::AnalyticsRecorderImpl>( + nc_context.event_logger), + nc_context.router); kNcContextMap->insert({nc_context.core, nc_context}); return nc_context.core; } +#else // !defined(NC_OSS_BUILD) +NC_INSTANCE NcCreateService() { + NcContext nc_context; + nc_context.router = new ::nearby::connections::ServiceControllerRouter(); + nc_context.core = new ::nearby::connections::Core(nc_context.router); + + kNcContextMap->insert({nc_context.core, nc_context}); + return nc_context.core; +} +#endif // !defined(NC_OSS_BUILD) void NcCloseService(NC_INSTANCE instance) { NcContext* nc_context = GetContext(instance); diff --git a/connections/c/nc.h b/connections/c/nc.h index b3dd2505..cc00735f 100644 --- a/connections/c/nc.h +++ b/connections/c/nc.h @@ -27,11 +27,13 @@ extern "C" { // Creates a new Nearby Connections service. NC_API NC_INSTANCE NcCreateService(); +#if !defined(NC_OSS_BUILD) // Creates a new Nearby Connections service with an event logger. // The passed-in |event_logger| must remain valid until NcCloseService() is // called. NC_API NC_INSTANCE NcCreateServiceWithEventLogger(const NC_EVENT_LOGGER* event_logger); +#endif // !defined(NC_OSS_BUILD) // Closes a Nearby Connections service. NC_API void NcCloseService(NC_INSTANCE instance); diff --git a/connections/core.h b/connections/core.h index 69edb815..b72ebc6d 100644 --- a/connections/core.h +++ b/connections/core.h @@ -25,6 +25,7 @@ #include "connections/advertising_options.h" #include "connections/connection_options.h" #include "connections/discovery_options.h" +#include "connections/implementation/analytics/analytics_recorder.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/service_controller_router.h" #include "connections/listeners.h" @@ -37,7 +38,6 @@ #include "connections/v3/discovery_options.h" #include "connections/v3/listeners.h" #include "connections/v3/listening_result.h" -#include "internal/analytics/event_logger.h" #include "internal/interop/device.h" #include "internal/interop/device_provider.h" @@ -49,9 +49,9 @@ class Core { public: explicit Core(ServiceControllerRouter* router); // Client needs to call this constructor if analytics logger is needed. - Core(::nearby::analytics::EventLogger* event_logger, + Core(std::unique_ptr analytics_recorder, ServiceControllerRouter* router) - : client_(event_logger), router_(router) {} + : client_(std::move(analytics_recorder)), router_(router) {} ~Core(); Core(Core&&); Core& operator=(Core&&); diff --git a/connections/implementation/BUILD b/connections/implementation/BUILD index ec42c3d2..334a3149 100644 --- a/connections/implementation/BUILD +++ b/connections/implementation/BUILD @@ -49,30 +49,143 @@ cc_library( ], ) +cc_library( + name = "service_id_constants", + hdrs = ["service_id_constants.h"], + visibility = ["//connections:__subpackages__"], + deps = [ + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:string_view", + ], +) + +cc_library( + name = "bwu_handler", + srcs = ["base_bwu_handler.cc"], + hdrs = [ + "base_bwu_handler.h", + "bwu_handler.h", + ], + visibility = ["//connections:__subpackages__"], + deps = [ + ":client_proxy", + ":endpoint_channel", + ":service_id_constants", + "//internal/platform:base", + "//internal/platform:logging", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/functional:any_invocable", + ], +) + +cc_library( + name = "offline_frames", + srcs = [ + "offline_frames.cc", + "offline_frames_validator.cc", + ], + hdrs = [ + "internal_payload.h", + "offline_frames.h", + "offline_frames_validator.h", + ], + visibility = ["//connections:__subpackages__"], + deps = [ + "//connections:core_types", + "//connections/implementation/flags:connections_flags", + "//connections/implementation/proto:offline_wire_formats_cc_proto", + "//internal/flags:nearby_flags", + "//internal/platform:base", + "//internal/platform:logging", + "//internal/platform:mac_address", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/time", + ], +) + +cc_library( + name = "client_proxy", + srcs = ["client_proxy.cc"], + hdrs = ["client_proxy.h"], + visibility = ["//connections:__subpackages__"], + deps = [ + "//connections:core_types", + "//connections/implementation/analytics", + "//connections/implementation/flags:connections_flags", + "//connections/implementation/mediums/advertisements:dct_advertisement", + "//connections/implementation/proto:offline_wire_formats_cc_proto", + "//connections/v3:v3_types", + "//internal/base:file_path", + "//internal/base:files", + "//internal/flags:nearby_flags", + "//internal/interop:device", + "//internal/platform:base", + "//internal/platform:cancellation_flag", + "//internal/platform:error_code_recorder", + "//internal/platform:logging", + "//internal/platform:mac_address", + "//internal/platform:types", + "//internal/platform/implementation:comm", + "//internal/platform/implementation:platform", + "//internal/platform/implementation:types", + "//proto:connections_enums_cc_proto", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/random", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/time", + "@com_google_absl//absl/types:span", + ], +) + +cc_library( + name = "endpoint_channel", + srcs = [ + "base_endpoint_channel.cc", + "endpoint_channel_manager.cc", + ], + hdrs = [ + "base_endpoint_channel.h", + "endpoint_channel.h", + "endpoint_channel_manager.h", + ], + visibility = ["//connections:__subpackages__"], + deps = [ + ":client_proxy", + ":offline_frames", + "//connections:core_types", + "//connections/implementation/analytics", + "//connections/implementation/flags:connections_flags", + "//internal/flags:nearby_flags", + "//internal/platform:base", + "//internal/platform:logging", + "//internal/platform:types", + "//internal/platform/implementation:types", + "//proto:connections_enums_cc_proto", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/time", + "@com_google_ukey2//:ukey2", + ], +) + cc_library( name = "internal", srcs = [ - "awdl_bwu_handler.cc", - "awdl_endpoint_channel.cc", - "base_bwu_handler.cc", - "base_endpoint_channel.cc", "base_pcp_handler.cc", - "ble_endpoint_channel.cc", - "ble_l2cap_endpoint_channel.cc", - "bluetooth_bwu_handler.cc", "bluetooth_device_name.cc", - "bluetooth_endpoint_channel.cc", "bwu_manager.cc", - "client_proxy.cc", "connections_authentication_transport.cc", "encryption_runner.cc", - "endpoint_channel_manager.cc", "endpoint_manager.cc", "injected_bluetooth_device_store.cc", "internal_payload.cc", "internal_payload_factory.cc", - "offline_frames.cc", - "offline_frames_validator.cc", "offline_service_controller.cc", "p2p_cluster_pcp_handler.cc", "p2p_point_to_point_pcp_handler.cc", @@ -80,41 +193,17 @@ cc_library( "payload_manager.cc", "pcp_manager.cc", "service_controller_router.cc", - "webrtc_bwu_handler.cc", - "webrtc_bwu_handler_stub.cc", - "webrtc_endpoint_channel.cc", - "wifi_direct_bwu_handler.cc", - "wifi_direct_endpoint_channel.cc", - "wifi_hotspot_bwu_handler.cc", - "wifi_hotspot_endpoint_channel.cc", - "wifi_lan_bwu_handler.cc", - "wifi_lan_endpoint_channel.cc", "wifi_lan_service_info.cc", ], hdrs = [ - "awdl_bwu_handler.h", - "awdl_endpoint_channel.h", - "base_bwu_handler.h", - "base_endpoint_channel.h", "base_pcp_handler.h", - "ble_endpoint_channel.h", - "ble_l2cap_endpoint_channel.h", - "bluetooth_bwu_handler.h", "bluetooth_device_name.h", - "bluetooth_endpoint_channel.h", - "bwu_handler.h", "bwu_manager.h", - "client_proxy.h", "connections_authentication_transport.h", "encryption_runner.h", - "endpoint_channel.h", - "endpoint_channel_manager.h", "endpoint_manager.h", "injected_bluetooth_device_store.h", - "internal_payload.h", "internal_payload_factory.h", - "offline_frames.h", - "offline_frames_validator.h", "offline_service_controller.h", "p2p_cluster_pcp_handler.h", "p2p_point_to_point_pcp_handler.h", @@ -124,49 +213,37 @@ cc_library( "pcp_manager.h", "service_controller.h", "service_controller_router.h", - "service_id_constants.h", - "webrtc_bwu_handler.h", - "webrtc_bwu_handler_stub.h", - "webrtc_endpoint_channel.h", - "wifi_direct_bwu_handler.h", - "wifi_direct_endpoint_channel.h", - "wifi_hotspot_bwu_handler.h", - "wifi_hotspot_endpoint_channel.h", - "wifi_lan_bwu_handler.h", - "wifi_lan_endpoint_channel.h", "wifi_lan_service_info.h", ], - copts = [ - "-DCORE_ADAPTER_DLL", - "-DNO_WEBRTC", - ], + copts = ["-DCORE_ADAPTER_DLL"], visibility = [ "//chrome/chromeos/assistant/data_migration/lib:__pkg__", "//connections:__pkg__", "//connections:partners", "//connections/implementation/fuzzers:__pkg__", - "//connections/implementation/mediums/multiplex:__pkg__", "//sharing:__subpackages__", ], deps = [ ":ble_advertisement", + ":bwu_handler", + ":client_proxy", + ":endpoint_channel", + ":offline_frames", + ":service_id_constants", ":types", "//connections:core_types", "//connections/implementation/analytics", "//connections/implementation/flags:connections_flags", "//connections/implementation/mediums", "//connections/implementation/mediums:utils", - "//connections/implementation/mediums:webrtc_utils", + "//connections/implementation/mediums:webrtc", + "//connections/implementation/mediums:webrtc_peer_id", "//connections/implementation/mediums/advertisements:dct_advertisement", "//connections/implementation/mediums/advertisements:util", "//connections/implementation/mediums/ble:ble_advertisement_header", "//connections/implementation/mediums/ble:ble_socket", "//connections/implementation/proto:offline_wire_formats_cc_proto", "//connections/v3:v3_types", - "//internal/analytics:event_logger", - "//internal/base:file_path", - "//internal/base:files", - "//internal/base:masker", "//internal/flags:nearby_flags", "//internal/interop:authentication_status", "//internal/interop:authentication_transport_interface", @@ -175,7 +252,6 @@ cc_library( "//internal/platform:cancellation_flag", "//internal/platform:comm", "//internal/platform:connection_info", - "//internal/platform:error_code_recorder", "//internal/platform:logging", "//internal/platform:mac_address", "//internal/platform:types", @@ -184,7 +260,6 @@ cc_library( "//internal/platform/implementation:platform", "//internal/platform/implementation:types", "//internal/platform/implementation:wifi_utils", - "//internal/proto/analytics:connections_log_cc_proto", "//proto:connections_enums_cc_proto", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:btree", @@ -192,7 +267,6 @@ cc_library( "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/functional:bind_front", - "@com_google_absl//absl/random", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", @@ -214,6 +288,7 @@ cc_library( "fake_bwu_handler.h", "fake_endpoint_channel.h", "mock_device.h", + "mock_endpoint_channel.h", "mock_service_controller.h", "mock_service_controller_router.h", "offline_simulation_user.h", @@ -223,7 +298,11 @@ cc_library( "//connections:__subpackages__", ], deps = [ + ":bwu_handler", + ":client_proxy", + ":endpoint_channel", ":internal", + ":offline_frames", "//connections:core_types", "//connections/implementation/analytics", "//connections/implementation/flags:connections_flags", @@ -247,30 +326,29 @@ cc_test( name = "bwu_test", srcs = [ "base_bwu_handler_test.cc", - "bluetooth_bwu_test.cc", "bwu_manager_test.cc", - "wifi_direct_bwu_test.cc", - "wifi_hotspot_bwu_test.cc", ], deps = [ + ":bwu_handler", + ":client_proxy", + ":endpoint_channel", ":internal", ":internal_test", + ":offline_frames", + ":service_id_constants", "//connections:core_types", + "//connections/implementation/analytics", "//connections/implementation/flags:connections_flags", "//connections/implementation/mediums", "//internal/flags:nearby_flags", "//internal/platform:base", - "//internal/platform:logging", - "//internal/platform:test_util", "//internal/platform:types", - "//internal/platform/flags:platform_flags", + "//internal/platform/implementation:platform", # build_cleaner: keep "//internal/platform/implementation/g3", # build_cleaner: keep - "//internal/proto/analytics:connections_log_cc_proto", "//proto:connections_enums_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", ], ) @@ -279,6 +357,7 @@ cc_test( name = "pcp_manager_test", srcs = ["pcp_manager_test.cc"], deps = [ + ":endpoint_channel", ":internal", ":internal_test", "//connections:core_types", @@ -304,17 +383,20 @@ cc_test( ], shard_count = 8, deps = [ + ":client_proxy", + ":endpoint_channel", ":internal", ":internal_test", + ":offline_frames", ":types", "//connections:core_types", "//connections/implementation/analytics", + "//connections/implementation/analytics:mock_analytics_recorder", "//connections/implementation/flags:connections_flags", "//connections/implementation/mediums", - "//connections/implementation/mediums:webrtc_utils", + "//connections/implementation/mediums:webrtc_peer_id", "//connections/implementation/proto:offline_wire_formats_cc_proto", "//connections/v3:v3_types", - "//internal/analytics:mock_event_logger", "//internal/flags:nearby_flags", "//internal/interop:authentication_status", "//internal/interop:authentication_transport_interface", @@ -325,7 +407,6 @@ cc_test( "//internal/platform:test_util", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep - "//internal/proto/analytics:connections_log_cc_proto", "//proto:connections_enums_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/base:core_headers", @@ -362,7 +443,7 @@ cc_test( name = "offline_frames_test", srcs = ["offline_frames_validator_test.cc"], deps = [ - ":internal", + ":offline_frames", "//connections:core_types", "//connections/implementation/flags:connections_flags", "//connections/implementation/proto:offline_wire_formats_cc_proto", @@ -382,12 +463,11 @@ cc_test( "client_proxy_test.cc", ], deps = [ - ":internal", - "//base:casts", + ":client_proxy", "//connections:core_types", + "//connections/implementation/analytics:mock_analytics_recorder", "//connections/implementation/flags:connections_flags", "//connections/v3:v3_types", - "//internal/analytics:mock_event_logger", "//internal/flags:nearby_flags", "//internal/interop:device", "//internal/platform:base", @@ -411,6 +491,8 @@ cc_test( "encryption_runner_test.cc", ], deps = [ + ":client_proxy", + ":endpoint_channel", ":internal", "//connections/implementation/analytics", "//internal/platform:base", @@ -432,9 +514,12 @@ cc_test( "endpoint_manager_test.cc", ], deps = [ + ":client_proxy", + ":endpoint_channel", ":internal", + ":internal_test", + ":offline_frames", "//connections:core_types", - "//connections/implementation/analytics", "//connections/implementation/flags:connections_flags", "//internal/flags:nearby_flags", "//internal/platform:base", @@ -459,14 +544,17 @@ cc_test( "endpoint_channel_manager_test.cc", ], deps = [ + ":client_proxy", + ":endpoint_channel", ":internal", + ":offline_frames", + "//connections/implementation/analytics", "//connections/implementation/flags:connections_flags", "//internal/flags:nearby_flags", "//internal/platform:base", "//internal/platform:logging", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep - "//internal/proto/analytics:connections_log_cc_proto", "//proto:connections_enums_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/strings", @@ -484,13 +572,11 @@ cc_test( ], deps = [ ":internal", - "//connections/implementation/analytics", + ":internal_test", "//internal/platform:base", "//internal/platform/implementation/g3", # build_cleaner: keep - "//proto:connections_enums_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/strings:string_view", - "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", ], ) @@ -503,8 +589,8 @@ cc_test( deps = [ ":internal", ":internal_test", + ":offline_frames", "//connections:core_types", - "//connections/implementation/analytics", "//internal/platform:base", "//internal/platform:logging", "//internal/platform:test_util", @@ -525,6 +611,7 @@ cc_test( "service_controller_router_test.cc", ], deps = [ + ":client_proxy", ":internal", ":internal_test", "//connections:core_types", @@ -575,9 +662,11 @@ cc_test( ], deps = [ ":internal", + ":offline_frames", "//connections:core_types", "//connections/implementation/proto:offline_wire_formats_cc_proto", "//internal/platform:base", + "//internal/platform:comm", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep "@com_github_protobuf_matchers//protobuf-matchers", @@ -586,26 +675,3 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) - -cc_test( - name = "wifi_lan_bwu_handler_test", - srcs = [ - "wifi_lan_bwu_handler_test.cc", - ], - deps = [ - ":internal", - "//connections:core_types", - "//connections/implementation/mediums", - "//internal/analytics:mock_event_logger", - "//internal/platform:base", - "//internal/platform:mock_platform", - "//internal/platform:test_util", - "//internal/platform/implementation:comm", - "//internal/platform/implementation:platform", - "//internal/platform/implementation:platform_impl", - "//internal/proto/analytics:connections_log_cc_proto", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/strings:string_view", - "@com_google_googletest//:gtest_main", - ], -) diff --git a/connections/implementation/analytics/BUILD b/connections/implementation/analytics/BUILD index 903272ff..d893305c 100644 --- a/connections/implementation/analytics/BUILD +++ b/connections/implementation/analytics/BUILD @@ -20,59 +20,92 @@ cc_library( name = "analytics", srcs = [ "analytics_recorder.cc", - "throughput_recorder.cc", ], hdrs = [ "advertising_metadata_params.h", "analytics_recorder.h", "connection_attempt_metadata_params.h", "discovery_metadata_params.h", - "packet_meta_data.h", - "throughput_recorder.h", + "operation_result_with_medium.h", ], copts = ["-DCORE_ADAPTER_DLL"], visibility = ["//connections:__subpackages__"], deps = [ "//connections:core_types", - "//internal/analytics:event_logger", + "//internal/platform:error_code_recorder", + "//proto:connections_enums_cc_proto", + "@com_google_absl//absl/time", + ], +) + +cc_library( + name = "analytics_recorder_impl", + srcs = [ + "analytics_recorder_impl.cc", + ], + hdrs = [ + "analytics_recorder_impl.h", + ], + copts = ["-DCORE_ADAPTER_DLL"], + visibility = [ + "//connections/c:__pkg__", + "//sharing:__pkg__", + ], + deps = [ + ":analytics", + "//connections:core_types", "//internal/platform:error_code_recorder", "//internal/platform:logging", "//internal/platform:types", "//internal/platform/implementation:types", - "//internal/proto/analytics:connections_log_cc_proto", + "//location/nearby/analytics/cpp/logging:event_logger", + "//location/nearby/analytics/cpp/proto:connections_log_cc_proto", "//proto:connections_enums_cc_proto", "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:btree", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/meta:type_traits", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/strings:string_view", "@com_google_absl//absl/time", "@com_google_protobuf//:protobuf_lite", ], ) +cc_library( + name = "mock_analytics_recorder", + testonly = True, + hdrs = [ + "mock_analytics_recorder.h", + ], + compatible_with = ["//buildenv/target:non_prod"], + visibility = ["//connections:__subpackages__"], + deps = [ + ":analytics", + "//connections:core_types", + "//internal/platform:error_code_recorder", + "//proto:connections_enums_cc_proto", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest_for_library_testonly", + ], +) + cc_test( name = "analytics_test", size = "small", srcs = [ - "analytics_recorder_test.cc", - "throughput_recorder_test.cc", + "analytics_recorder_impl_test.cc", ], shard_count = 16, deps = [ ":analytics", + ":analytics_recorder_impl", "//connections:core_types", - "//internal/analytics:mock_event_logger", "//internal/platform:base", "//internal/platform:error_code_recorder", - "//internal/platform:logging", "//internal/platform:test_util", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep - "//internal/proto/analytics:connections_log_cc_proto", - "//internal/test", + "//location/nearby/analytics/cpp/logging:mock_event_logger", + "//location/nearby/analytics/cpp/proto:connections_log_cc_proto", "//net/proto2/contrib/parse_proto:parse_text_proto", "//proto:connections_enums_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", diff --git a/connections/implementation/analytics/advertising_metadata_params.h b/connections/implementation/analytics/advertising_metadata_params.h index 2b3b3372..2b40864b 100644 --- a/connections/implementation/analytics/advertising_metadata_params.h +++ b/connections/implementation/analytics/advertising_metadata_params.h @@ -17,7 +17,7 @@ #include -#include "internal/proto/analytics/connections_log.pb.h" +#include "connections/implementation/analytics/operation_result_with_medium.h" namespace nearby { @@ -26,8 +26,7 @@ struct AdvertisingMetadataParams { bool is_extended_advertisement_supported = false; int connected_ap_frequency = 0; bool is_nfc_available = false; - std::vector + std::vector operation_result_with_mediums = {}; }; diff --git a/connections/implementation/analytics/analytics_recorder.cc b/connections/implementation/analytics/analytics_recorder.cc index f91edcef..67649104 100644 --- a/connections/implementation/analytics/analytics_recorder.cc +++ b/connections/implementation/analytics/analytics_recorder.cc @@ -14,921 +14,35 @@ #include "connections/implementation/analytics/analytics_recorder.h" -#include -#include -#include #include #include -#include #include -#include "absl/algorithm/container.h" -#include "absl/container/btree_map.h" -#include "absl/strings/string_view.h" -#include "absl/time/time.h" #include "connections/implementation/analytics/advertising_metadata_params.h" #include "connections/implementation/analytics/connection_attempt_metadata_params.h" #include "connections/implementation/analytics/discovery_metadata_params.h" -#include "connections/payload_type.h" -#include "connections/strategy.h" -#include "internal/analytics/event_logger.h" -#include "internal/platform/error_code_params.h" -#include "internal/platform/implementation/system_clock.h" -#include "internal/platform/logging.h" -#include "internal/platform/mutex_lock.h" -#include "internal/proto/analytics/connections_log.pb.h" +#include "connections/implementation/analytics/operation_result_with_medium.h" #include "proto/connections_enums.pb.h" -#include "google/protobuf/repeated_ptr_field.h" -namespace nearby { -namespace analytics { +namespace nearby::analytics { -namespace { -// const char kVersion_1_0_0[] = "v1.0.0"; -const char kVersion[] = "v1.5.0"; -constexpr absl::string_view kOnStartClientSession = "OnStartClientSession"; -const absl::Duration kConnectionTokenMaxLife = absl::Hours(24); - -using ::location::nearby::analytics::proto::ConnectionsLog; -using ::location::nearby::proto::connections::ACCEPTED; -using ::location::nearby::proto::connections::ADVERTISER; -using ::location::nearby::proto::connections::BandwidthUpgradeErrorStage; -using ::location::nearby::proto::connections::BandwidthUpgradeResult; -using ::location::nearby::proto::connections::BYTES; -using ::location::nearby::proto::connections::CLIENT_SESSION; -using ::location::nearby::proto::connections::CONNECTION_CLOSED; -using ::location::nearby::proto::connections::ConnectionAttemptDirection; -using ::location::nearby::proto::connections::ConnectionAttemptResult; -using ::location::nearby::proto::connections::ConnectionAttemptType; using ::location::nearby::proto::connections::ConnectionBand; -using ::location::nearby::proto::connections::ConnectionRequestResponse; -using ::location::nearby::proto::connections::ConnectionsStrategy; using ::location::nearby::proto::connections::ConnectionTechnology; -using ::location::nearby::proto::connections::DisconnectionReason; -using ::location::nearby::proto::connections::DISCOVERER; -using ::location::nearby::proto::connections::ERROR_CODE; -using ::location::nearby::proto::connections::EventType; -using ::location::nearby::proto::connections::FILE; -using ::location::nearby::proto::connections::IGNORED; -using ::location::nearby::proto::connections::INCOMING; -using ::location::nearby::proto::connections::INITIAL; using ::location::nearby::proto::connections::Medium; -using ::location::nearby::proto::connections::MOVED_TO_NEW_MEDIUM; -using ::location::nearby::proto::connections::NOT_SENT; -using ::location::nearby::proto::connections::OperationResultCategory; using ::location::nearby::proto::connections::OperationResultCode; -using ::location::nearby::proto::connections::OUTGOING; -using ::location::nearby::proto::connections::P2P_CLUSTER; -using ::location::nearby::proto::connections::P2P_POINT_TO_POINT; -using ::location::nearby::proto::connections::P2P_STAR; -using ::location::nearby::proto::connections::PayloadStatus; -using ::location::nearby::proto::connections::PayloadType; -using ::location::nearby::proto::connections::REJECTED; -using ::location::nearby::proto::connections::RESULT_SUCCESS; -using ::location::nearby::proto::connections::SessionRole; -using ::location::nearby::proto::connections::START_CLIENT_SESSION; -using ::location::nearby::proto::connections::START_STRATEGY_SESSION; -using ::location::nearby::proto::connections::STOP_CLIENT_SESSION; -using ::location::nearby::proto::connections::STOP_STRATEGY_SESSION; -using ::location::nearby::proto::connections::StopAdvertisingReason; -using ::location::nearby::proto::connections::StopDiscoveringReason; -using ::location::nearby::proto::connections::STREAM; -using ::location::nearby::proto::connections::UNFINISHED; -using ::location::nearby::proto::connections::UNFINISHED_ERROR; -using ::location::nearby::proto::connections::UNKNOWN_MEDIUM; -using ::location::nearby::proto::connections::UNKNOWN_PAYLOAD_TYPE; -using ::location::nearby::proto::connections::UNKNOWN_STRATEGY; -using ::location::nearby::proto::connections::UPGRADE_RESULT_SUCCESS; -using ::location::nearby::proto::connections::UPGRADE_SUCCESS; -using ::location::nearby::proto::connections::UPGRADE_UNFINISHED; -using ::location::nearby::proto::connections::UPGRADED; -using ::nearby::analytics::EventLogger; -using SafeDisconnectionResult = ::location::nearby::analytics::proto:: - ConnectionsLog::EstablishedConnection::SafeDisconnectionResult; - -OperationResultCategory ConvertToOperationResultCategory( - OperationResultCode result_code) { - if (result_code == OperationResultCode::DETAIL_SUCCESS) { - return OperationResultCategory::CATEGORY_SUCCESS; - } - // TODO(b/409865630): check later if we need to add back the dct error. - // Section of CATEGORY_DCT_ERROR, from 5000 to 5499 if (result_code - // >= OperationResultCode::DCT_ERROR_BLE_DISABLED) { - // return OperationResultCategory::CATEGORY_DCT_ERROR; - //} - - // Section of CATEGORY_NEARBY_ERROR, starting from 4500 to 4999 - if (result_code >= - OperationResultCode::NEARBY_BLE_ADVERTISEMENT_MAPPING_TO_MAC_ERROR) { - return OperationResultCategory::CATEGORY_NEARBY_ERROR; - } - // Section of CATEGORY_CONNECTIVITY_ERROR, starting from 3500 to 4499 - if (result_code >= - OperationResultCode::CONNECTIVITY_WIFI_AWARE_ATTACH_FAILURE) { - return OperationResultCategory::CATEGORY_CONNECTIVITY_ERROR; - } - // Section of CATEGORY_IO_ERROR, from 3000 to 3499 - if (result_code >= OperationResultCode::IO_FILE_OPENING_ERROR) { - return OperationResultCategory::CATEGORY_IO_ERROR; - } - // Section of CATEGORY_MISCELLANEOUS, from 2500 to 2999 - if (result_code >= - OperationResultCode::MISCELLEANEOUS_BLUETOOTH_MAC_ADDRESS_NULL) { - return OperationResultCategory::CATEGORY_MISCELLANEOUS; - } - // Section of CATEGORY_CLIENT_ERROR, from 2000 to 2499 - if (result_code >= - OperationResultCode:: - CLIENT_WIFI_DIRECT_ALREADY_HOSTING_DIRECT_GROUP_FOR_THIS_CLIENT) { - return OperationResultCategory::CATEGORY_CLIENT_ERROR; - } - // Section of CATEGORY_MEDIUM_UNAVAILABLE, from 1500 to 1999 - if (result_code >= OperationResultCode:: - MEDIUM_UNAVAILABLE_WIFI_AWARE_RESOURCE_NOT_AVAILABLE) { - return OperationResultCategory::CATEGORY_MEDIUM_UNAVAILABLE; - } - // Section of CATEGORY_DEVICE_STATE_ERROR, from 1000 to 1499 - if (result_code >= - OperationResultCode::DEVICE_STATE_ERROR_UNFINISHED_UPGRADE_ATTEMPTS) { - return OperationResultCategory::CATEGORY_DEVICE_STATE_ERROR; - } - // Section of CATEGORY_CLIENT_CANCELLATION, from 500 to 999 - if (result_code >= - OperationResultCode::CLIENT_CANCELLATION_REMOTE_IN_CANCELED_STATE) { - return OperationResultCategory::CATEGORY_CLIENT_CANCELLATION; - } - // Clarify other non success cases as unknown - return OperationResultCategory::CATEGORY_UNKNOWN; -} -} // namespace - -AnalyticsRecorder::AnalyticsRecorder(EventLogger* event_logger) - : event_logger_(event_logger) { - VLOG(1) << "Start AnalyticsRecorder ctor event_logger_=" << event_logger_; - LogStartSession(); -} - -AnalyticsRecorder::~AnalyticsRecorder() = default; - -bool AnalyticsRecorder::IsSessionLogged() { - MutexLock lock(&mutex_); - return session_was_logged_; -} - -int AnalyticsRecorder::GetLatestUpdateIndexLocked( - const std::vector& list) { - int latest_update_index = 0; - for (const auto& operation_result_with_medium : list) { - if (operation_result_with_medium.update_index() > latest_update_index) { - latest_update_index = operation_result_with_medium.update_index(); - } - } - return latest_update_index; -} - -void AnalyticsRecorder::OnStartAdvertising( - connections::Strategy strategy, const std::vector& mediums, - AdvertisingMetadataParams* advertising_metadata_params) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnStartAdvertising")) { - return; - } - if (!strategy.IsValid()) { - LOG(INFO) << "AnalyticsRecorder OnStartAdvertising with unknown " - "strategy, bail out."; - return; - } - // Initialize/update a StrategySession. - UpdateStrategySessionLocked(strategy, ADVERTISER); - - // Initialize and set a AdvertisingPhase. - started_advertising_phase_time_ = SystemClock::ElapsedRealtime(); - current_advertising_phase_ = - std::make_unique(); - absl::c_copy(mediums, RepeatedFieldBackInserter( - current_advertising_phase_->mutable_medium())); - // Set a AdvertisingMetadata. - AdvertisingMetadataParams default_params = {}; - if (advertising_metadata_params == nullptr) { - advertising_metadata_params = &default_params; - } - if (!advertising_metadata_params->operation_result_with_mediums.empty()) { - absl::c_copy(advertising_metadata_params->operation_result_with_mediums, - RepeatedFieldBackInserter( - current_advertising_phase_->mutable_adv_dis_result())); - } - auto* advertising_metadata = - current_advertising_phase_->mutable_advertising_metadata(); - advertising_metadata->set_supports_extended_ble_advertisements( - advertising_metadata_params->is_extended_advertisement_supported); - advertising_metadata->set_connected_ap_frequency( - advertising_metadata_params->connected_ap_frequency); - advertising_metadata->set_supports_nfc_technology( - advertising_metadata_params->is_nfc_available); -} - -void AnalyticsRecorder::OnStopAdvertising() { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnStopAdvertising")) { - return; - } - RecordAdvertisingPhaseDurationAndReasonLocked(/* on_stop= */ true); -} - -int AnalyticsRecorder::GetNextAdvertisingUpdateIndex() { - MutexLock lock(&mutex_); - - if (current_advertising_phase_ == nullptr) { - return 0; - } - return GetLatestUpdateIndexLocked( - std::vector( - current_advertising_phase_->adv_dis_result().begin(), - current_advertising_phase_->adv_dis_result().end())) + - 1; -} - -void AnalyticsRecorder::OnStartDiscovery( - connections::Strategy strategy, const std::vector& mediums, - DiscoveryMetadataParams* discovery_metadata_params) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnStartDiscovery")) { - return; - } - if (!strategy.IsValid()) { - LOG(INFO) << "AnalyticsRecorder OnStartDiscovery unknown " - "strategy enter, bail out."; - return; - } - - // Initialize/update a StrategySession. - UpdateStrategySessionLocked(strategy, DISCOVERER); - - // Initialize and set a DiscoveryPhase. - started_discovery_phase_time_ = SystemClock::ElapsedRealtime(); - current_discovery_phase_ = std::make_unique(); - absl::c_copy(mediums, RepeatedFieldBackInserter( - current_discovery_phase_->mutable_medium())); - // Set a DiscoveryMetadata. - DiscoveryMetadataParams default_params = {}; - if (discovery_metadata_params == nullptr) { - discovery_metadata_params = &default_params; - } - if (!discovery_metadata_params->operation_result_with_mediums.empty()) { - absl::c_copy(discovery_metadata_params->operation_result_with_mediums, - RepeatedFieldBackInserter( - current_discovery_phase_->mutable_adv_dis_result())); - } - auto* discovery_metadata = - current_discovery_phase_->mutable_discovery_metadata(); - discovery_metadata->set_supports_extended_ble_advertisements( - discovery_metadata_params->is_extended_advertisement_supported); - discovery_metadata->set_connected_ap_frequency( - discovery_metadata_params->connected_ap_frequency); - discovery_metadata->set_supports_nfc_technology( - discovery_metadata_params->is_nfc_available); -} - -void AnalyticsRecorder::OnStopDiscovery() { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnStopDiscovery")) { - return; - } - RecordDiscoveryPhaseDurationAndReasonLocked(/*on_stop=*/true); -} - -int AnalyticsRecorder::GetNextDiscoveryUpdateIndex() { - MutexLock lock(&mutex_); - if (current_discovery_phase_ == nullptr) { - return 0; - } - return GetLatestUpdateIndexLocked( - std::vector( - current_discovery_phase_->adv_dis_result().begin(), - current_discovery_phase_->adv_dis_result().end())) + - 1; -} - -void AnalyticsRecorder::OnStartedIncomingConnectionListening( - connections::Strategy strategy) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnStartedIncomingConnectionListening")) { - return; - } - UpdateStrategySessionLocked(strategy, ADVERTISER); - if (started_advertising_phase_time_ == absl::InfinitePast()) { - started_advertising_phase_time_ = SystemClock::ElapsedRealtime(); - } -} - -void AnalyticsRecorder::OnStoppedIncomingConnectionListening() { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnStoppedIncomingConnectionListening")) { - return; - } - RecordAdvertisingPhaseDurationAndReasonLocked(/* on_stop= */ false); -} - -void AnalyticsRecorder::OnEndpointFound(Medium medium) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnEndpointFound")) { - return; - } - if (current_discovery_phase_ == nullptr) { - LOG(INFO) << "Unable to record discovered endpoint due to null " - "current_discovery_phase_"; - return; - } - ConnectionsLog::DiscoveredEndpoint* discovered_endpoint = - current_discovery_phase_->add_discovered_endpoint(); - discovered_endpoint->set_medium(medium); - discovered_endpoint->set_latency_millis(absl::ToInt64Milliseconds( - SystemClock::ElapsedRealtime() - started_discovery_phase_time_)); -} - -void AnalyticsRecorder::OnRequestConnection( - const connections::Strategy& strategy, const std::string& endpoint_id) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("onRequestConnection")) { - return; - } - - UpdateStrategySessionLocked(strategy, DISCOVERER); - if (started_discovery_phase_time_ == absl::InfinitePast()) { - started_discovery_phase_time_ = SystemClock::ElapsedRealtime(); - } -} - -void AnalyticsRecorder::OnConnectionRequestReceived( - const std::string& remote_endpoint_id) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnConnectionRequestReceived")) { - return; - } - absl::Time current_time = SystemClock::ElapsedRealtime(); - auto connection_request = - std::make_unique(); - connection_request->set_duration_millis(absl::ToUnixMillis(current_time)); - connection_request->set_request_delay_millis(absl::ToInt64Milliseconds( - current_time - started_advertising_phase_time_)); - incoming_connection_requests_.insert( - {remote_endpoint_id, std::move(connection_request)}); -} - -void AnalyticsRecorder::OnConnectionRequestSent( - const std::string& remote_endpoint_id) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnConnectionRequestSent")) { - return; - } - absl::Time current_time = SystemClock::ElapsedRealtime(); - auto connection_request = - std::make_unique(); - connection_request->set_duration_millis(absl::ToUnixMillis(current_time)); - connection_request->set_request_delay_millis( - absl::ToInt64Milliseconds(current_time - started_discovery_phase_time_)); - outgoing_connection_requests_.insert( - {remote_endpoint_id, std::move(connection_request)}); -} - -void AnalyticsRecorder::OnRemoteEndpointAccepted( - const std::string& remote_endpoint_id) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnRemoteEndpointAccepted")) { - return; - } - RemoteEndpointRespondedLocked(remote_endpoint_id, ACCEPTED); -} - -void AnalyticsRecorder::OnLocalEndpointAccepted( - const std::string& remote_endpoint_id) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnLocalEndpointAccepted")) { - return; - } - LocalEndpointRespondedLocked(remote_endpoint_id, ACCEPTED); -} - -void AnalyticsRecorder::OnRemoteEndpointRejected( - const std::string& remote_endpoint_id) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnRemoteEndpointRejected")) { - return; - } - RemoteEndpointRespondedLocked(remote_endpoint_id, REJECTED); -} - -void AnalyticsRecorder::OnLocalEndpointRejected( - const std::string& remote_endpoint_id) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnLocalEndpointRejected")) { - return; - } - LocalEndpointRespondedLocked(remote_endpoint_id, REJECTED); -} - -void AnalyticsRecorder::OnIncomingConnectionAttempt( - ConnectionAttemptType type, Medium medium, ConnectionAttemptResult result, - absl::Duration duration, const std::string& connection_token, - ConnectionAttemptMetadataParams* connection_attempt_metadata_params) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnIncomingConnectionAttempt")) { - return; - } - if (current_strategy_session_ == nullptr) { - LOG(INFO) << "Unable to record incoming connection attempt due to " - "null current_strategy_session_"; - return; - } - - ConnectionAttemptMetadataParams default_params = {}; - if (connection_attempt_metadata_params == nullptr) { - connection_attempt_metadata_params = &default_params; - } - OnIncomingConnectionAttemptLocked(type, medium, result, duration, - connection_token, - connection_attempt_metadata_params); -} - -void AnalyticsRecorder::OnIncomingConnectionAttemptLocked( - location::nearby::proto::connections::ConnectionAttemptType type, - location::nearby::proto::connections::Medium medium, - location::nearby::proto::connections::ConnectionAttemptResult result, - absl::Duration duration, const std::string& connection_token, - ConnectionAttemptMetadataParams* connection_attempt_metadata_params) { - auto* connection_attempt = - current_strategy_session_->add_connection_attempt(); - connection_attempt->set_duration_millis(absl::ToInt64Milliseconds(duration)); - connection_attempt->set_type(type); - connection_attempt->set_direction(INCOMING); - connection_attempt->set_medium(medium); - connection_attempt->set_attempt_result(result); - connection_attempt->set_connection_token(connection_token); - - auto* connection_attempt_metadata = - connection_attempt->mutable_connection_attempt_metadata(); - connection_attempt_metadata->set_technology( - connection_attempt_metadata_params->technology); - connection_attempt_metadata->set_band( - connection_attempt_metadata_params->band); - connection_attempt_metadata->set_frequency( - connection_attempt_metadata_params->frequency); - connection_attempt_metadata->set_network_operator( - connection_attempt_metadata_params->network_operator); - connection_attempt_metadata->set_country_code( - connection_attempt_metadata_params->country_code); - connection_attempt_metadata->set_frequency( - connection_attempt_metadata_params->frequency); - connection_attempt_metadata->set_is_tdls_used( - connection_attempt_metadata_params->is_tdls_used); - connection_attempt_metadata->set_wifi_hotspot_status( - connection_attempt_metadata_params->wifi_hotspot_enabled); - connection_attempt_metadata->set_try_counts( - connection_attempt_metadata_params->try_count); - connection_attempt_metadata->set_max_tx_speed( - connection_attempt_metadata_params->max_wifi_tx_speed); - connection_attempt_metadata->set_max_rx_speed( - connection_attempt_metadata_params->max_wifi_rx_speed); - connection_attempt_metadata->set_wifi_channel_width( - connection_attempt_metadata_params->channel_width); - - auto operation_result_proto = - std::make_unique(); - operation_result_proto->set_result_code( - connection_attempt_metadata_params->operation_result_code); - operation_result_proto->set_result_category(ConvertToOperationResultCategory( - connection_attempt_metadata_params->operation_result_code)); - connection_attempt->set_allocated_operation_result( - operation_result_proto.release()); -} - -void AnalyticsRecorder::OnOutgoingConnectionAttempt( - const std::string& remote_endpoint_id, ConnectionAttemptType type, - Medium medium, ConnectionAttemptResult result, absl::Duration duration, - const std::string& connection_token, - ConnectionAttemptMetadataParams* connection_attempt_metadata_params) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnOutgoingConnectionAttempt")) { - return; - } - if (current_strategy_session_ == nullptr) { - LOG(INFO) << "Unable to record outgoing connection attempt due to " - "null current_strategy_session_"; - return; - } - - ConnectionAttemptMetadataParams default_params = {}; - if (connection_attempt_metadata_params == nullptr) { - connection_attempt_metadata_params = &default_params; - } - - // For the case of transfer a big file and the upgrades always failure, then - // there will have repeating upgrade attempt and cause many same attempt value - // be log. So add a method to skip. - if (ConnectionAttemptResultCodeExistedLocked( - medium, OUTGOING, connection_token, type, - connection_attempt_metadata_params->operation_result_code)) { - return; - } - - OnOutgoingConnectionAttemptLocked(remote_endpoint_id, type, medium, result, - duration, connection_token, - connection_attempt_metadata_params); -} - -void AnalyticsRecorder::OnOutgoingConnectionAttemptLocked( - const std::string& remote_endpoint_id, ConnectionAttemptType type, - Medium medium, ConnectionAttemptResult result, absl::Duration duration, - const std::string& connection_token, - ConnectionAttemptMetadataParams* connection_attempt_metadata_params) { - auto* connection_attempt = - current_strategy_session_->add_connection_attempt(); - connection_attempt->set_duration_millis(absl::ToInt64Milliseconds(duration)); - connection_attempt->set_type(type); - connection_attempt->set_direction(OUTGOING); - connection_attempt->set_medium(medium); - connection_attempt->set_attempt_result(result); - connection_attempt->set_connection_token(connection_token); - - auto* connection_attempt_metadata = - connection_attempt->mutable_connection_attempt_metadata(); - connection_attempt_metadata->set_technology( - connection_attempt_metadata_params->technology); - connection_attempt_metadata->set_band( - connection_attempt_metadata_params->band); - connection_attempt_metadata->set_frequency( - connection_attempt_metadata_params->frequency); - connection_attempt_metadata->set_network_operator( - connection_attempt_metadata_params->network_operator); - connection_attempt_metadata->set_country_code( - connection_attempt_metadata_params->country_code); - connection_attempt_metadata->set_frequency( - connection_attempt_metadata_params->frequency); - connection_attempt_metadata->set_is_tdls_used( - connection_attempt_metadata_params->is_tdls_used); - connection_attempt_metadata->set_wifi_hotspot_status( - connection_attempt_metadata_params->wifi_hotspot_enabled); - connection_attempt_metadata->set_try_counts( - connection_attempt_metadata_params->try_count); - connection_attempt_metadata->set_max_tx_speed( - connection_attempt_metadata_params->max_wifi_tx_speed); - connection_attempt_metadata->set_max_rx_speed( - connection_attempt_metadata_params->max_wifi_rx_speed); - connection_attempt_metadata->set_wifi_channel_width( - connection_attempt_metadata_params->channel_width); - - auto operation_result_proto = - std::make_unique(); - operation_result_proto->set_result_code( - connection_attempt_metadata_params->operation_result_code); - operation_result_proto->set_result_category(ConvertToOperationResultCategory( - connection_attempt_metadata_params->operation_result_code)); - connection_attempt->set_allocated_operation_result( - operation_result_proto.release()); - - if (type == INITIAL && result != RESULT_SUCCESS) { - auto it = outgoing_connection_requests_.find(remote_endpoint_id); - if (it != outgoing_connection_requests_.end()) { - // An outgoing, initial ConnectionAttempt has a corresponding - // ConnectionRequest that, since the ConnectionAttempt has failed, will - // never be delivered to the advertiser. - auto pair = outgoing_connection_requests_.extract(it); - std::unique_ptr& connection_request = - pair.mapped(); - connection_request->set_local_response(NOT_SENT); - connection_request->set_remote_response(NOT_SENT); - UpdateDiscovererConnectionRequestLocked(connection_request.get()); - } - } -} - -void AnalyticsRecorder::OnConnectionEstablished( - const std::string& endpoint_id, Medium medium, - const std::string& connection_token) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnConnectionEstablished")) { - return; - } - auto it = active_connections_.find(endpoint_id); - if (it != active_connections_.end()) { - const std::unique_ptr& logical_connection = it->second; - logical_connection->PhysicalConnectionEstablished(medium, connection_token); - } else { - active_connections_.insert( - {endpoint_id, - std::make_unique(medium, connection_token)}); - } -} - -void AnalyticsRecorder::OnConnectionClosed(const std::string& endpoint_id, - Medium medium, - DisconnectionReason reason, - SafeDisconnectionResult result) { - MutexLock lock(&mutex_); - LOG(INFO) << __func__ - << ": OnConnectionClosed is called with endpoint_id:" << endpoint_id - << ", medium:" << Medium_Name(medium) - << ", reason:" << DisconnectionReason_Name(reason) - << ", result:" << result; - - if (!CanRecordAnalyticsLocked("OnConnectionClosed")) { - return; - } - - if (current_strategy_session_ == nullptr) { - VLOG(1) << "AnalyticsRecorder CanRecordAnalytics Unexpected call " - << __func__ << " since current_strategy_session_ is required."; - return; - } - - auto it = active_connections_.find(endpoint_id); - if (it == active_connections_.end()) { - return; - } - const std::unique_ptr& logical_connection = it->second; - logical_connection->PhysicalConnectionClosed(medium, reason, result); - if (reason != UPGRADED) { - // Unless this is an upgraded connection, remove this from our active - // connections. Any future communication with an endpoint will need to be - // re-established with a new ConnectionRequest. - auto pair = active_connections_.extract(it); - std::unique_ptr& logical_connection = pair.mapped(); - - absl::c_copy( - logical_connection->GetEstablisedConnections(), - RepeatedFieldBackInserter( - current_strategy_session_->mutable_established_connection())); - } -} - -void AnalyticsRecorder::OnIncomingPayloadStarted( - const std::string& endpoint_id, std::int64_t payload_id, - connections::PayloadType type, std::int64_t total_size_bytes) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnIncomingPayloadStarted")) { - return; - } - auto it = active_connections_.find(endpoint_id); - if (it == active_connections_.end()) { - return; - } - const std::unique_ptr& logical_connection = it->second; - logical_connection->IncomingPayloadStarted( - payload_id, PayloadTypeToProtoPayloadType(type), total_size_bytes); -} - -void AnalyticsRecorder::OnPayloadChunkReceived(const std::string& endpoint_id, - std::int64_t payload_id, - std::int64_t chunk_size_bytes) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnPayloadChunkReceived")) { - return; - } - auto it = active_connections_.find(endpoint_id); - if (it == active_connections_.end()) { - return; - } - const std::unique_ptr& logical_connection = it->second; - logical_connection->ChunkReceived(payload_id, chunk_size_bytes); -} - -void AnalyticsRecorder::OnIncomingPayloadDone( - const std::string& endpoint_id, std::int64_t payload_id, - PayloadStatus status, OperationResultCode operation_result_code) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnIncomingPayloadDone")) { - return; - } - auto it = active_connections_.find(endpoint_id); - if (it == active_connections_.end()) { - return; - } - const std::unique_ptr& logical_connection = it->second; - logical_connection->IncomingPayloadDone(payload_id, status, - operation_result_code); -} - -void AnalyticsRecorder::OnOutgoingPayloadStarted( - const std::vector& endpoint_ids, std::int64_t payload_id, - connections::PayloadType type, std::int64_t total_size_bytes) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnOutgoingPayloadStarted")) { - return; - } - for (const auto& endpoint_id : endpoint_ids) { - auto it = active_connections_.find(endpoint_id); - if (it == active_connections_.end()) { - continue; - } - const std::unique_ptr& logical_connection = it->second; - logical_connection->OutgoingPayloadStarted( - payload_id, PayloadTypeToProtoPayloadType(type), total_size_bytes); - } -} - -void AnalyticsRecorder::OnPayloadChunkSent(const std::string& endpoint_id, - std::int64_t payload_id, - std::int64_t chunk_size_bytes) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnPayloadChunkSent")) { - return; - } - auto it = active_connections_.find(endpoint_id); - if (it == active_connections_.end()) { - return; - } - const std::unique_ptr& logical_connection = it->second; - logical_connection->ChunkSent(payload_id, chunk_size_bytes); -} - -void AnalyticsRecorder::OnOutgoingPayloadDone( - const std::string& endpoint_id, std::int64_t payload_id, - PayloadStatus status, OperationResultCode operation_result_code) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnOutgoingPayloadDone")) { - return; - } - auto it = active_connections_.find(endpoint_id); - if (it == active_connections_.end()) { - return; - } - - const std::unique_ptr& logical_connection = it->second; - logical_connection->OutgoingPayloadDone(payload_id, status, - operation_result_code); -} - -void AnalyticsRecorder::OnBandwidthUpgradeStarted( - const std::string& endpoint_id, Medium from_medium, Medium to_medium, - ConnectionAttemptDirection direction, const std::string& connection_token) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnBandwidthUpgradeStarted")) { - return; - } - auto bandwidth_upgrade_attempt = - std::make_unique(); - bandwidth_upgrade_attempt->set_duration_millis( - absl::ToUnixMillis(SystemClock::ElapsedRealtime())); - bandwidth_upgrade_attempt->set_from_medium(from_medium); - bandwidth_upgrade_attempt->set_to_medium(to_medium); - bandwidth_upgrade_attempt->set_direction(direction); - bandwidth_upgrade_attempt->set_connection_token(connection_token); - bandwidth_upgrade_attempts_.insert( - {endpoint_id, std::move(bandwidth_upgrade_attempt)}); -} - -void AnalyticsRecorder::UpdateBwUpgradeNetworkInfo( - const std::string& endpoint_id, int num_interfaces, - int num_ipv6_only_interfaces) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("UpdateBwUpgradeNetworkInfo")) { - return; - } - auto it = bandwidth_upgrade_attempts_.find(endpoint_id); - if (it == bandwidth_upgrade_attempts_.end()) { - return; - } - ConnectionsLog::BandwidthUpgradeAttempt* bandwidth_upgrade_attempt = - it->second.get(); - bandwidth_upgrade_attempt->set_num_interfaces(num_interfaces); - bandwidth_upgrade_attempt->set_num_ipv6_only_interfaces( - num_ipv6_only_interfaces); -} - -void AnalyticsRecorder::OnBandwidthUpgradeError( - const std::string& endpoint_id, BandwidthUpgradeResult result, - BandwidthUpgradeErrorStage error_stage, - OperationResultCode operation_result_code) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnBandwidthUpgradeError")) { - return; - } - // If the same records existed, drop this one. - if (EraseIfBandwidthUpgradeRecordExistedLocked( - endpoint_id, result, error_stage, operation_result_code)) { - return; - } - FinishUpgradeAttemptLocked(endpoint_id, result, error_stage, - operation_result_code); -} - -void AnalyticsRecorder::OnBandwidthUpgradeSuccess( - const std::string& endpoint_id) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnBandwidthUpgradeSuccess")) { - return; - } - FinishUpgradeAttemptLocked(endpoint_id, UPGRADE_RESULT_SUCCESS, - UPGRADE_SUCCESS, - OperationResultCode::DETAIL_SUCCESS); -} - -void AnalyticsRecorder::OnErrorCode(const ErrorCodeParams& params) { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("OnErrorCode")) { - return; - } - auto error_code = std::make_unique(); - error_code->set_medium(params.medium); - error_code->set_event(params.event); - error_code->set_connection_token(params.connection_token); - error_code->set_description(params.description); - - if (params.is_common_error) { - error_code->set_common_error(params.common_error); - } else { - switch (params.event) { - case location::nearby::errorcode::proto::START_ADVERTISING: - error_code->set_start_advertising_error(params.start_advertising_error); - break; - case location::nearby::errorcode::proto::STOP_ADVERTISING: - error_code->set_stop_advertising_error(params.stop_advertising_error); - break; - case location::nearby::errorcode::proto:: - START_LISTENING_INCOMING_CONNECTION: - error_code->set_start_listening_incoming_connection_error( - params.start_listening_incoming_connection_error); - break; - case location::nearby::errorcode::proto:: - STOP_LISTENING_INCOMING_CONNECTION: - error_code->set_stop_listening_incoming_connection_error( - params.stop_listening_incoming_connection_error); - break; - case location::nearby::errorcode::proto::START_DISCOVERING: - error_code->set_start_discovering_error(params.start_discovering_error); - break; - case location::nearby::errorcode::proto::STOP_DISCOVERING: - error_code->set_stop_discovering_error(params.stop_discovering_error); - break; - case location::nearby::errorcode::proto::CONNECT: - error_code->set_connect_error(params.connect_error); - break; - case location::nearby::errorcode::proto::DISCONNECT: - error_code->set_disconnect_error(params.disconnect_error); - break; - case location::nearby::errorcode::proto::UNKNOWN_EVENT: - default: - error_code->set_common_error(params.common_error); - break; - } - } - - ConnectionsLog connections_log; - connections_log.set_event_type(ERROR_CODE); - connections_log.set_version(kVersion); - connections_log.set_allocated_error_code(error_code.release()); - - VLOG(1) << "AnalyticsRecorder LogErrorCode connections_log=" - << connections_log.DebugString(); // NOLINT - - event_logger_->Log(connections_log); -} - -void AnalyticsRecorder::LogStartSession() { - MutexLock lock(&mutex_); - if (start_client_session_was_logged_) { - LOG(WARNING) << "AnalyticsRecorder CanRecordAnalytics Unexpected call " - << kOnStartClientSession - << " after start client session has already been logged."; - return; - } - - session_was_logged_ = false; - if (CanRecordAnalyticsLocked(kOnStartClientSession)) { - client_session_ = std::make_unique(); - started_client_session_time_ = SystemClock::ElapsedRealtime(); - start_client_session_was_logged_ = true; - LogEvent(START_CLIENT_SESSION); - } -} - -void AnalyticsRecorder::LogSession() { - MutexLock lock(&mutex_); - if (!CanRecordAnalyticsLocked("LogSession")) { - return; - } - FinishStrategySessionLocked(); - client_session_->set_duration_millis(absl::ToInt64Milliseconds( - SystemClock::ElapsedRealtime() - started_client_session_time_)); - LogClientSessionLocked(); - LogEvent(STOP_CLIENT_SESSION); - start_client_session_was_logged_ = false; - session_was_logged_ = true; -} std::unique_ptr AnalyticsRecorder::BuildAdvertisingMetadataParams( bool is_extended_advertisement_supported, int connected_ap_frequency, bool is_nfc_available, - const std::vector& + const std::vector& operation_result_with_mediums) { auto params = std::make_unique(); params->is_extended_advertisement_supported = is_extended_advertisement_supported; params->connected_ap_frequency = connected_ap_frequency; params->is_nfc_available = is_nfc_available; - params->operation_result_with_mediums = - std::move(operation_result_with_mediums); + params->operation_result_with_mediums = operation_result_with_mediums; return params; } @@ -936,15 +50,14 @@ std::unique_ptr AnalyticsRecorder::BuildDiscoveryMetadataParams( bool is_extended_advertisement_supported, int connected_ap_frequency, bool is_nfc_available, - const std::vector& + const std::vector& operation_result_with_mediums) { auto params = std::make_unique(); params->is_extended_advertisement_supported = is_extended_advertisement_supported; params->connected_ap_frequency = connected_ap_frequency; params->is_nfc_available = is_nfc_available; - params->operation_result_with_mediums = - std::move(operation_result_with_mediums); + params->operation_result_with_mediums = operation_result_with_mediums; return params; } @@ -1000,694 +113,4 @@ OperationResultCode AnalyticsRecorder::GetChannelIoErrorResultCodeFromMedium( } } -bool AnalyticsRecorder::CanRecordAnalyticsLocked( - absl::string_view method_name) { - VLOG(1) << "AnalyticsRecorder LogEvent " << method_name << " is calling."; - if (event_logger_ == nullptr) { - return false; - } - - if (session_was_logged_) { - VLOG(1) << "AnalyticsRecorder CanRecordAnalytics Unexpected call " - << method_name << " after session has already been logged."; - return false; - } - - return true; -} - -// TODO: b/391339677 - Investigate why we need to reset the resources. And -// verify in b/238375695 to see if we still meet the issue after removing the -// Reset function. -void AnalyticsRecorder::LogClientSessionLocked() { - ConnectionsLog connections_log; - connections_log.set_event_type(CLIENT_SESSION); - connections_log.set_allocated_client_session(client_session_.release()); - connections_log.set_version(kVersion); - - VLOG(1) << "AnalyticsRecorder LogClientSession connections_log=" - << connections_log.DebugString(); // NOLINT - - event_logger_->Log(connections_log); - client_session_ = nullptr; -} - -void AnalyticsRecorder::LogEvent(EventType event_type) { - ConnectionsLog connections_log; - connections_log.set_event_type(event_type); - connections_log.set_version(kVersion); - - VLOG(1) << "AnalyticsRecorder LogEvent connections_log=" - << connections_log.DebugString(); // NOLINT - - event_logger_->Log(connections_log); -} - -void AnalyticsRecorder::UpdateStrategySessionLocked( - connections::Strategy strategy, SessionRole role) { - // If we're not switching strategies, just update the current StrategySession - // with the new role. - if (strategy == current_strategy_ && current_strategy_session_ != nullptr) { - if (absl::c_linear_search(current_strategy_session_->role(), role)) { - // We've already acted as this role before, so make sure we've finished - // recording the previous round. - switch (role) { - case ADVERTISER: - FinishAdvertisingPhaseLocked(); - break; - case DISCOVERER: - FinishDiscoveryPhaseLocked(); - break; - default: - break; - } - } else { - current_strategy_session_->add_role(role); - } - } else { - // Otherwise, we're starting a new Strategy. - current_strategy_ = strategy; - FinishStrategySessionLocked(); - LogEvent(START_STRATEGY_SESSION); - current_strategy_session_ = - std::make_unique(); - started_strategy_session_time_ = SystemClock::ElapsedRealtime(); - current_strategy_session_->set_strategy( - StrategyToConnectionStrategy(strategy)); - current_strategy_session_->add_role(role); - } -} - -void AnalyticsRecorder::RecordAdvertisingPhaseDurationAndReasonLocked( - bool on_stop) const { - if (current_advertising_phase_ == nullptr) { - LOG(INFO) << "Unable to record advertising phase duration due to " - "null current_advertising_phase_"; - return; - } - if (!current_advertising_phase_->has_duration_millis()) { - current_advertising_phase_->set_duration_millis(absl::ToInt64Milliseconds( - SystemClock::ElapsedRealtime() - started_advertising_phase_time_)); - } - if (!current_advertising_phase_->has_stop_reason()) { - current_advertising_phase_->set_stop_reason( - on_stop ? StopAdvertisingReason::CLIENT_STOP_ADVERTISING - : StopAdvertisingReason::FINISH_SESSION_STOP_ADVERTISING); - } -} - -void AnalyticsRecorder::FinishAdvertisingPhaseLocked() { - if (current_advertising_phase_ != nullptr) { - for (const auto& item : incoming_connection_requests_) { - // ConnectionRequests still pending have been ignored by the local or - // remote (or both) endpoints. - const std::unique_ptr& - connection_request = item.second; - MarkConnectionRequestIgnoredLocked(connection_request.get()); - UpdateAdvertiserConnectionRequestLocked(connection_request.get()); - } - RecordAdvertisingPhaseDurationAndReasonLocked(/* on_stop= */ false); - if (current_strategy_session_ != nullptr) { - *current_strategy_session_->add_advertising_phase() = - *std::move(current_advertising_phase_); - } else { - LOG(INFO) << "Unable to record advertising phase due to null " - "current_strategy_session_"; - } - } - incoming_connection_requests_.clear(); -} - -void AnalyticsRecorder::RecordDiscoveryPhaseDurationAndReasonLocked( - bool on_stop) const { - if (current_discovery_phase_ == nullptr) { - LOG(INFO) << "Unable to record discovery phase duration due to " - "null current_discovery_phase_"; - return; - } - if (!current_discovery_phase_->has_duration_millis()) { - current_discovery_phase_->set_duration_millis(absl::ToInt64Milliseconds( - SystemClock::ElapsedRealtime() - started_discovery_phase_time_)); - } - // If the stop reason haven't been set yet, then set it. - if (!current_discovery_phase_->has_stop_reason()) { - current_discovery_phase_->set_stop_reason( - on_stop ? StopDiscoveringReason::CLIENT_STOP_DISCOVERING - : StopDiscoveringReason::FINISH_SESSION_STOP_DISCOVERING); - } -} - -void AnalyticsRecorder::FinishDiscoveryPhaseLocked() { - if (current_discovery_phase_ != nullptr) { - for (const auto& item : outgoing_connection_requests_) { - // ConnectionRequests still pending have been ignored by the local or - // remote (or both) endpoints. - const std::unique_ptr& - connection_request = item.second; - MarkConnectionRequestIgnoredLocked(connection_request.get()); - UpdateDiscovererConnectionRequestLocked(connection_request.get()); - } - RecordDiscoveryPhaseDurationAndReasonLocked(/* on_stop=*/false); - if (current_strategy_session_ != nullptr) { - *current_strategy_session_->add_discovery_phase() = - *std::move(current_discovery_phase_); - } else { - LOG(INFO) << "Unable to record discovery phase due to null " - "current_strategy_session_"; - } - } - outgoing_connection_requests_.clear(); -} - -bool AnalyticsRecorder::UpdateAdvertiserConnectionRequestLocked( - ConnectionsLog::ConnectionRequest* request) { - if (current_advertising_phase_ == nullptr) { - LOG(INFO) << "Unable to record advertiser connection request due to null " - "current_advertising_phase_"; - return false; - } - if (BothEndpointsRespondedLocked(request)) { - request->set_duration_millis( - absl::ToUnixMillis(SystemClock::ElapsedRealtime()) - - request->duration_millis()); - *current_advertising_phase_->add_received_connection_request() = *request; - return true; - } - return false; -} - -bool AnalyticsRecorder::UpdateDiscovererConnectionRequestLocked( - ConnectionsLog::ConnectionRequest* request) { - if (current_discovery_phase_ == nullptr) { - LOG(INFO) << "Unable to record discoverer connection request due " - "to null current_discovery_phase_."; - return false; - } - if (BothEndpointsRespondedLocked(request) || - request->local_response() == NOT_SENT) { - request->set_duration_millis( - absl::ToUnixMillis(SystemClock::ElapsedRealtime()) - - request->duration_millis()); - *current_discovery_phase_->add_sent_connection_request() = *request; - return true; - } - return false; -} - -bool AnalyticsRecorder::BothEndpointsRespondedLocked( - ConnectionsLog::ConnectionRequest* request) { - return request->has_local_response() && request->has_remote_response(); -} - -void AnalyticsRecorder::LocalEndpointRespondedLocked( - const std::string& remote_endpoint_id, ConnectionRequestResponse response) { - auto out = outgoing_connection_requests_.find(remote_endpoint_id); - if (out != outgoing_connection_requests_.end()) { - ConnectionsLog::ConnectionRequest* connection_request = out->second.get(); - connection_request->set_local_response(response); - if (UpdateDiscovererConnectionRequestLocked(connection_request)) { - outgoing_connection_requests_.erase(out); - } - } - auto in = incoming_connection_requests_.find(remote_endpoint_id); - if (in != incoming_connection_requests_.end()) { - ConnectionsLog::ConnectionRequest* connection_request = in->second.get(); - connection_request->set_local_response(response); - if (UpdateAdvertiserConnectionRequestLocked(connection_request)) { - incoming_connection_requests_.erase(in); - } - } -} - -void AnalyticsRecorder::RemoteEndpointRespondedLocked( - const std::string& remote_endpoint_id, ConnectionRequestResponse response) { - auto out = outgoing_connection_requests_.find(remote_endpoint_id); - if (out != outgoing_connection_requests_.end()) { - ConnectionsLog::ConnectionRequest* connection_request = out->second.get(); - connection_request->set_remote_response(response); - if (UpdateDiscovererConnectionRequestLocked(connection_request)) { - outgoing_connection_requests_.erase(out); - } - } - auto in = incoming_connection_requests_.find(remote_endpoint_id); - if (in != incoming_connection_requests_.end()) { - ConnectionsLog::ConnectionRequest* connection_request = in->second.get(); - connection_request->set_remote_response(response); - if (UpdateAdvertiserConnectionRequestLocked(connection_request)) { - incoming_connection_requests_.erase(in); - } - } -} - -void AnalyticsRecorder::MarkConnectionRequestIgnoredLocked( - ConnectionsLog::ConnectionRequest* request) { - if (!request->has_local_response()) { - request->set_local_response(IGNORED); - } - if (!request->has_remote_response()) { - request->set_remote_response(IGNORED); - } -} - -bool AnalyticsRecorder::ConnectionAttemptResultCodeExistedLocked( - Medium medium, ConnectionAttemptDirection direction, - const std::string& connection_token, ConnectionAttemptType type, - OperationResultCode operation_result_code) { - if (current_strategy_session_ == nullptr || - current_strategy_session_->connection_attempt_size() == 0) { - return false; - } - for (auto& connection_attempt : - current_strategy_session_->connection_attempt()) { - if (connection_attempt.medium() == medium && - connection_attempt.direction() == direction && - connection_attempt.connection_token() == connection_token && - connection_attempt.type() == type && - connection_attempt.operation_result().result_code() == - operation_result_code) { - return true; - } - } - - return false; -} - -// If bandwidth upgrade always failed on the same fromMedium, toMedium, result, -// stage and result code, we'll drop the duplicate logs for preventing the waste -// of log storage space -bool AnalyticsRecorder::EraseIfBandwidthUpgradeRecordExistedLocked( - const std::string& endpoint_id, BandwidthUpgradeResult result, - BandwidthUpgradeErrorStage error_stage, - OperationResultCode operation_result_code) { - if (current_strategy_session_ == nullptr) { - return false; - } - auto it = bandwidth_upgrade_attempts_.find(endpoint_id); - if (it != bandwidth_upgrade_attempts_.end()) { - ConnectionsLog::BandwidthUpgradeAttempt* attempt = it->second.get(); - for (auto& existing_attempt : - current_strategy_session_->upgrade_attempt()) { - if (attempt->from_medium() == existing_attempt.from_medium() && - attempt->to_medium() == existing_attempt.to_medium() && - result == existing_attempt.upgrade_result() && - error_stage == existing_attempt.error_stage() && - operation_result_code == - existing_attempt.operation_result().result_code()) { - bandwidth_upgrade_attempts_.erase(it); - return true; - } - } - } - return false; -} - -void AnalyticsRecorder::FinishUpgradeAttemptLocked( - const std::string& endpoint_id, BandwidthUpgradeResult result, - BandwidthUpgradeErrorStage error_stage, - OperationResultCode operation_result_code, bool erase_item) { - if (current_strategy_session_ == nullptr) { - LOG(INFO) << "Unable to record upgrade attempt due to null " - "current_strategy_session_"; - return; - } - // Add the BandwidthUpgradeAttempt in the current StrategySession. - auto it = bandwidth_upgrade_attempts_.find(endpoint_id); - if (it != bandwidth_upgrade_attempts_.end()) { - ConnectionsLog::BandwidthUpgradeAttempt* attempt = it->second.get(); - attempt->set_duration_millis( - absl::ToUnixMillis(SystemClock::ElapsedRealtime()) - - attempt->duration_millis()); - attempt->set_error_stage(error_stage); - attempt->set_upgrade_result(result); - - auto operation_result_proto = - std::make_unique(); - operation_result_proto->set_result_code(operation_result_code); - operation_result_proto->set_result_category( - ConvertToOperationResultCategory(operation_result_code)); - attempt->set_allocated_operation_result(operation_result_proto.release()); - *current_strategy_session_->add_upgrade_attempt() = *attempt; - if (erase_item) { - bandwidth_upgrade_attempts_.erase(it); - } - } -} - -void AnalyticsRecorder::FinishStrategySessionLocked() { - if (current_strategy_session_ != nullptr) { - FinishAdvertisingPhaseLocked(); - FinishDiscoveryPhaseLocked(); - - // Finish any unfinished LogicalConnections. - for (const auto& item : active_connections_) { - const std::unique_ptr& logical_connection = - item.second; - logical_connection->CloseAllPhysicalConnections(); - absl::c_copy( - logical_connection->GetEstablisedConnections(), - RepeatedFieldBackInserter( - current_strategy_session_->mutable_established_connection())); - } - active_connections_.clear(); - - // Finish any pending upgrade attempts. - for (const auto& item : bandwidth_upgrade_attempts_) { - FinishUpgradeAttemptLocked( - item.first, UNFINISHED_ERROR, UPGRADE_UNFINISHED, - OperationResultCode::DEVICE_STATE_ERROR_UNFINISHED_UPGRADE_ATTEMPTS, - /*erase_item=*/false); - } - bandwidth_upgrade_attempts_.clear(); - - // Add the StrategySession in ClientSession - if (current_strategy_session_ != nullptr) { - current_strategy_session_->set_duration_millis(absl::ToInt64Milliseconds( - SystemClock::ElapsedRealtime() - started_strategy_session_time_)); - *client_session_->add_strategy_session() = - *std::move(current_strategy_session_); - } - - current_strategy_session_ = nullptr; - current_strategy_ = connections::Strategy::kNone; - LogEvent(STOP_STRATEGY_SESSION); - } -} - -ConnectionsStrategy AnalyticsRecorder::StrategyToConnectionStrategy( - connections::Strategy strategy) { - if (strategy == connections::Strategy::kP2pCluster) { - return P2P_CLUSTER; - } - if (strategy == connections::Strategy::kP2pStar) { - return P2P_STAR; - } - if (strategy == connections::Strategy::kP2pPointToPoint) { - return P2P_POINT_TO_POINT; - } - return UNKNOWN_STRATEGY; -} - -PayloadType AnalyticsRecorder::PayloadTypeToProtoPayloadType( - connections::PayloadType type) { - switch (type) { - case connections::PayloadType::kBytes: - return BYTES; - case connections::PayloadType::kFile: - return FILE; - case connections::PayloadType::kStream: - return STREAM; - default: - return UNKNOWN_PAYLOAD_TYPE; - } -} - -void AnalyticsRecorder::PendingPayload::AddChunk( - std::int64_t chunk_size_bytes) { - num_bytes_transferred_ += chunk_size_bytes; - num_chunks_++; -} - -ConnectionsLog::Payload AnalyticsRecorder::PendingPayload::GetProtoPayload( - PayloadStatus status) { - ConnectionsLog::Payload payload; - payload.set_duration_millis( - absl::ToInt64Milliseconds(SystemClock::ElapsedRealtime() - start_time_)); - payload.set_type(type_); - payload.set_total_size_bytes(total_size_bytes_); - payload.set_num_bytes_transferred(num_bytes_transferred_); - payload.set_num_chunks(num_chunks_); - payload.set_status(status); - - auto operation_result_proto = - std::make_unique(); - operation_result_proto->set_result_code(operation_result_code_); - operation_result_proto->set_result_category( - ConvertToOperationResultCategory(operation_result_code_)); - payload.set_allocated_operation_result(operation_result_proto.release()); - - return payload; -} - -void AnalyticsRecorder::LogicalConnection::PhysicalConnectionEstablished( - Medium medium, const std::string& connection_token) { - if (current_medium_ != UNKNOWN_MEDIUM) { - LOG(WARNING) << "Unexpected call to PhysicalConnectionEstablished while " - "AnalyticsRecorder still has an active current medium."; - } - - auto established_connection = - std::make_unique(); - established_connection->set_medium(medium); - established_connection->set_duration_millis( - absl::ToUnixMillis(SystemClock::ElapsedRealtime())); - established_connection->set_connection_token(connection_token); - - auto operation_result_proto = - std::make_unique(); - operation_result_proto->set_result_code(OperationResultCode::DETAIL_SUCCESS); - operation_result_proto->set_result_category( - OperationResultCategory::CATEGORY_SUCCESS); - established_connection->set_allocated_operation_result( - operation_result_proto.release()); - physical_connections_.insert({medium, std::move(established_connection)}); - current_medium_ = medium; -} - -void AnalyticsRecorder::LogicalConnection::PhysicalConnectionClosed( - Medium medium, DisconnectionReason reason, SafeDisconnectionResult result) { - if (current_medium_ == UNKNOWN_MEDIUM) { - LOG(WARNING) << "Unexpected call to PhysicalConnectionClosed() for medium " - << Medium_Name(medium) - << " while AnalyticsRecorder has no active current medium"; - } else if (current_medium_ != medium) { - LOG(WARNING) << "Unexpected call to PhysicalConnectionClosed() for medium " - << Medium_Name(medium) - << "while AnalyticsRecorder has active medium " - << Medium_Name(current_medium_); - } - - auto it = physical_connections_.find(medium); - if (it == physical_connections_.end()) { - LOG(WARNING) - << "Unexpected call to physicalConnectionClosed() for medium " - << Medium_Name(medium) - << " with no corresponding EstablishedConnection that was previously" - " opened."; - return; - } - ConnectionsLog::EstablishedConnection* established_connection = - it->second.get(); - if (established_connection->has_disconnection_reason()) { - LOG(WARNING) << "Unexpected call to physicalConnectionClosed() for medium " - << Medium_Name(medium) - << " which already has disconnection reason " - << DisconnectionReason_Name( - established_connection->disconnection_reason()); - return; - } - FinishPhysicalConnection(established_connection, reason, result); - - if (medium == current_medium_) { - // If the EstablishedConnection we just closed was the one that we have - // marked as current, unset currentMedium. - current_medium_ = UNKNOWN_MEDIUM; - } -} - -void AnalyticsRecorder::LogicalConnection::CloseAllPhysicalConnections() { - for (const auto& physical_connection : physical_connections_) { - ConnectionsLog::EstablishedConnection* established_connection = - physical_connection.second.get(); - if (!established_connection->has_disconnection_reason()) { - FinishPhysicalConnection( - established_connection, UNFINISHED, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); - } - } - current_medium_ = UNKNOWN_MEDIUM; -} - -std::vector -AnalyticsRecorder::LogicalConnection::GetEstablisedConnections() { - std::vector established_connections; - if (current_medium_ != UNKNOWN_MEDIUM) { - LOG(WARNING) - << "AnalyticsRecorder expected no more active physical connections " - "before logging this endpoint connection."; - return established_connections; - } - std::transform(physical_connections_.begin(), physical_connections_.end(), - std::back_inserter(established_connections), - [](auto& kv) { return *kv.second; }); - physical_connections_.clear(); - - for (auto& established_connection : established_connections) { - if (absl::Milliseconds(established_connection.duration_millis()) >= - kConnectionTokenMaxLife) { - LOG(INFO) << "connection token exceed TTL, drop token."; - established_connection.set_connection_token(""); - } - } - - return established_connections; -} - -void AnalyticsRecorder::LogicalConnection::IncomingPayloadStarted( - std::int64_t payload_id, PayloadType type, std::int64_t total_size_bytes) { - incoming_payloads_.insert( - {payload_id, std::make_unique(type, total_size_bytes)}); -} - -void AnalyticsRecorder::LogicalConnection::ChunkReceived( - std::int64_t payload_id, std::int64_t size_bytes) { - auto it = incoming_payloads_.find(payload_id); - if (it == incoming_payloads_.end()) { - return; - } - PendingPayload* pending_payload = it->second.get(); - pending_payload->AddChunk(size_bytes); -} - -void AnalyticsRecorder::LogicalConnection::IncomingPayloadDone( - std::int64_t payload_id, PayloadStatus status, - OperationResultCode operation_result_code) { - if (current_medium_ == UNKNOWN_MEDIUM) { - LOG(WARNING) << "Unexpected call to incomingPayloadDone() while " - "AnalyticsRecorder has no active current medium."; - return; - } - auto it = physical_connections_.find(current_medium_); - if (it != physical_connections_.end()) { - const std::unique_ptr& - established_connection = it->second; - auto it = incoming_payloads_.find(payload_id); - if (it != incoming_payloads_.end()) { - it->second->SetOperationResultCode(operation_result_code); - *established_connection->add_received_payload() = - it->second->GetProtoPayload(status); - incoming_payloads_.erase(it); - } - } -} - -void AnalyticsRecorder::LogicalConnection::OutgoingPayloadStarted( - std::int64_t payload_id, PayloadType type, std::int64_t total_size_bytes) { - outgoing_payloads_.insert( - {payload_id, std::make_unique(type, total_size_bytes)}); -} - -void AnalyticsRecorder::LogicalConnection::ChunkSent(std::int64_t payload_id, - std::int64_t size_bytes) { - auto it = outgoing_payloads_.find(payload_id); - if (it == outgoing_payloads_.end()) { - return; - } - PendingPayload* payload = it->second.get(); - payload->AddChunk(size_bytes); -} - -void AnalyticsRecorder::LogicalConnection::OutgoingPayloadDone( - std::int64_t payload_id, PayloadStatus status, - OperationResultCode operation_result_code) { - if (current_medium_ == UNKNOWN_MEDIUM) { - LOG(WARNING) << "Unexpected call to outgoingPayloadDone() while " - "AnalyticsRecorder has no active current medium."; - return; - } - auto it = physical_connections_.find(current_medium_); - if (it != physical_connections_.end()) { - const std::unique_ptr& - established_connection = it->second; - auto it = outgoing_payloads_.find(payload_id); - if (it != outgoing_payloads_.end()) { - it->second->SetOperationResultCode(operation_result_code); - *established_connection->add_sent_payload() = - it->second->GetProtoPayload(status); - outgoing_payloads_.erase(it); - } - } -} - -void AnalyticsRecorder::LogicalConnection::FinishPhysicalConnection( - ConnectionsLog::EstablishedConnection* established_connection, - DisconnectionReason reason, SafeDisconnectionResult result) { - established_connection->set_disconnection_reason(reason); - established_connection->set_safe_disconnection_result(result); - established_connection->set_duration_millis( - absl::ToUnixMillis(SystemClock::ElapsedRealtime()) - - established_connection->duration_millis()); - - // Add any not-yet-finished payloads to this EstablishedConnection. - std::vector in_payloads = - ResolvePendingPayloads(incoming_payloads_, reason); - absl::c_move(in_payloads, - RepeatedFieldBackInserter( - established_connection->mutable_received_payload())); - std::vector out_payloads = - ResolvePendingPayloads(outgoing_payloads_, reason); - absl::c_move(out_payloads, - RepeatedFieldBackInserter( - established_connection->mutable_sent_payload())); -} - -std::vector -AnalyticsRecorder::LogicalConnection::ResolvePendingPayloads( - absl::btree_map>& - pending_payloads, - DisconnectionReason reason) { - std::vector completed_payloads; - absl::btree_map> - upgraded_payloads; - PayloadStatus status = - reason == UPGRADED ? MOVED_TO_NEW_MEDIUM : CONNECTION_CLOSED; - - OperationResultCode operation_result_code = - GetPendingPayloadResultCodeFromReason(reason); - for (const auto& item : pending_payloads) { - const std::unique_ptr& pending_payload = item.second; - pending_payload->SetOperationResultCode(operation_result_code); - ConnectionsLog::Payload proto_payload = - pending_payload->GetProtoPayload(status); - completed_payloads.push_back(proto_payload); - if (reason == UPGRADED) { - upgraded_payloads.insert( - {item.first, - std::make_unique(pending_payload->type(), - pending_payload->total_size_bytes(), - operation_result_code)}); - } - } - pending_payloads.clear(); - - if (reason == UPGRADED) { - // Re-populate the map with a new PendingPayload for each pending payload, - // since we expect them to be completed on the next EstablishedConnection. - pending_payloads = std::move(upgraded_payloads); - } - // Return the list of completed payloads to be added to the current - // EstablishedConnection. - return completed_payloads; -} - -OperationResultCode -AnalyticsRecorder::LogicalConnection::GetPendingPayloadResultCodeFromReason( - DisconnectionReason reason) { - switch (reason) { - case UPGRADED: - return OperationResultCode::MISCELLEANEOUS_MOVE_TO_NEW_MEDIUM; - case DisconnectionReason::LOCAL_DISCONNECTION: - return OperationResultCode::CLIENT_CANCELLATION_LOCAL_DISCONNECT; - case DisconnectionReason::REMOTE_DISCONNECTION: - return OperationResultCode::CLIENT_CANCELLATION_REMOTE_DISCONNECT; - default: - return OperationResultCode::NEARBY_GENERIC_CONNECTION_CLOSED; - } -} - -OperationResultCategory AnalyticsRecorder::GetOperationResultCategory( - location::nearby::proto::connections::OperationResultCode result_code) { - return ConvertToOperationResultCategory(result_code); -} - -} // namespace analytics -} // namespace nearby +} // namespace nearby::analytics diff --git a/connections/implementation/analytics/analytics_recorder.h b/connections/implementation/analytics/analytics_recorder.h index b24232b3..601d12bc 100644 --- a/connections/implementation/analytics/analytics_recorder.h +++ b/connections/implementation/analytics/analytics_recorder.h @@ -18,118 +18,98 @@ #include #include #include -#include #include -#include "absl/base/thread_annotations.h" -#include "absl/container/btree_map.h" -#include "absl/strings/string_view.h" #include "absl/time/time.h" #include "connections/implementation/analytics/advertising_metadata_params.h" #include "connections/implementation/analytics/connection_attempt_metadata_params.h" #include "connections/implementation/analytics/discovery_metadata_params.h" +#include "connections/implementation/analytics/operation_result_with_medium.h" #include "connections/payload_type.h" #include "connections/strategy.h" -#include "internal/analytics/event_logger.h" #include "internal/platform/error_code_params.h" -#include "internal/platform/implementation/system_clock.h" -#include "internal/platform/mutex.h" -#include "internal/proto/analytics/connections_log.pb.h" #include "proto/connections_enums.pb.h" -namespace nearby { -namespace analytics { +namespace nearby::analytics { + +enum class SafeDisconnectionResult { + kUnknown = 0, + kSafeDisconnection = 1, + kUnsafeDisconnection = 2, +}; class AnalyticsRecorder { public: - explicit AnalyticsRecorder(::nearby::analytics::EventLogger* event_logger); - virtual ~AnalyticsRecorder(); + AnalyticsRecorder() = default; + virtual ~AnalyticsRecorder() = default; // Advertising phase - void OnStartAdvertising( + virtual void OnStartAdvertising( connections::Strategy strategy, const std::vector& mediums, - AdvertisingMetadataParams* advertising_metadata_params) - ABSL_LOCKS_EXCLUDED(mutex_); - void OnStopAdvertising() ABSL_LOCKS_EXCLUDED(mutex_); + AdvertisingMetadataParams* advertising_metadata_params) = 0; + virtual void OnStopAdvertising() = 0; - // In case the client calls the {@link BasePcp#updateAdvertisingOptions()} - // multiple times, adds one index value to group the mediums results within - // the same UpdateAdvertisingOptions call, this API is to return the largest - // index value in current_advertising_phase. - int GetNextAdvertisingUpdateIndex() ABSL_LOCKS_EXCLUDED(mutex_); + virtual int GetNextAdvertisingUpdateIndex() = 0; // Connection listening - void OnStartedIncomingConnectionListening(connections::Strategy strategy) - ABSL_LOCKS_EXCLUDED(mutex_); - void OnStoppedIncomingConnectionListening() ABSL_LOCKS_EXCLUDED(mutex_); + virtual void OnStartedIncomingConnectionListening( + connections::Strategy strategy) = 0; + virtual void OnStoppedIncomingConnectionListening() = 0; // Discovery phase - void OnStartDiscovery( + virtual void OnStartDiscovery( connections::Strategy strategy, const std::vector& mediums, - DiscoveryMetadataParams* discovery_metadata_params) - ABSL_LOCKS_EXCLUDED(mutex_); - void OnStopDiscovery() ABSL_LOCKS_EXCLUDED(mutex_); + DiscoveryMetadataParams* discovery_metadata_params) = 0; + virtual void OnStopDiscovery() = 0; - // In case the client calls the {@link BasePcp#updateDiscoveryOptions()} - // multiple times, adds one index value to group the medium results within the - // same UpdateDiscoveryOptions call, this - // API is to return the latest index value in current_discovery_phase. - int GetNextDiscoveryUpdateIndex() ABSL_LOCKS_EXCLUDED(mutex_); - void OnEndpointFound(location::nearby::proto::connections::Medium medium) - ABSL_LOCKS_EXCLUDED(mutex_); + virtual int GetNextDiscoveryUpdateIndex() = 0; + virtual void OnEndpointFound( + location::nearby::proto::connections::Medium medium) = 0; // Connection request - void OnRequestConnection(const connections::Strategy& strategy, - const std::string& endpoint_id) - ABSL_LOCKS_EXCLUDED(mutex_); + virtual void OnRequestConnection(const connections::Strategy& strategy, + const std::string& endpoint_id) = 0; - void OnConnectionRequestReceived(const std::string& remote_endpoint_id) - ABSL_LOCKS_EXCLUDED(mutex_); - void OnConnectionRequestSent(const std::string& remote_endpoint_id) - ABSL_LOCKS_EXCLUDED(mutex_); - void OnRemoteEndpointAccepted(const std::string& remote_endpoint_id) - ABSL_LOCKS_EXCLUDED(mutex_); - void OnLocalEndpointAccepted(const std::string& remote_endpoint_id) - ABSL_LOCKS_EXCLUDED(mutex_); - void OnRemoteEndpointRejected(const std::string& remote_endpoint_id) - ABSL_LOCKS_EXCLUDED(mutex_); - void OnLocalEndpointRejected(const std::string& remote_endpoint_id) - ABSL_LOCKS_EXCLUDED(mutex_); + virtual void OnConnectionRequestReceived( + const std::string& remote_endpoint_id) = 0; + virtual void OnConnectionRequestSent( + const std::string& remote_endpoint_id) = 0; + virtual void OnRemoteEndpointAccepted( + const std::string& remote_endpoint_id) = 0; + virtual void OnLocalEndpointAccepted( + const std::string& remote_endpoint_id) = 0; + virtual void OnRemoteEndpointRejected( + const std::string& remote_endpoint_id) = 0; + virtual void OnLocalEndpointRejected( + const std::string& remote_endpoint_id) = 0; // Connection attempt - // Records an attempt with meta data at establishing an incoming physical - // connection. - void OnIncomingConnectionAttempt( + virtual void OnIncomingConnectionAttempt( location::nearby::proto::connections::ConnectionAttemptType type, location::nearby::proto::connections::Medium medium, location::nearby::proto::connections::ConnectionAttemptResult result, absl::Duration duration, const std::string& connection_token, - ConnectionAttemptMetadataParams* connection_attempt_metadata_params) - ABSL_LOCKS_EXCLUDED(mutex_); - // Records an attempt with meta data at establishing an outgoing physical - // connection. - void OnOutgoingConnectionAttempt( + ConnectionAttemptMetadataParams* connection_attempt_metadata_params) = 0; + virtual void OnOutgoingConnectionAttempt( const std::string& remote_endpoint_id, location::nearby::proto::connections::ConnectionAttemptType type, location::nearby::proto::connections::Medium medium, location::nearby::proto::connections::ConnectionAttemptResult result, absl::Duration duration, const std::string& connection_token, - ConnectionAttemptMetadataParams* connection_attempt_metadata_params) - ABSL_LOCKS_EXCLUDED(mutex_); + ConnectionAttemptMetadataParams* connection_attempt_metadata_params) = 0; + static std::unique_ptr BuildAdvertisingMetadataParams( bool is_extended_advertisement_supported = false, int connected_ap_frequency = 0, bool is_nfc_available = false, - const std::vector& + const std::vector& operation_result_with_mediums = {}); static std::unique_ptr BuildDiscoveryMetadataParams( bool is_extended_advertisement_supported = false, int connected_ap_frequency = 0, bool is_nfc_available = false, - const std::vector& + const std::vector& operation_result_with_mediums = {}); static std::unique_ptr @@ -147,363 +127,78 @@ class AnalyticsRecorder { GetChannelIoErrorResultCodeFromMedium( location::nearby::proto::connections::Medium medium); - // Connection establishedSafeDisconnectionResult - void OnConnectionEstablished( + // Connection established + virtual void OnConnectionEstablished( const std::string& endpoint_id, location::nearby::proto::connections::Medium medium, - const std::string& connection_token) ABSL_LOCKS_EXCLUDED(mutex_); - void OnConnectionClosed( + const std::string& connection_token) = 0; + virtual void OnConnectionClosed( const std::string& endpoint_id, location::nearby::proto::connections::Medium medium, location::nearby::proto::connections::DisconnectionReason reason, - location::nearby::analytics::proto::ConnectionsLog:: - EstablishedConnection::SafeDisconnectionResult result) - ABSL_LOCKS_EXCLUDED(mutex_); + SafeDisconnectionResult result) = 0; // Payload - void OnIncomingPayloadStarted(const std::string& endpoint_id, - std::int64_t payload_id, - connections::PayloadType type, - std::int64_t total_size_bytes) - ABSL_LOCKS_EXCLUDED(mutex_); - void OnPayloadChunkReceived(const std::string& endpoint_id, - std::int64_t payload_id, - std::int64_t chunk_size_bytes) - ABSL_LOCKS_EXCLUDED(mutex_); - void OnIncomingPayloadDone( + virtual void OnIncomingPayloadStarted(const std::string& endpoint_id, + std::int64_t payload_id, + connections::PayloadType type, + std::int64_t total_size_bytes) = 0; + virtual void OnPayloadChunkReceived(const std::string& endpoint_id, + std::int64_t payload_id, + std::int64_t chunk_size_bytes) = 0; + virtual void OnIncomingPayloadDone( const std::string& endpoint_id, std::int64_t payload_id, location::nearby::proto::connections::PayloadStatus status, location::nearby::proto::connections::OperationResultCode - operation_result_code) ABSL_LOCKS_EXCLUDED(mutex_); - void OnOutgoingPayloadStarted(const std::vector& endpoint_ids, - std::int64_t payload_id, - connections::PayloadType type, - std::int64_t total_size_bytes) - ABSL_LOCKS_EXCLUDED(mutex_); - void OnPayloadChunkSent(const std::string& endpoint_id, - std::int64_t payload_id, - std::int64_t chunk_size_bytes) - ABSL_LOCKS_EXCLUDED(mutex_); - void OnOutgoingPayloadDone( + operation_result_code) = 0; + virtual void OnOutgoingPayloadStarted( + const std::vector& endpoint_ids, std::int64_t payload_id, + connections::PayloadType type, std::int64_t total_size_bytes) = 0; + virtual void OnPayloadChunkSent(const std::string& endpoint_id, + std::int64_t payload_id, + std::int64_t chunk_size_bytes) = 0; + virtual void OnOutgoingPayloadDone( const std::string& endpoint_id, std::int64_t payload_id, location::nearby::proto::connections::PayloadStatus status, location::nearby::proto::connections::OperationResultCode - operation_result_code) ABSL_LOCKS_EXCLUDED(mutex_); + operation_result_code) = 0; // BandwidthUpgrade - void OnBandwidthUpgradeStarted( + virtual void OnBandwidthUpgradeStarted( const std::string& endpoint_id, location::nearby::proto::connections::Medium from_medium, location::nearby::proto::connections::Medium to_medium, location::nearby::proto::connections::ConnectionAttemptDirection direction, - const std::string& connection_token) ABSL_LOCKS_EXCLUDED(mutex_); - void UpdateBwUpgradeNetworkInfo(const std::string& endpoint_id, - int num_interfaces, - int num_ipv6_only_interfaces) - ABSL_LOCKS_EXCLUDED(mutex_); - void OnBandwidthUpgradeError( + const std::string& connection_token) = 0; + virtual void UpdateBwUpgradeNetworkInfo(const std::string& endpoint_id, + int num_interfaces, + int num_ipv6_only_interfaces) = 0; + virtual void OnBandwidthUpgradeError( const std::string& endpoint_id, location::nearby::proto::connections::BandwidthUpgradeResult result, location::nearby::proto::connections::BandwidthUpgradeErrorStage error_stage, location::nearby::proto::connections::OperationResultCode - operation_result_code) ABSL_LOCKS_EXCLUDED(mutex_); - void OnBandwidthUpgradeSuccess(const std::string& endpoint_id) - ABSL_LOCKS_EXCLUDED(mutex_); + operation_result_code) = 0; + virtual void OnBandwidthUpgradeSuccess(const std::string& endpoint_id) = 0; // Error Code - void OnErrorCode(const ErrorCodeParams& params); + virtual void OnErrorCode(const ErrorCodeParams& params) = 0; - // Log the start client session event with start client session logging - // resources setup (e.g. client_session_, started_client_session_time_) - void LogStartSession() ABSL_LOCKS_EXCLUDED(mutex_); + virtual void LogStartSession() = 0; + virtual void LogSession() = 0; - // Invokes event_logger_.Log() at the end of life of client. Log action is - // called in a separate thread to allow synchronous potentially lengthy - // execution. - void LogSession() ABSL_LOCKS_EXCLUDED(mutex_); + virtual bool IsSessionLogged() = 0; - bool IsSessionLogged(); - - location::nearby::proto::connections::OperationResultCategory + virtual location::nearby::proto::connections::OperationResultCategory GetOperationResultCategory( - location::nearby::proto::connections::OperationResultCode result_code); - - // Waits until all logs are sent to the backend. - // For testing only. - void Sync(); - - private: - // Tracks the chunks and duration of a Payload on a particular medium. - class PendingPayload { - public: - PendingPayload(location::nearby::proto::connections::PayloadType type, - std::int64_t total_size_bytes) - : PendingPayload(type, total_size_bytes, - location::nearby::proto::connections:: - OperationResultCode::DETAIL_UNKNOWN) {} - PendingPayload(location::nearby::proto::connections::PayloadType type, - std::int64_t total_size_bytes, - location::nearby::proto::connections::OperationResultCode - operation_result_code) - : start_time_(SystemClock::ElapsedRealtime()), - type_(type), - total_size_bytes_(total_size_bytes), - num_bytes_transferred_(0), - num_chunks_(0), - operation_result_code_(operation_result_code) {} - ~PendingPayload() = default; - - void AddChunk(std::int64_t chunk_size_bytes); - - location::nearby::analytics::proto::ConnectionsLog::Payload GetProtoPayload( - location::nearby::proto::connections::PayloadStatus status); - - location::nearby::proto::connections::PayloadType type() const { - return type_; - } - - std::int64_t total_size_bytes() const { return total_size_bytes_; } - - void SetOperationResultCode( - location::nearby::proto::connections::OperationResultCode - operation_result_code) { - operation_result_code_ = operation_result_code; - } - - private: - absl::Time start_time_; - location::nearby::proto::connections::PayloadType type_; - std::int64_t total_size_bytes_; - std::int64_t num_bytes_transferred_; - int num_chunks_; - location::nearby::proto::connections::OperationResultCode - operation_result_code_ = location::nearby::proto::connections:: - OperationResultCode::DETAIL_UNKNOWN; - }; - - class LogicalConnection { - public: - LogicalConnection( - location::nearby::proto::connections::Medium initial_medium, - const std::string& connection_token) { - PhysicalConnectionEstablished(initial_medium, connection_token); - } - LogicalConnection(const LogicalConnection&) = delete; - LogicalConnection(LogicalConnection&& other) - : current_medium_(std::move(other.current_medium_)), - physical_connections_(std::move(other.physical_connections_)), - incoming_payloads_(std::move(other.incoming_payloads_)), - outgoing_payloads_(std::move(other.outgoing_payloads_)) {} - LogicalConnection& operator=(const LogicalConnection&) = delete; - LogicalConnection&& operator=(LogicalConnection&&) = delete; - ~LogicalConnection() = default; - - void PhysicalConnectionEstablished( - location::nearby::proto::connections::Medium medium, - const std::string& connection_token); - void PhysicalConnectionClosed( - location::nearby::proto::connections::Medium medium, - location::nearby::proto::connections::DisconnectionReason reason, - location::nearby::analytics::proto::ConnectionsLog:: - EstablishedConnection::SafeDisconnectionResult result); - void CloseAllPhysicalConnections(); - - void IncomingPayloadStarted( - std::int64_t payload_id, - location::nearby::proto::connections::PayloadType type, - std::int64_t total_size_bytes); - void ChunkReceived(std::int64_t payload_id, std::int64_t size_bytes); - void IncomingPayloadDone( - std::int64_t payload_id, - location::nearby::proto::connections::PayloadStatus status, - location::nearby::proto::connections::OperationResultCode - operation_result_code); - void OutgoingPayloadStarted( - std::int64_t payload_id, - location::nearby::proto::connections::PayloadType type, - std::int64_t total_size_bytes); - void ChunkSent(std::int64_t payload_id, std::int64_t size_bytes); - void OutgoingPayloadDone( - std::int64_t payload_id, - location::nearby::proto::connections::PayloadStatus status, - location::nearby::proto::connections::OperationResultCode - operation_result_code); - - std::vector - GetEstablisedConnections(); - - private: - void FinishPhysicalConnection( - location::nearby::analytics::proto::ConnectionsLog:: - EstablishedConnection* established_connection, - location::nearby::proto::connections::DisconnectionReason reason, - location::nearby::analytics::proto::ConnectionsLog:: - EstablishedConnection::SafeDisconnectionResult result); - std::vector - ResolvePendingPayloads( - absl::btree_map>& - pending_payloads, - location::nearby::proto::connections::DisconnectionReason reason); - location::nearby::proto::connections::OperationResultCode - GetPendingPayloadResultCodeFromReason( - location::nearby::proto::connections::DisconnectionReason reason); - - location::nearby::proto::connections::Medium current_medium_ = - location::nearby::proto::connections::UNKNOWN_MEDIUM; - absl::btree_map> - physical_connections_; - absl::btree_map> - incoming_payloads_; - absl::btree_map> - outgoing_payloads_; - }; - - bool CanRecordAnalyticsLocked(absl::string_view method_name) - ABSL_SHARED_LOCKS_REQUIRED(mutex_); - - // Callbacks the ConnectionsLog proto byte array data to the EventLogger with - // ClientSession sub-proto. - void LogClientSessionLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - // Callbacks the ConnectionsLog proto byte array data to the EventLogger. - void LogEvent(location::nearby::proto::connections::EventType event_type); - - void UpdateStrategySessionLocked( - connections::Strategy strategy, - location::nearby::proto::connections::SessionRole role) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - void RecordAdvertisingPhaseDurationAndReasonLocked(bool on_stop) const - ABSL_SHARED_LOCKS_REQUIRED(mutex_); - void FinishAdvertisingPhaseLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - void RecordDiscoveryPhaseDurationAndReasonLocked(bool on_stop) const - ABSL_SHARED_LOCKS_REQUIRED(mutex_); - void FinishDiscoveryPhaseLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - bool UpdateAdvertiserConnectionRequestLocked( - location::nearby::analytics::proto::ConnectionsLog::ConnectionRequest* - request) ABSL_SHARED_LOCKS_REQUIRED(mutex_); - bool UpdateDiscovererConnectionRequestLocked( - location::nearby::analytics::proto::ConnectionsLog::ConnectionRequest* - request) ABSL_SHARED_LOCKS_REQUIRED(mutex_); - bool BothEndpointsRespondedLocked( - location::nearby::analytics::proto::ConnectionsLog::ConnectionRequest* - request) ABSL_SHARED_LOCKS_REQUIRED(mutex_); - void LocalEndpointRespondedLocked( - const std::string& remote_endpoint_id, - location::nearby::proto::connections::ConnectionRequestResponse response) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - void RemoteEndpointRespondedLocked( - const std::string& remote_endpoint_id, - location::nearby::proto::connections::ConnectionRequestResponse response) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - void MarkConnectionRequestIgnoredLocked( - location::nearby::analytics::proto::ConnectionsLog::ConnectionRequest* - request) ABSL_SHARED_LOCKS_REQUIRED(mutex_); - void OnIncomingConnectionAttemptLocked( - location::nearby::proto::connections::ConnectionAttemptType type, - location::nearby::proto::connections::Medium medium, - location::nearby::proto::connections::ConnectionAttemptResult result, - absl::Duration duration, const std::string& connection_token, - ConnectionAttemptMetadataParams* connection_attempt_metadata_params) - ABSL_SHARED_LOCKS_REQUIRED(mutex_); - void OnOutgoingConnectionAttemptLocked( - const std::string& remote_endpoint_id, - location::nearby::proto::connections::ConnectionAttemptType type, - location::nearby::proto::connections::Medium medium, - location::nearby::proto::connections::ConnectionAttemptResult result, - absl::Duration duration, const std::string& connection_token, - ConnectionAttemptMetadataParams* connection_attempt_metadata_params) - ABSL_SHARED_LOCKS_REQUIRED(mutex_); - bool ConnectionAttemptResultCodeExistedLocked( - location::nearby::proto::connections::Medium medium, - location::nearby::proto::connections::ConnectionAttemptDirection - direction, - const std::string& connection_token, - location::nearby::proto::connections::ConnectionAttemptType type, location::nearby::proto::connections::OperationResultCode - operation_result_code) ABSL_SHARED_LOCKS_REQUIRED(mutex_); - bool EraseIfBandwidthUpgradeRecordExistedLocked( - const std::string& endpoint_id, - location::nearby::proto::connections::BandwidthUpgradeResult result, - location::nearby::proto::connections::BandwidthUpgradeErrorStage - error_stage, - location::nearby::proto::connections::OperationResultCode - operation_result_code) ABSL_SHARED_LOCKS_REQUIRED(mutex_); - void FinishUpgradeAttemptLocked( - const std::string& endpoint_id, - location::nearby::proto::connections::BandwidthUpgradeResult result, - location::nearby::proto::connections::BandwidthUpgradeErrorStage - error_stage, - location::nearby::proto::connections::OperationResultCode - operation_result_code, - bool erase_item = true) ABSL_SHARED_LOCKS_REQUIRED(mutex_); - void FinishStrategySessionLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + result_code) = 0; - int GetLatestUpdateIndexLocked( - const std::vector& list) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - location::nearby::proto::connections::ConnectionsStrategy - StrategyToConnectionStrategy(connections::Strategy strategy); - location::nearby::proto::connections::PayloadType - PayloadTypeToProtoPayloadType(connections::PayloadType type); - - // Not owned by AnalyticsRecorder. Pointer must refer to a valid object - // that outlives the one constructed. - ::nearby::analytics::EventLogger* event_logger_; - - // Protects all sub-protos reading and writing in ConnectionLog. - Mutex mutex_; - - // ClientSession - std::unique_ptr< - location::nearby::analytics::proto::ConnectionsLog::ClientSession> - client_session_; - absl::Time started_client_session_time_; - bool session_was_logged_ ABSL_GUARDED_BY(mutex_) = false; - bool start_client_session_was_logged_ ABSL_GUARDED_BY(mutex_) = false; - - // Current StrategySession - connections::Strategy current_strategy_ ABSL_GUARDED_BY(mutex_) = - connections::Strategy::kNone; - std::unique_ptr< - location::nearby::analytics::proto::ConnectionsLog::StrategySession> - current_strategy_session_ ABSL_GUARDED_BY(mutex_); - absl::Time started_strategy_session_time_ ABSL_GUARDED_BY(mutex_); - - // Current AdvertisingPhase - std::unique_ptr< - location::nearby::analytics::proto::ConnectionsLog::AdvertisingPhase> - current_advertising_phase_; - absl::Time started_advertising_phase_time_ = absl::InfinitePast(); - - // Current DiscoveryPhase - std::unique_ptr< - location::nearby::analytics::proto::ConnectionsLog::DiscoveryPhase> - current_discovery_phase_; - absl::Time started_discovery_phase_time_ = absl::InfinitePast(); - - absl::btree_map> - incoming_connection_requests_ ABSL_GUARDED_BY(mutex_); - absl::btree_map> - outgoing_connection_requests_ ABSL_GUARDED_BY(mutex_); - absl::btree_map> - active_connections_ ABSL_GUARDED_BY(mutex_); - absl::btree_map> - bandwidth_upgrade_attempts_ ABSL_GUARDED_BY(mutex_); + virtual void Sync() = 0; }; -} // namespace analytics -} // namespace nearby +} // namespace nearby::analytics #endif // ANALYTICS_ANALYTICS_RECORDER_H_ diff --git a/connections/implementation/analytics/analytics_recorder_impl.cc b/connections/implementation/analytics/analytics_recorder_impl.cc new file mode 100644 index 00000000..75d155a9 --- /dev/null +++ b/connections/implementation/analytics/analytics_recorder_impl.cc @@ -0,0 +1,16 @@ +// Copyright 2026 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/analytics/analytics_recorder_impl.h" + diff --git a/connections/implementation/analytics/analytics_recorder_impl.h b/connections/implementation/analytics/analytics_recorder_impl.h new file mode 100644 index 00000000..b26ffb01 --- /dev/null +++ b/connections/implementation/analytics/analytics_recorder_impl.h @@ -0,0 +1,150 @@ +// Copyright 2026 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 CONNECTIONS_IMPLEMENTATION_ANALYTICS_ANALYTICS_RECORDER_IMPL_H_ +#define CONNECTIONS_IMPLEMENTATION_ANALYTICS_ANALYTICS_RECORDER_IMPL_H_ + +#include +#include +#include + +#include "absl/time/time.h" +#include "connections/implementation/analytics/analytics_recorder.h" +#include "location/nearby/analytics/cpp/logging/event_logger.h" +#include "proto/connections_enums.pb.h" + +namespace nearby::analytics { + +class AnalyticsRecorderImpl : public AnalyticsRecorder { + public: + explicit AnalyticsRecorderImpl(EventLogger* event_logger = nullptr) + : event_logger_(event_logger) {} + ~AnalyticsRecorderImpl() override = default; + + void OnStartAdvertising( + connections::Strategy strategy, + const std::vector& mediums, + AdvertisingMetadataParams* advertising_metadata_params) override {} + void OnStopAdvertising() override {} + int GetNextAdvertisingUpdateIndex() override { return 0; } + void OnStartedIncomingConnectionListening( + connections::Strategy strategy) override {} + void OnStoppedIncomingConnectionListening() override {} + void OnStartDiscovery( + connections::Strategy strategy, + const std::vector& mediums, + DiscoveryMetadataParams* discovery_metadata_params) override {} + void OnStopDiscovery() override {} + int GetNextDiscoveryUpdateIndex() override { return 0; } + void OnEndpointFound( + location::nearby::proto::connections::Medium medium) override {} + void OnRequestConnection(const connections::Strategy& strategy, + const std::string& endpoint_id) override {} + void OnConnectionRequestReceived( + const std::string& remote_endpoint_id) override {} + void OnConnectionRequestSent(const std::string& remote_endpoint_id) override {} + void OnRemoteEndpointAccepted( + const std::string& remote_endpoint_id) override {} + void OnLocalEndpointAccepted( + const std::string& remote_endpoint_id) override {} + void OnRemoteEndpointRejected( + const std::string& remote_endpoint_id) override {} + void OnLocalEndpointRejected( + const std::string& remote_endpoint_id) override {} + void OnIncomingConnectionAttempt( + location::nearby::proto::connections::ConnectionAttemptType type, + location::nearby::proto::connections::Medium medium, + location::nearby::proto::connections::ConnectionAttemptResult result, + absl::Duration duration, const std::string& connection_token, + ConnectionAttemptMetadataParams* connection_attempt_metadata_params) + override {} + void OnOutgoingConnectionAttempt( + const std::string& remote_endpoint_id, + location::nearby::proto::connections::ConnectionAttemptType type, + location::nearby::proto::connections::Medium medium, + location::nearby::proto::connections::ConnectionAttemptResult result, + absl::Duration duration, const std::string& connection_token, + ConnectionAttemptMetadataParams* connection_attempt_metadata_params) + override {} + void OnConnectionEstablished( + const std::string& endpoint_id, + location::nearby::proto::connections::Medium medium, + const std::string& connection_token) override {} + void OnConnectionClosed( + const std::string& endpoint_id, + location::nearby::proto::connections::Medium medium, + location::nearby::proto::connections::DisconnectionReason reason, + SafeDisconnectionResult result) override {} + void OnIncomingPayloadStarted(const std::string& endpoint_id, + std::int64_t payload_id, + connections::PayloadType type, + std::int64_t total_size_bytes) override {} + void OnPayloadChunkReceived(const std::string& endpoint_id, + std::int64_t payload_id, + std::int64_t chunk_size_bytes) override {} + void OnIncomingPayloadDone( + const std::string& endpoint_id, std::int64_t payload_id, + location::nearby::proto::connections::PayloadStatus status, + location::nearby::proto::connections::OperationResultCode + operation_result_code) override {} + void OnOutgoingPayloadStarted(const std::vector& endpoint_ids, + std::int64_t payload_id, + connections::PayloadType type, + std::int64_t total_size_bytes) override {} + void OnPayloadChunkSent(const std::string& endpoint_id, + std::int64_t payload_id, + std::int64_t chunk_size_bytes) override {} + void OnOutgoingPayloadDone( + const std::string& endpoint_id, std::int64_t payload_id, + location::nearby::proto::connections::PayloadStatus status, + location::nearby::proto::connections::OperationResultCode + operation_result_code) override {} + void OnBandwidthUpgradeStarted( + const std::string& endpoint_id, + location::nearby::proto::connections::Medium from_medium, + location::nearby::proto::connections::Medium to_medium, + location::nearby::proto::connections::ConnectionAttemptDirection + direction, + const std::string& connection_token) override {} + void UpdateBwUpgradeNetworkInfo(const std::string& endpoint_id, + int num_interfaces, + int num_ipv6_only_interfaces) override {} + void OnBandwidthUpgradeError( + const std::string& endpoint_id, + location::nearby::proto::connections::BandwidthUpgradeResult result, + location::nearby::proto::connections::BandwidthUpgradeErrorStage + error_stage, + location::nearby::proto::connections::OperationResultCode + operation_result_code) override {} + void OnBandwidthUpgradeSuccess(const std::string& endpoint_id) override {} + void OnErrorCode(const ErrorCodeParams& params) override {} + void LogStartSession() override {} + void LogSession() override {} + bool IsSessionLogged() override { return false; } + location::nearby::proto::connections::OperationResultCategory + GetOperationResultCategory( + location::nearby::proto::connections::OperationResultCode result_code) + override { + return location::nearby::proto::connections::CATEGORY_UNKNOWN; + } + void Sync() override {} + + private: + EventLogger* event_logger_; +}; + +} // namespace nearby::analytics + +#endif // CONNECTIONS_IMPLEMENTATION_ANALYTICS_ANALYTICS_RECORDER_IMPL_H_ + diff --git a/connections/implementation/analytics/analytics_recorder_test.cc b/connections/implementation/analytics/analytics_recorder_test.cc deleted file mode 100644 index c431e862..00000000 --- a/connections/implementation/analytics/analytics_recorder_test.cc +++ /dev/null @@ -1,2593 +0,0 @@ -// Copyright 2022-2023 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/analytics/analytics_recorder.h" - -#include - -#include -#include -#include -#include - -#include "net/proto2/contrib/parse_proto/parse_text_proto.h" -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/time/time.h" -#include "connections/implementation/analytics/connection_attempt_metadata_params.h" -#include "connections/payload_type.h" -#include "connections/strategy.h" -#include "internal/analytics/mock_event_logger.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/error_code_params.h" -#include "internal/platform/error_code_recorder.h" -#include "internal/platform/exception.h" -#include "internal/platform/medium_environment.h" -#include "internal/proto/analytics/connections_log.proto.h" -#include "internal/test/fake_clock.h" -#include "proto/connections_enums.proto.h" - -namespace nearby { -namespace analytics { -namespace { - -using ::location::nearby::analytics::proto::ConnectionsLog; -using ::location::nearby::errorcode::proto::DISCONNECT; -using ::location::nearby::errorcode::proto::DISCONNECT_NETWORK_FAILED; -using ::location::nearby::errorcode::proto::INVALID_PARAMETER; -using ::location::nearby::errorcode::proto::NULL_BLUETOOTH_DEVICE_NAME; -using ::location::nearby::errorcode::proto::START_DISCOVERING; -using ::location::nearby::errorcode::proto::START_EXTENDED_DISCOVERING_FAILED; -using ::location::nearby::errorcode::proto:: - TACHYON_SEND_MESSAGE_STATUS_EXCEPTION; -using ::location::nearby::proto::connections::BLE; -using ::location::nearby::proto::connections::BLUETOOTH; -using ::location::nearby::proto::connections::CLIENT_SESSION; -using ::location::nearby::proto::connections::ERROR_CODE; -using ::location::nearby::proto::connections::EventType; -using ::location::nearby::proto::connections::INCOMING; -using ::location::nearby::proto::connections::INITIAL; -using ::location::nearby::proto::connections::LOCAL_DISCONNECTION; -using ::location::nearby::proto::connections::Medium; -using ::location::nearby::proto::connections::OperationResultCategory; -using ::location::nearby::proto::connections::OperationResultCode; -using ::location::nearby::proto::connections::RESULT_ERROR; -using ::location::nearby::proto::connections::RESULT_SUCCESS; -using ::location::nearby::proto::connections::START_CLIENT_SESSION; -using ::location::nearby::proto::connections::START_STRATEGY_SESSION; -using ::location::nearby::proto::connections::STOP_CLIENT_SESSION; -using ::location::nearby::proto::connections::STOP_STRATEGY_SESSION; -using ::location::nearby::proto::connections::SUCCESS; -using ::location::nearby::proto::connections::UPGRADED; -using ::location::nearby::proto::connections::WEB_RTC; -using ::location::nearby::proto::connections::WIFI_LAN; -using ::location::nearby::proto::connections::WIFI_LAN_MEDIUM_ERROR; -using ::location::nearby::proto::connections::WIFI_LAN_SOCKET_CREATION; -using ::nearby::analytics::MockEventLogger; -using ::proto2::contrib::parse_proto::ParseTextProtoOrDie; -using ::testing::Contains; -using ::protobuf_matchers::EqualsProto; -using ::testing::Not; - -constexpr absl::Duration kDefaultTimeout = absl::Milliseconds(1000); - -class FakeEventLogger : public MockEventLogger { - public: - explicit FakeEventLogger(CountDownLatch& client_session_done_latch) - : client_session_done_latch_(client_session_done_latch) {} - - FakeEventLogger(CountDownLatch& client_session_done_latch, - CountDownLatch* start_client_session_done_latch_ptr) - : client_session_done_latch_(client_session_done_latch), - start_client_session_done_latch_ptr_( - start_client_session_done_latch_ptr) {} - - void Log(const ConnectionsLog& message) override { - EventType event_type = message.event_type(); - logged_event_types_.push_back(event_type); - if (event_type == CLIENT_SESSION) { - logged_client_session_count_++; - logged_client_session_ = message.client_session(); - } - if (event_type == ERROR_CODE) { - error_code_ = message.error_code(); - } - if (event_type == STOP_CLIENT_SESSION) { - client_session_done_latch_.CountDown(); - } - if (start_client_session_done_latch_ptr_ != nullptr && - event_type == START_CLIENT_SESSION) { - start_client_session_done_latch_ptr_->CountDown(); - } - } - - int GetLoggedClientSessionCount() const { - return logged_client_session_count_; - } - - const ConnectionsLog::ClientSession& GetLoggedClientSession() { - return logged_client_session_; - } - - const ConnectionsLog::ErrorCode& GetErrorCode() { return error_code_; } - - std::vector GetLoggedEventTypes() { return logged_event_types_; } - - void SetClientSessionDoneLatch( - const CountDownLatch& client_session_done_latch) { - client_session_done_latch_ = client_session_done_latch; - } - - void SetStartClientSessionDoneLatchPtr( - CountDownLatch* start_client_session_done_latch_ptr) { - start_client_session_done_latch_ptr_ = start_client_session_done_latch_ptr; - } - - private: - int logged_client_session_count_ = 0; - CountDownLatch& client_session_done_latch_; - CountDownLatch* start_client_session_done_latch_ptr_ = nullptr; - ConnectionsLog::ClientSession logged_client_session_; - ConnectionsLog::ErrorCode error_code_; - std::vector logged_event_types_; -}; - -class AnalyticsRecorderTest : public ::testing::Test { - protected: - void SetUp() override { - MediumEnvironment::Instance().Start({.use_simulated_clock = true}); - } - - void TearDown() override { MediumEnvironment::Instance().Stop(); } - - FakeClock& GetFakeClock() const { - return *MediumEnvironment::Instance().GetSimulatedClock().value(); - } -}; - -// Test if session_was_logged_ is reset by checking if LogSession can take -// effect again or not. -TEST_F(AnalyticsRecorderTest, SessionOnlyLoggedOnceWorks) { - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - analytics_recorder.LogSession(); - analytics_recorder.LogSession(); - analytics_recorder.LogSession(); - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - // Only called once. - EXPECT_EQ(event_logger.GetLoggedClientSessionCount(), 1); -} - -TEST_F(AnalyticsRecorderTest, SetFieldsCorrectlyForNestedAdvertisingCalls) { - connections::Strategy strategy = connections::Strategy::kP2pStar; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - ConnectionsLog::OperationResultWithMedium operation_result; - operation_result.set_medium(BLUETOOTH); - operation_result.set_result_code(OperationResultCode::DETAIL_SUCCESS); - operation_result.set_result_category( - OperationResultCategory::CATEGORY_SUCCESS); - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - advertising_metadata_params->operation_result_with_mediums = { - operation_result}; - GetFakeClock().FastForward(absl::Milliseconds(50)); - analytics_recorder.OnStartAdvertising(strategy, /*mediums=*/{BLE, BLUETOOTH}, - advertising_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStopAdvertising(); - operation_result.set_medium(BLE); - advertising_metadata_params->operation_result_with_mediums = { - operation_result}; - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnStartAdvertising(strategy, /*mediums=*/{BLUETOOTH}, - advertising_metadata_params.get()); - - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 650 - strategy_session { - duration_millis: 600 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 100 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - adv_dis_result { - medium: BLUETOOTH - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - stop_reason: CLIENT_STOP_ADVERTISING - } - advertising_phase { - duration_millis: 300 - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - adv_dis_result { - medium: BLE - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - stop_reason: FINISH_SESSION_STOP_ADVERTISING - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - (EqualsProto(strategy_session_proto))); -} - -TEST_F(AnalyticsRecorderTest, SetFieldsCorrectlyForNestedDiscoveryCalls) { - connections::Strategy strategy = connections::Strategy::kP2pStar; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - ConnectionsLog::OperationResultWithMedium operation_result; - operation_result.set_medium(BLUETOOTH); - operation_result.set_result_code(OperationResultCode::DETAIL_SUCCESS); - operation_result.set_result_category( - OperationResultCategory::CATEGORY_SUCCESS); - ConnectionsLog::OperationResultWithMedium operation_result2; - operation_result2.set_medium(BLE); - operation_result2.set_result_code(OperationResultCode::DETAIL_SUCCESS); - operation_result2.set_result_category( - OperationResultCategory::CATEGORY_SUCCESS); - - auto discovery_metadata_params = - analytics_recorder.BuildDiscoveryMetadataParams( - /*is_extended_advertisement_supported*/ true, - /*connected_ap_frequency*/ 1, /*is_nfc_available=*/false, - {operation_result, operation_result2}); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartDiscovery(strategy, /*mediums=*/{BLE, BLUETOOTH}, - discovery_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnStopDiscovery(); - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnEndpointFound(BLUETOOTH); - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.OnEndpointFound(BLE); - GetFakeClock().FastForward(absl::Milliseconds(500)); - - auto discovery_metadata_params2 = - analytics_recorder.BuildDiscoveryMetadataParams( - /*is_extended_advertisement_supported*/ true, - /*connected_ap_frequency*/ 1, /*is_nfc_available=*/false, - {operation_result}); - analytics_recorder.OnStartDiscovery(strategy, /*mediums=*/{BLUETOOTH}, - discovery_metadata_params2.get()); - GetFakeClock().FastForward(absl::Milliseconds(600)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 2100 - strategy_session { - duration_millis: 2000 - strategy: P2P_STAR - role: DISCOVERER - discovery_phase { - duration_millis: 200 - medium: BLE - medium: BLUETOOTH - discovered_endpoint { - medium: BLUETOOTH - latency_millis: 500 - } - discovered_endpoint { - medium: BLE - latency_millis: 900 - } - discovery_metadata { - supports_extended_ble_advertisements: true - connected_ap_frequency: 1 - supports_nfc_technology: false - } - adv_dis_result { - medium: BLUETOOTH - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - adv_dis_result { - medium: BLE - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - stop_reason: CLIENT_STOP_DISCOVERING - } - discovery_phase { - duration_millis: 600 - medium: BLUETOOTH - discovery_metadata { - supports_extended_ble_advertisements: true - connected_ap_frequency: 1 - supports_nfc_technology: false - } - adv_dis_result { - medium: BLUETOOTH - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - stop_reason: FINISH_SESSION_STOP_DISCOVERING - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); -} - -TEST_F(AnalyticsRecorderTest, - OneStrategySessionForMultipleRoundsOfDiscoveryAdvertising) { - connections::Strategy strategy = connections::Strategy::kP2pStar; - std::vector mediums = {BLE, BLUETOOTH}; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising(strategy, mediums, - advertising_metadata_params.get()); - auto discovery_metadata_params = - analytics_recorder.BuildDiscoveryMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnStartDiscovery(strategy, mediums, - discovery_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnStopAdvertising(); - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.OnStopDiscovery(); - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.OnStartAdvertising(strategy, mediums, - advertising_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(600)); - analytics_recorder.OnStopAdvertising(); - GetFakeClock().FastForward(absl::Milliseconds(700)); - analytics_recorder.OnStartDiscovery(strategy, mediums, - discovery_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(800)); - analytics_recorder.OnStopDiscovery(); - GetFakeClock().FastForward(absl::Milliseconds(900)); - analytics_recorder.OnStartDiscovery(strategy, mediums, {}); - GetFakeClock().FastForward(absl::Milliseconds(1000)); - analytics_recorder.OnStartAdvertising(strategy, mediums, - advertising_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(1100)); - analytics_recorder.OnStopDiscovery(); - GetFakeClock().FastForward(absl::Milliseconds(1200)); - analytics_recorder.OnStopAdvertising(); - GetFakeClock().FastForward(absl::Milliseconds(1300)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - std::vector event_types = event_logger.GetLoggedEventTypes(); - EXPECT_THAT(event_types, Contains(START_STRATEGY_SESSION).Times(1)); - EXPECT_THAT(event_types, Contains(STOP_STRATEGY_SESSION).Times(1)); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 9100 - strategy_session { - duration_millis: 9000 - strategy: P2P_STAR - role: ADVERTISER - role: DISCOVERER - discovery_phase { - duration_millis: 700 - medium: BLE - medium: BLUETOOTH - discovery_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: CLIENT_STOP_DISCOVERING - } - discovery_phase { - duration_millis: 800 - medium: BLE - medium: BLUETOOTH - discovery_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: CLIENT_STOP_DISCOVERING - } - discovery_phase { - duration_millis: 2100 - medium: BLE - medium: BLUETOOTH - discovery_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: CLIENT_STOP_DISCOVERING - } - advertising_phase { - duration_millis: 500 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: CLIENT_STOP_ADVERTISING - } - advertising_phase { - duration_millis: 600 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: CLIENT_STOP_ADVERTISING - } - advertising_phase { - duration_millis: 2300 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: CLIENT_STOP_ADVERTISING - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); -} - -TEST_F(AnalyticsRecorderTest, AdvertiserConnectionRequestsWorks) { - std::string endpoint_id_0 = "endpoint_id_0"; - std::string endpoint_id_1 = "endpoint_id_1"; - std::string endpoint_id_2 = "endpoint_id_2"; - std::string endpoint_id_3 = "endpoint_id_3"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - ConnectionsLog::OperationResultWithMedium operation_result; - operation_result.set_medium(BLE); - operation_result.set_result_code(OperationResultCode::DETAIL_SUCCESS); - operation_result.set_result_category( - OperationResultCategory::CATEGORY_SUCCESS); - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - advertising_metadata_params->operation_result_with_mediums = { - operation_result}; - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - advertising_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnConnectionRequestReceived(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnLocalEndpointAccepted(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.OnConnectionRequestReceived(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(600)); - analytics_recorder.OnLocalEndpointAccepted(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(700)); - analytics_recorder.OnRemoteEndpointRejected(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(800)); - analytics_recorder.OnConnectionRequestReceived(endpoint_id_2); - GetFakeClock().FastForward(absl::Milliseconds(900)); - analytics_recorder.OnLocalEndpointRejected(endpoint_id_2); - GetFakeClock().FastForward(absl::Milliseconds(1000)); - analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_2); - GetFakeClock().FastForward(absl::Milliseconds(1100)); - analytics_recorder.OnConnectionRequestReceived(endpoint_id_3); - GetFakeClock().FastForward(absl::Milliseconds(1200)); - analytics_recorder.OnLocalEndpointRejected(endpoint_id_3); - GetFakeClock().FastForward(absl::Milliseconds(1300)); - analytics_recorder.OnRemoteEndpointRejected(endpoint_id_3); - GetFakeClock().FastForward(absl::Milliseconds(1400)); - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 10500 - strategy_session { - duration_millis: 10400 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 10400 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - adv_dis_result { - medium: BLE - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - stop_reason: FINISH_SESSION_STOP_ADVERTISING - received_connection_request { - duration_millis: 700 - request_delay_millis: 200 - local_response: ACCEPTED - remote_response: ACCEPTED - } - received_connection_request { - duration_millis: 1300 - request_delay_millis: 1400 - local_response: ACCEPTED - remote_response: REJECTED - } - received_connection_request { - duration_millis: 1900 - request_delay_millis: 3500 - local_response: REJECTED - remote_response: ACCEPTED - } - received_connection_request { - duration_millis: 2500 - request_delay_millis: 6500 - local_response: REJECTED - remote_response: REJECTED - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); -} - -TEST_F(AnalyticsRecorderTest, DiscoveryConnectionRequestsWorks) { - std::string endpoint_id_0 = "endpoint_id_0"; - std::string endpoint_id_1 = "endpoint_id_1"; - std::string endpoint_id_2 = "endpoint_id_2"; - std::string endpoint_id_3 = "endpoint_id_3"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - ConnectionsLog::OperationResultWithMedium operation_result; - operation_result.set_medium(BLUETOOTH); - operation_result.set_result_code(OperationResultCode::DETAIL_SUCCESS); - operation_result.set_result_category( - OperationResultCategory::CATEGORY_SUCCESS); - auto discovery_metadata_params = - analytics_recorder.BuildDiscoveryMetadataParams(); - discovery_metadata_params->operation_result_with_mediums = {operation_result}; - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartDiscovery(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - discovery_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnConnectionRequestSent(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnLocalEndpointAccepted(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.OnConnectionRequestSent(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(600)); - analytics_recorder.OnLocalEndpointAccepted(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(700)); - analytics_recorder.OnRemoteEndpointRejected(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(800)); - analytics_recorder.OnConnectionRequestSent(endpoint_id_2); - GetFakeClock().FastForward(absl::Milliseconds(900)); - analytics_recorder.OnLocalEndpointRejected(endpoint_id_2); - GetFakeClock().FastForward(absl::Milliseconds(1000)); - analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_2); - GetFakeClock().FastForward(absl::Milliseconds(1100)); - - analytics_recorder.OnConnectionRequestSent(endpoint_id_3); - GetFakeClock().FastForward(absl::Milliseconds(1200)); - analytics_recorder.OnLocalEndpointRejected(endpoint_id_3); - GetFakeClock().FastForward(absl::Milliseconds(1300)); - analytics_recorder.OnRemoteEndpointRejected(endpoint_id_3); - GetFakeClock().FastForward(absl::Milliseconds(1400)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 10500 - strategy_session { - duration_millis: 10400 - strategy: P2P_STAR - role: DISCOVERER - discovery_phase { - duration_millis: 10400 - medium: BLE - medium: BLUETOOTH - discovery_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - adv_dis_result { - medium: BLUETOOTH - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - stop_reason: FINISH_SESSION_STOP_DISCOVERING - sent_connection_request { - duration_millis: 700 - request_delay_millis: 200 - local_response: ACCEPTED - remote_response: ACCEPTED - } - sent_connection_request { - duration_millis: 1300 - request_delay_millis: 1400 - local_response: ACCEPTED - remote_response: REJECTED - } - sent_connection_request { - duration_millis: 1900 - request_delay_millis: 3500 - local_response: REJECTED - remote_response: ACCEPTED - } - sent_connection_request { - duration_millis: 2500 - request_delay_millis: 6500 - local_response: REJECTED - remote_response: REJECTED - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); -} - -TEST_F(AnalyticsRecorderTest, - AdvertiserUnfinishedConnectionRequestsIncludedAsIgnored) { - std::string endpoint_id_0 = "endpoint_id_0"; - std::string endpoint_id_1 = "endpoint_id_1"; - std::string endpoint_id_2 = "endpoint_id_2"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - ConnectionsLog::OperationResultWithMedium operation_result; - operation_result.set_medium(BLUETOOTH); - operation_result.set_result_code(OperationResultCode::DETAIL_SUCCESS); - operation_result.set_result_category( - OperationResultCategory::CATEGORY_SUCCESS); - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - advertising_metadata_params->operation_result_with_mediums = { - operation_result}; - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - advertising_metadata_params.get()); - // Ignored by local. - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnConnectionRequestReceived(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(400)); - - // Ignored by remote. - analytics_recorder.OnConnectionRequestReceived(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.OnLocalEndpointAccepted(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(600)); - - // Ignored by both. - analytics_recorder.OnConnectionRequestReceived(endpoint_id_2); - GetFakeClock().FastForward(absl::Milliseconds(700)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 2800 - strategy_session { - duration_millis: 2700 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 2700 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - adv_dis_result { - medium: BLUETOOTH - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - stop_reason: FINISH_SESSION_STOP_ADVERTISING - received_connection_request { - duration_millis: 2500 - request_delay_millis: 200 - local_response: IGNORED - remote_response: ACCEPTED - } - received_connection_request { - duration_millis: 1800 - request_delay_millis: 900 - local_response: ACCEPTED - remote_response: IGNORED - } - received_connection_request { - duration_millis: 700 - request_delay_millis: 2000 - local_response: IGNORED - remote_response: IGNORED - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); -} - -TEST_F(AnalyticsRecorderTest, - DiscovererUnfinishedConnectionRequestsIncludedAsIgnored) { - std::string endpoint_id_0 = "endpoint_id_0"; - std::string endpoint_id_1 = "endpoint_id_1"; - std::string endpoint_id_2 = "endpoint_id_2"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - ConnectionsLog::OperationResultWithMedium operation_result; - operation_result.set_medium(BLUETOOTH); - operation_result.set_result_code(OperationResultCode::DETAIL_SUCCESS); - operation_result.set_result_category( - OperationResultCategory::CATEGORY_SUCCESS); - auto discovery_metadata_params = - analytics_recorder.BuildDiscoveryMetadataParams(); - discovery_metadata_params->operation_result_with_mediums = {operation_result}; - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartDiscovery(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - discovery_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(200)); - - // Ignored by local. - analytics_recorder.OnConnectionRequestSent(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(400)); - - // Ignored by remote. - analytics_recorder.OnConnectionRequestSent(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.OnLocalEndpointAccepted(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(600)); - - // Ignored by both. - analytics_recorder.OnConnectionRequestSent(endpoint_id_2); - GetFakeClock().FastForward(absl::Milliseconds(700)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 2800 - strategy_session { - duration_millis: 2700 - strategy: P2P_STAR - role: DISCOVERER - discovery_phase { - duration_millis: 2700 - medium: BLE - medium: BLUETOOTH - discovery_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - adv_dis_result { - medium: BLUETOOTH - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - stop_reason: FINISH_SESSION_STOP_DISCOVERING - sent_connection_request { - duration_millis: 2500 - request_delay_millis: 200 - local_response: IGNORED - remote_response: ACCEPTED - } - sent_connection_request { - duration_millis: 1800 - request_delay_millis: 900 - local_response: ACCEPTED - remote_response: IGNORED - } - sent_connection_request { - duration_millis: 700 - request_delay_millis: 2000 - local_response: IGNORED - remote_response: IGNORED - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); -} - -TEST_F(AnalyticsRecorderTest, SuccessfulIncomingConnectionAttempt) { - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - ConnectionsLog::OperationResultWithMedium operation_result; - operation_result.set_medium(BLUETOOTH); - operation_result.set_result_code(OperationResultCode::DETAIL_SUCCESS); - operation_result.set_result_category( - OperationResultCategory::CATEGORY_SUCCESS); - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - advertising_metadata_params->operation_result_with_mediums = { - operation_result}; - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - advertising_metadata_params.get()); - - auto connections_attempt_metadata_params = - std::make_unique(); - connections_attempt_metadata_params->operation_result_code = - OperationResultCode::DETAIL_SUCCESS; - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnIncomingConnectionAttempt( - INITIAL, BLUETOOTH, RESULT_SUCCESS, absl::Duration{}, - /*connection_token=*/"", connections_attempt_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnStopAdvertising(); - GetFakeClock().FastForward(absl::Milliseconds(400)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 1000 - strategy_session { - duration_millis: 900 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 500 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: CLIENT_STOP_ADVERTISING - adv_dis_result { - medium: BLUETOOTH - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - } - connection_attempt { - duration_millis: 0 - type: INITIAL - direction: INCOMING - medium: BLUETOOTH - attempt_result: RESULT_SUCCESS - connection_token: "" - connection_attempt_metadata { - technology: CONNECTION_TECHNOLOGY_UNKNOWN_TECHNOLOGY - band: CONNECTION_BAND_UNKNOWN_BAND - frequency: -1 - network_operator: "" - country_code: "" - is_tdls_used: false - try_counts: 0 - wifi_hotspot_status: false - max_tx_speed: 0 - max_rx_speed: 0 - wifi_channel_width: -1 - } - operation_result { - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); -} - -TEST_F(AnalyticsRecorderTest, - FailedConnectionAttemptUpdatesConnectionRequestNotSent) { - std::string endpoint_id = "endpoint_id"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto connections_attempt_metadata_params = - analytics_recorder.BuildConnectionAttemptMetadataParams( - ::location::nearby::proto::connections:: - CONNECTION_TECHNOLOGY_HOTSPOT_LOCALONLY, - ::location::nearby::proto::connections:: - CONNECTION_BAND_WIFI_BAND_6GHZ, - /*frequency*/ 2400, /*try_count*/ 0, /*network_operator*/ {}, - /*country_code*/ {}, /*is_tdls_used*/ false, - /*wifi_hotspot_enabled*/ false, /*max_wifi_tx_speed*/ 0, - /*max_wifi_rx_speed*/ 0, /*channel_width*/ 0, - OperationResultCode::CONNECTIVITY_BT_CLIENT_SOCKET_CREATION_FAILURE); - auto discovery_metadata_params = - analytics_recorder.BuildDiscoveryMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartDiscovery(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - discovery_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnConnectionRequestSent(endpoint_id); - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnOutgoingConnectionAttempt( - endpoint_id, INITIAL, BLUETOOTH, RESULT_ERROR, absl::Duration{}, - /*connection_token=*/"", connections_attempt_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(400)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 1000 - strategy_session { - duration_millis: 900 - strategy: P2P_STAR - role: DISCOVERER - discovery_phase { - duration_millis: 900 - medium: BLE - medium: BLUETOOTH - discovery_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: FINISH_SESSION_STOP_DISCOVERING - sent_connection_request { - duration_millis: 300 - request_delay_millis: 200 - local_response: NOT_SENT - remote_response: NOT_SENT - } - } - connection_attempt { - duration_millis: 0 - type: INITIAL - direction: OUTGOING - medium: BLUETOOTH - attempt_result: RESULT_ERROR - connection_token: "" - connection_attempt_metadata { - technology: CONNECTION_TECHNOLOGY_HOTSPOT_LOCALONLY - band: CONNECTION_BAND_WIFI_BAND_6GHZ - frequency: 2400 - network_operator: "" - country_code: "" - is_tdls_used: false - try_counts: 0 - wifi_hotspot_status: false - max_tx_speed: 0 - max_rx_speed: 0 - wifi_channel_width: 0 - } - operation_result { - result_category: CATEGORY_CONNECTIVITY_ERROR - result_code: CONNECTIVITY_BT_CLIENT_SOCKET_CREATION_FAILURE - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); -} - -TEST_F(AnalyticsRecorderTest, - UnfinishedEstablishedConnectionsAddedAsUnfinished) { - std::string endpoint_id = "endpoint_id"; - std::string connection_token = "connection_token"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - advertising_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnConnectionEstablished(endpoint_id, BLUETOOTH, - connection_token); - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnConnectionClosed( - endpoint_id, BLUETOOTH, UPGRADED, - ConnectionsLog::EstablishedConnection::UNKNOWN_SAFE_DISCONNECTION_RESULT); - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.OnConnectionEstablished(endpoint_id, WIFI_LAN, - connection_token); - GetFakeClock().FastForward(absl::Milliseconds(500)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 1500 - strategy_session { - duration_millis: 1400 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 1400 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: FINISH_SESSION_STOP_ADVERTISING - } - established_connection { - duration_millis: 300 - medium: BLUETOOTH - disconnection_reason: UPGRADED - connection_token: "connection_token" - safe_disconnection_result: UNKNOWN_SAFE_DISCONNECTION_RESULT - operation_result { - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - } - established_connection { - duration_millis: 500 - medium: WIFI_LAN - disconnection_reason: UNFINISHED - connection_token: "connection_token" - safe_disconnection_result: SAFE_DISCONNECTION - operation_result { - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); -} - -TEST_F(AnalyticsRecorderTest, OutgoingPayloadUpgraded) { - std::string endpoint_id = "endpoint_id"; - std::int64_t payload_id = 123456789; - std::string connection_token = "connection_token"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - advertising_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnConnectionEstablished(endpoint_id, BLUETOOTH, - connection_token); - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnOutgoingPayloadStarted( - {endpoint_id}, payload_id, connections::PayloadType::kFile, 50); - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.OnPayloadChunkSent(endpoint_id, payload_id, 10); - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.OnPayloadChunkSent(endpoint_id, payload_id, 10); - GetFakeClock().FastForward(absl::Milliseconds(600)); - analytics_recorder.OnConnectionClosed( - endpoint_id, BLUETOOTH, UPGRADED, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); - GetFakeClock().FastForward(absl::Milliseconds(700)); - analytics_recorder.OnConnectionEstablished(endpoint_id, WIFI_LAN, - connection_token); - GetFakeClock().FastForward(absl::Milliseconds(800)); - analytics_recorder.OnPayloadChunkSent(endpoint_id, payload_id, 10); - GetFakeClock().FastForward(absl::Milliseconds(900)); - analytics_recorder.OnPayloadChunkSent(endpoint_id, payload_id, 10); - GetFakeClock().FastForward(absl::Milliseconds(1000)); - analytics_recorder.OnPayloadChunkSent(endpoint_id, payload_id, 10); - GetFakeClock().FastForward(absl::Milliseconds(1100)); - analytics_recorder.OnOutgoingPayloadDone(endpoint_id, payload_id, SUCCESS, - OperationResultCode::DETAIL_SUCCESS); - GetFakeClock().FastForward(absl::Milliseconds(1200)); - analytics_recorder.OnConnectionClosed( - endpoint_id, WIFI_LAN, LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); - GetFakeClock().FastForward(absl::Milliseconds(1300)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 9100 - strategy_session { - duration_millis: 9000 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 9000 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: FINISH_SESSION_STOP_ADVERTISING - } - established_connection { - duration_millis: 1800 - medium: BLUETOOTH - sent_payload { - duration_millis: 1500 - type: FILE - total_size_bytes: 50 - num_bytes_transferred: 20 - num_chunks: 2 - status: MOVED_TO_NEW_MEDIUM - operation_result { - result_category: CATEGORY_MISCELLANEOUS - result_code: MISCELLEANEOUS_MOVE_TO_NEW_MEDIUM - } - } - disconnection_reason: UPGRADED - connection_token: "connection_token" - safe_disconnection_result: SAFE_DISCONNECTION - operation_result { - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - } - established_connection { - duration_millis: 5000 - medium: WIFI_LAN - sent_payload { - duration_millis: 4500 - type: FILE - total_size_bytes: 50 - num_bytes_transferred: 30 - num_chunks: 3 - status: SUCCESS - operation_result { - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - } - disconnection_reason: LOCAL_DISCONNECTION - connection_token: "connection_token" - safe_disconnection_result: SAFE_DISCONNECTION - operation_result { - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); -} - -TEST_F(AnalyticsRecorderTest, UpgradeAttemptWorks) { - std::string endpoint_id = "endpoint_id"; - std::string endpoint_id_1 = "endpoint_id_1"; - std::string endpoint_id_2 = "endpoint_id_2"; - std::string connection_token = "connection_token"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - advertising_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(200)); - - analytics_recorder.OnBandwidthUpgradeStarted(endpoint_id, BLE, WIFI_LAN, - INCOMING, connection_token); - GetFakeClock().FastForward(absl::Milliseconds(300)); - - analytics_recorder.OnBandwidthUpgradeStarted( - endpoint_id_1, BLUETOOTH, WIFI_LAN, INCOMING, connection_token); - // Error to upgrade. - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.OnBandwidthUpgradeError( - endpoint_id, WIFI_LAN_MEDIUM_ERROR, WIFI_LAN_SOCKET_CREATION, - OperationResultCode::CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL); - // Success to upgrade. - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.OnBandwidthUpgradeSuccess(endpoint_id_1); - // Upgrade is unfinished. - GetFakeClock().FastForward(absl::Milliseconds(600)); - analytics_recorder.OnBandwidthUpgradeStarted( - endpoint_id_2, BLUETOOTH, WIFI_LAN, INCOMING, connection_token); - GetFakeClock().FastForward(absl::Milliseconds(700)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 2800 - strategy_session { - duration_millis: 2700 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 2700 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: FINISH_SESSION_STOP_ADVERTISING - } - upgrade_attempt { - duration_millis: 700 - direction: INCOMING - from_medium: BLE - to_medium: WIFI_LAN - upgrade_result: WIFI_LAN_MEDIUM_ERROR - error_stage: WIFI_LAN_SOCKET_CREATION - connection_token: "connection_token" - operation_result { - result_category: CATEGORY_CONNECTIVITY_ERROR - result_code: CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL - } - } - upgrade_attempt { - duration_millis: 900 - direction: INCOMING - from_medium: BLUETOOTH - to_medium: WIFI_LAN - upgrade_result: UPGRADE_RESULT_SUCCESS - error_stage: UPGRADE_SUCCESS - connection_token: "connection_token" - operation_result { - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - } - upgrade_attempt { - duration_millis: 700 - direction: INCOMING - from_medium: BLUETOOTH - to_medium: WIFI_LAN - upgrade_result: UNFINISHED_ERROR - error_stage: UPGRADE_UNFINISHED - connection_token: "connection_token" - operation_result { - result_category: CATEGORY_DEVICE_STATE_ERROR - result_code: DEVICE_STATE_ERROR_UNFINISHED_UPGRADE_ATTEMPTS - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); -} - -TEST_F(AnalyticsRecorderTest, StartListeningForIncomingConnectionsWorks) { - std::string endpoint_id = "endpoint_id"; - std::string endpoint_id_1 = "endpoint_id_1"; - std::string endpoint_id_2 = "endpoint_id_2"; - std::string connection_token = "connection_token"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartedIncomingConnectionListening( - connections::Strategy::kP2pStar); - GetFakeClock().FastForward(absl::Milliseconds(200)); - - analytics_recorder.OnBandwidthUpgradeStarted(endpoint_id, BLE, WIFI_LAN, - INCOMING, connection_token); - - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnBandwidthUpgradeStarted( - endpoint_id_1, BLUETOOTH, WIFI_LAN, INCOMING, connection_token); - GetFakeClock().FastForward(absl::Milliseconds(400)); - // Error to upgrade. - analytics_recorder.OnBandwidthUpgradeError( - endpoint_id, WIFI_LAN_MEDIUM_ERROR, WIFI_LAN_SOCKET_CREATION, - OperationResultCode::CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL); - GetFakeClock().FastForward(absl::Milliseconds(500)); - // Success to upgrade. - analytics_recorder.OnBandwidthUpgradeSuccess(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(600)); - - analytics_recorder.LogSession(); - // ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 2100 - strategy_session { - duration_millis: 2000 - strategy: P2P_STAR - role: ADVERTISER - upgrade_attempt { - direction: INCOMING - duration_millis: 700 - from_medium: BLE - to_medium: WIFI_LAN - upgrade_result: WIFI_LAN_MEDIUM_ERROR - error_stage: WIFI_LAN_SOCKET_CREATION - connection_token: "connection_token" - operation_result { - result_category: CATEGORY_CONNECTIVITY_ERROR - result_code: CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL - } - } - upgrade_attempt { - direction: INCOMING - duration_millis: 900 - from_medium: BLUETOOTH - to_medium: WIFI_LAN - upgrade_result: UPGRADE_RESULT_SUCCESS - error_stage: UPGRADE_SUCCESS - connection_token: "connection_token" - operation_result { - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); -} - -TEST_F(AnalyticsRecorderTest, SetErrorCodeFieldsCorrectly) { - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto discovery_metadata_params = - analytics_recorder.BuildDiscoveryMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartDiscovery(connections::Strategy::kP2pStar, - /*mediums=*/{WEB_RTC}, - discovery_metadata_params.get()); - - ErrorCodeParams error_code_params = ErrorCodeRecorder::BuildErrorCodeParams( - WEB_RTC, DISCONNECT, DISCONNECT_NETWORK_FAILED, - TACHYON_SEND_MESSAGE_STATUS_EXCEPTION, "", "connection_token"); - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnErrorCode(error_code_params); - GetFakeClock().FastForward(absl::Milliseconds(300)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ErrorCode error_code_proto = ParseTextProtoOrDie(R"pb( - medium: WEB_RTC - event: DISCONNECT - description: TACHYON_SEND_MESSAGE_STATUS_EXCEPTION - disconnect_error: DISCONNECT_NETWORK_FAILED - connection_token: "connection_token" - )pb"); - - EXPECT_THAT(event_logger.GetErrorCode(), EqualsProto(error_code_proto)); -} - -TEST_F(AnalyticsRecorderTest, - SetErrorCodeFieldsCorrectlyForUnknownDescription) { - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto discovery_metadata_params = - analytics_recorder.BuildDiscoveryMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartDiscovery(connections::Strategy::kP2pStar, - /*mediums=*/{BLUETOOTH}, - discovery_metadata_params.get()); - - ErrorCodeParams error_code_params; - // Skip setting error_code_params.description - error_code_params.medium = BLUETOOTH; - error_code_params.event = START_DISCOVERING; - error_code_params.start_discovering_error = START_EXTENDED_DISCOVERING_FAILED; - error_code_params.connection_token = "connection_token"; - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnErrorCode(error_code_params); - GetFakeClock().FastForward(absl::Milliseconds(300)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ErrorCode error_code_proto = ParseTextProtoOrDie(R"pb( - medium: BLUETOOTH - event: START_DISCOVERING - description: UNKNOWN - start_discovering_error: START_EXTENDED_DISCOVERING_FAILED - connection_token: "connection_token" - )pb"); - - EXPECT_THAT(event_logger.GetErrorCode(), EqualsProto(error_code_proto)); -} - -TEST_F(AnalyticsRecorderTest, SetErrorCodeFieldsCorrectlyForCommonError) { - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto discovery_metadata_params = - analytics_recorder.BuildDiscoveryMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartDiscovery(connections::Strategy::kP2pStar, - /*mediums=*/{BLUETOOTH}, - discovery_metadata_params.get()); - - ErrorCodeParams error_code_params = ErrorCodeRecorder::BuildErrorCodeParams( - BLUETOOTH, START_DISCOVERING, INVALID_PARAMETER, - NULL_BLUETOOTH_DEVICE_NAME, "", "connection_token"); - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnErrorCode(error_code_params); - GetFakeClock().FastForward(absl::Milliseconds(300)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ErrorCode error_code_proto = ParseTextProtoOrDie(R"pb( - medium: BLUETOOTH - event: START_DISCOVERING - description: NULL_BLUETOOTH_DEVICE_NAME - common_error: INVALID_PARAMETER - connection_token: "connection_token" - )pb"); - - EXPECT_THAT(event_logger.GetErrorCode(), EqualsProto(error_code_proto)); -} - -TEST_F(AnalyticsRecorderTest, CheckIfSessionWasLogged) { - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - GetFakeClock().FastForward(absl::Milliseconds(100)); - // LogSession to count down client_session_done_latch. - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - EXPECT_TRUE(analytics_recorder.IsSessionLogged()); -} - -TEST_F(AnalyticsRecorderTest, ConstructAnalyticsRecorder) { - CountDownLatch client_session_done_latch(0); - CountDownLatch start_client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch, - &start_client_session_done_latch); - - // Call the constructor to count down the session_done_latch. - AnalyticsRecorder analytics_recorder(&event_logger); - ASSERT_TRUE(start_client_session_done_latch.Await(kDefaultTimeout).result()); - - std::vector event_types = event_logger.GetLoggedEventTypes(); - EXPECT_EQ(event_types.size(), 1); - EXPECT_THAT(event_types, Contains(START_CLIENT_SESSION).Times(1)); -} - -TEST_F( - AnalyticsRecorderTest, - StartClientSessionOnlyLoggedOnceWorksAfterAnalyticsRecorderIsConstructed) { - CountDownLatch client_session_done_latch(0); - CountDownLatch start_client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch, - &start_client_session_done_latch); - - // Call the constructor to count down the start_client_session_done_latch. - AnalyticsRecorder analytics_recorder(&event_logger); - ASSERT_TRUE(start_client_session_done_latch.Await(kDefaultTimeout).result()); - - // Log start client session once. - EXPECT_THAT(event_logger.GetLoggedEventTypes(), - Contains(START_CLIENT_SESSION).Times(1)); - - // Reset the start_client_session_done_latch. However, LogStartSession cannot - // count down the start_client_session_done_latch. - CountDownLatch new_start_client_session_done_latch(1); - event_logger.SetStartClientSessionDoneLatchPtr( - &new_start_client_session_done_latch); - analytics_recorder.LogStartSession(); - ASSERT_FALSE( - new_start_client_session_done_latch.Await(kDefaultTimeout).result()); - - // No more start client session was logged. - EXPECT_THAT(event_logger.GetLoggedEventTypes(), - Contains(START_CLIENT_SESSION).Times(1)); -} - -TEST_F(AnalyticsRecorderTest, - CanLogStartClientSessionOnceAgainAfterSessionWasLogged) { - CountDownLatch client_session_done_latch(0); - CountDownLatch start_client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch, - &start_client_session_done_latch); - - // Call the constructor to count down the start_client_session_done_latch. - AnalyticsRecorder analytics_recorder(&event_logger); - ASSERT_TRUE(start_client_session_done_latch.Await(kDefaultTimeout).result()); - - // Log start client session once. - EXPECT_THAT(event_logger.GetLoggedEventTypes(), - Contains(START_CLIENT_SESSION).Times(1)); - - // Reset the client_session_done_latch. Call LogSession to count down the - // client_session_done_latch. - CountDownLatch new_client_session_done_latch(1); - event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - // Reset the start_client_session_done_latch. Call LogStartSession to count - // down the start_client_session_done_latch. - CountDownLatch new_start_client_session_done_latch(1); - event_logger.SetStartClientSessionDoneLatchPtr( - &new_start_client_session_done_latch); - analytics_recorder.LogStartSession(); - analytics_recorder.LogStartSession(); - analytics_recorder.LogStartSession(); - analytics_recorder.LogStartSession(); - ASSERT_TRUE( - new_start_client_session_done_latch.Await(kDefaultTimeout).result()); - - // Can log start client session once again. - EXPECT_THAT(event_logger.GetLoggedEventTypes(), - Contains(START_CLIENT_SESSION).Times(2)); -} - -TEST_F(AnalyticsRecorderTest, - ClearcIncomingConnectionRequestsAfterSessionWasLogged) { - std::string endpoint_id_0 = "endpoint_id_0"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - advertising_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnConnectionRequestReceived(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnLocalEndpointAccepted(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(500)); - - // LogSession - analytics_recorder.LogSession(); // call ResetClientSessionLoggingResouces - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto1 = - ParseTextProtoOrDie(R"pb( - duration_millis: 1500 - strategy_session { - duration_millis: 1400 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 1400 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: FINISH_SESSION_STOP_ADVERTISING - received_connection_request { - duration_millis: 700 - request_delay_millis: 200 - local_response: ACCEPTED - remote_response: ACCEPTED - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto1)); - - // LogStartSession - CountDownLatch new_start_client_session_done_latch(1); - event_logger.SetStartClientSessionDoneLatchPtr( - &new_start_client_session_done_latch); - GetFakeClock().FastForward(absl::Milliseconds(600)); - analytics_recorder.LogStartSession(); - ASSERT_TRUE( - new_start_client_session_done_latch.Await(kDefaultTimeout).result()); - - // LogSession again - CountDownLatch new_client_session_done_latch(1); - event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); - std::string endpoint_id_1 = "endpoint_id_1"; - GetFakeClock().FastForward(absl::Milliseconds(700)); - analytics_recorder.OnConnectionRequestReceived(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(800)); - analytics_recorder.OnLocalEndpointAccepted(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(900)); - analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(1000)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(new_client_session_done_latch.Await(kDefaultTimeout).result()); - - // - if the current_strategy_session_ and current_advertising_phase_ are not - // reset, the duplicate advertising_phase (with the additional - // received_connection_request) will append to the strategy_session) - ConnectionsLog::ClientSession strategy_session_proto2 = ParseTextProtoOrDie( - R"pb( - duration_millis: 0 - strategy_session { - duration_millis: 0 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 0 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - received_connection_request { - duration_millis: 0 - request_delay_millis: 0 - local_response: ACCEPTED - remote_response: ACCEPTED - } - } - advertising_phase { - duration_millis: 0 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - received_connection_request { - duration_millis: 0 - request_delay_millis: 0 - local_response: ACCEPTED - remote_response: ACCEPTED - } - received_connection_request { - duration_millis: 0 - request_delay_millis: 0 - local_response: ACCEPTED - remote_response: ACCEPTED - } - } - })pb"); - EXPECT_THAT(event_logger.GetLoggedClientSession(), - Not(EqualsProto(strategy_session_proto2))); -} - -TEST_F(AnalyticsRecorderTest, - ClearcOutgoingConnectionRequestsAfterSessionWasLogged) { - std::string endpoint_id_0 = "endpoint_id_0"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto discovery_metadata_params = - analytics_recorder.BuildDiscoveryMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartDiscovery(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - discovery_metadata_params.get()); - - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnConnectionRequestSent(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnLocalEndpointAccepted(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_0); - GetFakeClock().FastForward(absl::Milliseconds(500)); - - // LogSession - analytics_recorder.LogSession(); // call ResetClientSessionLoggingResouces - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto1 = - ParseTextProtoOrDie(R"pb( - duration_millis: 1500 - strategy_session { - duration_millis: 1400 - strategy: P2P_STAR - role: DISCOVERER - discovery_phase { - duration_millis: 1400 - medium: BLE - medium: BLUETOOTH - discovery_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: FINISH_SESSION_STOP_DISCOVERING - sent_connection_request { - duration_millis: 700 - request_delay_millis: 200 - local_response: ACCEPTED - remote_response: ACCEPTED - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto1)); - - // LogStartSession - CountDownLatch new_start_client_session_done_latch(1); - event_logger.SetStartClientSessionDoneLatchPtr( - &new_start_client_session_done_latch); - analytics_recorder.LogStartSession(); - ASSERT_TRUE( - new_start_client_session_done_latch.Await(kDefaultTimeout).result()); - - // LogSession again - CountDownLatch new_client_session_done_latch(1); - event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); - std::string endpoint_id_1 = "endpoint_id_1"; - GetFakeClock().FastForward(absl::Milliseconds(600)); - analytics_recorder.OnConnectionRequestSent(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(700)); - analytics_recorder.OnLocalEndpointAccepted(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(800)); - analytics_recorder.OnRemoteEndpointAccepted(endpoint_id_1); - GetFakeClock().FastForward(absl::Milliseconds(900)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - // - if the current_strategy_session_ and current_discovery_phase_ are - // not reset, the duplicate discovery_phase (with the additional - // sent_connection_request) will append to the strategy_session) - ConnectionsLog::ClientSession strategy_session_proto2 = - ParseTextProtoOrDie(R"pb( - duration_millis: 0 - strategy_session { - duration_millis: 0 - strategy: P2P_STAR - role: DISCOVERER - discovery_phase { - duration_millis: 0 - medium: BLE - medium: BLUETOOTH - discovery_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - sent_connection_request { - duration_millis: 0 - request_delay_millis: 0 - local_response: ACCEPTED - remote_response: ACCEPTED - } - } - discovery_phase { - duration_millis: 0 - medium: BLE - medium: BLUETOOTH - discovery_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - sent_connection_request { - duration_millis: 0 - request_delay_millis: 0 - local_response: ACCEPTED - remote_response: ACCEPTED - } - sent_connection_request { - duration_millis: 0 - request_delay_millis: 0 - local_response: ACCEPTED - remote_response: ACCEPTED - } - } - })pb"); - EXPECT_THAT(event_logger.GetLoggedClientSession(), - Not(EqualsProto(strategy_session_proto2))); -} - -TEST_F(AnalyticsRecorderTest, ClearcActiveConnectionsAfterSessionWasLogged) { - connections::Strategy strategy = connections::Strategy::kP2pStar; - std::vector mediums = {BLE, BLUETOOTH}; - std::string endpoint_id = "endpoint_id"; - std::string connection_token = "connection_token"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising(strategy, mediums, - advertising_metadata_params.get()); - - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnConnectionEstablished(endpoint_id, BLUETOOTH, - connection_token); - GetFakeClock().FastForward(absl::Milliseconds(300)); - - // LogSession - analytics_recorder.LogSession(); // call ResetClientSessionLoggingResouces - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - ConnectionsLog::ClientSession strategy_session_proto1 = - ParseTextProtoOrDie(R"pb( - duration_millis: 600 - strategy_session { - duration_millis: 500 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 500 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: FINISH_SESSION_STOP_ADVERTISING - } - established_connection { - duration_millis: 300 - medium: BLUETOOTH - disconnection_reason: UNFINISHED - connection_token: "connection_token" - safe_disconnection_result: SAFE_DISCONNECTION - operation_result { - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto1)); - - // LogStartSession - CountDownLatch new_start_client_session_done_latch(1); - event_logger.SetStartClientSessionDoneLatchPtr( - &new_start_client_session_done_latch); - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.LogStartSession(); - ASSERT_TRUE( - new_start_client_session_done_latch.Await(kDefaultTimeout).result()); - - // LogSession again - CountDownLatch new_client_session_done_latch(1); - event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - // - if the current_strategy_session_ and advertising_phase_ are not - // reset, the duplicate advertising_phase_ (with the additional - // will append to the strategy_session), and the active connection (i.e. - // established_connection) will stay there. - ConnectionsLog::ClientSession strategy_session_proto2 = - ParseTextProtoOrDie(R"pb( - duration_millis: 0 - strategy_session { - duration_millis: 0 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 0 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - } - advertising_phase { - duration_millis: 0 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - } - established_connection { - duration_millis: 0 - medium: BLUETOOTH - disconnection_reason: UNFINISHED - connection_token: "connection_token" - safe_disconnection_result: SAFE_DISCONNECTION - operation_result { - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - Not(EqualsProto(strategy_session_proto2))); -} - -TEST_F(AnalyticsRecorderTest, - ClearBandwidthUpgradeAttemptsAfterSessionWasLogged) { - std::string endpoint_id = "endpoint_id"; - std::string endpoint_id_1 = "endpoint_id_1"; - std::string endpoint_id_2 = "endpoint_id_2"; - std::string connection_token = "connection_token"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - advertising_metadata_params.get()); - - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnBandwidthUpgradeStarted(endpoint_id, BLE, WIFI_LAN, - INCOMING, connection_token); - - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnBandwidthUpgradeStarted( - endpoint_id_1, BLUETOOTH, WIFI_LAN, INCOMING, connection_token); - // - Error to upgrade. - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.OnBandwidthUpgradeError( - endpoint_id, WIFI_LAN_MEDIUM_ERROR, WIFI_LAN_SOCKET_CREATION, - OperationResultCode::CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL); - // - Success to upgrade. - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.OnBandwidthUpgradeSuccess(endpoint_id_1); - - // - Upgrade is unfinished. - GetFakeClock().FastForward(absl::Milliseconds(600)); - analytics_recorder.OnBandwidthUpgradeStarted( - endpoint_id_2, BLUETOOTH, WIFI_LAN, INCOMING, connection_token); - - GetFakeClock().FastForward(absl::Milliseconds(700)); - // LogSession - analytics_recorder.LogSession(); // call ResetClientSessionLoggingResouces - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - // - if the current_strategy_session_ and advertising_phase_ are not - // reset, the duplicate advertising_phase_, and the upgrade_attempts (i.e. - // bandwidth_upgrade_attempts_) will stay there. - ConnectionsLog::ClientSession strategy_session_proto1 = - ParseTextProtoOrDie(R"pb( - duration_millis: 2800 - strategy_session { - duration_millis: 2700 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 2700 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: FINISH_SESSION_STOP_ADVERTISING - } - upgrade_attempt { - direction: INCOMING - duration_millis: 700 - from_medium: BLE - to_medium: WIFI_LAN - upgrade_result: WIFI_LAN_MEDIUM_ERROR - error_stage: WIFI_LAN_SOCKET_CREATION - connection_token: "connection_token" - operation_result { - result_category: CATEGORY_CONNECTIVITY_ERROR - result_code: CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL - } - } - upgrade_attempt { - direction: INCOMING - duration_millis: 900 - from_medium: BLUETOOTH - to_medium: WIFI_LAN - upgrade_result: UPGRADE_RESULT_SUCCESS - error_stage: UPGRADE_SUCCESS - connection_token: "connection_token" - operation_result { - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - } - upgrade_attempt { - direction: INCOMING - duration_millis: 700 - from_medium: BLUETOOTH - to_medium: WIFI_LAN - upgrade_result: UNFINISHED_ERROR - error_stage: UPGRADE_UNFINISHED - connection_token: "connection_token" - operation_result { - result_category: CATEGORY_DEVICE_STATE_ERROR - result_code: DEVICE_STATE_ERROR_UNFINISHED_UPGRADE_ATTEMPTS - } - } - })pb"); - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto1)); - - // LogStartSession - CountDownLatch new_start_client_session_done_latch(1); - event_logger.SetStartClientSessionDoneLatchPtr( - &new_start_client_session_done_latch); - GetFakeClock().FastForward(absl::Milliseconds(800)); - analytics_recorder.LogStartSession(); - ASSERT_TRUE( - new_start_client_session_done_latch.Await(kDefaultTimeout).result()); - - // LogSession again - CountDownLatch new_client_session_done_latch(1); - event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); - GetFakeClock().FastForward(absl::Milliseconds(900)); - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto2 = - ParseTextProtoOrDie(R"pb( - duration_millis: 0 - strategy_session { - duration_millis: 0 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 0 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - } - advertising_phase { - duration_millis: 0 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - } - upgrade_attempt { - direction: INCOMING - from_medium: BLE - to_medium: WIFI_LAN - upgrade_result: WIFI_LAN_MEDIUM_ERROR - error_stage: WIFI_LAN_SOCKET_CREATION - connection_token: "connection_token" - operation_result { - result_category: CATEGORY_CONNECTIVITY_ERROR - result_code: CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL - } - } - upgrade_attempt { - direction: INCOMING - from_medium: BLUETOOTH - to_medium: WIFI_LAN - upgrade_result: UPGRADE_RESULT_SUCCESS - error_stage: UPGRADE_SUCCESS - connection_token: "connection_token" - operation_result { - result_category: CATEGORY_SUCCESS - result_code: DETAIL_SUCCESS - } - } - upgrade_attempt { - direction: INCOMING - from_medium: BLUETOOTH - to_medium: WIFI_LAN - upgrade_result: UNFINISHED_ERROR - error_stage: UPGRADE_UNFINISHED - connection_token: "connection_token" - operation_result { - result_category: CATEGORY_DEVICE_STATE_ERROR - result_code: DEVICE_STATE_ERROR_UNFINISHED_UPGRADE_ATTEMPTS - } - } - })pb"); - EXPECT_THAT(event_logger.GetLoggedClientSession(), - Not(EqualsProto(strategy_session_proto2))); -} - -// Test if current_strategy_ is reset by checking if the same strategy would -// be logged for different client sessions or not. If yes, it should be logged. -// Otherwise, not. -TEST_F(AnalyticsRecorderTest, - CanLogSeparateStartStrategySessionForSameStrategyAfterSessionWasLogged) { - connections::Strategy strategy = connections::Strategy::kP2pStar; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising(connections::Strategy::kP2pStar, - /*mediums=*/{BLUETOOTH}, - advertising_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnStopAdvertising(); - - GetFakeClock().FastForward(absl::Milliseconds(300)); - // LogSession - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - // The same strategy session shouldn't be logged again with the same client - // session. - EXPECT_THAT(event_logger.GetLoggedEventTypes(), - Contains(START_STRATEGY_SESSION).Times(1)); - - // LogStartSession - CountDownLatch new_start_client_session_done_latch(1); - event_logger.SetStartClientSessionDoneLatchPtr( - &new_start_client_session_done_latch); - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.LogStartSession(); - ASSERT_TRUE( - new_start_client_session_done_latch.Await(kDefaultTimeout).result()); - - // LogSession again - CountDownLatch new_client_session_done_latch(1); - event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); - - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.OnStartAdvertising(strategy, /*mediums=*/{BLUETOOTH}, - advertising_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(600)); - analytics_recorder.OnStopAdvertising(); - GetFakeClock().FastForward(absl::Milliseconds(700)); - - analytics_recorder.LogSession(); - ASSERT_TRUE(new_client_session_done_latch.Await(kDefaultTimeout).result()); - - EXPECT_THAT(event_logger.GetLoggedEventTypes(), - Contains(START_STRATEGY_SESSION).Times(2)); -} - -// Test if current_strategy_session_ is reset. If not, the same strategy session -// proto will be logged. -TEST_F(AnalyticsRecorderTest, - NotLogSameStrategySessionProtoAfterSessionWasLogged) { - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - // Via OnStartAdvertising, current_strategy_session_is set in - // UpdateStrategySessionLocked. - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - advertising_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnStopAdvertising(); - GetFakeClock().FastForward(absl::Milliseconds(300)); - // LogSession - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 600 - strategy_session { - duration_millis: 500 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 200 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: CLIENT_STOP_ADVERTISING - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); - - // LogStartSession - CountDownLatch new_start_client_session_done_latch(1); - event_logger.SetStartClientSessionDoneLatchPtr( - &new_start_client_session_done_latch); - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.LogStartSession(); - ASSERT_TRUE( - new_start_client_session_done_latch.Await(kDefaultTimeout).result()); - - // LogSession again - // - if current_strategy_session_ is reset, the same - // strategy_session_proto will be logged. - CountDownLatch new_client_session_done_latch(1); - event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.LogSession(); - ASSERT_TRUE(new_client_session_done_latch.Await(kDefaultTimeout).result()); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - Not(EqualsProto(strategy_session_proto))); -} - -// Test if current_advertising_phase_ is reset. -TEST_F(AnalyticsRecorderTest, - NotLogDuplicateAdvertisingPhaseAfterSessionWasLogged) { - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising( - connections::Strategy::kP2pStar, - /*mediums=*/{BLUETOOTH}, - advertising_metadata_params.get()); // set current_advertising_phase_ - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnStopAdvertising(); - GetFakeClock().FastForward(absl::Milliseconds(300)); - - // LogSession - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto1 = - ParseTextProtoOrDie(R"pb( - duration_millis: 600 - strategy_session { - duration_millis: 500 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 200 - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: CLIENT_STOP_ADVERTISING - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto1)); - - // LogStartSession - CountDownLatch new_start_client_session_done_latch(1); - event_logger.SetStartClientSessionDoneLatchPtr( - &new_start_client_session_done_latch); - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.LogStartSession(); - ASSERT_TRUE( - new_start_client_session_done_latch.Await(kDefaultTimeout).result()); - - // LogSession again - // - if the current_strategy_session_ and current_advertising_phase_ are - // not reset, the same strategy_session with two same advertising_phase will - // be logged. - CountDownLatch new_client_session_done_latch(1); - event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto2 = - ParseTextProtoOrDie(R"pb( - duration_millis: 0 - strategy_session { - duration_millis: 0 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 0 - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - } - advertising_phase { - duration_millis: 0 - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - } - })pb"); - EXPECT_THAT(event_logger.GetLoggedClientSession(), - Not(EqualsProto(strategy_session_proto2))); -} - -// Test if current_discovery_phase_ is reset. -TEST_F(AnalyticsRecorderTest, - NotLogDuplicateDiscoveryPhaseAfterSessionWasLogged) { - connections::Strategy strategy = connections::Strategy::kP2pStar; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - auto discovery_metadata_params = - analytics_recorder.BuildDiscoveryMetadataParams( - /*is_extended_advertisement_supported*/ true, - /*connected_ap_frequency*/ 1, /*is_nfc_available=*/false); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartDiscovery( - strategy, {BLUETOOTH}, - discovery_metadata_params.get()); // set current_discovery_phase_ - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnStopDiscovery(); - GetFakeClock().FastForward(absl::Milliseconds(300)); - analytics_recorder.OnEndpointFound(BLUETOOTH); - GetFakeClock().FastForward(absl::Milliseconds(400)); - // LogSession - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto1 = - ParseTextProtoOrDie(R"pb( - duration_millis: 1000 - strategy_session { - duration_millis: 900 - strategy: P2P_STAR - role: DISCOVERER - discovery_phase { - duration_millis: 200 - medium: BLUETOOTH - discovered_endpoint { - medium: BLUETOOTH - latency_millis: 500 - } - discovery_metadata { - supports_extended_ble_advertisements: true - connected_ap_frequency: 1 - supports_nfc_technology: false - } - stop_reason: CLIENT_STOP_DISCOVERING - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto1)); - - // LogStartSession - CountDownLatch new_start_client_session_done_latch(1); - event_logger.SetStartClientSessionDoneLatchPtr( - &new_start_client_session_done_latch); - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.LogStartSession(); - ASSERT_TRUE( - new_start_client_session_done_latch.Await(kDefaultTimeout).result()); - - // LogSession again - // - if the current_strategy_session_ and current_discovery_phase_ are not - // reset, the same strategy_session with two same discovery_phase will be - // logged. - CountDownLatch new_client_session_done_latch(1); - event_logger.SetClientSessionDoneLatch(new_client_session_done_latch); - GetFakeClock().FastForward(absl::Milliseconds(600)); - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto2 = - ParseTextProtoOrDie(R"pb( - duration_millis: 0 - strategy_session { - duration_millis: 0 - strategy: P2P_STAR - role: DISCOVERER - discovery_phase { - duration_millis: 0 - medium: BLUETOOTH - discovered_endpoint { medium: BLUETOOTH } - discovery_metadata { - supports_extended_ble_advertisements: true - connected_ap_frequency: 1 - supports_nfc_technology: false - } - stop_reason: CLIENT_STOP_DISCOVERING - } - discovery_phase { - duration_millis: 0 - medium: BLUETOOTH - discovery_metadata { - supports_extended_ble_advertisements: true - connected_ap_frequency: 1 - supports_nfc_technology: false - } - } - })pb"); - EXPECT_THAT(event_logger.GetLoggedClientSession(), - Not(EqualsProto(strategy_session_proto2))); -} - -TEST_F(AnalyticsRecorderTest, - NotAddNewConnectionWithoutCallingOnStartAdvertising) { - std::string endpoint_id = "endpoint_id"; - - CountDownLatch client_session_done_latch(1); - FakeEventLogger event_logger(client_session_done_latch); - AnalyticsRecorder analytics_recorder(&event_logger); - - // via OnStartAdvertising, current_strategy_session_ is set in - // UpdateStrategySessionLocked. - auto advertising_metadata_params = - analytics_recorder.BuildAdvertisingMetadataParams(); - GetFakeClock().FastForward(absl::Milliseconds(100)); - analytics_recorder.OnStartAdvertising(connections::Strategy::kP2pStar, - /*mediums=*/{BLE, BLUETOOTH}, - advertising_metadata_params.get()); - GetFakeClock().FastForward(absl::Milliseconds(200)); - analytics_recorder.OnStopAdvertising(); - GetFakeClock().FastForward(absl::Milliseconds(300)); - - // LogSession - analytics_recorder.LogSession(); - ASSERT_TRUE(client_session_done_latch.Await(kDefaultTimeout).result()); - - ConnectionsLog::ClientSession strategy_session_proto = - ParseTextProtoOrDie(R"pb( - duration_millis: 600 - strategy_session { - duration_millis: 500 - strategy: P2P_STAR - role: ADVERTISER - advertising_phase { - duration_millis: 200 - medium: BLE - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: CLIENT_STOP_ADVERTISING - } - })pb"); - - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); - - // Without calling OnStartAdvertising won't create new - // current_strategy_session_. - GetFakeClock().FastForward(absl::Milliseconds(400)); - analytics_recorder.OnConnectionEstablished(endpoint_id, BLUETOOTH, - /*connection_token=*/""); - GetFakeClock().FastForward(absl::Milliseconds(500)); - analytics_recorder.OnConnectionClosed( - endpoint_id, BLUETOOTH, UPGRADED, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); - GetFakeClock().FastForward(absl::Milliseconds(600)); - analytics_recorder.LogSession(); - - // The proto won't change. - EXPECT_THAT(event_logger.GetLoggedClientSession(), - EqualsProto(strategy_session_proto)); -} - -} // namespace -} // namespace analytics -} // namespace nearby diff --git a/connections/implementation/analytics/discovery_metadata_params.h b/connections/implementation/analytics/discovery_metadata_params.h index 6442b03c..b90b2703 100644 --- a/connections/implementation/analytics/discovery_metadata_params.h +++ b/connections/implementation/analytics/discovery_metadata_params.h @@ -17,7 +17,7 @@ #include -#include "internal/proto/analytics/connections_log.pb.h" +#include "connections/implementation/analytics/operation_result_with_medium.h" namespace nearby { @@ -26,8 +26,7 @@ struct DiscoveryMetadataParams { bool is_extended_advertisement_supported = false; int connected_ap_frequency = 0; bool is_nfc_available = false; - std::vector + std::vector operation_result_with_mediums = {}; }; diff --git a/connections/implementation/analytics/mock_analytics_recorder.h b/connections/implementation/analytics/mock_analytics_recorder.h new file mode 100644 index 00000000..d407d98b --- /dev/null +++ b/connections/implementation/analytics/mock_analytics_recorder.h @@ -0,0 +1,196 @@ +// Copyright 2026 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 ANALYTICS_MOCK_ANALYTICS_RECORDER_H_ +#define ANALYTICS_MOCK_ANALYTICS_RECORDER_H_ + +#include +#include +#include + +#include "gmock/gmock.h" +#include "absl/time/time.h" +#include "connections/implementation/analytics/advertising_metadata_params.h" +#include "connections/implementation/analytics/analytics_recorder.h" +#include "connections/implementation/analytics/connection_attempt_metadata_params.h" +#include "connections/implementation/analytics/discovery_metadata_params.h" +#include "connections/payload_type.h" +#include "connections/strategy.h" +#include "internal/platform/error_code_params.h" +#include "proto/connections_enums.pb.h" + +namespace nearby::analytics { + +class MockAnalyticsRecorder : public AnalyticsRecorder { + public: + MockAnalyticsRecorder() = default; + ~MockAnalyticsRecorder() override = default; + + // Advertising phase + MOCK_METHOD(void, OnStartAdvertising, + (connections::Strategy strategy, + const std::vector& + mediums, + AdvertisingMetadataParams* advertising_metadata_params), + (override)); + MOCK_METHOD(void, OnStopAdvertising, (), (override)); + + MOCK_METHOD(int, GetNextAdvertisingUpdateIndex, (), (override)); + + // Connection listening + MOCK_METHOD(void, OnStartedIncomingConnectionListening, + (connections::Strategy strategy), (override)); + MOCK_METHOD(void, OnStoppedIncomingConnectionListening, (), (override)); + + // Discovery phase + MOCK_METHOD(void, OnStartDiscovery, + (connections::Strategy strategy, + const std::vector& + mediums, + DiscoveryMetadataParams* discovery_metadata_params), + (override)); + MOCK_METHOD(void, OnStopDiscovery, (), (override)); + + MOCK_METHOD(int, GetNextDiscoveryUpdateIndex, (), (override)); + MOCK_METHOD(void, OnEndpointFound, + (location::nearby::proto::connections::Medium medium), + (override)); + + // Connection request + MOCK_METHOD(void, OnRequestConnection, + (const connections::Strategy& strategy, + const std::string& endpoint_id), + (override)); + + MOCK_METHOD(void, OnConnectionRequestReceived, + (const std::string& remote_endpoint_id), (override)); + MOCK_METHOD(void, OnConnectionRequestSent, + (const std::string& remote_endpoint_id), (override)); + MOCK_METHOD(void, OnRemoteEndpointAccepted, + (const std::string& remote_endpoint_id), (override)); + MOCK_METHOD(void, OnLocalEndpointAccepted, + (const std::string& remote_endpoint_id), (override)); + MOCK_METHOD(void, OnRemoteEndpointRejected, + (const std::string& remote_endpoint_id), (override)); + MOCK_METHOD(void, OnLocalEndpointRejected, + (const std::string& remote_endpoint_id), (override)); + + // Connection attempt + MOCK_METHOD( + void, OnIncomingConnectionAttempt, + (location::nearby::proto::connections::ConnectionAttemptType type, + location::nearby::proto::connections::Medium medium, + location::nearby::proto::connections::ConnectionAttemptResult result, + absl::Duration duration, const std::string& connection_token, + ConnectionAttemptMetadataParams* connection_attempt_metadata_params), + (override)); + MOCK_METHOD( + void, OnOutgoingConnectionAttempt, + (const std::string& remote_endpoint_id, + location::nearby::proto::connections::ConnectionAttemptType type, + location::nearby::proto::connections::Medium medium, + location::nearby::proto::connections::ConnectionAttemptResult result, + absl::Duration duration, const std::string& connection_token, + ConnectionAttemptMetadataParams* connection_attempt_metadata_params), + (override)); + + // Connection established + MOCK_METHOD(void, OnConnectionEstablished, + (const std::string& endpoint_id, + location::nearby::proto::connections::Medium medium, + const std::string& connection_token), + (override)); + MOCK_METHOD(void, OnConnectionClosed, + (const std::string& endpoint_id, + location::nearby::proto::connections::Medium medium, + location::nearby::proto::connections::DisconnectionReason reason, + SafeDisconnectionResult result), + (override)); + + // Payload + MOCK_METHOD(void, OnIncomingPayloadStarted, + (const std::string& endpoint_id, std::int64_t payload_id, + connections::PayloadType type, std::int64_t total_size_bytes), + (override)); + MOCK_METHOD(void, OnPayloadChunkReceived, + (const std::string& endpoint_id, std::int64_t payload_id, + std::int64_t chunk_size_bytes), + (override)); + MOCK_METHOD(void, OnIncomingPayloadDone, + (const std::string& endpoint_id, std::int64_t payload_id, + location::nearby::proto::connections::PayloadStatus status, + location::nearby::proto::connections::OperationResultCode + operation_result_code), + (override)); + MOCK_METHOD(void, OnOutgoingPayloadStarted, + (const std::vector& endpoint_ids, + std::int64_t payload_id, connections::PayloadType type, + std::int64_t total_size_bytes), + (override)); + MOCK_METHOD(void, OnPayloadChunkSent, + (const std::string& endpoint_id, std::int64_t payload_id, + std::int64_t chunk_size_bytes), + (override)); + MOCK_METHOD(void, OnOutgoingPayloadDone, + (const std::string& endpoint_id, std::int64_t payload_id, + location::nearby::proto::connections::PayloadStatus status, + location::nearby::proto::connections::OperationResultCode + operation_result_code), + (override)); + + // BandwidthUpgrade + MOCK_METHOD(void, OnBandwidthUpgradeStarted, + (const std::string& endpoint_id, + location::nearby::proto::connections::Medium from_medium, + location::nearby::proto::connections::Medium to_medium, + location::nearby::proto::connections::ConnectionAttemptDirection + direction, + const std::string& connection_token), + (override)); + MOCK_METHOD(void, UpdateBwUpgradeNetworkInfo, + (const std::string& endpoint_id, int num_interfaces, + int num_ipv6_only_interfaces), + (override)); + MOCK_METHOD(void, OnBandwidthUpgradeError, + (const std::string& endpoint_id, + location::nearby::proto::connections::BandwidthUpgradeResult + result, + location::nearby::proto::connections::BandwidthUpgradeErrorStage + error_stage, + location::nearby::proto::connections::OperationResultCode + operation_result_code), + (override)); + MOCK_METHOD(void, OnBandwidthUpgradeSuccess, (const std::string& endpoint_id), + (override)); + + // Error Code + MOCK_METHOD(void, OnErrorCode, (const ErrorCodeParams& params), (override)); + + MOCK_METHOD(void, LogStartSession, (), (override)); + MOCK_METHOD(void, LogSession, (), (override)); + + MOCK_METHOD(bool, IsSessionLogged, (), (override)); + + MOCK_METHOD( + location::nearby::proto::connections::OperationResultCategory, + GetOperationResultCategory, + (location::nearby::proto::connections::OperationResultCode result_code), + (override)); + + MOCK_METHOD(void, Sync, (), (override)); +}; + +} // namespace nearby::analytics + +#endif // ANALYTICS_MOCK_ANALYTICS_RECORDER_H_ diff --git a/connections/implementation/analytics/operation_result_with_medium.h b/connections/implementation/analytics/operation_result_with_medium.h new file mode 100644 index 00000000..483acc14 --- /dev/null +++ b/connections/implementation/analytics/operation_result_with_medium.h @@ -0,0 +1,55 @@ +// Copyright 2026 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 ANALYTICS_OPERATION_RESULT_WITH_MEDIUM_H_ +#define ANALYTICS_OPERATION_RESULT_WITH_MEDIUM_H_ + +#include + +#include "proto/connections_enums.pb.h" + +namespace nearby::analytics { + +struct OperationResultWithMedium { + location::nearby::proto::connections::Medium medium = + location::nearby::proto::connections::UNKNOWN_MEDIUM; + std::optional update_index; + location::nearby::proto::connections::OperationResultCategory + result_category = location::nearby::proto::connections::CATEGORY_UNKNOWN; + location::nearby::proto::connections::OperationResultCode result_code = + location::nearby::proto::connections::DETAIL_UNKNOWN; + std::optional + connection_mode; + + void set_medium(location::nearby::proto::connections::Medium m) { + medium = m; + } + void set_update_index(int i) { update_index = i; } + void set_result_category( + location::nearby::proto::connections::OperationResultCategory c) { + result_category = c; + } + void set_result_code( + location::nearby::proto::connections::OperationResultCode c) { + result_code = c; + } + void set_connection_mode( + location::nearby::proto::connections::ConnectionMode m) { + connection_mode = m; + } +}; + +} // namespace nearby::analytics + +#endif // ANALYTICS_OPERATION_RESULT_WITH_MEDIUM_H_ diff --git a/connections/implementation/analytics/packet_meta_data.h b/connections/implementation/analytics/packet_meta_data.h deleted file mode 100644 index 09d2a85a..00000000 --- a/connections/implementation/analytics/packet_meta_data.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2022-2023 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 NEARBY_CONNECTIONS_IMPLEMENTATION_ANALYTICS_PACKET_META_DATA_H_ -#define NEARBY_CONNECTIONS_IMPLEMENTATION_ANALYTICS_PACKET_META_DATA_H_ - -#include - -#include "absl/time/time.h" -#include "internal/platform/implementation/system_clock.h" -#include "internal/platform/system_clock.h" - -namespace nearby { -namespace analytics { - -struct PacketMetaData { - int packet_size; - absl::Time file_io_start_time; - absl::Time file_io_end_time; - absl::Time encryption_start_time; - absl::Time encryption_end_time; - absl::Time socket_io_start_time; - absl::Time socket_io_end_time; - - void Reset() { - file_io_start_time = SystemClock::ElapsedRealtime(); - socket_io_start_time = SystemClock::ElapsedRealtime(); - socket_io_start_time = SystemClock::ElapsedRealtime(); - packet_size = 0; - } - - void SetPacketSize(int packet_size) { - this->packet_size = packet_size; - } - - int GetPacketSize() { - return packet_size; - } - - void StartFileIo() { - file_io_start_time = SystemClock::ElapsedRealtime(); - } - - void StopFileIo() { - file_io_end_time = SystemClock::ElapsedRealtime(); - } - - void StartEncryption() { - encryption_start_time = SystemClock::ElapsedRealtime(); - } - - void StopEncryption() { - encryption_end_time = SystemClock::ElapsedRealtime(); - } - - void StartSocketIo() { - socket_io_start_time = SystemClock::ElapsedRealtime(); - } - - void StopSocketIo() { - socket_io_end_time = SystemClock::ElapsedRealtime(); - } - - int64_t GetEncryptionTimeInMillis() { - if (encryption_end_time > encryption_start_time) { - return absl::ToInt64Milliseconds(encryption_end_time - - encryption_start_time); - } - return 0L; - } - - int64_t GetFileIoTimeInMillis() { - if (file_io_end_time > file_io_start_time) { - return absl::ToInt64Milliseconds(file_io_end_time - file_io_start_time); - } - return 0L; - } - - int64_t GetSocketIoTimeInMillis() { - if (socket_io_end_time > socket_io_start_time) { - return absl::ToInt64Milliseconds(socket_io_end_time - - socket_io_start_time); - } - return 0L; - } -}; - -} // namespace analytics -} // namespace nearby - -#endif // NEARBY_CONNECTIONS_IMPLEMENTATION_ANALYTICS_PACKET_META_DATA_H_ diff --git a/connections/implementation/analytics/throughput_recorder.cc b/connections/implementation/analytics/throughput_recorder.cc deleted file mode 100644 index 7833d4ad..00000000 --- a/connections/implementation/analytics/throughput_recorder.cc +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2022-2023 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/analytics/throughput_recorder.h" - -#include - -#include -#include -#include -#include -#include - -#include "absl/container/flat_hash_map.h" -#include "absl/meta/type_traits.h" -#include "absl/strings/str_format.h" -#include "absl/time/time.h" -#include "internal/platform/implementation/system_clock.h" -#include "internal/platform/logging.h" -#include "internal/platform/mutex_lock.h" - -namespace nearby { -namespace analytics { - -namespace { -constexpr int kDefaultThroughoutKbps = 0; -constexpr int kKbInBytes = 1024; -constexpr int kSecInMs = 1000; -} // namespace - -ThroughputRecorder::ThroughputRecorder(int64_t payload_id) - : payload_id_(payload_id) {} - -ThroughputRecorderContainer& ThroughputRecorderContainer::GetInstance() { - alignas(ThroughputRecorderContainer) static char - storage[sizeof(ThroughputRecorderContainer)]; - static ThroughputRecorderContainer* env = - new (&storage) ThroughputRecorderContainer(); - return *env; -} - -void ThroughputRecorder::Start(PayloadType payload_type, - PayloadDirection payload_direction) { - std::string direction = - (payload_direction == PayloadDirection::INCOMING_PAYLOAD) ? "; Receive" - : "; Send"; - - VLOG(1) << "Start TP profiling for payload_id:" << payload_id_ << direction; - - if (payload_type == PayloadType::kUnknown) { - VLOG(1) << "Ignore ThroughputRecorder::start for Unknown Payload type"; - return; - } - - MutexLock lock(&mutex_); - start_timestamp_ = SystemClock::ElapsedRealtime(); - payload_type_ = payload_type; - payload_direction_ = payload_direction; - // Add packetLostAlarm later -} - -bool ThroughputRecorder::Stop() { - MutexLock lock(&mutex_); - VLOG(1) << "Stop TP profiling for payload_id:" << payload_id_; - if (payload_type_ == PayloadType::kUnknown) { - VLOG(1) << "Ignore ThroughputRecorder::stop as it never start"; - return false; - } - { - // Add packetLostAlarm stop process later - absl::Time stop_timestamp = SystemClock::ElapsedRealtime(); - int64_t total_byte_size = 0; - int medium_size = throughputs_.size(); - - // The worse case is the socket/connect blocking the write request, never - // got return when writing a frame out, it would get a very good data rate - // for this case. e.g. use 60 seconds to send a file and failed, the counter - // only get the duration as 30 seconds because the last write request - // blocked. - if (!success_) { - if (!throughputs_.empty()) { - for (auto& tp : throughputs_) { - tp.second.SetLastTimestamp(stop_timestamp); - } - } - } - - // calculate throughput by medium - for (auto& tp : throughputs_) { - tp.second.dump(); - total_byte_size += tp.second.GetTotalByteSize(); - } - - throughputs_.clear(); - - int64_t total_millis = - absl::ToInt64Milliseconds(stop_timestamp - start_timestamp_); - throughput_kbps_ = CalculateThroughputKBps(total_byte_size, total_millis); - int throughput_mbps = CalculateThroughputMBps(throughput_kbps_); - - // calculate overall throughput if there are multiple mediums - if (medium_size > 1) { - if (throughput_kbps_ != kDefaultThroughoutKbps) { - std::string dump_content = absl::StrFormat( - "%s %s data(%d bytes) %s, overall used %d milliseconds, " - "throughput " - "is %d MB/s (%d KB/s), File IO takes %d ms, %s takes %d " - "ms, " - "Socket IO takes %d ms", - (payload_direction_ == PayloadDirection::INCOMING_PAYLOAD) - ? "Received" - : "Sent", - ToString(payload_type_), total_byte_size, - success_ ? "SUCCEEDED" : "FAILED", total_millis, throughput_mbps, - throughput_kbps_, file_io_time_, - (payload_direction_ == PayloadDirection::INCOMING_PAYLOAD) - ? "Decryption" - : "Encryption", - encryption_time_, socket_io_time_); - LOG(INFO) << dump_content; - } - } - } - return true; -} - -void ThroughputRecorder::MarkAsSuccess() { - MutexLock lock(&mutex_); - success_ = true; -} - -int ThroughputRecorder::CalculateThroughputKBps(int64_t total_byte_size, - int64_t total_millis) { - if (total_millis > 0) { - return (int)(total_byte_size * kSecInMs / kKbInBytes / total_millis); - } - return kDefaultThroughoutKbps; -} - -int ThroughputRecorder::CalculateThroughputMBps(int throughputKBps) { - return throughputKBps / kKbInBytes; -} - -void ThroughputRecorder::Throughput::Add(int frame_size, int64_t file_io_time, - int64_t encryption_time, - int64_t socket_io_time) { - total_byte_size_ += frame_size; - // reset the last timestamp - last_timestamp_ = SystemClock::ElapsedRealtime(); - file_io_time_ += file_io_time; - encryption_time_ += encryption_time; - socket_io_time_ += socket_io_time; -} - -bool ThroughputRecorder::Throughput::dump() { - int64_t total_millis = - absl::ToInt64Milliseconds(last_timestamp_ - start_timestamp_); - int throughput_kbps = CalculateThroughputKBps(total_byte_size_, total_millis); - if (throughput_kbps == kDefaultThroughoutKbps) { - return false; - } - int throughpu_mbps = CalculateThroughputMBps(throughput_kbps); - int64_t other = - total_millis - file_io_time_ - encryption_time_ - socket_io_time_; - std::string dump_content = absl::StrFormat( - "%s %s data(%ld bytes) via %s used %ld milliseconds, throughput is %d " - "MB/s (%d KB/s), File IO takes %ld ms, %s takes %ld ms, " - "Socket IO takes %ld ms, " - "Other takes %ld ms", - (payload_direction_ == PayloadDirection::INCOMING_PAYLOAD) ? "Received" - : "Sent", - ToString(payload_type_), total_byte_size_, - location::nearby::proto::connections::Medium_Name(medium_), total_millis, - throughpu_mbps, throughput_kbps, file_io_time_, - (payload_direction_ == PayloadDirection::INCOMING_PAYLOAD) ? "Decryption" - : "Encryption", - encryption_time_, socket_io_time_, other); - LOG(INFO) << dump_content; - return true; -} - -ThroughputRecorder::Throughput& ThroughputRecorder::GetThroughput( - Medium medium, int64_t duration_millis) { - auto it = throughputs_.find(medium); - if (it == throughputs_.end()) { - auto throughput = new Throughput( - medium, - SystemClock::ElapsedRealtime() - absl::Milliseconds(duration_millis), - payload_type_, payload_direction_); - throughputs_.emplace(medium, std::move(*throughput)); - delete throughput; - return throughputs_.find(medium)->second; - } - return it->second; -} - -int ThroughputRecorder::GetThroughputsSize() { - MutexLock lock(&mutex_); - return throughputs_.size(); -} - -int ThroughputRecorder::GetThroughputKbps() { return throughput_kbps_; } - -int64_t ThroughputRecorder::GetDurationMillis() { return duration_millis_; } - -void ThroughputRecorder::OnFrameSent(Medium medium, - PacketMetaData& packetMetaData) { - MutexLock lock(&mutex_); - if (payload_type_ == PayloadType::kUnknown) { - VLOG(1) << "PayloadType is invalid, return"; - return; - } - - duration_millis_ = packetMetaData.GetEncryptionTimeInMillis() + - packetMetaData.GetFileIoTimeInMillis() + - packetMetaData.GetSocketIoTimeInMillis(); - GetThroughput(medium, duration_millis_) - .Add(packetMetaData.packet_size, packetMetaData.GetFileIoTimeInMillis(), - packetMetaData.GetEncryptionTimeInMillis(), - packetMetaData.GetSocketIoTimeInMillis()); - CalculateDurationTimes(packetMetaData); -} - -void ThroughputRecorder::OnFrameReceived(Medium medium, - PacketMetaData& packetMetaData) { - MutexLock lock(&mutex_); - if (payload_type_ == PayloadType::kUnknown) { - VLOG(1) << "PayloadType is invalid, return"; - return; - } - - // Add packetLostAlarm process later - duration_millis_ = packetMetaData.GetEncryptionTimeInMillis() + - packetMetaData.GetFileIoTimeInMillis() + - packetMetaData.GetSocketIoTimeInMillis(); - GetThroughput(medium, duration_millis_) - .Add(packetMetaData.packet_size, packetMetaData.GetFileIoTimeInMillis(), - packetMetaData.GetEncryptionTimeInMillis(), - packetMetaData.GetSocketIoTimeInMillis()); - CalculateDurationTimes(packetMetaData); -} - -void ThroughputRecorder::CalculateDurationTimes(PacketMetaData packetMetaData) { - encryption_time_ += packetMetaData.GetEncryptionTimeInMillis(); - socket_io_time_ += packetMetaData.GetSocketIoTimeInMillis(); - file_io_time_ += packetMetaData.GetFileIoTimeInMillis(); -} - -std::string ThroughputRecorder::ToString(PayloadType type) { - switch (type) { - case PayloadType::kBytes: - return std::string("Bytes"); - case PayloadType::kStream: - return std::string("Stream"); - case PayloadType::kFile: - return std::string("File"); - case PayloadType::kUnknown: - return std::string("Unknown"); - } -} - -// Inplementation for ThroughputRecorderContainer - -void ThroughputRecorderContainer::Shutdown() { - MutexLock lock(&mutex_); - VLOG(1) << __func__ << ". Num of Instance:" << throughput_recorders_.size(); - for (auto& throughput_recorder : throughput_recorders_) { - VLOG(1) << "Stop instance: " << throughput_recorder.second; - throughput_recorder.second->Stop(); - delete throughput_recorder.second; - } - throughput_recorders_.clear(); -} - -ThroughputRecorder* ThroughputRecorderContainer::GetTPRecorder( - const int64_t payload_id, PayloadDirection payload_direction) { - MutexLock lock(&mutex_); - auto it = throughput_recorders_.find( - std::pair(payload_id, payload_direction)); - if (it == throughput_recorders_.end()) { - auto instance = new ThroughputRecorder(payload_id); - std::string direction = - (payload_direction == PayloadDirection::INCOMING_PAYLOAD) ? "; Receive" - : "; Send"; - VLOG(1) << "Add ThroughputRecorder instance : " << instance - << " for payload_id:" << payload_id << direction; - throughput_recorders_.emplace( - std::pair(payload_id, payload_direction), - instance); - return instance; - } - - return it->second; -} - -void ThroughputRecorderContainer::StopTPRecorder( - const int64_t payload_id, PayloadDirection payload_direction) { - MutexLock lock(&mutex_); - std::string direction = - (payload_direction == PayloadDirection::INCOMING_PAYLOAD) ? "; Receive" - : "; Send"; - auto it = throughput_recorders_.find( - std::pair(payload_id, payload_direction)); - if (it != throughput_recorders_.end()) { - VLOG(1) << "Found and stop/delete ThroughputRecorder instance : " - << &(it->second) << " for payload_id:" << payload_id << direction; - it->second->Stop(); - delete it->second; - throughput_recorders_.erase( - std::pair(payload_id, payload_direction)); - return; - } - VLOG(1) << "No ThroughputRecorder found for :" << payload_id; -} - -int ThroughputRecorderContainer::GetSize() { - MutexLock lock(&mutex_); - return throughput_recorders_.size(); -} - -} // namespace analytics -} // namespace nearby diff --git a/connections/implementation/analytics/throughput_recorder.h b/connections/implementation/analytics/throughput_recorder.h deleted file mode 100644 index e6dcdc05..00000000 --- a/connections/implementation/analytics/throughput_recorder.h +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2022-2023 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 NEARBY_CONNECTIONS_IMPLEMENTATION_ANALYTICS_THROUGHPUT_RECORDER_H_ -#define NEARBY_CONNECTIONS_IMPLEMENTATION_ANALYTICS_THROUGHPUT_RECORDER_H_ - -#include -#include -#include - -#include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" -#include "absl/time/time.h" -#include "connections/implementation/analytics/packet_meta_data.h" -#include "connections/payload_type.h" -#include "internal/platform/mutex.h" -#include "proto/connections_enums.pb.h" - -namespace nearby { -namespace analytics { - -// The following aliases are only for users' convenience. -using ::location::nearby::proto::connections::Medium; -using ::nearby::connections::PayloadType; -// Enum to represent if a payload is incoming or outgoing. -using ::nearby::connections::PayloadDirection; - -class ThroughputRecorder { - public: - explicit ThroughputRecorder(int64_t payload_id); - ~ThroughputRecorder() = default; - - void Start(PayloadType payload_type, PayloadDirection payload_direction); - bool Stop() ABSL_LOCKS_EXCLUDED(mutex_); - static int CalculateThroughputKBps(int64_t total_byte_size, - int64_t total_millis); - static int CalculateThroughputMBps(int throughputKBps); - - class Throughput { - public: - Throughput() = default; - ~Throughput() = default; - Throughput(Medium medium, absl::Time start_timestamp, - PayloadType payload_type, PayloadDirection payload_direction) - : medium_(medium), - start_timestamp_(start_timestamp), - payload_type_(payload_type), - payload_direction_(payload_direction) {} - - void Add(int frame_size, int64_t file_io_time, int64_t encryption_time, - int64_t socket_io_time); - - void SetLastTimestamp(absl::Time time_stamp) { - last_timestamp_ = time_stamp; - } - - int64_t GetTotalByteSize() { return total_byte_size_; } - - bool dump(); - - private: - Medium medium_; - absl::Time start_timestamp_; - PayloadType payload_type_; - int64_t total_byte_size_ = 0; - absl::Time last_timestamp_; - PayloadDirection payload_direction_ = PayloadDirection::INCOMING_PAYLOAD; - int64_t file_io_time_ = 0; - int64_t encryption_time_ = 0; - int64_t socket_io_time_ = 0; - }; - - Throughput& GetThroughput(Medium medium, int64_t duration_millis); - int GetThroughputsSize(); - int GetThroughputKbps(); - int64_t GetDurationMillis(); - void OnFrameSent(Medium medium, PacketMetaData& packetMetaData); - void OnFrameReceived(Medium medium, PacketMetaData& packetMetaData); - void MarkAsSuccess(); - - private: - void CalculateDurationTimes(PacketMetaData packetMetaData); - static std::string ToString(PayloadType type); - - Mutex mutex_; - int64_t payload_id_ = 0; - absl::Time start_timestamp_; - PayloadType payload_type_ = PayloadType::kUnknown; - PayloadDirection payload_direction_ = PayloadDirection::INCOMING_PAYLOAD; - absl::flat_hash_map throughputs_; - bool success_ = false; - - int64_t file_io_time_ = 0; - int64_t encryption_time_ = 0; - int64_t socket_io_time_ = 0; - int64_t duration_millis_ = 0; - int throughput_kbps_ = 0; -}; - -class ThroughputRecorderContainer { - public: - ThroughputRecorderContainer(const ThroughputRecorderContainer&) = delete; - ThroughputRecorderContainer& operator=(const ThroughputRecorderContainer&) = - delete; - - static ThroughputRecorderContainer& GetInstance(); - void Shutdown() ABSL_LOCKS_EXCLUDED(mutex_); - - ThroughputRecorder* GetTPRecorder(int64_t payload_id, - PayloadDirection payload_direction) - ABSL_LOCKS_EXCLUDED(mutex_); - void StopTPRecorder(int64_t payload_id, PayloadDirection payload_direction) - ABSL_LOCKS_EXCLUDED(mutex_); - int GetSize() ABSL_LOCKS_EXCLUDED(mutex_); - - private: - // This is a singleton object, for which destructor will never be called. - // Constructor will be invoked once from Instance() static method. - // Object is create in-place (with a placement new) to guarantee that - // destructor is not scheduled for execution at exit. - ThroughputRecorderContainer() = default; - ~ThroughputRecorderContainer() = default; - - Mutex mutex_; - // std::pair for - absl::flat_hash_map, ThroughputRecorder*> - throughput_recorders_ ABSL_GUARDED_BY(mutex_); -}; - -} // namespace analytics -} // namespace nearby - -#endif // NEARBY_CONNECTIONS_IMPLEMENTATION_ANALYTICS_THROUGHPUT_RECORDER_H_ diff --git a/connections/implementation/analytics/throughput_recorder_test.cc b/connections/implementation/analytics/throughput_recorder_test.cc deleted file mode 100644 index 8b31add3..00000000 --- a/connections/implementation/analytics/throughput_recorder_test.cc +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2022-2023 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/analytics/throughput_recorder.h" - -#include - -#include -#include - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "internal/platform/logging.h" -#include "proto/connections_enums.pb.h" - -namespace nearby { -namespace analytics { -namespace { -// TODO(b/246693797): Add unit tests coverage for throughput recorder code - -constexpr int64_t kPayloadIdA = 123456789; -constexpr int64_t kPayloadIdB = 987654321; -constexpr int kFrameSize = 10 * 64 * 1024; -constexpr int64_t kTotalByteSize1GB = 1024 * 1024 * 1024; -constexpr int64_t kTotalMillis10Sec = 10 * 1000; -constexpr int kTPResultKBPerSec = 1024 * 1024 / 10; -constexpr int kTPKBPerSec = 100 * 1024; -constexpr int kTPResultMBPerSec = 100; - -// class ThroughputRecorderTest : public testing::Test { -class ThroughputRecorderTest : public testing::TestWithParam { - protected: - ThroughputRecorderTest() = default; - ~ThroughputRecorderTest() override { - ThroughputRecorderContainer::GetInstance().Shutdown(); - } - - ThroughputRecorderContainer& tp_recorder_container_ = - ThroughputRecorderContainer::GetInstance(); -}; - -INSTANTIATE_TEST_SUITE_P(ParametrisedTestThroughputRecorderTest, - ThroughputRecorderTest, testing::Values(true, false)); - -TEST(ThroughputRecorder, CalculateThroughputKBps) { - EXPECT_EQ(ThroughputRecorder::CalculateThroughputKBps(kTotalByteSize1GB, - kTotalMillis10Sec), - kTPResultKBPerSec); - EXPECT_EQ(ThroughputRecorder::CalculateThroughputKBps(kTotalByteSize1GB, 0), - 0); -} - -TEST(ThroughputRecorder, CalculateThroughputMBps) { - EXPECT_EQ(ThroughputRecorder::CalculateThroughputMBps(kTPKBPerSec), - kTPResultMBPerSec); -} - -TEST(ThroughputRecorderContainer, InstanceCreate_ContainerSize) { - ThroughputRecorderContainer& TPRecorderContainer = - ThroughputRecorderContainer::GetInstance(); - TPRecorderContainer.GetTPRecorder(kPayloadIdA, - PayloadDirection::OUTGOING_PAYLOAD); - TPRecorderContainer.GetTPRecorder(kPayloadIdB, - PayloadDirection::INCOMING_PAYLOAD); - EXPECT_EQ(ThroughputRecorderContainer::GetInstance().GetSize(), 2); - ThroughputRecorderContainer::GetInstance().Shutdown(); - EXPECT_EQ(ThroughputRecorderContainer::GetInstance().GetSize(), 0); -} - -TEST_F(ThroughputRecorderTest, OnFrameSentSaveTransferredSize) { - auto TPRecorder = tp_recorder_container_.GetTPRecorder( - kPayloadIdA, PayloadDirection::OUTGOING_PAYLOAD); - TPRecorder->Start(PayloadType::kFile, PayloadDirection::OUTGOING_PAYLOAD); - - PacketMetaData packet_meta_data; - packet_meta_data.SetPacketSize(kFrameSize); - TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE, - packet_meta_data); - TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE, - packet_meta_data); - TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE, - packet_meta_data); - - auto throughput = - TPRecorder->GetThroughput(location::nearby::proto::connections::BLE, 0); - EXPECT_EQ(throughput.GetTotalByteSize(), kFrameSize * 3); -} - -TEST_F(ThroughputRecorderTest, OnIgnoreUnkownPaylaodType) { - auto TPRecorder = tp_recorder_container_.GetTPRecorder( - kPayloadIdA, PayloadDirection::OUTGOING_PAYLOAD); - TPRecorder->Start(PayloadType::kUnknown, PayloadDirection::OUTGOING_PAYLOAD); - - PacketMetaData packet_meta_data; - TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE, - packet_meta_data); - EXPECT_EQ(TPRecorder->GetThroughputsSize(), 0); - - TPRecorder->Start(PayloadType::kUnknown, PayloadDirection::INCOMING_PAYLOAD); - TPRecorder->OnFrameReceived(location::nearby::proto::connections::BLE, - packet_meta_data); - EXPECT_EQ(TPRecorder->GetThroughputsSize(), 0); -} - -TEST_P(ThroughputRecorderTest, OnFrameSentStopAndDump) { - auto TPRecorder = tp_recorder_container_.GetTPRecorder( - kPayloadIdA, PayloadDirection::OUTGOING_PAYLOAD); - TPRecorder->Start(PayloadType::kFile, PayloadDirection::OUTGOING_PAYLOAD); - - PacketMetaData packet_meta_data; - packet_meta_data.SetPacketSize(kFrameSize); - packet_meta_data.StartFileIo(); - absl::SleepFor(absl::Milliseconds(5)); - packet_meta_data.StopFileIo(); - packet_meta_data.StartEncryption(); - absl::SleepFor(absl::Milliseconds(6)); - packet_meta_data.StopEncryption(); - packet_meta_data.StartSocketIo(); - absl::SleepFor(absl::Milliseconds(7)); - packet_meta_data.StopSocketIo(); - TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE, - packet_meta_data); - EXPECT_EQ(TPRecorder->GetDurationMillis(), - packet_meta_data.GetEncryptionTimeInMillis() + - packet_meta_data.GetFileIoTimeInMillis() + - packet_meta_data.GetSocketIoTimeInMillis()); - - packet_meta_data.SetPacketSize(kFrameSize); - packet_meta_data.StartFileIo(); - absl::SleepFor(absl::Milliseconds(15)); - packet_meta_data.StopFileIo(); - packet_meta_data.StartEncryption(); - absl::SleepFor(absl::Milliseconds(16)); - packet_meta_data.StopEncryption(); - packet_meta_data.StartSocketIo(); - absl::SleepFor(absl::Milliseconds(17)); - packet_meta_data.StopSocketIo(); - TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE, - packet_meta_data); - - if (GetParam() == true) { - LOG(INFO) << "MarkAsSuccess"; - TPRecorder->MarkAsSuccess(); - } - EXPECT_TRUE(TPRecorder->Stop()); - EXPECT_NE(TPRecorder->GetThroughputKbps(), 0); -} - -TEST_F(ThroughputRecorderTest, OnFrameSentStopAndDumpForMultiMeadium) { - auto TPRecorder = tp_recorder_container_.GetTPRecorder( - kPayloadIdA, PayloadDirection::OUTGOING_PAYLOAD); - TPRecorder->Start(PayloadType::kFile, PayloadDirection::OUTGOING_PAYLOAD); - - PacketMetaData packet_meta_data1; - packet_meta_data1.SetPacketSize(kFrameSize); - packet_meta_data1.StartFileIo(); - absl::SleepFor(absl::Milliseconds(5)); - packet_meta_data1.StopFileIo(); - packet_meta_data1.StartEncryption(); - absl::SleepFor(absl::Milliseconds(6)); - packet_meta_data1.StopEncryption(); - packet_meta_data1.StartSocketIo(); - absl::SleepFor(absl::Milliseconds(7)); - packet_meta_data1.StopSocketIo(); - TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE, - packet_meta_data1); - - PacketMetaData packet_meta_data2; - packet_meta_data2.SetPacketSize(kFrameSize); - packet_meta_data2.StartFileIo(); - absl::SleepFor(absl::Milliseconds(15)); - packet_meta_data2.StopFileIo(); - packet_meta_data2.StartEncryption(); - absl::SleepFor(absl::Milliseconds(16)); - packet_meta_data2.StopEncryption(); - packet_meta_data2.StartSocketIo(); - absl::SleepFor(absl::Milliseconds(17)); - packet_meta_data2.StopSocketIo(); - TPRecorder->OnFrameSent(location::nearby::proto::connections::WIFI_LAN, - packet_meta_data2); - - TPRecorder->MarkAsSuccess(); - EXPECT_TRUE(TPRecorder->Stop()); - EXPECT_NE(TPRecorder->GetThroughputKbps(), 0); -} - -TEST_F(ThroughputRecorderTest, OnFrameReceivedCheckDurationMillis) { - auto TPRecorder = tp_recorder_container_.GetTPRecorder( - kPayloadIdA, PayloadDirection::INCOMING_PAYLOAD); - TPRecorder->Start(PayloadType::kFile, PayloadDirection::INCOMING_PAYLOAD); - - PacketMetaData packet_meta_data; - packet_meta_data.SetPacketSize(kFrameSize); - packet_meta_data.StartFileIo(); - absl::SleepFor(absl::Milliseconds(5)); - packet_meta_data.StopFileIo(); - packet_meta_data.StartEncryption(); - absl::SleepFor(absl::Milliseconds(6)); - packet_meta_data.StopEncryption(); - packet_meta_data.StartSocketIo(); - absl::SleepFor(absl::Milliseconds(7)); - packet_meta_data.StopSocketIo(); - TPRecorder->OnFrameReceived(location::nearby::proto::connections::BLE, - packet_meta_data); - EXPECT_EQ(TPRecorder->GetDurationMillis(), - packet_meta_data.GetEncryptionTimeInMillis() + - packet_meta_data.GetFileIoTimeInMillis() + - packet_meta_data.GetSocketIoTimeInMillis()); -} - -TEST_F(ThroughputRecorderTest, OnTPRecorderNotStarted) { - auto TPRecorder = tp_recorder_container_.GetTPRecorder( - kPayloadIdA, PayloadDirection::OUTGOING_PAYLOAD); - auto throughput = - TPRecorder->GetThroughput(location::nearby::proto::connections::BLE, 0); - EXPECT_FALSE(throughput.dump()); -} - -} // namespace -} // namespace analytics -} // namespace nearby diff --git a/connections/implementation/awdl_bwu_handler_test.cc b/connections/implementation/awdl_bwu_handler_test.cc new file mode 100644 index 00000000..627a7af6 --- /dev/null +++ b/connections/implementation/awdl_bwu_handler_test.cc @@ -0,0 +1,576 @@ +// Copyright 2026 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/awdl_bwu_handler.h" + +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "connections/implementation/awdl_endpoint_channel.h" +#include "connections/implementation/bwu_handler.h" +#include "connections/implementation/client_proxy.h" +#include "connections/implementation/mediums/awdl.h" +#include "connections/implementation/mediums/mediums.h" +#include "connections/strategy.h" +#include "internal/analytics/mock_event_logger.h" +#include "internal/analytics/sharing_log_matchers.h" +#include "internal/platform/awdl.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/cancellation_flag.h" +#include "internal/platform/count_down_latch.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/awdl.h" +#include "internal/platform/implementation/platform.h" +#include "internal/platform/implementation/psk_info.h" +#include "internal/platform/input_stream.h" +#include "internal/platform/medium_environment.h" +#include "internal/platform/mock_input_stream.h" +#include "internal/platform/mock_output_stream.h" +#include "internal/platform/nsd_service_info.h" +#include "internal/platform/output_stream.h" +#include "internal/proto/analytics/connections_log.pb.h" + +namespace nearby { + +class MockAwdlSocket : public api::AwdlSocket { + public: + MOCK_METHOD(InputStream&, GetInputStream, (), (override)); + MOCK_METHOD(OutputStream&, GetOutputStream, (), (override)); + MOCK_METHOD(Exception, Close, (), (override)); +}; + +class MockAwdlServerSocket : public api::AwdlServerSocket { + public: + MOCK_METHOD(std::string, GetIPAddress, (), (const, override)); + MOCK_METHOD(int, GetPort, (), (const, override)); + MOCK_METHOD(std::unique_ptr, Accept, (), (override)); + MOCK_METHOD(Exception, Close, (), (override)); +}; + +class MockAwdlMedium : public api::AwdlMedium { + public: + MOCK_METHOD(bool, IsNetworkConnected, (), (const, override)); + MOCK_METHOD(bool, StartAdvertising, (const NsdServiceInfo& nsd_service_info), + (override)); + MOCK_METHOD(bool, StopAdvertising, (const NsdServiceInfo& nsd_service_info), + (override)); + MOCK_METHOD(bool, StartDiscovery, + (const std::string& service_type, + DiscoveredServiceCallback callback), + (override)); + MOCK_METHOD(bool, StopDiscovery, (const std::string& service_type), + (override)); + MOCK_METHOD(std::unique_ptr, ConnectToService, + (const NsdServiceInfo& remote_service_info, + CancellationFlag* cancellation_flag), + (override)); + MOCK_METHOD(std::unique_ptr, ConnectToService, + (const NsdServiceInfo& remote_service_info, + const api::PskInfo& psk_info, + CancellationFlag* cancellation_flag), + (override)); + MOCK_METHOD(std::unique_ptr, ListenForService, + (int port), (override)); + MOCK_METHOD(std::unique_ptr, ListenForService, + (const api::PskInfo& psk_info, int port), (override)); + MOCK_METHOD((std::optional>), + GetDynamicPortRange, (), (override)); +}; + +MockAwdlMedium* awdl_medium_mock = nullptr; + +namespace connections { +namespace { + +using ::location::nearby::analytics::proto::ConnectionsLog; +using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame; +using ::location::nearby::connections::OfflineFrame; +using ::location::nearby::connections::V1Frame; +using ::location::nearby::proto::connections::EventType; +using ::location::nearby::proto::connections::OperationResultCode; +using ::nearby::analytics::HasEventType; +using ::testing::_; +using ::testing::ByMove; +using ::protobuf_matchers::EqualsProto; +using ::testing::Matcher; +using ::testing::MockFunction; +using ::testing::Return; +using ::testing::ReturnRef; +using ::testing::StrictMock; + +constexpr absl::string_view kServiceId{"service_id"}; +constexpr absl::string_view kEndpointId{"endpoint_id"}; +constexpr absl::string_view kServiceName{"awdl_srv"}; +constexpr absl::string_view kServiceType{"_awdl._tcp"}; +constexpr absl::string_view kPassword{"password123"}; +constexpr absl::string_view kChannelName{"channel_name"}; + +class AwdlBwuHandlerTest : public ::testing::Test { + protected: + AwdlBwuHandlerTest() + : handler_(mediums_, incoming_connection_callback_.AsStdFunction()) {} + + void SetUp() override { + // By default, network is connected. + ON_CALL(*awdl_medium_mock, IsNetworkConnected()) + .WillByDefault(Return(true)); + } + + Mediums mediums_; + MockFunction)> + incoming_connection_callback_; + AwdlBwuHandler handler_; + nearby::analytics::MockEventLogger mock_event_logger_; + MockInputStream mock_input_stream_; + MockOutputStream mock_output_stream_; +}; + +TEST_F(AwdlBwuHandlerTest, + CreateUpgradedEndpointChannel_InvalidCredentials_Fails) { + ClientProxy client(&mock_event_logger_); + BandwidthUpgradeNegotiationFrame::UpgradePathInfo path_info; + path_info.mutable_awdl_credentials(); // Empty credentials + + auto result = + static_cast(&handler_)->CreateUpgradedEndpointChannel( + &client, "service_id", "endpoint_id", path_info); + + ASSERT_TRUE(result.has_error()); + EXPECT_EQ(result.error().operation_result_code().value(), + OperationResultCode::CONNECTIVITY_AWDL_INVALID_CREDENTIAL); +} + +TEST_F(AwdlBwuHandlerTest, CreateUpgradedEndpointChannel_Success) { + ClientProxy client(&mock_event_logger_); + client.AddCancellationFlag(std::string(kEndpointId)); + MockInputStream input_stream; + MockOutputStream output_stream; + auto awdl_socket = std::make_unique(); + EXPECT_CALL(*awdl_socket, GetInputStream()) + .WillRepeatedly(ReturnRef(input_stream)); + EXPECT_CALL(*awdl_socket, GetOutputStream()) + .WillRepeatedly(ReturnRef(output_stream)); + + EXPECT_CALL(*awdl_medium_mock, StartDiscovery(_, _)) + .WillOnce([](const std::string& service_type, + api::AwdlMedium::DiscoveredServiceCallback callback) { + NsdServiceInfo service_info; + service_info.SetServiceName(std::string(kServiceName)); + service_info.SetServiceType(service_type); + if (callback.service_discovered_cb) { + NsdServiceInfo service_info_copy = service_info; + callback.service_discovered_cb(service_info_copy); + } + return true; + }); + EXPECT_CALL(*awdl_medium_mock, StopDiscovery(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(*awdl_medium_mock, ConnectToService(_, _, _)) + .WillOnce(Return(ByMove(std::move(awdl_socket)))); + + BandwidthUpgradeNegotiationFrame::UpgradePathInfo path_info; + auto* credentials = path_info.mutable_awdl_credentials(); + credentials->set_service_name(kServiceName); + credentials->set_service_type(kServiceType); + credentials->set_password(kPassword); + + auto result = + static_cast(&handler_)->CreateUpgradedEndpointChannel( + &client, std::string(kServiceId), std::string(kEndpointId), + path_info); + + EXPECT_TRUE(result.has_value()); +} + +TEST_F(AwdlBwuHandlerTest, + InitializeUpgradedMediumForEndpoint_StartAcceptingConnectionsFails) { + MediumEnvironment::Instance().Start({.use_simulated_clock = true}); + ClientProxy client(&mock_event_logger_); + client.AddCancellationFlag(std::string(kEndpointId)); + + EXPECT_CALL(*awdl_medium_mock, ListenForService(_, 0)) + .WillOnce(Return(ByMove(nullptr))); + + ByteArray result = handler_.InitializeUpgradedMediumForEndpoint( + &client, std::string(kServiceId), std::string(kEndpointId)); + + EXPECT_TRUE(result.Empty()); + MediumEnvironment::Instance().Stop(); +} + +TEST_F(AwdlBwuHandlerTest, InitializeUpgradedMediumForEndpoint_Success) { + MediumEnvironment::Instance().Start({.use_simulated_clock = true}); + // The reason for putting ClientProxy inside a C++ { } block so it destructs + // before the simulated clock is restored. Otherwise, the simulated clock + // would stopped before ClientProxy went out of scope, causing its destructor + // to log the session duration using the real system clock. If 1 or more + // real-world milliseconds elapsed between the test start and test end, this + // duration evaluated to something > 0. + { + ClientProxy client(&mock_event_logger_); + client.GetAnalyticsRecorder().OnStartAdvertising( + Strategy::kP2pPointToPoint, + {location::nearby::proto::connections::Medium::BLUETOOTH}, + /*advertising_metadata_params=*/nullptr); + client.GetAnalyticsRecorder().OnBandwidthUpgradeStarted( + std::string(kEndpointId), + location::nearby::proto::connections::Medium::BLUETOOTH, + location::nearby::proto::connections::Medium::AWDL, + location::nearby::proto::connections::ConnectionAttemptDirection:: + OUTGOING, + /*connection_token=*/""); + client.AddCancellationFlag(std::string(kEndpointId)); + + auto awdl_server_socket = std::make_unique(); + auto* awdl_server_socket_ptr = awdl_server_socket.get(); + EXPECT_CALL(*awdl_server_socket_ptr, GetPort()) + .WillRepeatedly(Return(8080)); + EXPECT_CALL(*awdl_server_socket_ptr, Accept()) + .WillOnce(Return(ByMove(nullptr))); + EXPECT_CALL(*awdl_server_socket_ptr, Close()) + .WillRepeatedly(Return(Exception{Exception::kSuccess})); + + EXPECT_CALL(*awdl_medium_mock, ListenForService(_, 0)) + .WillOnce(Return(ByMove(std::move(awdl_server_socket)))); + + std::string captured_service_name; + std::string captured_service_type; + EXPECT_CALL(*awdl_medium_mock, StartAdvertising(_)) + .WillOnce([&](const NsdServiceInfo& nsd_service_info) { + captured_service_name = nsd_service_info.GetServiceName(); + captured_service_type = nsd_service_info.GetServiceType(); + return true; + }); + + ByteArray result = handler_.InitializeUpgradedMediumForEndpoint( + &client, std::string(kServiceId), std::string(kEndpointId)); + + EXPECT_FALSE(result.Empty()); + OfflineFrame expected_frame; + expected_frame.set_version(OfflineFrame::V1); + expected_frame.mutable_v1()->set_type( + V1Frame::BANDWIDTH_UPGRADE_NEGOTIATION); + auto* bwu_frame = + expected_frame.mutable_v1()->mutable_bandwidth_upgrade_negotiation(); + bwu_frame->set_event_type( + BandwidthUpgradeNegotiationFrame::UPGRADE_PATH_AVAILABLE); + auto* upgrade_path_info = bwu_frame->mutable_upgrade_path_info(); + upgrade_path_info->set_medium( + BandwidthUpgradeNegotiationFrame::UpgradePathInfo::AWDL); + upgrade_path_info->set_supports_client_introduction_ack(true); + upgrade_path_info->set_supports_disabling_encryption(true); + auto* awdl_credentials = upgrade_path_info->mutable_awdl_credentials(); + awdl_credentials->set_service_name(captured_service_name); + awdl_credentials->set_service_type(captured_service_type); + + // The password is automatically generated and set in the handle start, we + // can obtain it from the credential Since we mock StartAcceptingConnections + // instead of using real awdl, GetPskInfo won't work perfectly. However + // InitializeUpgradedMediumForEndpoint internally calls + // parser::ForBwuAwdlPathAvailable which puts the generated password. We + // will extract it from result directly to build expected frame. + OfflineFrame result_frame; + EXPECT_TRUE(result_frame.ParseFromString(std::string(result))); + awdl_credentials->set_password(result_frame.v1() + .bandwidth_upgrade_negotiation() + .upgrade_path_info() + .awdl_credentials() + .password()); + + EXPECT_THAT(result_frame, EqualsProto(expected_frame)); + + constexpr absl::string_view kClientSessionLog = R"pb( + event_type: CLIENT_SESSION + client_session { duration_millis: 0 } + version: "v1.5.0" + )pb"; + constexpr absl::string_view kExpectedUpgradeLog = R"pb( + event_type: CLIENT_SESSION + client_session { + duration_millis: 0 + strategy_session { + duration_millis: 0 + strategy: P2P_POINT_TO_POINT + role: ADVERTISER + advertising_phase { + duration_millis: 0 + medium: BLUETOOTH + advertising_metadata { + supports_extended_ble_advertisements: false + connected_ap_frequency: 0 + supports_nfc_technology: false + } + stop_reason: FINISH_SESSION_STOP_ADVERTISING + } + upgrade_attempt { + direction: OUTGOING + duration_millis: 0 + from_medium: BLUETOOTH + to_medium: AWDL + upgrade_result: UNFINISHED_ERROR + error_stage: UPGRADE_UNFINISHED + connection_token: "" + operation_result { + result_category: CATEGORY_DEVICE_STATE_ERROR + result_code: DEVICE_STATE_ERROR_UNFINISHED_UPGRADE_ATTEMPTS + } + } + } + } + version: "v1.5.0" + )pb"; + EXPECT_CALL(mock_event_logger_, + Log(Matcher( + HasEventType(EventType::STOP_STRATEGY_SESSION)))) + .Times(1); + EXPECT_CALL(mock_event_logger_, + Log(Matcher( + HasEventType(EventType::STOP_CLIENT_SESSION)))) + .Times(3); + EXPECT_CALL(mock_event_logger_, + Log(Matcher( + HasEventType(EventType::START_CLIENT_SESSION)))) + .Times(3); + EXPECT_CALL( + mock_event_logger_, + Log(Matcher(EqualsProto(kClientSessionLog)))) + .Times(2); + EXPECT_CALL( + mock_event_logger_, + Log(Matcher(EqualsProto(kExpectedUpgradeLog)))); + // Flush pending logs. + client.GetAnalyticsRecorder().LogSession(); + handler_.RevertInitiatorState(); + } + MediumEnvironment::Instance().Stop(); +} + +TEST_F(AwdlBwuHandlerTest, OnIncomingAwdlConnection_Success) { + MediumEnvironment::Instance().Start({.use_simulated_clock = true}); + ClientProxy client(&mock_event_logger_); + client.AddCancellationFlag(std::string(kEndpointId)); + + auto awdl_server_socket = std::make_unique(); + auto* awdl_server_socket_ptr = awdl_server_socket.get(); + EXPECT_CALL(*awdl_server_socket_ptr, Close()) + .WillRepeatedly(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(*awdl_server_socket_ptr, Accept()) + .WillOnce([this]() { + auto awdl_socket = std::make_unique(); + EXPECT_CALL(*awdl_socket, GetInputStream()) + .WillRepeatedly(ReturnRef(mock_input_stream_)); + EXPECT_CALL(*awdl_socket, GetOutputStream()) + .WillRepeatedly(ReturnRef(mock_output_stream_)); + return awdl_socket; + }) + .WillRepeatedly([]() { + absl::SleepFor(absl::Seconds(5)); + return nullptr; + }); + + EXPECT_CALL(*awdl_medium_mock, ListenForService(_, 0)) + .WillOnce(Return(ByMove(std::move(awdl_server_socket)))); + EXPECT_CALL(*awdl_medium_mock, StartAdvertising(_)).WillOnce(Return(true)); + + CountDownLatch latch(1); + EXPECT_CALL(incoming_connection_callback_, Call(&client, _)) + .WillOnce([&latch](ClientProxy* client, + std::unique_ptr + connection) { latch.CountDown(); }); + + ByteArray result = handler_.InitializeUpgradedMediumForEndpoint( + &client, std::string(kServiceId), std::string(kEndpointId)); + EXPECT_FALSE(result.Empty()); + + auto await_result = latch.Await(absl::Seconds(5)); + EXPECT_TRUE(await_result.ok()); + + handler_.RevertInitiatorState(); + MediumEnvironment::Instance().Stop(); +} + +TEST_F(AwdlBwuHandlerTest, AwdlIncomingSocket_ToStringAndClose) { + MediumEnvironment::Instance().Start({.use_simulated_clock = true}); + ClientProxy client(&mock_event_logger_); + client.AddCancellationFlag(std::string(kEndpointId)); + + auto awdl_server_socket = std::make_unique(); + auto* awdl_server_socket_ptr = awdl_server_socket.get(); + EXPECT_CALL(*awdl_server_socket_ptr, Close()) + .WillRepeatedly(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(*awdl_server_socket_ptr, Accept()) + .WillOnce([this]() { + auto awdl_socket = std::make_unique(); + EXPECT_CALL(*awdl_socket, GetInputStream()) + .WillRepeatedly(ReturnRef(mock_input_stream_)); + EXPECT_CALL(*awdl_socket, GetOutputStream()) + .WillRepeatedly(ReturnRef(mock_output_stream_)); + EXPECT_CALL(*awdl_socket, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + return awdl_socket; + }) + .WillRepeatedly([]() { + absl::SleepFor(absl::Seconds(5)); + return nullptr; + }); + + EXPECT_CALL(*awdl_medium_mock, ListenForService(_, 0)) + .WillOnce(Return(ByMove(std::move(awdl_server_socket)))); + EXPECT_CALL(*awdl_medium_mock, StartAdvertising(_)).WillOnce(Return(true)); + + CountDownLatch latch(1); + EXPECT_CALL(incoming_connection_callback_, Call(&client, _)) + .WillOnce([&latch](ClientProxy* client, + std::unique_ptr + connection) { + EXPECT_FALSE(connection->socket->ToString().empty()); + connection->socket->Close(); + latch.CountDown(); + }); + + ByteArray result = handler_.InitializeUpgradedMediumForEndpoint( + &client, std::string(kServiceId), std::string(kEndpointId)); + EXPECT_FALSE(result.Empty()); + + auto await_result = latch.Await(absl::Seconds(5)); + EXPECT_TRUE(await_result.ok()); + + handler_.RevertInitiatorState(); + MediumEnvironment::Instance().Stop(); +} + +TEST_F(AwdlBwuHandlerTest, HandleRevertInitiatorStateForService_Success) { + MediumEnvironment::Instance().Start({.use_simulated_clock = true}); + ClientProxy client(&mock_event_logger_); + client.AddCancellationFlag(std::string(kEndpointId)); + + auto awdl_server_socket = std::make_unique(); + auto* awdl_server_socket_ptr = awdl_server_socket.get(); + EXPECT_CALL(*awdl_server_socket_ptr, Close()) + .WillRepeatedly(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(*awdl_server_socket_ptr, Accept()).WillRepeatedly([]() { + absl::SleepFor(absl::Seconds(10)); + return nullptr; + }); + + EXPECT_CALL(*awdl_medium_mock, ListenForService(_, 0)) + .WillOnce(Return(ByMove(std::move(awdl_server_socket)))); + EXPECT_CALL(*awdl_medium_mock, StartAdvertising(_)).WillOnce(Return(true)); + EXPECT_CALL(*awdl_medium_mock, StopAdvertising(_)).WillOnce(Return(true)); + + handler_.InitializeUpgradedMediumForEndpoint(&client, std::string(kServiceId), + std::string(kEndpointId)); + + handler_.RevertInitiatorState(); + MediumEnvironment::Instance().Stop(); +} + +TEST_F(AwdlBwuHandlerTest, GetUpgradeMedium_ReturnsAwdl) { + auto* bwu_handler = static_cast(&handler_); + EXPECT_EQ(bwu_handler->GetUpgradeMedium(), + location::nearby::proto::connections::Medium::AWDL); +} + +TEST_F(AwdlBwuHandlerTest, OnEndpointDisconnect_DoesNotCrash) { + ClientProxy client(&mock_event_logger_); + auto* bwu_handler = static_cast(&handler_); + // This method is a no-op, just verifying it doesn't crash. + bwu_handler->OnEndpointDisconnect(&client, std::string(kEndpointId)); +} + +class AwdlEndpointChannelTest : public ::testing::Test { + protected: + void SetUp() override { + ON_CALL(*awdl_medium_mock, IsNetworkConnected()) + .WillByDefault(Return(true)); + mock_socket_ = std::make_unique>(); + EXPECT_CALL(*mock_socket_, GetInputStream()) + .WillRepeatedly(ReturnRef(mock_input_stream_)); + EXPECT_CALL(*mock_socket_, GetOutputStream()) + .WillRepeatedly(ReturnRef(mock_output_stream_)); + } + + std::unique_ptr> mock_socket_; + StrictMock mock_input_stream_; + StrictMock mock_output_stream_; + Awdl awdl_medium_; +}; + +TEST_F(AwdlEndpointChannelTest, CloseImpl_StopsDiscoveryIfOutgoing) { + EXPECT_CALL(*mock_socket_, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(mock_input_stream_, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(mock_output_stream_, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(*awdl_medium_mock, StartDiscovery(_, _)).WillOnce(Return(true)); + EXPECT_CALL(*awdl_medium_mock, StopDiscovery(_)).WillOnce(Return(true)); + awdl_medium_.StartDiscovery(std::string(kServiceId), {}); + + AwdlEndpointChannel channel( + std::string(kServiceId), std::string(kChannelName), + AwdlSocket(std::move(mock_socket_)), &awdl_medium_, + /*is_outgoing=*/true); + + channel.Close(location::nearby::proto::connections::DisconnectionReason:: + UNKNOWN_DISCONNECTION_REASON); +} + +TEST_F(AwdlEndpointChannelTest, CloseImpl_DoesNotStopDiscoveryIfIncoming) { + EXPECT_CALL(*mock_socket_, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(mock_input_stream_, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(mock_output_stream_, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + + AwdlEndpointChannel channel( + std::string(kServiceId), std::string(kChannelName), + AwdlSocket(std::move(mock_socket_)), &awdl_medium_, + /*is_outgoing=*/false); + + channel.Close(location::nearby::proto::connections::DisconnectionReason:: + UNKNOWN_DISCONNECTION_REASON); +} + +TEST_F(AwdlEndpointChannelTest, EnableMultiplexSocket_CallsSocket) { + AwdlEndpointChannel channel( + std::string(kServiceId), std::string(kChannelName), + AwdlSocket(std::move(mock_socket_)), &awdl_medium_, + /*is_outgoing=*/true); + + EXPECT_TRUE(channel.EnableMultiplexSocket()); +} + +} // namespace +} // namespace connections +namespace api { + +std::unique_ptr ImplementationPlatform::CreateAwdlMedium() { + auto medium = std::make_unique(); + awdl_medium_mock = medium.get(); + return medium; +} + +} // namespace api +} // namespace nearby diff --git a/connections/implementation/base_bwu_handler.cc b/connections/implementation/base_bwu_handler.cc index 15f14aa2..29dbb8f0 100644 --- a/connections/implementation/base_bwu_handler.cc +++ b/connections/implementation/base_bwu_handler.cc @@ -20,7 +20,6 @@ #include "connections/implementation/client_proxy.h" #include "connections/implementation/service_id_constants.h" -#include "internal/platform/byte_array.h" #include "internal/platform/logging.h" namespace nearby { @@ -30,16 +29,16 @@ BaseBwuHandler::BaseBwuHandler( IncomingConnectionCallback incoming_connection_callback) : incoming_connection_callback_(std::move(incoming_connection_callback)) {} -ByteArray BaseBwuHandler::InitializeUpgradedMediumForEndpoint( +std::string BaseBwuHandler::InitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& service_id, const std::string& endpoint_id) { std::string upgrade_service_id = WrapInitiatorUpgradeServiceId(service_id); // Perform any medium-specific handling in the child class. - ByteArray upgrade_path_available_frame = + std::string upgrade_path_available_frame = HandleInitializeUpgradedMediumForEndpoint(client, upgrade_service_id, endpoint_id); - if (!upgrade_path_available_frame.Empty()) { + if (!upgrade_path_available_frame.empty()) { upgrade_service_id_to_active_endpoint_ids_[upgrade_service_id].insert( endpoint_id); } diff --git a/connections/implementation/base_bwu_handler.h b/connections/implementation/base_bwu_handler.h index 3f8e40ad..d95aacd8 100644 --- a/connections/implementation/base_bwu_handler.h +++ b/connections/implementation/base_bwu_handler.h @@ -22,7 +22,6 @@ #include "absl/container/flat_hash_set.h" #include "connections/implementation/bwu_handler.h" #include "connections/implementation/client_proxy.h" -#include "internal/platform/byte_array.h" namespace nearby { namespace connections { @@ -35,7 +34,7 @@ class BaseBwuHandler : public BwuHandler { IncomingConnectionCallback incoming_connection_callback); // BwuHandler implementation: - ByteArray InitializeUpgradedMediumForEndpoint( + std::string InitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& service_id, const std::string& endpoint_id) final; void RevertInitiatorState() final; @@ -51,7 +50,7 @@ class BaseBwuHandler : public BwuHandler { // respectively, to handle medium-specific logic. // HandleRevertInitiatorStateForService is only invoked after the last // endpoint for the service is reverted. - virtual ByteArray HandleInitializeUpgradedMediumForEndpoint( + virtual std::string HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, const std::string& endpoint_id) = 0; virtual void HandleRevertInitiatorStateForService( diff --git a/connections/implementation/base_bwu_handler_test.cc b/connections/implementation/base_bwu_handler_test.cc index da3f87ab..a8c8c0be 100644 --- a/connections/implementation/base_bwu_handler_test.cc +++ b/connections/implementation/base_bwu_handler_test.cc @@ -24,7 +24,6 @@ #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" #include "connections/implementation/service_id_constants.h" -#include "internal/platform/byte_array.h" #include "internal/platform/expected.h" namespace nearby { @@ -55,8 +54,8 @@ class BwuHandlerImpl : public BaseBwuHandler { const std::vector& handle_revert_calls() const { return handle_revert_calls_; } - void set_handle_initialize_output(ByteArray bytes) { - handle_initialize_output_ = bytes; + void set_handle_initialize_output(absl::string_view bytes) { + handle_initialize_output_ = std::string(bytes); } private: @@ -73,7 +72,7 @@ class BwuHandlerImpl : public BaseBwuHandler { const std::string& endpoint_id) final {} // BaseBwuHandler implementation: - ByteArray HandleInitializeUpgradedMediumForEndpoint( + std::string HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, const std::string& endpoint_id) final { handle_initialize_calls_.push_back({.client = client, @@ -86,7 +85,7 @@ class BwuHandlerImpl : public BaseBwuHandler { handle_revert_calls_.push_back({.service_id = upgrade_service_id}); } - ByteArray handle_initialize_output_; + std::string handle_initialize_output_; std::vector handle_initialize_calls_; std::vector handle_revert_calls_; }; @@ -95,7 +94,7 @@ TEST(BaseBwuHandlerTest, InitializeAndRevert) { ClientProxy client; BwuHandlerImpl handler; - ByteArray expected_output{"not empty"}; + absl::string_view expected_output{"not empty"}; handler.set_handle_initialize_output(expected_output); // Initialize two upgrade endpoints for service A and one for service B. @@ -150,7 +149,7 @@ TEST(BaseBwuHandlerTest, InitializeAndRevertAll) { ClientProxy client; BwuHandlerImpl handler; - ByteArray expected_output{"not empty"}; + absl::string_view expected_output{"not empty"}; handler.set_handle_initialize_output(expected_output); handler.InitializeUpgradedMediumForEndpoint(&client, /*service_id=*/"A", @@ -169,7 +168,7 @@ TEST(BaseBwuHandlerTest, Initialize_Failure_EmptyUpgradePathAvailableFrame) { ClientProxy client; BwuHandlerImpl handler; - ByteArray expected_output{}; + absl::string_view expected_output{}; handler.set_handle_initialize_output(expected_output); handler.InitializeUpgradedMediumForEndpoint(&client, /*service_id=*/"A", @@ -191,7 +190,7 @@ TEST(BaseBwuHandlerTest, Initialize_StillWorkWithUpgradeServiceIdSuffix) { ClientProxy client; BwuHandlerImpl handler; - ByteArray expected_output{"not empty"}; + absl::string_view expected_output{"not empty"}; handler.set_handle_initialize_output(expected_output); // The method should be robust and not add _another_ upgrade suffix @@ -208,7 +207,7 @@ TEST(BaseBwuHandlerTest, Revert_Failure_CantFindService) { ClientProxy client; BwuHandlerImpl handler; - ByteArray expected_output{"not empty"}; + absl::string_view expected_output{"not empty"}; handler.set_handle_initialize_output(expected_output); handler.InitializeUpgradedMediumForEndpoint(&client, /*service_id=*/"A", /*endpoint_id=*/"1"); @@ -222,7 +221,7 @@ TEST(BaseBwuHandlerTest, Revert_Failure_CantFindEndpoint) { ClientProxy client; BwuHandlerImpl handler; - ByteArray expected_output{"not empty"}; + absl::string_view expected_output{"not empty"}; handler.set_handle_initialize_output(expected_output); handler.InitializeUpgradedMediumForEndpoint(&client, /*service_id=*/"A", /*endpoint_id=*/"1"); diff --git a/connections/implementation/base_endpoint_channel.cc b/connections/implementation/base_endpoint_channel.cc index 0687d7be..05e4a303 100644 --- a/connections/implementation/base_endpoint_channel.cc +++ b/connections/implementation/base_endpoint_channel.cc @@ -40,15 +40,13 @@ #include "internal/platform/mutex_lock.h" #include "internal/platform/output_stream.h" -namespace nearby { -namespace connections { +namespace nearby::connections { namespace { -using ::location::nearby::analytics::proto::ConnectionsLog; using ::location::nearby::proto::connections::Medium::BLE; using ::location::nearby::proto::connections::Medium::BLE_L2CAP; -using DisconnectionReason = - ::location::nearby::proto::connections::DisconnectionReason; +using ::nearby::analytics::SafeDisconnectionResult; +using ::location::nearby::proto::connections::DisconnectionReason; Exception WriteInt(OutputStream* writer, std::int32_t value) { return Base64Utils::WriteInt(writer, value); @@ -92,21 +90,18 @@ BaseEndpointChannel::BaseEndpointChannel( try_count_(try_count) {} ExceptionOr BaseEndpointChannel::Read() { - PacketMetaData packet_meta_data; - return Read(packet_meta_data); -} - -ExceptionOr BaseEndpointChannel::Read( - PacketMetaData& packet_meta_data) { ByteArray result; { MutexLock lock(&reader_mutex_); - packet_meta_data.StartSocketIo(); ExceptionOr read_int; + // currently there's not way to have both kRefactorBleL2cap flag working AND + // have mediums other than ble_l2cap working. upstream may change this in the future + // + // So we have to explicitly add a condition to skip this pathway when medium is l2cap if (NearbyFlags::GetInstance().GetBoolFlag( config_package_nearby::nearby_connections_feature:: - kRefactorBleL2cap)) { + kRefactorBleL2cap) && GetMedium() == BLE_L2CAP) { ExceptionOr read_control_block_bytes = DispatchPacket(); if (!read_control_block_bytes.ok()) { LOG(WARNING) << __func__ << ": Failed to dispatch packet: " @@ -133,8 +128,6 @@ ExceptionOr BaseEndpointChannel::Read( if (!read_bytes.ok()) { return read_bytes; } - packet_meta_data.StopSocketIo(); - packet_meta_data.SetPacketSize(read_int.result() + sizeof(std::int32_t)); result = std::move(read_bytes.result()); } @@ -144,7 +137,6 @@ ExceptionOr BaseEndpointChannel::Read( if (IsEncryptionEnabledLocked()) { // If encryption is enabled, decode the message. std::string input(std::move(result)); - packet_meta_data.StartEncryption(); std::unique_ptr decrypted_data = crypto_context_->DecodeMessageFromPeer(input); if (decrypted_data) { @@ -157,7 +149,7 @@ ExceptionOr BaseEndpointChannel::Read( // and let it through if it is, otherwise message is erased. // TODO(apolyudov): verify this happens at most once per session. result = {}; - auto parsed = parser::FromBytes(ByteArray(input)); + auto parsed = parser::FromBytes(input); if (parsed.ok()) { if (parser::GetFrameType(parsed.result()) == location::nearby::connections::V1Frame::KEEP_ALIVE) { @@ -175,7 +167,6 @@ ExceptionOr BaseEndpointChannel::Read( << ": Unable to parse data as unencrypted message."; } } - packet_meta_data.StopEncryption(); if (result.Empty()) { LOG(WARNING) << __func__ << ": Unable to parse read result."; return ExceptionOr(message_exception); @@ -190,13 +181,7 @@ ExceptionOr BaseEndpointChannel::Read( return ExceptionOr(result); } -Exception BaseEndpointChannel::Write(const ByteArray& data) { - PacketMetaData packet_meta_data; - return Write(data.AsStringView(), packet_meta_data); -} - -Exception BaseEndpointChannel::Write(absl::string_view data, - PacketMetaData& packet_meta_data) { +Exception BaseEndpointChannel::Write(absl::string_view data) { { MutexLock pause_lock(&is_paused_mutex_); if (is_paused_) { @@ -217,9 +202,7 @@ Exception BaseEndpointChannel::Write(absl::string_view data, MutexLock crypto_lock(&crypto_mutex_); if (IsEncryptionEnabledLocked()) { // If encryption is enabled, encode the message. - packet_meta_data.StartEncryption(); encrypted = crypto_context_->EncodeMessageToPeer(data); - packet_meta_data.StopEncryption(); if (!encrypted) { LOG(WARNING) << __func__ << ": Failed to encrypt data."; return {Exception::kIo}; @@ -235,7 +218,6 @@ Exception BaseEndpointChannel::Write(absl::string_view data, return {Exception::kIo}; } - packet_meta_data.StartSocketIo(); Exception write_exception; if (NearbyFlags::GetInstance().GetBoolFlag( config_package_nearby::nearby_connections_feature:: @@ -262,8 +244,6 @@ Exception BaseEndpointChannel::Write(absl::string_view data, << ": Failed to flush writer: " << flush_exception.value; return flush_exception; } - packet_meta_data.StopSocketIo(); - packet_meta_data.SetPacketSize(data_size + sizeof(std::uint32_t)); } { @@ -326,7 +306,7 @@ void BaseEndpointChannel::SetAnalyticsRecorder( void BaseEndpointChannel::Close( location::nearby::proto::connections::DisconnectionReason reason) { - Close(reason, ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + Close(reason, SafeDisconnectionResult::kSafeDisconnection); } void BaseEndpointChannel::Close( @@ -490,5 +470,4 @@ std::unique_ptr BaseEndpointChannel::EncodeMessageForTests( return crypto_context_->EncodeMessageToPeer(data); } -} // namespace connections -} // namespace nearby +} // namespace nearby::connections diff --git a/connections/implementation/base_endpoint_channel.h b/connections/implementation/base_endpoint_channel.h index 9946b9fa..421a8a9f 100644 --- a/connections/implementation/base_endpoint_channel.h +++ b/connections/implementation/base_endpoint_channel.h @@ -23,7 +23,6 @@ #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "connections/implementation/analytics/analytics_recorder.h" -#include "connections/implementation/analytics/packet_meta_data.h" #include "connections/implementation/endpoint_channel.h" #include "internal/platform/byte_array.h" #include "internal/platform/condition_variable.h" @@ -32,10 +31,7 @@ #include "internal/platform/mutex.h" #include "internal/platform/output_stream.h" -namespace nearby { -namespace connections { - -using analytics::PacketMetaData; +namespace nearby::connections { class BaseEndpointChannel : public EndpointChannel { public: @@ -51,20 +47,16 @@ class BaseEndpointChannel : public EndpointChannel { ~BaseEndpointChannel() override = default; // EndpointChannel: - ExceptionOr Read() override; - ExceptionOr Read(PacketMetaData& packet_meta_data) + ExceptionOr Read() ABSL_LOCKS_EXCLUDED(reader_mutex_, crypto_mutex_, last_read_mutex_) override; - Exception Write(const ByteArray& data) override; - Exception Write(absl::string_view data, PacketMetaData& packet_meta_data) + Exception Write(absl::string_view data) ABSL_LOCKS_EXCLUDED(writer_mutex_, crypto_mutex_) override; void Close() ABSL_LOCKS_EXCLUDED(is_paused_mutex_) override; void Close(location::nearby::proto::connections::DisconnectionReason reason) override; - void Close( - location::nearby::proto::connections::DisconnectionReason reason, - location::nearby::analytics::proto::ConnectionsLog:: - EstablishedConnection::SafeDisconnectionResult result) override; + void Close(location::nearby::proto::connections::DisconnectionReason reason, + nearby::analytics::SafeDisconnectionResult result) override; bool IsClosed() const ABSL_LOCKS_EXCLUDED(is_paused_mutex_) override; std::string GetType() const override; std::string GetServiceId() const override; @@ -176,7 +168,6 @@ class BaseEndpointChannel : public EndpointChannel { std::string endpoint_id_ = ""; }; -} // namespace connections -} // namespace nearby +} // namespace nearby::connections #endif // CORE_INTERNAL_BASE_ENDPOINT_CHANNEL_H_ diff --git a/connections/implementation/base_endpoint_channel_test.cc b/connections/implementation/base_endpoint_channel_test.cc index a1bad6e0..1e3cbbde 100644 --- a/connections/implementation/base_endpoint_channel_test.cc +++ b/connections/implementation/base_endpoint_channel_test.cc @@ -108,20 +108,26 @@ std::function MakeDataMonitor(const std::string& label, std::pair, std::shared_ptr> -DoDhKeyExchange(BaseEndpointChannel* channel_a, - BaseEndpointChannel* channel_b) { +DoDhKeyExchange(std::shared_ptr channel_a, + std::shared_ptr channel_b) { std::shared_ptr context_a; std::shared_ptr context_b; EncryptionRunner crypto_a; EncryptionRunner crypto_b; ClientProxy proxy_a; ClientProxy proxy_b; - CountDownLatch latch(2); + std::shared_ptr shared_channel_a = channel_a; + std::shared_ptr shared_channel_b = channel_b; + + // Create a shared_ptr for the latch to prevent Use-After-Free if the + // negotiation times out and this function returns early. + auto latch = std::make_shared(2); + crypto_a.StartClient( - &proxy_a, "endpoint_id", channel_a, + &proxy_a, "endpoint_id", shared_channel_a, { .on_success_cb = - [&latch, &context_a]( + [latch, &context_a]( const std::string& endpoint_id, std::unique_ptr ukey2, const std::string& auth_token, @@ -131,20 +137,19 @@ DoDhKeyExchange(BaseEndpointChannel* channel_a, auto context = ukey2->ToConnectionContext(); EXPECT_NE(context, nullptr); context_a = std::move(context); - latch.CountDown(); + latch->CountDown(); }, .on_failure_cb = - [&latch](const std::string& endpoint_id, - EndpointChannel* channel) { + [latch](const std::string& endpoint_id) { LOG(INFO) << "client-A side key negotiation failed"; - latch.CountDown(); + latch->CountDown(); }, }); crypto_b.StartServer( - &proxy_b, "endpoint_id", channel_b, + &proxy_b, "endpoint_id", shared_channel_b, { .on_success_cb = - [&latch, &context_b]( + [latch, &context_b]( const std::string& endpoint_id, std::unique_ptr ukey2, const std::string& auth_token, @@ -154,16 +159,15 @@ DoDhKeyExchange(BaseEndpointChannel* channel_a, auto context = ukey2->ToConnectionContext(); EXPECT_NE(context, nullptr); context_b = std::move(context); - latch.CountDown(); + latch->CountDown(); }, .on_failure_cb = - [&latch](const std::string& endpoint_id, - EndpointChannel* channel) { + [latch](const std::string& endpoint_id) { LOG(INFO) << "client-B side key negotiation failed"; - latch.CountDown(); + latch->CountDown(); }, }); - EXPECT_TRUE(latch.Await(absl::Milliseconds(5000)).result()); + EXPECT_TRUE(latch->Await(absl::Milliseconds(5000)).result()); return std::make_pair(std::move(context_a), std::move(context_b)); } @@ -174,7 +178,7 @@ class BaseEndpointChannelTest : public ::testing::Test { NearbyFlags::GetInstance().ResetOverridedValues(); } - const ByteArray kTestData{"test_data"}; + const absl::string_view kTestData = "test_data"; }; TEST_F(BaseEndpointChannelTest, ReadSucceedsWhenFlagDisabled) { @@ -189,7 +193,7 @@ TEST_F(BaseEndpointChannelTest, ReadSucceedsWhenFlagDisabled) { channel_a.Write(kTestData); ByteArray rx_message = std::move(channel_b.Read().result()); - EXPECT_EQ(rx_message, kTestData); + EXPECT_EQ(rx_message.AsStringView(), kTestData); } TEST_F(BaseEndpointChannelTest, ReadCallsDispatchPacketWhenFlagEnabled) { @@ -203,13 +207,14 @@ TEST_F(BaseEndpointChannelTest, ReadCallsDispatchPacketWhenFlagEnabled) { TestEndpointChannel channel_b(pipe_a.first.get(), pipe_b.second.get()); EXPECT_CALL(channel_b, DispatchPacket) - .WillOnce(::testing::Return(ExceptionOr(kTestData))); + .WillOnce(::testing::Return( + ExceptionOr(ByteArray(std::string(kTestData))))); channel_a.Write(kTestData); auto read_byte = channel_b.Read(); EXPECT_TRUE(read_byte.ok()); - EXPECT_EQ(read_byte.result(), kTestData); + EXPECT_EQ(read_byte.result().AsStringView(), kTestData); } TEST_F(BaseEndpointChannelTest, @@ -243,10 +248,10 @@ TEST_F(BaseEndpointChannelTest, ReadWrite) { auto pipe_b = CreatePipe(); // channel_b writes to pipe_b, reads from pipe_a. TestEndpointChannel channel_a(pipe_b.first.get(), pipe_a.second.get()); TestEndpointChannel channel_b(pipe_a.first.get(), pipe_b.second.get()); - ByteArray tx_message{"data message"}; + absl::string_view tx_message = "data message"; channel_a.Write(tx_message); ByteArray rx_message = std::move(channel_b.Read().result()); - EXPECT_EQ(rx_message, tx_message); + EXPECT_EQ(rx_message.AsStringView(), tx_message); } TEST_F(BaseEndpointChannelTest, ChannelUnencryptedByDefault) { @@ -264,20 +269,22 @@ TEST_F(BaseEndpointChannelTest, TryDecrypt) { absl::string_view kMessage = "message"; auto pipe_a = CreatePipe(); // channel_a writes to pipe_a, reads from pipe_b. auto pipe_b = CreatePipe(); // channel_b writes to pipe_b, reads from pipe_a. - TestEndpointChannel channel_a(pipe_b.first.get(), pipe_a.second.get()); - TestEndpointChannel channel_b(pipe_a.first.get(), pipe_b.second.get()); - auto [context_a, context_b] = DoDhKeyExchange(&channel_a, &channel_b); + auto channel_a = std::make_shared(pipe_b.first.get(), + pipe_a.second.get()); + auto channel_b = std::make_shared(pipe_a.first.get(), + pipe_b.second.get()); + auto [context_a, context_b] = DoDhKeyExchange(channel_a, channel_b); ASSERT_NE(context_a, nullptr); ASSERT_NE(context_b, nullptr); - channel_a.EnableEncryption(context_a); - channel_b.EnableEncryption(context_b); + channel_a->EnableEncryption(context_a); + channel_b->EnableEncryption(context_b); std::unique_ptr encrypted_message = - channel_a.EncodeMessageForTests(kMessage); + channel_a->EncodeMessageForTests(kMessage); ExceptionOr decrypted_message = - channel_b.TryDecrypt(ByteArray(*encrypted_message)); + channel_b->TryDecrypt(ByteArray(*encrypted_message)); - EXPECT_TRUE(channel_b.IsEncrypted()); + EXPECT_TRUE(channel_b->IsEncrypted()); EXPECT_TRUE(decrypted_message.ok()); EXPECT_EQ(decrypted_message.result().AsStringView(), kMessage); } @@ -285,16 +292,18 @@ TEST_F(BaseEndpointChannelTest, TryDecrypt) { TEST_F(BaseEndpointChannelTest, TryDecryptFailsWhenDecryptionFails) { auto pipe_a = CreatePipe(); // channel_a writes to pipe_a, reads from pipe_b. auto pipe_b = CreatePipe(); // channel_b writes to pipe_b, reads from pipe_a. - TestEndpointChannel channel_a(pipe_b.first.get(), pipe_a.second.get()); - TestEndpointChannel channel_b(pipe_a.first.get(), pipe_b.second.get()); - auto [context_a, context_b] = DoDhKeyExchange(&channel_a, &channel_b); + auto channel_a = std::make_shared(pipe_b.first.get(), + pipe_a.second.get()); + auto channel_b = std::make_shared(pipe_a.first.get(), + pipe_b.second.get()); + auto [context_a, context_b] = DoDhKeyExchange(channel_a, channel_b); ASSERT_NE(context_a, nullptr); - channel_a.EnableEncryption(context_a); + channel_a->EnableEncryption(context_a); ExceptionOr result = - channel_a.TryDecrypt(ByteArray("invalid message")); + channel_a->TryDecrypt(ByteArray("invalid message")); - EXPECT_TRUE(channel_a.IsEncrypted()); + EXPECT_TRUE(channel_a->IsEncrypted()); EXPECT_FALSE(result.ok()); EXPECT_EQ(result.exception(), Exception::kExecution); } @@ -332,12 +341,12 @@ TEST_F(BaseEndpointChannelTest, NotEncryptedReadWriteCanBeIntercepted) { EXPECT_EQ(channel_b.GetType(), "BLE"); // Start data transfer - ByteArray tx_message{"data message"}; + absl::string_view tx_message = "data message"; channel_a.Write(tx_message); ByteArray rx_message = std::move(channel_b.Read().result()); // Verify expectations. - EXPECT_EQ(rx_message, tx_message); + EXPECT_EQ(rx_message.AsStringView(), tx_message); { absl::MutexLock lock(mutex); std::string message{tx_message}; @@ -365,13 +374,15 @@ TEST_F(BaseEndpointChannelTest, EncryptedReadWriteCanNotBeIntercepted) { // to server "b". auto server_b = CreatePipe(); // Data pump "b" reads from client "b", writes // to server "a". - TestEndpointChannel channel_a(server_a.first.get(), client_a.second.get()); - TestEndpointChannel channel_b(server_b.first.get(), client_b.second.get()); + auto channel_a = std::make_shared(server_a.first.get(), + client_a.second.get()); + auto channel_b = std::make_shared(server_b.first.get(), + client_b.second.get()); - ON_CALL(channel_a, GetMedium).WillByDefault([]() { + ON_CALL(*channel_a, GetMedium).WillByDefault([]() { return Medium::BLUETOOTH; }); - ON_CALL(channel_b, GetMedium).WillByDefault([]() { + ON_CALL(*channel_b, GetMedium).WillByDefault([]() { return Medium::BLUETOOTH; }); @@ -384,24 +395,24 @@ TEST_F(BaseEndpointChannelTest, EncryptedReadWriteCanNotBeIntercepted) { MakeDataMonitor("monitor_b", &capture_b, &mutex))); // Run DH key exchange; setup encryption contexts for channels. - auto [context_a, context_b] = DoDhKeyExchange(&channel_a, &channel_b); + auto [context_a, context_b] = DoDhKeyExchange(channel_a, channel_b); ASSERT_NE(context_a, nullptr); ASSERT_NE(context_b, nullptr); - channel_a.EnableEncryption(context_a); - channel_b.EnableEncryption(context_b); + channel_a->EnableEncryption(context_a); + channel_b->EnableEncryption(context_b); - EXPECT_EQ(channel_a.GetType(), "ENCRYPTED_BLUETOOTH"); - EXPECT_EQ(channel_b.GetType(), "ENCRYPTED_BLUETOOTH"); - EXPECT_TRUE(channel_a.IsEncrypted()); - EXPECT_TRUE(channel_b.IsEncrypted()); + EXPECT_EQ(channel_a->GetType(), "ENCRYPTED_BLUETOOTH"); + EXPECT_EQ(channel_b->GetType(), "ENCRYPTED_BLUETOOTH"); + EXPECT_TRUE(channel_a->IsEncrypted()); + EXPECT_TRUE(channel_b->IsEncrypted()); // Start data transfer - ByteArray tx_message{"data message"}; - channel_a.Write(tx_message); - ByteArray rx_message = std::move(channel_b.Read().result()); + absl::string_view tx_message = "data message"; + channel_a->Write(tx_message); + ByteArray rx_message = std::move(channel_b->Read().result()); // Verify expectations. - EXPECT_EQ(rx_message, tx_message); + EXPECT_EQ(rx_message.AsStringView(), tx_message); { absl::MutexLock lock(mutex); std::string message{tx_message}; @@ -410,8 +421,8 @@ TEST_F(BaseEndpointChannelTest, EncryptedReadWriteCanNotBeIntercepted) { } // Shutdown test environment. - channel_a.Close(DisconnectionReason::LOCAL_DISCONNECTION); - channel_b.Close(DisconnectionReason::REMOTE_DISCONNECTION); + channel_a->Close(DisconnectionReason::LOCAL_DISCONNECTION); + channel_b->Close(DisconnectionReason::REMOTE_DISCONNECTION); } TEST_F(BaseEndpointChannelTest, CanBesuspendedAndResumed) { @@ -432,8 +443,8 @@ TEST_F(BaseEndpointChannelTest, CanBesuspendedAndResumed) { EXPECT_EQ(channel_b.GetType(), "WIFI_LAN"); // Start data transfer - ByteArray tx_message{"data message"}; - ByteArray more_message{"more data"}; + absl::string_view tx_message = "data message"; + absl::string_view more_message = "more data"; channel_a.Write(tx_message); ByteArray rx_message = std::move(channel_b.Read().result()); @@ -459,7 +470,7 @@ TEST_F(BaseEndpointChannelTest, CanBesuspendedAndResumed) { // Resume; verify that data transfer comepleted. channel_a.Resume(); EXPECT_TRUE(latch.Await(absl::Milliseconds(1000)).result()); - EXPECT_EQ(read_more, more_message); + EXPECT_EQ(read_more.AsStringView(), more_message); // Shutdown test environment. channel_a.Close(DisconnectionReason::LOCAL_DISCONNECTION); @@ -485,43 +496,45 @@ TEST_F(BaseEndpointChannelTest, ReadUnencryptedFrameOnEncryptedChannel) { // Setup test communication environment. auto pipe_a = CreatePipe(); // channel_a writes to pipe_a, reads from pipe_b. auto pipe_b = CreatePipe(); // channel_b writes to pipe_b, reads from pipe_a. - TestEndpointChannel channel_a(pipe_b.first.get(), pipe_a.second.get()); - TestEndpointChannel channel_b(pipe_a.first.get(), pipe_b.second.get()); + auto channel_a = std::make_shared(pipe_b.first.get(), + pipe_a.second.get()); + auto channel_b = std::make_shared(pipe_a.first.get(), + pipe_b.second.get()); - ON_CALL(channel_a, GetMedium).WillByDefault([]() { + ON_CALL(*channel_a, GetMedium).WillByDefault([]() { return Medium::BLUETOOTH; }); - ON_CALL(channel_b, GetMedium).WillByDefault([]() { + ON_CALL(*channel_b, GetMedium).WillByDefault([]() { return Medium::BLUETOOTH; }); // Run DH key exchange; setup encryption contexts for channels. But only // encrypt |channel_b|. - auto [context_a, context_b] = DoDhKeyExchange(&channel_a, &channel_b); + auto [context_a, context_b] = DoDhKeyExchange(channel_a, channel_b); ASSERT_NE(context_a, nullptr); ASSERT_NE(context_b, nullptr); - channel_b.EnableEncryption(context_b); + channel_b->EnableEncryption(context_b); - EXPECT_EQ(channel_a.GetType(), "BLUETOOTH"); - EXPECT_EQ(channel_b.GetType(), "ENCRYPTED_BLUETOOTH"); + EXPECT_EQ(channel_a->GetType(), "BLUETOOTH"); + EXPECT_EQ(channel_b->GetType(), "ENCRYPTED_BLUETOOTH"); // An unencrypted KeepAlive should succeed. - ByteArray keep_alive_message = parser::ForKeepAlive(); - channel_a.Write(keep_alive_message); - ExceptionOr result = channel_b.Read(); + std::string keep_alive_message = parser::ForKeepAlive(); + channel_a->Write(keep_alive_message); + ExceptionOr result = channel_b->Read(); EXPECT_TRUE(result.ok()); - EXPECT_EQ(result.result(), keep_alive_message); + EXPECT_EQ(result.result().AsStringView(), keep_alive_message); // An unencrypted data frame should fail. - ByteArray tx_message{"data message"}; - channel_a.Write(tx_message); - result = channel_b.Read(); + absl::string_view tx_message = "data message"; + channel_a->Write(tx_message); + result = channel_b->Read(); EXPECT_FALSE(result.ok()); EXPECT_EQ(result.exception(), Exception::kInvalidProtocolBuffer); // Shutdown test environment. - channel_a.Close(DisconnectionReason::LOCAL_DISCONNECTION); - channel_b.Close(DisconnectionReason::REMOTE_DISCONNECTION); + channel_a->Close(DisconnectionReason::LOCAL_DISCONNECTION); + channel_b->Close(DisconnectionReason::REMOTE_DISCONNECTION); } } // namespace diff --git a/connections/implementation/base_pcp_handler.cc b/connections/implementation/base_pcp_handler.cc index 406d35f2..ae833f73 100644 --- a/connections/implementation/base_pcp_handler.cc +++ b/connections/implementation/base_pcp_handler.cc @@ -36,7 +36,9 @@ #include "connections/advertising_options.h" #include "connections/connection_options.h" #include "connections/discovery_options.h" +#include "connections/implementation/analytics/analytics_recorder.h" #include "connections/implementation/analytics/connection_attempt_metadata_params.h" +#include "connections/implementation/analytics/operation_result_with_medium.h" #include "connections/implementation/bwu_manager.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/connections_authentication_transport.h" @@ -51,7 +53,6 @@ #include "connections/implementation/mediums/webrtc_peer_id.h" #include "connections/implementation/offline_frames.h" #include "connections/implementation/pcp.h" -#include "connections/implementation/proto/offline_wire_formats.pb.h" #include "connections/implementation/webrtc_state.h" #include "connections/listeners.h" #include "connections/medium_selector.h" @@ -84,11 +85,24 @@ #include "internal/platform/runnable.h" #include "internal/platform/wifi.h" #include "internal/platform/wifi_lan_connection_info.h" -#include "proto/connections_enums.pb.h" namespace nearby::connections { namespace { +using ::location::nearby::connections::ConnectionRequestFrame; +using ::location::nearby::connections::ConnectionResponseFrame; +using ::location::nearby::connections::ConnectionsDevice; +using ::location::nearby::connections::MediumMetadata; +using ::location::nearby::connections::OfflineFrame; +using ::location::nearby::connections::OsInfo; +using ::location::nearby::connections::PresenceDevice; +using ::location::nearby::connections::V1Frame; +using ::location::nearby::proto::connections::OperationResultCode; +using ::location::nearby::proto::connections::WifiDirectAuthType; +using ::nearby::analytics::AnalyticsRecorder; +using ::nearby::analytics::OperationResultWithMedium; +using ::securegcm::UKey2Handshake; + constexpr int kEndpointCancelAlarmTimeout = 10; std::string AuthenticationStatusToString(nearby::AuthenticationStatus status) { @@ -101,20 +115,8 @@ std::string AuthenticationStatusToString(nearby::AuthenticationStatus status) { return "failure"; } } -} // namespace -using ::location::nearby::analytics::proto::ConnectionsLog; -using ::location::nearby::connections::ConnectionRequestFrame; -using ::location::nearby::connections::ConnectionResponseFrame; -using ::location::nearby::connections::ConnectionsDevice; -using ::location::nearby::connections::MediumMetadata; -using ::location::nearby::connections::OfflineFrame; -using ::location::nearby::connections::OsInfo; -using ::location::nearby::connections::PresenceDevice; -using ::location::nearby::connections::V1Frame; -using ::location::nearby::proto::connections::OperationResultCode; -using ::location::nearby::proto::connections::WifiDirectAuthType; -using ::securegcm::UKey2Handshake; +} // namespace BasePcpHandler::BasePcpHandler(Mediums* mediums, EndpointManager* endpoint_manager, @@ -212,8 +214,7 @@ std::vector BasePcpHandler::GetConnectionInfoFromResult( } WifiLanConnectionInfo info( std::string(ip_address.begin(), ip_address.end()), - absl::StrCat(absl::Hex(port, absl::kZeroPad16)), "", - {}); + absl::StrCat(absl::Hex(port, absl::kZeroPad16)), "", {}); connection_infos.push_back(info); } } @@ -279,11 +280,10 @@ Status BasePcpHandler::StartAdvertising( // Save the advertising options for local reference in later process // like upgrading bandwidth. advertising_listener_ = info.listener; - client->StartedAdvertising( - service_id, GetStrategy(), info.listener, - absl::MakeSpan(result.mediums), - std::move(result.operation_result_with_mediums), - compatible_advertising_options); + client->StartedAdvertising(service_id, GetStrategy(), info.listener, + absl::MakeSpan(result.mediums), + result.operation_result_with_mediums, + compatible_advertising_options); client->UpdateLocalEndpointInfo(info.endpoint_info.string_data()); response.Set({Status::kSuccess}); }); @@ -424,25 +424,25 @@ BooleanMediumSelector BasePcpHandler::ComputeIntersectionOfSupportedMediums( pending_connection_info.connection_options.connection_info .supported_wifi_direct_auth_types; LOG(INFO) << "Remote supported WifiDirect auth types: " - << absl::StrJoin( - remote_supported_wifi_direct_auth_types, ", ", - [](std::string* out, int auth_type) { - absl::StrAppend( - out, - WifiDirectAuthType_Name( - static_cast(auth_type))); - }); + << absl::StrJoin( + remote_supported_wifi_direct_auth_types, ", ", + [](std::string* out, int auth_type) { + absl::StrAppend( + out, + WifiDirectAuthType_Name( + static_cast(auth_type))); + }); auto local_supported_wifi_direct_auth_types = mediums_->GetWifiDirect().GetSupportedWifiDirectAuthTypes(); LOG(INFO) << "Local supported WifiDirect auth types: " - << absl::StrJoin( - local_supported_wifi_direct_auth_types, ", ", - [](std::string* out, int auth_type) { - absl::StrAppend( - out, - WifiDirectAuthType_Name( - static_cast(auth_type))); - }); + << absl::StrJoin( + local_supported_wifi_direct_auth_types, ", ", + [](std::string* out, int auth_type) { + absl::StrAppend( + out, + WifiDirectAuthType_Name( + static_cast(auth_type))); + }); bool found_common_auth_type = false; for (const auto& auth_type : local_supported_wifi_direct_auth_types) { if (auth_type == WifiDirectAuthType::WIFI_DIRECT_TYPE_UNKNOWN) { @@ -510,11 +510,11 @@ Status BasePcpHandler::StartDiscovery(ClientProxy* client, MutexLock lock(&discovered_endpoint_mutex_); discovered_endpoints_.clear(); } - client->StartedDiscovery( - service_id, GetStrategy(), std::move(listener), - absl::MakeSpan(result.mediums), - std::move(result.operation_result_with_mediums), - stripped_discovery_options); + client->StartedDiscovery(service_id, GetStrategy(), + std::move(listener), + absl::MakeSpan(result.mediums), + result.operation_result_with_mediums, + stripped_discovery_options); response.Set({Status::kSuccess}); }); return WaitForResult(absl::StrCat("StartDiscovery(", service_id, ")"), @@ -587,34 +587,50 @@ void BasePcpHandler::RunOnPcpHandlerThread(const std::string& name, serial_executor_.Execute(name, std::move(runnable)); } -EncryptionRunner::ResultListener BasePcpHandler::GetResultListener() { +EncryptionRunner::ResultListener BasePcpHandler::GetResultListener( + std::shared_ptr endpoint_channel) { + std::weak_ptr weak_channel = endpoint_channel; + return { .on_success_cb = - [this](const std::string& endpoint_id, - std::unique_ptr ukey2, - const std::string& auth_token, - const ByteArray& raw_auth_token) { + [this, weak_channel](const std::string& endpoint_id, + std::unique_ptr ukey2, + const std::string& auth_token, + const ByteArray& raw_auth_token) { + auto channel = weak_channel.lock(); + if (!channel) return; + RunOnPcpHandlerThread( "encryption-success", - [this, endpoint_id, raw_ukey2 = ukey2.release(), auth_token, - raw_auth_token]() RUN_ON_PCP_HANDLER_THREAD() mutable { - OnEncryptionSuccessRunnable( - endpoint_id, std::unique_ptr(raw_ukey2), - auth_token, raw_auth_token); - }); + [this, endpoint_id, weak_channel, raw_ukey2 = ukey2.release(), + auth_token, raw_auth_token]() + RUN_ON_PCP_HANDLER_THREAD() mutable { + std::unique_ptr ukey2(raw_ukey2); + auto channel = weak_channel.lock(); + if (!channel) return; + OnEncryptionSuccessRunnable(endpoint_id, std::move(ukey2), + auth_token, raw_auth_token, + channel); + }); }, .on_failure_cb = - [this](const std::string& endpoint_id, EndpointChannel* channel) { + [this, weak_channel](const std::string& endpoint_id) { + auto channel = weak_channel.lock(); + if (!channel) return; + RunOnPcpHandlerThread( "encryption-failure", - [this, endpoint_id, channel]() RUN_ON_PCP_HANDLER_THREAD() { - LOG(ERROR) - << "Encryption failed for endpoint_id=" << endpoint_id - << " on medium=" - << location::nearby::proto::connections::Medium_Name( - channel->GetMedium()); - OnEncryptionFailureRunnable(endpoint_id, channel); - }); + [this, endpoint_id, weak_channel]() + RUN_ON_PCP_HANDLER_THREAD() { + auto channel = weak_channel.lock(); + if (!channel) return; + LOG(ERROR) + << "Encryption failed for endpoint_id=" << endpoint_id + << " on medium=" + << location::nearby::proto::connections::Medium_Name( + channel->GetMedium()); + OnEncryptionFailureRunnable(endpoint_id, channel); + }); }, }; } @@ -622,36 +638,49 @@ EncryptionRunner::ResultListener BasePcpHandler::GetResultListener() { EncryptionRunner::ResultListener BasePcpHandler::GetResultListenerV3( const NearbyDeviceProvider& device_provider, const NearbyDevice& remote_device, - const EndpointChannel& endpoint_channel) { + std::shared_ptr endpoint_channel) { + std::weak_ptr weak_channel = endpoint_channel; + return { .on_success_cb = - [this, &device_provider, &remote_device, &endpoint_channel]( + [this, &device_provider, &remote_device, weak_channel]( const std::string& endpoint_id, std::unique_ptr ukey2, const std::string& auth_token, const ByteArray& raw_auth_token) { + auto channel = weak_channel.lock(); + if (!channel) return; + RunOnPcpHandlerThread( "encryption-success", - [this, &device_provider, &remote_device, &endpoint_channel, - raw_ukey2 = ukey2.release(), auth_token, - raw_auth_token]() RUN_ON_PCP_HANDLER_THREAD() mutable { - OnEncryptionSuccessRunnableV3( - remote_device, std::unique_ptr(raw_ukey2), - auth_token, raw_auth_token, endpoint_channel, - device_provider); - }); + [this, &device_provider, &remote_device, weak_channel, + raw_ukey2 = ukey2.release(), auth_token, raw_auth_token]() + RUN_ON_PCP_HANDLER_THREAD() mutable { + std::unique_ptr ukey2(raw_ukey2); + auto channel = weak_channel.lock(); + if (!channel) return; + OnEncryptionSuccessRunnableV3( + remote_device, std::move(ukey2), auth_token, + raw_auth_token, channel, device_provider); + }); }, .on_failure_cb = - [this](const std::string& endpoint_id, EndpointChannel* channel) { + [this, weak_channel](const std::string& endpoint_id) { + auto channel = weak_channel.lock(); + if (!channel) return; + RunOnPcpHandlerThread( "encryption-failure", - [this, endpoint_id, channel]() RUN_ON_PCP_HANDLER_THREAD() { - LOG(ERROR) - << "Encryption failed for endpoint_id=" << endpoint_id - << " on medium=" - << location::nearby::proto::connections::Medium_Name( - channel->GetMedium()); - OnEncryptionFailureRunnable(endpoint_id, channel); - }); + [this, endpoint_id, weak_channel]() + RUN_ON_PCP_HANDLER_THREAD() { + auto channel = weak_channel.lock(); + if (!channel) return; + LOG(ERROR) + << "Encryption failed for endpoint_id=" << endpoint_id + << " on medium=" + << location::nearby::proto::connections::Medium_Name( + channel->GetMedium()); + OnEncryptionFailureRunnable(endpoint_id, channel); + }); }, }; } @@ -659,7 +688,7 @@ EncryptionRunner::ResultListener BasePcpHandler::GetResultListenerV3( void BasePcpHandler::OnEncryptionSuccessRunnableV3( const NearbyDevice& remote_device, std::unique_ptr ukey2, absl::string_view auth_token, const ByteArray& raw_auth_token, - const EndpointChannel& endpoint_channel, + std::shared_ptr endpoint_channel, const NearbyDeviceProvider& device_provider) { // Quick fail if we've been removed from pending connections while we were // busy running UKEY2. @@ -674,7 +703,11 @@ void BasePcpHandler::OnEncryptionSuccessRunnableV3( } BasePcpHandler::PendingConnectionInfo& pending_connection_info = it->second; - + // Verify pointer equality to avoid accidental action on superseded + // channels. + if (endpoint_channel != pending_connection_info.channel) { + return; + } // TODO(b/300149127): Add test coverage. if (!ukey2) { // Fail early, if there is no crypto context. @@ -697,8 +730,15 @@ void BasePcpHandler::OnEncryptionSuccessRunnableV3( // // TODO(b/305004353): Authenticate the connection in the responder role for // outgoing connections. - if (!pending_connection_info.is_incoming) { + if (pending_connection_info.is_incoming) { LOG(ERROR) << __func__ << ": only outgoing connections are supported"; + ProcessPreConnectionInitiationFailure( + pending_connection_info.client, pending_connection_info.medium, + remote_device.GetEndpointId(), pending_connection_info.channel.get(), + pending_connection_info.is_incoming, /*log_failure=*/true, + pending_connection_info.start_time, {Status::kConnectionRejected}, + OperationResultCode::DETAIL_UNKNOWN, + pending_connection_info.result.lock().get()); return; } @@ -724,7 +764,8 @@ void BasePcpHandler::OnEncryptionSuccessRunnableV3( void BasePcpHandler::OnEncryptionSuccessRunnable( const std::string& endpoint_id, std::unique_ptr ukey2, - const std::string& auth_token, const ByteArray& raw_auth_token) { + const std::string& auth_token, const ByteArray& raw_auth_token, + std::shared_ptr endpoint_channel) { // Quick fail if we've been removed from pending connections while we were // busy running UKEY2. // TODO(b/316421187): Add test coverage @@ -738,6 +779,12 @@ void BasePcpHandler::OnEncryptionSuccessRunnable( BasePcpHandler::PendingConnectionInfo& pending_connection_info = it->second; + // Verify pointer equality to avoid accidental action on superseded + // channels. + if (endpoint_channel != pending_connection_info.channel) { + return; + } + if (!ukey2) { // Fail early, if there is no crypto context. ProcessPreConnectionInitiationFailure( @@ -801,24 +848,21 @@ void BasePcpHandler::RegisterDeviceAfterEncryptionSuccess( } void BasePcpHandler::OnEncryptionFailureRunnable( - const std::string& endpoint_id, EndpointChannel* endpoint_channel) { + const std::string& endpoint_id, + std::shared_ptr endpoint_channel) { auto it = pending_connections_.find(endpoint_id); if (it == pending_connections_.end()) { LOG(INFO) - << "Connection not found on UKEY negotination complete; endpoint_id=" + << "Connection not found on UKEY negotiation complete; endpoint_id=" << endpoint_id; return; } BasePcpHandler::PendingConnectionInfo& pending_connection_info = it->second; - // We had a bug here, caused by a race with EncryptionRunner. We now verify - // the EndpointChannel to avoid it. In a simultaneous connection, we clean - // up one of the two EndpointChannels and then update our pendingConnections - // with the winning channel's state. Closing a channel that was in the - // middle of EncryptionRunner would trigger onEncryptionFailed, and, since - // the map had already updated with the winning EndpointChannel, we closed - // it too by accident. - if (*endpoint_channel != *pending_connection_info.channel) { + + // Verify pointer equality to avoid accidental action on superseded + // channels. + if (endpoint_channel != pending_connection_info.channel) { LOG(INFO) << "Not destroying channel [mismatch]: passed=" << endpoint_channel->GetName() << "; expected=" << pending_connection_info.channel->GetName(); @@ -871,8 +915,8 @@ ConnectionInfo BasePcpHandler::FillConnectionInfo( connection_info.supported_wifi_direct_auth_types = mediums_->GetWifiDirect().GetSupportedWifiDirectAuthTypes(); VLOG(1) << "Set SupportedWifiDirectAuthTypes for WIFI_DIRECT: " - << absl::StrJoin(connection_info.supported_wifi_direct_auth_types, - ","); + << absl::StrJoin(connection_info.supported_wifi_direct_auth_types, + ","); } else { connection_info.supported_wifi_direct_auth_types = {}; } @@ -975,8 +1019,8 @@ Status BasePcpHandler::RequestConnection( client, channel_medium, endpoint_id, channel.get(), /*is_incoming=*/false, /*log_failure=*/true, start_time, {Status::kEndpointIoError}, - client->GetAnalyticsRecorder() - .GetChannelIoErrorResultCodeFromMedium(channel_medium), + AnalyticsRecorder::GetChannelIoErrorResultCodeFromMedium( + channel_medium), result.get()); return; } @@ -1005,17 +1049,32 @@ Status BasePcpHandler::RequestConnection( pending_connection_info.medium = channel->GetMedium(); pending_connection_info.channel = std::move(channel); - EndpointChannel* endpoint_channel = - pending_connections_ - .emplace(endpoint_id, std::move(pending_connection_info)) - .first->second.channel.get(); + std::shared_ptr channel_to_close_on_failure = + pending_connection_info.channel; + auto [it, inserted] = pending_connections_.emplace( + endpoint_id, std::move(pending_connection_info)); + if (!inserted) { + LOG(ERROR) << "Failed to add outgoing connection to pending set; " + "endpoint_id=" + << endpoint_id + << ". Likely a collision with an existing pending " + "connection."; + if (channel_to_close_on_failure) { + channel_to_close_on_failure->Close( + location::nearby::proto::connections::DisconnectionReason:: + IO_ERROR); + } + result->Set({Status::kEndpointIoError}); + return; + } + std::shared_ptr endpoint_channel = it->second.channel; LOG(INFO) << "Initiating secure connection: endpoint_id=" << endpoint_id; // Next, we'll set up encryption. When it's done, our future will return // and RequestConnection() will finish. encryption_runner_.StartClient(client, endpoint_id, endpoint_channel, - GetResultListener()); + GetResultListener(endpoint_channel)); }); LOG(INFO) << "Waiting for connection to complete: endpoint_id=" << endpoint_id; @@ -1122,8 +1181,8 @@ Status BasePcpHandler::RequestConnectionV3( client, channel_medium, endpoint_id, channel.get(), /*is_incoming=*/false, /*log_failure=*/true, start_time, {Status::kEndpointIoError}, - client->GetAnalyticsRecorder() - .GetChannelIoErrorResultCodeFromMedium(channel_medium), + AnalyticsRecorder::GetChannelIoErrorResultCodeFromMedium( + channel_medium), result.get()); return; } @@ -1144,7 +1203,7 @@ Status BasePcpHandler::RequestConnectionV3( pending_connection_info.client = client; pending_connection_info.remote_endpoint_info = endpoint->endpoint_info; pending_connection_info.nonce = connection_info.nonce; - pending_connection_info.is_incoming = true; + pending_connection_info.is_incoming = false; pending_connection_info.start_time = start_time; pending_connection_info.listener = info.listener; pending_connection_info.connection_options = connection_options; @@ -1152,10 +1211,25 @@ Status BasePcpHandler::RequestConnectionV3( pending_connection_info.medium = channel->GetMedium(); pending_connection_info.channel = std::move(channel); - EndpointChannel* endpoint_channel = - pending_connections_ - .emplace(endpoint_id, std::move(pending_connection_info)) - .first->second.channel.get(); + std::shared_ptr channel_to_close_on_failure = + pending_connection_info.channel; + auto [it, inserted] = pending_connections_.emplace( + endpoint_id, std::move(pending_connection_info)); + if (!inserted) { + LOG(ERROR) << "Failed to add outgoing connection to pending set; " + "endpoint_id=" + << endpoint_id + << ". Likely a collision with an existing pending " + "connection."; + if (channel_to_close_on_failure) { + channel_to_close_on_failure->Close( + location::nearby::proto::connections::DisconnectionReason:: + IO_ERROR); + } + result->Set({Status::kEndpointIoError}); + return; + } + std::shared_ptr endpoint_channel = it->second.channel; LOG(INFO) << "Initiating secure connection: endpoint_id=" << endpoint_id; @@ -1165,7 +1239,7 @@ Status BasePcpHandler::RequestConnectionV3( encryption_runner_.StartClient( client, endpoint_id, endpoint_channel, GetResultListenerV3(*(client->GetLocalDeviceProvider()), - remote_device, *endpoint_channel)); + remote_device, endpoint_channel)); }); LOG(INFO) << "Waiting for connection to complete: endpoint_id=" << endpoint_id; @@ -1231,22 +1305,21 @@ void BasePcpHandler::StripOutUnavailableMediums( } } -std::unique_ptr +OperationResultWithMedium BasePcpHandler::GetOperationResultWithMediumByResultCode( ClientProxy* client, location::nearby::proto::connections::Medium medium, int update_index, location::nearby::proto::connections::OperationResultCode operation_result_code, location::nearby::proto::connections::ConnectionMode connection_mode) { - auto operation_result_with_medium = - std::make_unique(); - operation_result_with_medium->set_medium(medium); - operation_result_with_medium->set_result_code(operation_result_code); - operation_result_with_medium->set_result_category( + OperationResultWithMedium operation_result_with_medium; + operation_result_with_medium.set_medium(medium); + operation_result_with_medium.set_result_code(operation_result_code); + operation_result_with_medium.set_result_category( client->GetAnalyticsRecorder().GetOperationResultCategory( operation_result_code)); - operation_result_with_medium->set_connection_mode(connection_mode); - operation_result_with_medium->set_update_index(update_index); + operation_result_with_medium.set_connection_mode(connection_mode); + operation_result_with_medium.set_update_index(update_index); return operation_result_with_medium; } @@ -1512,8 +1585,7 @@ Status BasePcpHandler::AcceptConnection(ClientProxy* client, Exception write_exception = channel->Write(parser::ForConnectionResponse( - Status::kSuccess, client->GetLocalOsInfo(), - client->GetLocalMultiplexSocketBitmask())); + Status::kSuccess, client->GetLocalOsInfo())); if (!write_exception.Ok()) { LOG(INFO) << "AcceptConnection: failed to send response: endpoint_id=" << endpoint_id; @@ -1574,8 +1646,7 @@ Status BasePcpHandler::RejectConnection(ClientProxy* client, Exception write_exception = channel->Write(parser::ForConnectionResponse( - Status::kConnectionRejected, client->GetLocalOsInfo(), - client->GetLocalMultiplexSocketBitmask())); + Status::kConnectionRejected, client->GetLocalOsInfo())); if (!write_exception.Ok()) { LOG(INFO) << "RejectConnection: failed to send response: endpoint_id=" << endpoint_id; @@ -1600,8 +1671,7 @@ Status BasePcpHandler::RejectConnection(ClientProxy* client, void BasePcpHandler::OnIncomingFrame( OfflineFrame& frame, const std::string& endpoint_id, ClientProxy* client, - location::nearby::proto::connections::Medium medium, - PacketMetaData& packet_meta_data) { + location::nearby::proto::connections::Medium medium) { CountDownLatch latch(1); RunOnPcpHandlerThread( "incoming-frame", @@ -1967,8 +2037,7 @@ Exception BasePcpHandler::OnIncomingConnection( /*is_incoming=*/true, /*log_failure=*/wrapped_frame.exception() != Exception::kNoData, start_time, {Status::kError}, - client->GetAnalyticsRecorder().GetChannelIoErrorResultCodeFromMedium( - medium), + AnalyticsRecorder::GetChannelIoErrorResultCodeFromMedium(medium), nullptr); } return wrapped_frame.GetException(); @@ -2133,14 +2202,23 @@ Exception BasePcpHandler::OnIncomingConnection( pending_connection_info.medium = channel->GetMedium(); pending_connection_info.channel = std::move(channel); - auto* owned_channel = pending_connections_ - .emplace(connection_request.endpoint_id(), - std::move(pending_connection_info)) - .first->second.channel.get(); + auto [it, inserted] = pending_connections_.emplace( + connection_request.endpoint_id(), std::move(pending_connection_info)); + // This should not happen since BreakTie() above should have checked that + // the endpoint_id is not already in pending_connections_. + if (!inserted) { + LOG(ERROR) << "Failed to add incoming connection to pending set; " + "endpoint_id=" + << connection_request.endpoint_id() + << ". Likely a collision with an existing pending connection."; + return {Exception::kIo}; + } + std::shared_ptr endpoint_channel = it->second.channel; // Next, we'll set up encryption. encryption_runner_.StartServer(client, connection_request.endpoint_id(), - owned_channel, GetResultListener()); + endpoint_channel, + GetResultListener(endpoint_channel)); return {Exception::kSuccess}; } @@ -2383,26 +2461,6 @@ void BasePcpHandler::EvaluateConnectionResult(ClientProxy* client, std::move(context))) { response_code = {Status::kEndpointUnknown}; } - - std::shared_ptr channel = - channel_manager_->GetChannelForEndpoint(endpoint_id); - if (channel != nullptr) { - if (client->IsMultiplexSocketSupported(endpoint_id, - channel->GetMedium())) { - if (!channel->EnableMultiplexSocket()) { - LOG(INFO) << "MultiplexSocket is not implemented for Medium: " - << location::nearby::proto::connections::Medium_Name( - channel->GetMedium()); - } else { - LOG(INFO) << "MultiplexSocket is supported for Medium: " - << location::nearby::proto::connections::Medium_Name( - channel->GetMedium()) - << " on both sides."; - } - } - } else { - LOG(INFO) << "channel is null"; - } } else { LOG(INFO) << "Pending connection rejected; endpoint_id=" << endpoint_id; response_code = {Status::kConnectionRejected}; @@ -2477,8 +2535,8 @@ ExceptionOr BasePcpHandler::ReadConnectionRequestFrame( return ExceptionOr(wrapped_bytes.exception()); } - ByteArray bytes = std::move(wrapped_bytes.result()); - ExceptionOr wrapped_frame = parser::FromBytes(bytes); + ExceptionOr wrapped_frame = + parser::FromBytes(wrapped_bytes.result().AsStringView()); if (wrapped_frame.GetException().Raised(Exception::kInvalidProtocolBuffer)) { return ExceptionOr(Exception::kIo); } @@ -2510,7 +2568,7 @@ void BasePcpHandler::LogConnectionAttemptFailure( connections_attempt_metadata_params; if (endpoint_channel != nullptr) { connections_attempt_metadata_params = - client->GetAnalyticsRecorder().BuildConnectionAttemptMetadataParams( + AnalyticsRecorder::BuildConnectionAttemptMetadataParams( endpoint_channel->GetTechnology(), endpoint_channel->GetBand(), endpoint_channel->GetFrequency(), endpoint_channel->GetTryCount()); connections_attempt_metadata_params->operation_result_code = @@ -2536,12 +2594,11 @@ void BasePcpHandler::LogConnectionAttemptSuccess( connections_attempt_metadata_params; if (pending_connection_info.channel != nullptr) { connections_attempt_metadata_params = - pending_connection_info.client->GetAnalyticsRecorder() - .BuildConnectionAttemptMetadataParams( - pending_connection_info.channel->GetTechnology(), - pending_connection_info.channel->GetBand(), - pending_connection_info.channel->GetFrequency(), - pending_connection_info.channel->GetTryCount()); + AnalyticsRecorder::BuildConnectionAttemptMetadataParams( + pending_connection_info.channel->GetTechnology(), + pending_connection_info.channel->GetBand(), + pending_connection_info.channel->GetFrequency(), + pending_connection_info.channel->GetTryCount()); connections_attempt_metadata_params->operation_result_code = OperationResultCode::DETAIL_SUCCESS; } else { diff --git a/connections/implementation/base_pcp_handler.h b/connections/implementation/base_pcp_handler.h index a1c3db7c..49cf0b70 100644 --- a/connections/implementation/base_pcp_handler.h +++ b/connections/implementation/base_pcp_handler.h @@ -31,7 +31,7 @@ #include "connections/advertising_options.h" #include "connections/connection_options.h" #include "connections/discovery_options.h" -#include "connections/implementation/analytics/packet_meta_data.h" +#include "connections/implementation/analytics/operation_result_with_medium.h" #include "connections/implementation/bwu_manager.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/encryption_runner.h" @@ -158,10 +158,10 @@ class BasePcpHandler : public PcpHandler, const std::string& endpoint_id) override; // @EndpointManagerReaderThread - void OnIncomingFrame(location::nearby::connections::OfflineFrame& frame, - const std::string& endpoint_id, ClientProxy* client, - location::nearby::proto::connections::Medium medium, - analytics::PacketMetaData& packet_meta_data) override; + void OnIncomingFrame( + location::nearby::connections::OfflineFrame& frame, + const std::string& endpoint_id, ClientProxy* client, + location::nearby::proto::connections::Medium medium) override; // Called when an endpoint disconnects while we're waiting for both sides to // approve/reject the connection. @@ -190,8 +190,7 @@ class BasePcpHandler : public PcpHandler, // If success, the mediums on which we are now advertising/discovering, for // analytics. std::vector mediums; - std::vector + std::vector operation_result_with_mediums; }; @@ -413,8 +412,7 @@ class BasePcpHandler : public PcpHandler, void StripOutWifiHotspotMedium(ConnectionInfo& connection_info); - std::unique_ptr + nearby::analytics::OperationResultWithMedium GetOperationResultWithMediumByResultCode( ClientProxy* client, location::nearby::proto::connections::Medium medium, int update_index, @@ -476,11 +474,11 @@ class BasePcpHandler : public PcpHandler, // Only (possibly) vector for incoming connections. std::vector supported_mediums; - // Keep track of a channel before we pass it to EndpointChannelManager. This - // is owned until the call to OnEncryptionSuccessRunnableV3 or - // OnEncryptionSuccessRunnable when ownership is transferred to the - // EndpointManager. - std::unique_ptr channel; + // Keep track of a channel before it is registered with the + // EndpointManager. This reference is held during the handshake phase and + // passed to the EndpointManager upon successful encryption + // (OnEncryptionSuccessRunnableV3 or OnEncryptionSuccessRunnable). + std::shared_ptr channel; // Crypto context; initially empty; established first thing after channel // creation by running UKey2 session. While it is in progress, we keep track @@ -509,24 +507,27 @@ class BasePcpHandler : public PcpHandler, void OnEncryptionFailureImpl(const std::string& endpoint_id, EndpointChannel* channel); - EncryptionRunner::ResultListener GetResultListener(); + EncryptionRunner::ResultListener GetResultListener( + std::shared_ptr endpoint_channel); EncryptionRunner::ResultListener GetResultListenerV3( const NearbyDeviceProvider& device_provider, const NearbyDevice& remote_device, - const EndpointChannel& endpoint_channel); + std::shared_ptr endpoint_channel); void OnEncryptionSuccessRunnable( const std::string& endpoint_id, std::unique_ptr ukey2, - const std::string& auth_token, const ByteArray& raw_auth_token); + const std::string& auth_token, const ByteArray& raw_auth_token, + std::shared_ptr endpoint_channel); void OnEncryptionSuccessRunnableV3( const NearbyDevice& remote_device, std::unique_ptr<::securegcm::UKey2Handshake> ukey2, absl::string_view auth_token, const ByteArray& raw_auth_token, - const EndpointChannel& endpoint_channel, + std::shared_ptr endpoint_channel, const NearbyDeviceProvider& device_provider); - void OnEncryptionFailureRunnable(const std::string& endpoint_id, - EndpointChannel* endpoint_channel); + void OnEncryptionFailureRunnable( + const std::string& endpoint_id, + std::shared_ptr endpoint_channel); void RegisterDeviceAfterEncryptionSuccess( std::string_view endpoint_id, std::unique_ptr<::securegcm::UKey2Handshake> ukey2, diff --git a/connections/implementation/base_pcp_handler_test.cc b/connections/implementation/base_pcp_handler_test.cc index 10f6bdd7..93f29a5e 100644 --- a/connections/implementation/base_pcp_handler_test.cc +++ b/connections/implementation/base_pcp_handler_test.cc @@ -32,7 +32,8 @@ #include "connections/advertising_options.h" #include "connections/connection_options.h" #include "connections/discovery_options.h" -#include "connections/implementation/analytics/packet_meta_data.h" +#include "connections/implementation/analytics/analytics_recorder.h" +#include "connections/implementation/analytics/mock_analytics_recorder.h" #include "connections/implementation/base_endpoint_channel.h" #include "connections/implementation/bwu_manager.h" #include "connections/implementation/client_proxy.h" @@ -55,8 +56,6 @@ #include "connections/status.h" #include "connections/strategy.h" #include "connections/v3/connection_listening_options.h" -#include "internal/analytics/mock_event_logger.h" -#include "internal/analytics/sharing_log_matchers.h" #include "internal/flags/nearby_flags.h" #include "internal/interop/authentication_status.h" #include "internal/interop/authentication_transport.h" @@ -72,29 +71,21 @@ #include "internal/platform/medium_environment.h" #include "internal/platform/output_stream.h" #include "internal/platform/pipe.h" -#include "internal/proto/analytics/connections_log.pb.h" #include "proto/connections_enums.pb.h" #include "proto/connections_enums.proto.h" -namespace nearby { -namespace connections { +namespace nearby::connections { namespace { -using ::location::nearby::analytics::proto::ConnectionsLog; using ::location::nearby::connections::OsInfo; -using ::location::nearby::proto::connections::EventType; using ::location::nearby::proto::connections::Medium; -using ::nearby::analytics::HasEventType; using ::testing::_; using ::testing::AtLeast; -using ::protobuf_matchers::EqualsProto; -using ::testing::Invoke; using ::testing::Matcher; using ::testing::MockFunction; using ::testing::NiceMock; using ::testing::Return; using ::testing::StrictMock; -using ::testing::proto::Partially; constexpr absl::string_view kTestEndpointId = "REMOTETEST"; @@ -171,7 +162,7 @@ class MockEndpointChannel : public BaseEndpointChannel { output_stream_(std::move(writer)) {} ExceptionOr DoRead() { return BaseEndpointChannel::Read(); } - Exception DoWrite(const ByteArray& data) { + Exception DoWrite(absl::string_view data) { if (broken_write_) { return {Exception::kFailed}; } @@ -182,7 +173,7 @@ class MockEndpointChannel : public BaseEndpointChannel { } MOCK_METHOD(ExceptionOr, Read, (), (override)); - MOCK_METHOD(Exception, Write, (const ByteArray& data), (override)); + MOCK_METHOD(Exception, Write, (absl::string_view data), (override)); MOCK_METHOD(void, CloseImpl, (), (override)); MOCK_METHOD(location::nearby::proto::connections::Medium, GetMedium, (), (const, override)); @@ -454,15 +445,20 @@ class BasePcpHandlerTest }; BasePcpHandlerTest() { - client_ = std::make_unique(&mock_event_logger_); + client_ = std::make_unique(CreateAnalyticsRecorder()); } void SetUp() override { MacAddress::FromString("12:34:56:78:9a:bc", remote_mac_address_); } - void TearDown() override { - env_.Stop(); + void TearDown() override { env_.Stop(); } + + std::unique_ptr CreateAnalyticsRecorder() { + auto recorder = + std::make_unique(); + mock_analytics_recorder_ptr_ = recorder.get(); + return recorder; } void StartAdvertising(ClientProxy* client, MockPcpHandler* pcp_handler, @@ -576,26 +572,26 @@ class BasePcpHandlerTest // the peer channel. The rest of the exchange must happen for the benefit of // DH key exchange. EXPECT_CALL(*channel_a, Read()) - .WillRepeatedly(Invoke( - [channel = channel_a.get()]() { return channel->DoRead(); })); + .WillRepeatedly( + [channel = channel_a.get()]() { return channel->DoRead(); }); EXPECT_CALL(*channel_a, Write(_)) .WillOnce(Return(Exception{Exception::kSuccess})) .WillRepeatedly( - Invoke([channel = channel_a.get()](const ByteArray& data) { + [channel = channel_a.get()](absl::string_view data) { return channel->DoWrite(data); - })); + }); EXPECT_CALL(*channel_a, GetMedium).WillRepeatedly(Return(medium)); EXPECT_CALL(*channel_a, GetLastReadTimestamp) .WillRepeatedly(Return(absl::Now())); EXPECT_CALL(*channel_a, IsPaused).WillRepeatedly(Return(false)); EXPECT_CALL(*channel_b, Read()) - .WillRepeatedly(Invoke( - [channel = channel_b.get()]() { return channel->DoRead(); })); + .WillRepeatedly( + [channel = channel_b.get()]() { return channel->DoRead(); }); EXPECT_CALL(*channel_b, Write(_)) .WillRepeatedly( - Invoke([channel = channel_b.get()](const ByteArray& data) { + [channel = channel_b.get()](absl::string_view data) { return channel->DoWrite(data); - })); + }); EXPECT_CALL(*channel_b, GetMedium).WillRepeatedly(Return(medium)); EXPECT_CALL(*channel_b, GetLastReadTimestamp) .WillRepeatedly(Return(absl::Now())); @@ -622,20 +618,20 @@ class BasePcpHandlerTest // the peer channel. The rest of the exchange must happen for the benefit of // DH key exchange. EXPECT_CALL(*channel_a, Read()) - .WillRepeatedly(Invoke( - [channel = channel_a.get()]() { return channel->DoRead(); })); + .WillRepeatedly( + [channel = channel_a.get()]() { return channel->DoRead(); }); EXPECT_CALL(*channel_a, GetMedium).WillRepeatedly(Return(medium)); EXPECT_CALL(*channel_a, GetLastReadTimestamp) .WillRepeatedly(Return(absl::Now())); EXPECT_CALL(*channel_a, IsPaused).WillRepeatedly(Return(false)); EXPECT_CALL(*channel_b, Read()) - .WillRepeatedly(Invoke( - [channel = channel_b.get()]() { return channel->DoRead(); })); + .WillRepeatedly( + [channel = channel_b.get()]() { return channel->DoRead(); }); EXPECT_CALL(*channel_b, Write(_)) .WillRepeatedly( - Invoke([channel = channel_b.get()](const ByteArray& data) { + [channel = channel_b.get()](absl::string_view data) { return channel->DoWrite(data); - })); + }); EXPECT_CALL(*channel_b, GetMedium).WillRepeatedly(Return(medium)); EXPECT_CALL(*channel_b, GetLastReadTimestamp) .WillRepeatedly(Return(absl::Now())); @@ -646,7 +642,7 @@ class BasePcpHandlerTest void RequestConnection( const std::string& endpoint_id, std::unique_ptr channel_a, - MockEndpointChannel* channel_b, ClientProxy* client, + std::shared_ptr channel_b, ClientProxy* client, MockPcpHandler* pcp_handler, location::nearby::proto::connections::Medium connect_medium, std::atomic_int* flag = nullptr, @@ -675,15 +671,15 @@ class BasePcpHandlerTest auto allowed_mediums = pcp_handler->GetDiscoveryMediums(client); EXPECT_CALL(*pcp_handler, ConnectImpl) - .WillOnce(Invoke([&channel_a, connect_medium]( - ClientProxy* client, - MockPcpHandler::DiscoveredEndpoint* endpoint) { + .WillOnce([&channel_a, connect_medium]( + ClientProxy* client, + MockPcpHandler::DiscoveredEndpoint* endpoint) { return MockPcpHandler::ConnectImplResult{ .medium = connect_medium, .status = {Status::kSuccess}, .endpoint_channel = std::move(channel_a), }; - })); + }); for (const auto& discovered_medium : allowed_mediums) { pcp_handler->OnEndpointFound( @@ -716,7 +712,7 @@ class BasePcpHandlerTest void RequestConnectionV3( const NearbyDevice& remote_device, std::unique_ptr channel_a, - MockEndpointChannel* channel_b, ClientProxy* client, + std::shared_ptr channel_b, ClientProxy* client, MockPcpHandler* pcp_handler, location::nearby::proto::connections::Medium connect_medium, FakePresenceDeviceProvider* fake_presence_device_provider, @@ -756,15 +752,15 @@ class BasePcpHandlerTest EXPECT_CALL(*pcp_handler, ConnectImpl) .WillRepeatedly( - Invoke([&channel_a, connect_medium]( - ClientProxy* client, - MockPcpHandler::DiscoveredEndpoint* endpoint) { + [&channel_a, connect_medium]( + ClientProxy* client, + MockPcpHandler::DiscoveredEndpoint* endpoint) { return MockPcpHandler::ConnectImplResult{ .medium = connect_medium, .status = {Status::kSuccess}, .endpoint_channel = std::move(channel_a), }; - })); + }); for (const auto& discovered_medium : allowed_mediums) { pcp_handler->OnEndpointFound( @@ -796,7 +792,7 @@ class BasePcpHandlerTest void RequestConnectionWifiLanFail( const std::string& endpoint_id, std::unique_ptr channel_a, - MockEndpointChannel* channel_b, ClientProxy* client, + std::shared_ptr channel_b, ClientProxy* client, MockPcpHandler* pcp_handler, std::atomic_int* flag = nullptr, Status expected_result = {Status::kSuccess}) { ConnectionRequestInfo info{ @@ -824,8 +820,8 @@ class BasePcpHandlerTest EXPECT_CALL(*pcp_handler, ConnectImpl) .WillRepeatedly( - Invoke([&channel_a](ClientProxy* client, - MockPcpHandler::DiscoveredEndpoint* endpoint) { + [&channel_a](ClientProxy* client, + MockPcpHandler::DiscoveredEndpoint* endpoint) { if (endpoint->medium == location::nearby::proto::connections::WIFI_LAN) { LOG(INFO) << "Connect with Medium WIFI_LAN failed."; @@ -844,7 +840,7 @@ class BasePcpHandlerTest .endpoint_channel = std::move(channel_a), }; } - })); + }); for (const auto& discovered_medium : allowed_mediums) { pcp_handler->OnEndpointFound( @@ -900,7 +896,7 @@ class BasePcpHandlerTest MediumEnvironment& env_ = MediumEnvironment::Instance(); NiceMock mock_device_; MacAddress remote_mac_address_; - nearby::analytics::MockEventLogger mock_event_logger_; + nearby::analytics::MockAnalyticsRecorder* mock_analytics_recorder_ptr_; std::unique_ptr client_; }; @@ -1137,12 +1133,13 @@ TEST_F(BasePcpHandlerTest, WifiMediumFailFallBackToBT) { auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; - auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(AtLeast(0)); - RequestConnectionWifiLanFail(endpoint_id, std::move(channel_a), - channel_b.get(), client_.get(), &pcp_handler); + RequestConnectionWifiLanFail(endpoint_id, std::move(channel_a), channel_b, + client_.get(), &pcp_handler); LOG(INFO) << "RequestConnection complete"; channel_b->Close(); bwu.Shutdown(); @@ -1162,12 +1159,13 @@ TEST_P(BasePcpHandlerTest, RequestConnectionChangesState) { auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; - auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(AtLeast(0)); - RequestConnection("1234", std::move(channel_a), channel_b.get(), - client_.get(), &pcp_handler, connect_medium); + RequestConnection("1234", std::move(channel_a), channel_b, client_.get(), + &pcp_handler, connect_medium); LOG(INFO) << "RequestConnection complete"; EXPECT_TRUE(pcp_handler.HasOutgoingConnections(client_.get())); EXPECT_FALSE(pcp_handler.HasIncomingConnections(client_.get())); @@ -1208,12 +1206,13 @@ TEST_P(BasePcpHandlerTest, CanRequestConnectionPresence) { auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; - auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(AtLeast(0)); - RequestConnection("1234", std::move(channel_a), channel_b.get(), - client_.get(), &pcp_handler, connect_medium); + RequestConnection("1234", std::move(channel_a), channel_b, client_.get(), + &pcp_handler, connect_medium); LOG(INFO) << "RequestConnection complete"; channel_b->Close(); bwu.Shutdown(); @@ -1237,12 +1236,13 @@ TEST_P(BasePcpHandlerTest, CanRequestConnectionLegacy) { auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; - auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(AtLeast(0)); - RequestConnection("1234", std::move(channel_a), channel_b.get(), - client_.get(), &pcp_handler, connect_medium); + RequestConnection("1234", std::move(channel_a), channel_b, client_.get(), + &pcp_handler, connect_medium); LOG(INFO) << "RequestConnection complete"; channel_b->Close(); bwu.Shutdown(); @@ -1267,11 +1267,12 @@ TEST_P(BasePcpHandlerTest, RequestConnectionV3) { auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; - const auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(AtLeast(0)); - RequestConnectionV3(mock_device_, std::move(channel_a), channel_b.get(), + RequestConnectionV3(mock_device_, std::move(channel_a), channel_b, client_.get(), &pcp_handler, connect_medium, &provider); LOG(INFO) << "RequestConnectionV3 complete"; channel_b->Close(); @@ -1298,12 +1299,13 @@ TEST_P(BasePcpHandlerTest, RequestConnectionV3_AuthenticationFailure) { auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; - const auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(AtLeast(0)); RequestConnectionV3( - mock_device_, std::move(channel_a), channel_b.get(), client_.get(), + mock_device_, std::move(channel_a), channel_b, client_.get(), &pcp_handler, connect_medium, &provider, /*flag=*/nullptr, /*expected_result=*/{Status::kSuccess}, /*expected_authentication_status=*/AuthenticationStatus::kFailure); @@ -1329,7 +1331,8 @@ TEST_P(BasePcpHandlerTest, RequestConnectionV3_ConnectImplFailure) { auto mediums = pcp_handler.GetDiscoveryMediums(client_.get()); auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnectionForConnectFailure(connect_medium); - const auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_b, CloseImpl).Times(1); EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(AtLeast(0)); ConnectionRequestInfo info{ @@ -1353,7 +1356,7 @@ TEST_P(BasePcpHandlerTest, RequestConnectionV3_ConnectImplFailure) { auto allowed_mediums = pcp_handler.GetDiscoveryMediums(client_.get()); EXPECT_CALL(pcp_handler, ConnectImpl) - .WillRepeatedly(Invoke( + .WillRepeatedly( [connect_medium](ClientProxy* client, MockPcpHandler::DiscoveredEndpoint* endpoint) { return MockPcpHandler::ConnectImplResult{ @@ -1361,7 +1364,7 @@ TEST_P(BasePcpHandlerTest, RequestConnectionV3_ConnectImplFailure) { .status = {Status::kError}, .endpoint_channel = nullptr, }; - })); + }); for (const auto& discovered_medium : allowed_mediums) { pcp_handler.OnEndpointFound( @@ -1404,7 +1407,8 @@ TEST_P(BasePcpHandlerTest, RequestConnection_ConnectImplFailure) { auto mediums = pcp_handler.GetDiscoveryMediums(client_.get()); auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnectionForConnectFailure(connect_medium); - const auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_b, CloseImpl).Times(1); EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(AtLeast(0)); ConnectionRequestInfo info{ @@ -1428,7 +1432,7 @@ TEST_P(BasePcpHandlerTest, RequestConnection_ConnectImplFailure) { auto allowed_mediums = pcp_handler.GetDiscoveryMediums(client_.get()); EXPECT_CALL(pcp_handler, ConnectImpl) - .WillRepeatedly(Invoke( + .WillRepeatedly( [connect_medium](ClientProxy* client, MockPcpHandler::DiscoveredEndpoint* endpoint) { return MockPcpHandler::ConnectImplResult{ @@ -1436,7 +1440,7 @@ TEST_P(BasePcpHandlerTest, RequestConnection_ConnectImplFailure) { .status = {Status::kError}, .endpoint_channel = nullptr, }; - })); + }); for (const auto& discovered_medium : allowed_mediums) { pcp_handler.OnEndpointFound( @@ -1476,12 +1480,13 @@ TEST_P(BasePcpHandlerTest, IoError_RequestConnectionV3Fails) { auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; - auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_a, CloseImpl).Times(AtLeast(1)); EXPECT_CALL(*channel_b, CloseImpl).Times(AtLeast(1)); channel_b->broken_write_ = true; EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(AtLeast(0)); - RequestConnectionV3(mock_device_, std::move(channel_a), channel_b.get(), + RequestConnectionV3(mock_device_, std::move(channel_a), channel_b, client_.get(), &pcp_handler, connect_medium, nullptr, nullptr, {Status::kEndpointIoError}); LOG(INFO) << "RequestConnectionV3 complete"; @@ -1504,13 +1509,14 @@ TEST_P(BasePcpHandlerTest, IoError_RequestConnectionFails) { auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; - auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_a, CloseImpl).Times(AtLeast(1)); EXPECT_CALL(*channel_b, CloseImpl).Times(AtLeast(1)); channel_b->broken_write_ = true; EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(AtLeast(0)); - RequestConnection(endpoint_id, std::move(channel_a), channel_b.get(), - client_.get(), &pcp_handler, connect_medium, nullptr, + RequestConnection(endpoint_id, std::move(channel_a), channel_b, client_.get(), + &pcp_handler, connect_medium, nullptr, {Status::kEndpointIoError}); LOG(INFO) << "RequestConnection complete"; channel_b->Close(); @@ -1532,11 +1538,12 @@ TEST_P(BasePcpHandlerTest, AcceptConnectionChangesState) { auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; - auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); - RequestConnection(endpoint_id, std::move(channel_a), channel_b.get(), - client_.get(), &pcp_handler, connect_medium); + RequestConnection(endpoint_id, std::move(channel_a), channel_b, client_.get(), + &pcp_handler, connect_medium); LOG(INFO) << "Attempting to accept connection: id=" << endpoint_id; EXPECT_EQ(pcp_handler.AcceptConnection(client_.get(), endpoint_id, {}), Status{Status::kSuccess}); @@ -1560,9 +1567,10 @@ TEST_P(BasePcpHandlerTest, RejectConnectionChangesState) { auto mediums = pcp_handler.GetDiscoveryMediums(client_.get()); auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnection(connect_medium); - auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(1); - RequestConnection(endpoint_id, std::move(channel_pair.first), channel_b.get(), + RequestConnection(endpoint_id, std::move(channel_pair.first), channel_b, client_.get(), &pcp_handler, connect_medium); LOG(INFO) << "Attempting to reject connection: id=" << endpoint_id; EXPECT_EQ(pcp_handler.RejectConnection(client_.get(), endpoint_id), @@ -1582,17 +1590,17 @@ TEST_P(BasePcpHandlerTest, OnIncomingFrameChangesState) { EndpointManager em(&ecm); BwuManager bwu(m, em, ecm, {}, {}); MockPcpHandler pcp_handler(&m, &em, &ecm, &bwu); - analytics::PacketMetaData packet_meta_data; StartDiscovery(client_.get(), &pcp_handler); auto mediums = pcp_handler.GetDiscoveryMediums(client_.get()); auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; - auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); - RequestConnection(endpoint_id, std::move(channel_a), channel_b.get(), - client_.get(), &pcp_handler, connect_medium); + RequestConnection(endpoint_id, std::move(channel_a), channel_b, client_.get(), + &pcp_handler, connect_medium); LOG(INFO) << "Attempting to accept connection: id=" << endpoint_id; EXPECT_CALL(mock_connection_listener_.accepted_cb, Call).Times(1); EXPECT_CALL(mock_connection_listener_.disconnected_cb, Call) @@ -1601,11 +1609,11 @@ TEST_P(BasePcpHandlerTest, OnIncomingFrameChangesState) { Status{Status::kSuccess}); LOG(INFO) << "Simulating remote accept: id=" << endpoint_id; OsInfo os_info; - auto frame = parser::FromBytes(parser::ForConnectionResponse( - Status::kSuccess, os_info, /*multiplex_socket_bitmask=*/0)); + auto frame = parser::FromBytes( + parser::ForConnectionResponse(Status::kSuccess, os_info)); EXPECT_CALL(mock_connection_listener_.bandwidth_changed_cb, Call).Times(1); pcp_handler.OnIncomingFrame(frame.result(), endpoint_id, client_.get(), - connect_medium, packet_meta_data); + connect_medium); LOG(INFO) << "Closing connection: id=" << endpoint_id; channel_b->Close(); bwu.Shutdown(); @@ -1629,10 +1637,11 @@ TEST_P(BasePcpHandlerTest, DestructorIsCalledOnProtocolEndpoint) { auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; - auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); - RequestConnection(endpoint_id, std::move(channel_a), channel_b.get(), + RequestConnection(endpoint_id, std::move(channel_a), channel_b, client_.get(), &pcp_handler, connect_medium, &destroyed_flag); mediums_count = mediums.size(); @@ -1671,11 +1680,12 @@ TEST_P(BasePcpHandlerTest, MultipleMediumsProduceSingleEndpointLostEvent) { auto connect_medium = mediums[mediums.size() - 1]; auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; - auto& channel_b = channel_pair.second; + std::shared_ptr channel_b = + std::move(channel_pair.second); EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); EXPECT_CALL(mock_discovery_listener_.endpoint_lost_cb, Call).Times(1); - RequestConnection(endpoint_id, std::move(channel_a), channel_b.get(), + RequestConnection(endpoint_id, std::move(channel_a), channel_b, client_.get(), &pcp_handler, connect_medium, &destroyed_flag); auto allowed_mediums = pcp_handler.GetDiscoveryMediums(client_.get()); @@ -1787,7 +1797,7 @@ TEST_F(BasePcpHandlerTest, InjectEndpoint) { EXPECT_TRUE(client_->IsDiscovering()); EXPECT_CALL(pcp_handler, InjectEndpointImpl(client_.get(), service_id, _)) - .WillOnce(Invoke([&pcp_handler, &endpoint_id]( + .WillOnce([&pcp_handler, &endpoint_id]( ClientProxy* client, const std::string& service_id, const OutOfBandConnectionMetadata& metadata) { pcp_handler.OnEndpointFound( @@ -1803,7 +1813,7 @@ TEST_F(BasePcpHandlerTest, InjectEndpoint) { MockContext{nullptr}, })); return Status{Status::kSuccess}; - })); + }); pcp_handler.InjectEndpoint( client_.get(), service_id, OutOfBandConnectionMetadata{ @@ -1851,30 +1861,30 @@ TEST_F(BasePcpHandlerTest, ::testing::InSequence seq; EXPECT_CALL(mock_discovery_listener_.endpoint_found_cb, Call) - .WillOnce(Invoke([id = endpoint_id](const std::string& endpoint_id, + .WillOnce([id = endpoint_id](const std::string& endpoint_id, const ByteArray& endpoint_info, const std::string& service_id) { EXPECT_EQ(endpoint_id, id); EXPECT_EQ(endpoint_info, ByteArray{"ABCD"}); - })); + }); EXPECT_CALL(mock_discovery_listener_.endpoint_lost_cb, Call) - .WillOnce(Invoke([id = endpoint_id](const std::string& endpoint_id) { + .WillOnce([id = endpoint_id](const std::string& endpoint_id) { EXPECT_EQ(endpoint_id, id); - })); + }); EXPECT_CALL(mock_discovery_listener_.endpoint_found_cb, Call) - .WillOnce(Invoke([id = endpoint_id](const std::string& endpoint_id, + .WillOnce([id = endpoint_id](const std::string& endpoint_id, const ByteArray& endpoint_info, const std::string& service_id) { EXPECT_EQ(endpoint_id, id); EXPECT_EQ(endpoint_info, ByteArray{"ABCDEF"}); - })); + }); EXPECT_CALL(mock_discovery_listener_.endpoint_lost_cb, Call) - .WillOnce(Invoke([id = endpoint_id](const std::string& endpoint_id) { + .WillOnce([id = endpoint_id](const std::string& endpoint_id) { EXPECT_EQ(endpoint_id, id); - })); + }); // Found endpoint on Bluetooth pcp_handler.OnEndpointFound( @@ -1964,7 +1974,7 @@ TEST_F(BasePcpHandlerTest, TestStartStopEndpointLostAlarm) { EXPECT_TRUE(client_->IsDiscovering()); EXPECT_CALL(pcp_handler, InjectEndpointImpl) - .WillOnce(Invoke([&pcp_handler, &endpoint_id]( + .WillOnce([&pcp_handler, &endpoint_id]( ClientProxy* client, const std::string& service_id, const OutOfBandConnectionMetadata& metadata) { pcp_handler.OnEndpointFound( @@ -1980,7 +1990,7 @@ TEST_F(BasePcpHandlerTest, TestStartStopEndpointLostAlarm) { MockContext{nullptr}, })); return Status{Status::kSuccess}; - })); + }); pcp_handler.InjectEndpoint( client_.get(), service_id, OutOfBandConnectionMetadata{ @@ -2027,7 +2037,7 @@ TEST_F(BasePcpHandlerTest, TestStartEndpointLostByMediumAlarms) { EXPECT_TRUE(client_->IsDiscovering()); EXPECT_CALL(pcp_handler, InjectEndpointImpl) - .WillOnce(Invoke([&pcp_handler, &endpoint_id]( + .WillOnce([&pcp_handler, &endpoint_id]( ClientProxy* client, const std::string& service_id, const OutOfBandConnectionMetadata& metadata) { pcp_handler.OnEndpointFound( @@ -2043,7 +2053,7 @@ TEST_F(BasePcpHandlerTest, TestStartEndpointLostByMediumAlarms) { MockContext{nullptr}, })); return Status{Status::kSuccess}; - })); + }); pcp_handler.InjectEndpoint( client_.get(), service_id, OutOfBandConnectionMetadata{ @@ -2094,7 +2104,7 @@ TEST_F(BasePcpHandlerTest, TestEndpointFoundStopsAlarm) { EXPECT_CALL(pcp_handler, InjectEndpointImpl) .Times(2) .WillRepeatedly( - Invoke([&pcp_handler, &endpoint_id, &first_call]( + [&pcp_handler, &endpoint_id, &first_call]( ClientProxy* client, const std::string& service_id, const OutOfBandConnectionMetadata& metadata) { ByteArray endpoint_info; @@ -2117,7 +2127,7 @@ TEST_F(BasePcpHandlerTest, TestEndpointFoundStopsAlarm) { MockContext{nullptr}, })); return Status{Status::kSuccess}; - })); + }); pcp_handler.InjectEndpoint( client_.get(), service_id, OutOfBandConnectionMetadata{ @@ -2271,20 +2281,20 @@ TEST_F(BasePcpHandlerTest, TestDeviceFilterForConnectionsWithUnknown) { ASSERT_TRUE(client_->IsListeningForIncomingConnections()); ASSERT_TRUE(pcp_handler.CanReceiveIncomingConnection(client_.get())); auto channel_pair = SetupConnection(Medium::BLUETOOTH); - ByteArray serialized_frame = parser::ForConnectionRequestConnections( + std::string serialized_frame = parser::ForConnectionRequestConnections( {}, { .local_endpoint_id = "ABCD", .local_endpoint_info = ByteArray("local endpoint"), }); location::nearby::connections::OfflineFrame frame; - frame.ParseFromString(serialized_frame.AsStringView()); + frame.ParseFromString(serialized_frame); frame.mutable_v1()->mutable_connection_request()->clear_connections_device(); frame.mutable_v1()->mutable_connection_request()->clear_presence_device(); ASSERT_FALSE(frame.v1().connection_request().has_connections_device()); ASSERT_FALSE(frame.v1().connection_request().has_presence_device()); // do a dummy write to get to the actual write. - channel_pair.first->Write(ByteArray()); - channel_pair.first->Write(ByteArray(frame.SerializeAsString())); + channel_pair.first->Write(""); + channel_pair.first->Write(frame.SerializeAsString()); EXPECT_TRUE(pcp_handler .OnIncomingConnection( client_.get(), ByteArray("remote endpoint"), @@ -2321,20 +2331,20 @@ TEST_F(BasePcpHandlerTest, TestDeviceFilterForPresenceWithUnknown) { ASSERT_TRUE(client_->IsListeningForIncomingConnections()); ASSERT_TRUE(pcp_handler.CanReceiveIncomingConnection(client_.get())); auto channel_pair = SetupConnection(Medium::BLUETOOTH); - ByteArray serialized_frame = parser::ForConnectionRequestConnections( + std::string serialized_frame = parser::ForConnectionRequestConnections( {}, { .local_endpoint_id = "ABCD", .local_endpoint_info = ByteArray("local endpoint"), }); location::nearby::connections::OfflineFrame frame; - frame.ParseFromString(serialized_frame.AsStringView()); + frame.ParseFromString(serialized_frame); frame.mutable_v1()->mutable_connection_request()->clear_connections_device(); frame.mutable_v1()->mutable_connection_request()->clear_presence_device(); ASSERT_FALSE(frame.v1().connection_request().has_connections_device()); ASSERT_FALSE(frame.v1().connection_request().has_presence_device()); // do a dummy write to get to the actual write. - channel_pair.first->Write(ByteArray()); - channel_pair.first->Write(ByteArray(frame.SerializeAsString())); + channel_pair.first->Write(""); + channel_pair.first->Write(frame.SerializeAsString()); EXPECT_EQ(pcp_handler .OnIncomingConnection( client_.get(), ByteArray("remote endpoint"), @@ -2370,21 +2380,21 @@ TEST_F(BasePcpHandlerTest, TestDeviceFilterForPresenceWithConnections) { ASSERT_TRUE(client_->IsListeningForIncomingConnections()); ASSERT_TRUE(pcp_handler.CanReceiveIncomingConnection(client_.get())); auto channel_pair = SetupConnection(Medium::BLUETOOTH); - ByteArray serialized_frame = parser::ForConnectionRequestConnections( + std::string serialized_frame = parser::ForConnectionRequestConnections( {}, { .local_endpoint_id = "ABCD", .local_endpoint_info = ByteArray("local endpoint"), }); location::nearby::connections::OfflineFrame frame; - frame.ParseFromString(serialized_frame.AsStringView()); + frame.ParseFromString(serialized_frame); frame.mutable_v1() ->mutable_connection_request() ->mutable_connections_device() ->set_endpoint_id("ABCD"); ASSERT_TRUE(frame.v1().connection_request().has_connections_device()); // do a dummy write to get to the actual write. - channel_pair.first->Write(ByteArray()); - channel_pair.first->Write(ByteArray(frame.SerializeAsString())); + channel_pair.first->Write(""); + channel_pair.first->Write(frame.SerializeAsString()); EXPECT_EQ(pcp_handler .OnIncomingConnection( client_.get(), ByteArray("remote endpoint"), @@ -2420,21 +2430,21 @@ TEST_F(BasePcpHandlerTest, TestDeviceFilterForPresenceWithPresence) { ASSERT_TRUE(client_->IsListeningForIncomingConnections()); ASSERT_TRUE(pcp_handler.CanReceiveIncomingConnection(client_.get())); auto channel_pair = SetupConnection(Medium::BLUETOOTH); - ByteArray serialized_frame = parser::ForConnectionRequestConnections( + std::string serialized_frame = parser::ForConnectionRequestConnections( {}, { .local_endpoint_id = "ABCD", .local_endpoint_info = ByteArray("local endpoint"), }); location::nearby::connections::OfflineFrame frame; - frame.ParseFromString(serialized_frame.AsStringView()); + frame.ParseFromString(serialized_frame); frame.mutable_v1() ->mutable_connection_request() ->mutable_presence_device() ->set_endpoint_id("ABCD"); ASSERT_TRUE(frame.v1().connection_request().has_presence_device()); // do a dummy write to get to the actual write. - channel_pair.first->Write(ByteArray()); - channel_pair.first->Write(ByteArray(frame.SerializeAsString())); + channel_pair.first->Write(""); + channel_pair.first->Write(frame.SerializeAsString()); EXPECT_TRUE(pcp_handler .OnIncomingConnection( client_.get(), ByteArray("remote endpoint"), @@ -2469,21 +2479,21 @@ TEST_F(BasePcpHandlerTest, TestDeviceFilterForConnectionsWithConnections) { ASSERT_TRUE(client_->IsListeningForIncomingConnections()); ASSERT_TRUE(pcp_handler.CanReceiveIncomingConnection(client_.get())); auto channel_pair = SetupConnection(Medium::BLUETOOTH); - ByteArray serialized_frame = parser::ForConnectionRequestConnections( + std::string serialized_frame = parser::ForConnectionRequestConnections( {}, { .local_endpoint_id = "ABCD", .local_endpoint_info = ByteArray("local endpoint"), }); location::nearby::connections::OfflineFrame frame; - frame.ParseFromString(serialized_frame.AsStringView()); + frame.ParseFromString(serialized_frame); frame.mutable_v1() ->mutable_connection_request() ->mutable_connections_device() ->set_endpoint_id("ABCD"); ASSERT_TRUE(frame.v1().connection_request().has_connections_device()); // do a dummy write to get to the actual write. - channel_pair.first->Write(ByteArray()); - channel_pair.first->Write(ByteArray(frame.SerializeAsString())); + channel_pair.first->Write(""); + channel_pair.first->Write(frame.SerializeAsString()); EXPECT_TRUE(pcp_handler .OnIncomingConnection( client_.get(), ByteArray("remote endpoint"), @@ -2518,21 +2528,21 @@ TEST_F(BasePcpHandlerTest, TestDeviceFilterForConnectionsWithPresence) { ASSERT_TRUE(client_->IsListeningForIncomingConnections()); ASSERT_TRUE(pcp_handler.CanReceiveIncomingConnection(client_.get())); auto channel_pair = SetupConnection(Medium::BLUETOOTH); - ByteArray serialized_frame = parser::ForConnectionRequestConnections( + std::string serialized_frame = parser::ForConnectionRequestConnections( {}, { .local_endpoint_id = "ABCD", .local_endpoint_info = ByteArray("local endpoint"), }); location::nearby::connections::OfflineFrame frame; - frame.ParseFromString(serialized_frame.AsStringView()); + frame.ParseFromString(serialized_frame); frame.mutable_v1() ->mutable_connection_request() ->mutable_presence_device() ->set_endpoint_id("ABCD"); ASSERT_TRUE(frame.v1().connection_request().has_presence_device()); // do a dummy write to get to the actual write. - channel_pair.first->Write(ByteArray()); - channel_pair.first->Write(ByteArray(frame.SerializeAsString())); + channel_pair.first->Write(""); + channel_pair.first->Write(frame.SerializeAsString()); EXPECT_EQ(pcp_handler .OnIncomingConnection( client_.get(), ByteArray("remote endpoint"), @@ -2544,7 +2554,8 @@ TEST_F(BasePcpHandlerTest, TestDeviceFilterForConnectionsWithPresence) { } TEST_F(BasePcpHandlerTest, IncomingConnectionFailsWithEmptyEndpointId) { - env_.Start(); + env_.Start({.use_simulated_clock = true}); + client_ = std::make_unique(CreateAnalyticsRecorder()); Mediums m; EndpointChannelManager ecm; EndpointManager em(&ecm); @@ -2561,6 +2572,8 @@ TEST_F(BasePcpHandlerTest, IncomingConnectionFailsWithEmptyEndpointId) { MockPcpHandler::StartOperationResult{.status = {Status::kSuccess}})); EXPECT_CALL(pcp_handler, CanReceiveIncomingConnection) .WillRepeatedly(Return(true)); + EXPECT_CALL(*mock_analytics_recorder_ptr_, + OnStartedIncomingConnectionListening(_)); EXPECT_TRUE(pcp_handler .StartListeningForIncomingConnections(client_.get(), "service", options, {}) @@ -2568,7 +2581,7 @@ TEST_F(BasePcpHandlerTest, IncomingConnectionFailsWithEmptyEndpointId) { ASSERT_TRUE(client_->IsListeningForIncomingConnections()); ASSERT_TRUE(pcp_handler.CanReceiveIncomingConnection(client_.get())); auto channel_pair = SetupConnection(Medium::BLUETOOTH); - ByteArray serialized_frame = parser::ForConnectionRequestConnections( + std::string serialized_frame = parser::ForConnectionRequestConnections( {}, { .local_endpoint_id = "", .local_endpoint_info = ByteArray("local endpoint"), @@ -2576,53 +2589,19 @@ TEST_F(BasePcpHandlerTest, IncomingConnectionFailsWithEmptyEndpointId) { // At this point the connection request doesn't have an endpoint ID field // set, so we do that here. location::nearby::connections::OfflineFrame frame; - frame.ParseFromString(serialized_frame.AsStringView()); + frame.ParseFromString(serialized_frame); frame.mutable_v1()->mutable_connection_request()->set_endpoint_id(""); ASSERT_TRUE(frame.v1().connection_request().has_endpoint_id()); // do a dummy write to get to the actual write. - channel_pair.first->Write(ByteArray()); - channel_pair.first->Write(ByteArray(frame.SerializeAsString())); - absl::string_view expected_log = R"pb( - event_type: CLIENT_SESSION - client_session { - strategy_session { - connection_attempt { - type: INITIAL - direction: INCOMING - medium: BLUETOOTH - attempt_result: RESULT_ERROR - operation_result { - result_category: CATEGORY_CONNECTIVITY_ERROR - result_code: CONNECTIVITY_CHANNEL_IO_ERROR_ON_BT - } - } - } - } - )pb"; - absl::string_view client_session_log = R"pb( - event_type: CLIENT_SESSION - client_session { duration_millis: 0 } - version: "v1.5.0" - )pb"; - EXPECT_CALL(mock_event_logger_, - Log(Matcher( - HasEventType(EventType::STOP_STRATEGY_SESSION)))) - .Times(1); - EXPECT_CALL(mock_event_logger_, - Log(Matcher( - HasEventType(EventType::STOP_CLIENT_SESSION)))) - .Times(3); - EXPECT_CALL(mock_event_logger_, - Log(Matcher( - HasEventType(EventType::START_CLIENT_SESSION)))) - .Times(3); + channel_pair.first->Write(""); + channel_pair.first->Write(frame.SerializeAsString()); + EXPECT_CALL(*mock_analytics_recorder_ptr_, LogSession()).Times(3); + EXPECT_CALL(*mock_analytics_recorder_ptr_, LogStartSession()).Times(3); EXPECT_CALL( - mock_event_logger_, - Log(Matcher(EqualsProto(client_session_log)))) - .Times(2); - EXPECT_CALL(mock_event_logger_, Log(Matcher( - Partially(EqualsProto(expected_log))))); - + *mock_analytics_recorder_ptr_, + OnIncomingConnectionAttempt( + location::nearby::proto::connections::INITIAL, Medium::BLUETOOTH, + location::nearby::proto::connections::RESULT_ERROR, _, _, _)); EXPECT_EQ(pcp_handler .OnIncomingConnection( client_.get(), ByteArray("remote endpoint"), @@ -2636,7 +2615,7 @@ TEST_F(BasePcpHandlerTest, IncomingConnectionFailsWithEmptyEndpointId) { TEST_F(BasePcpHandlerTest, IncomingConnectionWithNoDataFailsWithoutLogging) { env_.Start({.use_simulated_clock = true}); // Recreate ClientProxy so that AnalyticRecorder uses simulated clock. - client_ = std::make_unique(&mock_event_logger_); + client_ = std::make_unique(CreateAnalyticsRecorder()); Mediums m; EndpointChannelManager ecm; EndpointManager em(&ecm); @@ -2653,6 +2632,8 @@ TEST_F(BasePcpHandlerTest, IncomingConnectionWithNoDataFailsWithoutLogging) { MockPcpHandler::StartOperationResult{.status = {Status::kSuccess}})); EXPECT_CALL(pcp_handler, CanReceiveIncomingConnection) .WillRepeatedly(Return(true)); + EXPECT_CALL(*mock_analytics_recorder_ptr_, + OnStartedIncomingConnectionListening(_)); EXPECT_TRUE(pcp_handler .StartListeningForIncomingConnections(client_.get(), "service", options, {}) @@ -2664,59 +2645,8 @@ TEST_F(BasePcpHandlerTest, IncomingConnectionWithNoDataFailsWithoutLogging) { std::move(input_a), std::move(output_a)); EXPECT_CALL(*input_channel, Read()) .WillRepeatedly(Return(ExceptionOr(Exception::kNoData))); - absl::string_view expected_log = R"pb( - event_type: CLIENT_SESSION - client_session { - strategy_session { - connection_attempt { - type: INITIAL - direction: INCOMING - attempt_result: RESULT_ERROR - } - } - } - )pb"; - absl::string_view client_session_log = R"pb( - event_type: CLIENT_SESSION - client_session { duration_millis: 0 } - version: "v1.5.0" - )pb"; - absl::string_view client_session_log2 = R"pb( - event_type: CLIENT_SESSION - client_session { - duration_millis: 0 - strategy_session { - duration_millis: 0 - strategy: UNKNOWN_STRATEGY - role: ADVERTISER - } - } - version: "v1.5.0" - )pb"; - EXPECT_CALL(mock_event_logger_, - Log(Matcher( - HasEventType(EventType::STOP_STRATEGY_SESSION)))) - .Times(1); - EXPECT_CALL(mock_event_logger_, - Log(Matcher( - HasEventType(EventType::STOP_CLIENT_SESSION)))) - .Times(3); - EXPECT_CALL(mock_event_logger_, - Log(Matcher( - HasEventType(EventType::START_CLIENT_SESSION)))) - .Times(3); - EXPECT_CALL( - mock_event_logger_, - Log(Matcher(EqualsProto(client_session_log)))) - .Times(2); - EXPECT_CALL( - mock_event_logger_, - Log(Matcher(EqualsProto(client_session_log2)))); - EXPECT_CALL( - mock_event_logger_, - Log(Matcher(Partially(EqualsProto(expected_log))))) - .Times(0); - + EXPECT_CALL(*mock_analytics_recorder_ptr_, LogSession()).Times(3); + EXPECT_CALL(*mock_analytics_recorder_ptr_, LogStartSession()).Times(3); EXPECT_EQ( pcp_handler .OnIncomingConnection(client_.get(), ByteArray("remote endpoint"), @@ -3020,5 +2950,4 @@ TEST_F(BasePcpHandlerTest, TestForceUpdateEndpointIdAdvertisingOption) { } } // namespace -} // namespace connections -} // namespace nearby +} // namespace nearby::connections diff --git a/connections/implementation/bwu_handler.h b/connections/implementation/bwu_handler.h index cd0fc073..b1704983 100644 --- a/connections/implementation/bwu_handler.h +++ b/connections/implementation/bwu_handler.h @@ -21,7 +21,6 @@ #include "absl/functional/any_invocable.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" -#include "internal/platform/byte_array.h" #include "internal/platform/expected.h" namespace nearby { @@ -53,7 +52,7 @@ class BwuHandler { // that hasn't already been done), and returns a serialized UpgradePathInfo // that can be sent to the Responder. // @BwuHandlerThread - virtual ByteArray InitializeUpgradedMediumForEndpoint( + virtual std::string InitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& service_id, const std::string& endpoint_id) = 0; diff --git a/connections/implementation/bwu_manager.cc b/connections/implementation/bwu_manager.cc index fd41cfb2..0cbafc7a 100644 --- a/connections/implementation/bwu_manager.cc +++ b/connections/implementation/bwu_manager.cc @@ -25,9 +25,8 @@ #include "absl/functional/bind_front.h" #include "absl/strings/str_cat.h" #include "absl/time/time.h" +#include "connections/implementation/analytics/analytics_recorder.h" #include "connections/implementation/analytics/connection_attempt_metadata_params.h" -#include "connections/implementation/awdl_bwu_handler.h" -#include "connections/implementation/bluetooth_bwu_handler.h" #include "connections/implementation/bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" @@ -38,16 +37,7 @@ #include "connections/implementation/offline_frames.h" #include "connections/implementation/service_id_constants.h" #include "internal/flags/nearby_flags.h" -#ifdef NO_WEBRTC -#include "connections/implementation/webrtc_bwu_handler_stub.h" -#else -#include "connections/implementation/webrtc_bwu_handler.h" -#endif -#include "connections/implementation/wifi_direct_bwu_handler.h" -#include "connections/implementation/wifi_hotspot_bwu_handler.h" -#include "connections/implementation/wifi_lan_bwu_handler.h" #include "connections/medium_selector.h" -#include "internal/platform/byte_array.h" #include "internal/platform/cancelable_alarm.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/expected.h" @@ -72,6 +62,8 @@ using ::location::nearby::proto::connections::ConnectionAttemptResult; using ::location::nearby::proto::connections::ConnectionAttemptType; using ::location::nearby::proto::connections::DisconnectionReason; using ::location::nearby::proto::connections::OperationResultCode; +using ::nearby::analytics::AnalyticsRecorder; + } // namespace BwuManager::BwuManager( @@ -135,43 +127,37 @@ void BwuManager::InitBwuHandlers() { if (config_.allow_upgrade_to.awdl) { handlers_.emplace( Medium::AWDL, - std::make_unique( - *mediums_, + mediums_->GetAwdl().CreateBwuHandler( absl::bind_front(&BwuManager::OnIncomingConnection, this))); } if (config_.allow_upgrade_to.wifi_hotspot) { handlers_.emplace( Medium::WIFI_HOTSPOT, - std::make_unique( - *mediums_, + mediums_->GetWifiHotspot().CreateBwuHandler( absl::bind_front(&BwuManager::OnIncomingConnection, this))); } if (config_.allow_upgrade_to.wifi_direct) { handlers_.emplace( Medium::WIFI_DIRECT, - std::make_unique( - *mediums_, + mediums_->GetWifiDirect().CreateBwuHandler( absl::bind_front(&BwuManager::OnIncomingConnection, this))); } if (config_.allow_upgrade_to.wifi_lan) { handlers_.emplace( Medium::WIFI_LAN, - std::make_unique( - *mediums_, + mediums_->GetWifiLan().CreateBwuHandler( absl::bind_front(&BwuManager::OnIncomingConnection, this))); } if (config_.allow_upgrade_to.web_rtc) { handlers_.emplace( Medium::WEB_RTC, - std::make_unique( - *mediums_, + mediums_->GetWebRtc().CreateBwuHandler( absl::bind_front(&BwuManager::OnIncomingConnection, this))); } if (config_.allow_upgrade_to.bluetooth) { handlers_.emplace( Medium::BLUETOOTH, - std::make_unique( - *mediums_, + mediums_->GetBluetoothClassic().CreateBwuHandler( absl::bind_front(&BwuManager::OnIncomingConnection, this))); } } @@ -197,8 +183,9 @@ void BwuManager::Shutdown() { medium_ = Medium::UNKNOWN_MEDIUM; endpoint_id_to_bwu_medium_.clear(); for (auto& medium_handler_pair : handlers_) { - assert(medium_handler_pair.second); - medium_handler_pair.second->RevertInitiatorState(); + if (medium_handler_pair.second != nullptr) { + medium_handler_pair.second->RevertInitiatorState(); + } } handlers_.clear(); @@ -341,12 +328,12 @@ void BwuManager::InitiateBwuForEndpoint(ClientProxy* client, } std::string service_id = channel->GetServiceId(); - ByteArray bytes = handler->InitializeUpgradedMediumForEndpoint( + std::string bytes = handler->InitializeUpgradedMediumForEndpoint( client, service_id, endpoint_id); // Because we grab the endpointChannel first thing, it is possible the // endpointChannel is stale by the time we attempt to write over it. - if (bytes.Empty()) { + if (bytes.empty()) { LOG(ERROR) << "BwuManager couldn't complete the upgrade for endpoint " << endpoint_id << " to medium " << location::nearby::proto::connections::Medium_Name( @@ -401,8 +388,7 @@ void BwuManager::InitiateBwuForEndpoint(ClientProxy* client, void BwuManager::OnIncomingFrame(OfflineFrame& frame, const std::string& endpoint_id, - ClientProxy* client, Medium medium, - PacketMetaData& packet_meta_data) { + ClientProxy* client, Medium medium) { V1Frame::FrameType frame_type = parser::GetFrameType(frame); if (frame_type != V1Frame::BANDWIDTH_UPGRADE_NEGOTIATION) return; @@ -549,7 +535,7 @@ BwuHandler* BwuManager::GetHandlerForMedium(Medium medium) const { } void BwuManager::OnBwuNegotiationFrame( - ClientProxy* client, const BandwidthUpgradeNegotiationFrame frame, + ClientProxy* client, const BandwidthUpgradeNegotiationFrame& frame, const std::string& endpoint_id) { LOG(INFO) << "OnBwuNegotiationFrame: processing incoming " << BandwidthUpgradeNegotiationFrame::EventType_Name( @@ -586,9 +572,19 @@ void BwuManager::OnBwuNegotiationFrame( OperationResultCode::NEARBY_GENERIC_REMOTE_UPGRADE_FAILURE); break; case BandwidthUpgradeNegotiationFrame::LAST_WRITE_TO_PRIOR_CHANNEL: + if (!in_progress_upgrades_.contains(endpoint_id)) { + LOG(ERROR) << "Received LAST_WRITE_TO_PRIOR_CHANNEL for endpoint " + << endpoint_id << " but no upgrade is in progress."; + return; + } ProcessLastWriteToPriorChannelEvent(client, endpoint_id); break; case BandwidthUpgradeNegotiationFrame::SAFE_TO_CLOSE_PRIOR_CHANNEL: + if (!in_progress_upgrades_.contains(endpoint_id)) { + LOG(ERROR) << "Received SAFE_TO_CLOSE_PRIOR_CHANNEL for endpoint " + << endpoint_id << " but no upgrade is in progress."; + return; + } ProcessSafeToClosePriorChannelEvent(client, endpoint_id); break; default: @@ -680,7 +676,7 @@ void BwuManager::OnIncomingConnection( connections_attempt_metadata_params; if (channel != nullptr) { connections_attempt_metadata_params = - client->GetAnalyticsRecorder().BuildConnectionAttemptMetadataParams( + AnalyticsRecorder::BuildConnectionAttemptMetadataParams( channel->GetTechnology(), channel->GetBand(), channel->GetFrequency(), channel->GetTryCount()); connections_attempt_metadata_params->operation_result_code = @@ -891,7 +887,7 @@ void BwuManager::ProcessBwuPathAvailableEvent( if (channel != nullptr) { std::unique_ptr connections_attempt_metadata_params = - client->GetAnalyticsRecorder().BuildConnectionAttemptMetadataParams( + AnalyticsRecorder::BuildConnectionAttemptMetadataParams( channel->GetTechnology(), channel->GetBand(), channel->GetFrequency(), channel->GetTryCount()); connections_attempt_metadata_params->operation_result_code = @@ -1138,7 +1134,7 @@ bool BwuManager::ReadClientIntroductionFrame( auto data = channel->Read(); timeout_alarm.Cancel(); if (!data.ok()) return false; - auto transfer(parser::FromBytes(data.result())); + auto transfer(parser::FromBytes(data.result().AsStringView())); if (!transfer.ok()) { LOG(ERROR) << "In ReadClientIntroductionFrame, attempted to read a " "ClientIntroductionFrame from EndpointChannel " @@ -1189,7 +1185,7 @@ bool BwuManager::ReadClientIntroductionAckFrame(EndpointChannel* channel) { auto data = channel->Read(); timeout_alarm.Cancel(); if (!data.ok()) return false; - auto transfer(parser::FromBytes(data.result())); + auto transfer(parser::FromBytes(data.result().AsStringView())); if (!transfer.ok()) return false; OfflineFrame frame = transfer.result(); if (!frame.has_v1() || !frame.v1().has_bandwidth_upgrade_negotiation()) @@ -1219,9 +1215,8 @@ void BwuManager::ProcessLastWriteToPriorChannelEvent( // loss). But now that we've received this definitive final write over that // prior EndpointChannel, we can let the remote device that they can safely // close their end of this now-dormant EndpointChannel. - EndpointChannel* previous_endpoint_channel = - previous_endpoint_channels_[endpoint_id].get(); - if (!previous_endpoint_channel) { + auto it = previous_endpoint_channels_.find(endpoint_id); + if (it == previous_endpoint_channels_.end()) { LOG(ERROR) << "BwuManager received a BWU_NEGOTIATION.LAST_WRITE_TO_PRIOR_CHANNEL " "OfflineFrame for unknown endpoint " @@ -1229,6 +1224,12 @@ void BwuManager::ProcessLastWriteToPriorChannelEvent( successfully_upgraded_endpoints_.emplace(endpoint_id); return; } + EndpointChannel* previous_endpoint_channel = it->second.get(); + if (!previous_endpoint_channel) { + LOG(ERROR) << "previous_endpoint_channel is null for endpoint " + << endpoint_id; + return; + } LOG(INFO) << "ProcessLastWriteToPriorChannelEvent: service_id=" << previous_endpoint_channel->GetServiceId() @@ -1281,6 +1282,13 @@ void BwuManager::ProcessSafeToClosePriorChannelEvent( // or not (as is the case with Android's Bluetooth sockets, where closing // instantly throws an IOException on the remote device). auto item = previous_endpoint_channels_.extract(endpoint_id); + if (item.empty()) { + LOG(ERROR) + << "BwuManager received a BWU_NEGOTIATION.SAFE_TO_CLOSE_PRIOR_CHANNEL " + "OfflineFrame for unknown endpoint " + << endpoint_id << ", can't complete the upgrade protocol."; + return; + } auto& previous_endpoint_channel = item.mapped(); if (previous_endpoint_channel == nullptr) { LOG(ERROR) diff --git a/connections/implementation/bwu_manager.h b/connections/implementation/bwu_manager.h index 2c5c766c..d2f53913 100644 --- a/connections/implementation/bwu_manager.h +++ b/connections/implementation/bwu_manager.h @@ -92,8 +92,7 @@ class BwuManager : public EndpointManager::FrameProcessor { // @EndpointManagerReaderThread void OnIncomingFrame(location::nearby::connections::OfflineFrame& frame, const std::string& endpoint_id, ClientProxy* client, - Medium medium, - PacketMetaData& packet_meta_data) override; + Medium medium) override; // Cleans up in-progress upgrades after endpoint disconnection. // @EndpointManagerReaderThread @@ -144,7 +143,7 @@ class BwuManager : public EndpointManager::FrameProcessor { // upgrade. void OnBwuNegotiationFrame( ClientProxy* client, - const location::nearby::connections::BandwidthUpgradeNegotiationFrame + const location::nearby::connections::BandwidthUpgradeNegotiationFrame& frame, const string& endpoint_id); diff --git a/connections/implementation/bwu_manager_test.cc b/connections/implementation/bwu_manager_test.cc index 40cd3e50..f622ab5b 100644 --- a/connections/implementation/bwu_manager_test.cc +++ b/connections/implementation/bwu_manager_test.cc @@ -22,6 +22,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" #include "connections/connection_options.h" +#include "connections/implementation/analytics/analytics_recorder.h" #include "connections/implementation/bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" @@ -41,19 +42,17 @@ #include "internal/platform/exception.h" #include "internal/platform/feature_flags.h" #include "internal/platform/service_address.h" -#include "internal/proto/analytics/connections_log.pb.h" #include "proto/connections_enums.pb.h" -namespace nearby { -namespace connections { +namespace nearby::connections { namespace { -using ::location::nearby::analytics::proto::ConnectionsLog; using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame; using ::location::nearby::connections::MediumRole; using ::location::nearby::connections::OfflineFrame; using ::location::nearby::connections::OsInfo; using ::location::nearby::connections::V1Frame; using ::location::nearby::proto::connections::DisconnectionReason; +using ::nearby::analytics::SafeDisconnectionResult; constexpr absl::string_view kServiceIdA = "ServiceA"; constexpr absl::string_view kServiceIdB = "ServiceB"; @@ -148,7 +147,7 @@ class BwuManagerTest : public ::testing::Test { void UnRegisterChannelForEndpoint(absl::string_view endpoint_id) { ecm_.UnregisterChannelForEndpoint( std::string(endpoint_id), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + SafeDisconnectionResult::kSafeDisconnection); } // Upgrade from |initial_medium| to |upgrade_medium|, close down the BLUETOOTH @@ -188,12 +187,12 @@ class BwuManagerTest : public ::testing::Test { parser::FromBytes(parser::ForBwuLastWrite()); bwu_manager_->OnIncomingFrame(last_write_frame.result(), std::string(endpoint_id), &client_, - initial_medium, packet_meta_data_); + initial_medium); ExceptionOr safe_to_close_frame = parser::FromBytes(parser::ForBwuSafeToClose()); bwu_manager_->OnIncomingFrame(safe_to_close_frame.result(), std::string(endpoint_id), &client_, - initial_medium, packet_meta_data_); + initial_medium); return upgraded_channel; } @@ -209,7 +208,6 @@ class BwuManagerTest : public ::testing::Test { FakeBwuHandler* fake_wifi_direct_bwu_handler_ = nullptr; FakeBwuHandler* fake_wifi_hotspot_bwu_handler_ = nullptr; std::unique_ptr bwu_manager_; - PacketMetaData packet_meta_data_; }; TEST(BwuManagerBaseTest, AllowToUpgradeMedium) { @@ -233,9 +231,9 @@ TEST(BwuManagerBaseTest, AllowToUpgradeMedium) { bwu_manager->InitiateBwuForEndpoint(&client, std::string(kEndpointId1), Medium::WIFI_LAN); EXPECT_TRUE(bwu_manager->IsUpgradeOngoing(std::string(kEndpointId1))); - ecm.UnregisterChannelForEndpoint( - std::string(kEndpointId1), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + ecm.UnregisterChannelForEndpoint(std::string(kEndpointId1), + DisconnectionReason::LOCAL_DISCONNECTION, + SafeDisconnectionResult::kSafeDisconnection); auto channel2 = std::make_unique( Medium::BLUETOOTH, std::string(kServiceIdA)); @@ -244,9 +242,9 @@ TEST(BwuManagerBaseTest, AllowToUpgradeMedium) { bwu_manager->InitiateBwuForEndpoint(&client, std::string(kEndpointId2), Medium::WIFI_HOTSPOT); EXPECT_TRUE(bwu_manager->IsUpgradeOngoing(std::string(kEndpointId2))); - ecm.UnregisterChannelForEndpoint( - std::string(kEndpointId2), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + ecm.UnregisterChannelForEndpoint(std::string(kEndpointId2), + DisconnectionReason::LOCAL_DISCONNECTION, + SafeDisconnectionResult::kSafeDisconnection); auto channel3 = std::make_unique( Medium::BLUETOOTH, std::string(kServiceIdA)); @@ -255,9 +253,9 @@ TEST(BwuManagerBaseTest, AllowToUpgradeMedium) { bwu_manager->InitiateBwuForEndpoint(&client, std::string(kEndpointId3), Medium::WIFI_DIRECT); EXPECT_TRUE(bwu_manager->IsUpgradeOngoing(std::string(kEndpointId3))); - ecm.UnregisterChannelForEndpoint( - std::string(kEndpointId3), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + ecm.UnregisterChannelForEndpoint(std::string(kEndpointId3), + DisconnectionReason::LOCAL_DISCONNECTION, + SafeDisconnectionResult::kSafeDisconnection); auto channel4 = std::make_unique( Medium::WEB_RTC, std::string(kServiceIdA)); @@ -266,9 +264,9 @@ TEST(BwuManagerBaseTest, AllowToUpgradeMedium) { bwu_manager->InitiateBwuForEndpoint(&client, std::string(kEndpointId4), Medium::BLUETOOTH); EXPECT_FALSE(bwu_manager->IsUpgradeOngoing(std::string(kEndpointId4))); - ecm.UnregisterChannelForEndpoint( - std::string(kEndpointId4), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + ecm.UnregisterChannelForEndpoint(std::string(kEndpointId4), + DisconnectionReason::LOCAL_DISCONNECTION, + SafeDisconnectionResult::kSafeDisconnection); bwu_manager->Shutdown(); } @@ -308,9 +306,9 @@ TEST(BwuManagerBaseTest, InitiateBwu_NeedToSwitchRole_Success) { Medium::WIFI_HOTSPOT); EXPECT_FALSE(bwu_manager->IsUpgradeOngoing(std::string(kEndpointId1))); - ecm.UnregisterChannelForEndpoint( - std::string(kEndpointId1), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + ecm.UnregisterChannelForEndpoint(std::string(kEndpointId1), + DisconnectionReason::LOCAL_DISCONNECTION, + SafeDisconnectionResult::kSafeDisconnection); bwu_manager->Shutdown(); NearbyFlags::GetInstance().OverrideBoolFlagValue( config_package_nearby::nearby_connections_feature:: @@ -370,12 +368,12 @@ TEST_P(BwuManagerTestParam, InitiateBwu_Success) { parser::FromBytes(parser::ForBwuLastWrite()); bwu_manager_->OnIncomingFrame(last_write_frame.result(), std::string(kEndpointId1), &client_, - Medium::BLUETOOTH, packet_meta_data_); + Medium::BLUETOOTH); ExceptionOr safe_to_close_frame = parser::FromBytes(parser::ForBwuSafeToClose()); bwu_manager_->OnIncomingFrame(safe_to_close_frame.result(), std::string(kEndpointId1), &client_, - Medium::BLUETOOTH, packet_meta_data_); + Medium::BLUETOOTH); // Confirm that upgrade channel is resumed after initial channel is shut down. // Note: If we didn't grab the shared initial channel pointer above, this @@ -501,7 +499,7 @@ TEST_F(BwuManagerTest, CountDownLatch latch(1); ecm_.UnregisterChannelForEndpoint( std::string(kEndpointId1), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + SafeDisconnectionResult::kUnsafeDisconnection); bwu_manager_->OnEndpointDisconnect( &client_, upgrade_service_id, std::string(kEndpointId1), latch, DisconnectionReason::LOCAL_DISCONNECTION); @@ -515,7 +513,7 @@ TEST_F(BwuManagerTest, CountDownLatch latch(1); ecm_.UnregisterChannelForEndpoint( std::string(kEndpointId2), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + SafeDisconnectionResult::kUnsafeDisconnection); bwu_manager_->OnEndpointDisconnect( &client_, upgrade_service_id, std::string(kEndpointId2), latch, DisconnectionReason::LOCAL_DISCONNECTION); @@ -549,7 +547,7 @@ TEST_F(BwuManagerTest, CountDownLatch latch(1); ecm_.UnregisterChannelForEndpoint( std::string(kEndpointId1), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + SafeDisconnectionResult::kUnsafeDisconnection); bwu_manager_->OnEndpointDisconnect( &client_, upgrade_service_id, std::string(kEndpointId1), latch, DisconnectionReason::LOCAL_DISCONNECTION); @@ -569,7 +567,7 @@ TEST_F(BwuManagerTest, CountDownLatch latch(1); ecm_.UnregisterChannelForEndpoint( std::string(kEndpointId2), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + SafeDisconnectionResult::kUnsafeDisconnection); bwu_manager_->OnEndpointDisconnect( &client_, upgrade_service_id, std::string(kEndpointId2), latch, DisconnectionReason::LOCAL_DISCONNECTION); @@ -606,7 +604,7 @@ TEST_F(BwuManagerTest, EXPECT_EQ(2u, ecm_.GetConnectedEndpointsCount()); ecm_.UnregisterChannelForEndpoint( std::string(kEndpointId1), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + SafeDisconnectionResult::kUnsafeDisconnection); EXPECT_EQ(1u, ecm_.GetConnectedEndpointsCount()); bwu_manager_->OnEndpointDisconnect( &client_, upgrade_service_id_A, std::string(kEndpointId1), latch, @@ -626,7 +624,7 @@ TEST_F(BwuManagerTest, CountDownLatch latch(1); ecm_.UnregisterChannelForEndpoint( std::string(kEndpointId2), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + SafeDisconnectionResult::kUnsafeDisconnection); EXPECT_EQ(0u, ecm_.GetConnectedEndpointsCount()); bwu_manager_->OnEndpointDisconnect( &client_, upgrade_service_id_B, std::string(kEndpointId2), latch, @@ -660,7 +658,7 @@ TEST_F(BwuManagerTest, EXPECT_EQ(2u, ecm_.GetConnectedEndpointsCount()); ecm_.UnregisterChannelForEndpoint( std::string(kEndpointId1), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + SafeDisconnectionResult::kUnsafeDisconnection); EXPECT_EQ(1u, ecm_.GetConnectedEndpointsCount()); bwu_manager_->OnEndpointDisconnect( &client_, upgrade_service_id_A, std::string(kEndpointId1), latch, @@ -680,7 +678,7 @@ TEST_F(BwuManagerTest, CountDownLatch latch(1); ecm_.UnregisterChannelForEndpoint( std::string(kEndpointId2), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + SafeDisconnectionResult::kUnsafeDisconnection); EXPECT_EQ(0u, ecm_.GetConnectedEndpointsCount()); bwu_manager_->OnEndpointDisconnect( &client_, upgrade_service_id_B, std::string(kEndpointId2), latch, @@ -736,7 +734,7 @@ TEST_F( CountDownLatch latch(1); ecm_.UnregisterChannelForEndpoint( std::string(kEndpointId1), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + SafeDisconnectionResult::kUnsafeDisconnection); bwu_manager_->OnEndpointDisconnect( &client_, upgrade_service_id_A, std::string(kEndpointId1), latch, DisconnectionReason::LOCAL_DISCONNECTION); @@ -761,7 +759,7 @@ TEST_F( CountDownLatch latch(1); ecm_.UnregisterChannelForEndpoint( std::string(kEndpointId2), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + SafeDisconnectionResult::kUnsafeDisconnection); bwu_manager_->OnEndpointDisconnect( &client_, upgrade_service_id_A, std::string(kEndpointId2), latch, DisconnectionReason::LOCAL_DISCONNECTION); @@ -782,7 +780,7 @@ TEST_F( CountDownLatch latch(1); ecm_.UnregisterChannelForEndpoint( std::string(kEndpointId3), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + SafeDisconnectionResult::kUnsafeDisconnection); bwu_manager_->OnEndpointDisconnect( &client_, upgrade_service_id_B, std::string(kEndpointId3), latch, DisconnectionReason::LOCAL_DISCONNECTION); @@ -803,7 +801,7 @@ TEST_F( CountDownLatch latch(1); ecm_.UnregisterChannelForEndpoint( std::string(kEndpointId4), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + SafeDisconnectionResult::kUnsafeDisconnection); bwu_manager_->OnEndpointDisconnect( &client_, upgrade_service_id_B, std::string(kEndpointId4), latch, DisconnectionReason::LOCAL_DISCONNECTION); @@ -826,7 +824,7 @@ TEST_F( CountDownLatch latch(1); ecm_.UnregisterChannelForEndpoint( std::string(kEndpointId5), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + SafeDisconnectionResult::kUnsafeDisconnection); bwu_manager_->OnEndpointDisconnect( &client_, upgrade_service_id_B, std::string(kEndpointId5), latch, DisconnectionReason::LOCAL_DISCONNECTION); @@ -871,7 +869,7 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnUpgradeFailure_FlagEnabled) { parser::FromBytes(parser::ForBwuFailure(info)); bwu_manager_->OnIncomingFrame(upgrade_failure.result(), std::string(kEndpointId3), &client_, - Medium::WEB_RTC, packet_meta_data_); + Medium::WEB_RTC); // With the flag enabled, we can safely revert WebRTC just for service B // because service B has no active WebRTC endpoints. @@ -908,7 +906,7 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnUpgradeFailure_FlagDisabled) { parser::FromBytes(parser::ForBwuFailure(info)); bwu_manager_->OnIncomingFrame(upgrade_failure.result(), std::string(kEndpointId3), &client_, - Medium::WEB_RTC, packet_meta_data_); + Medium::WEB_RTC); // With the flag disabled, we don't revert if there are still connected // endpoints for _any_ service. We don't have service-level bookkeeping; we @@ -924,12 +922,12 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnDisconnect_WifiDirect) { OfflineFrame frame; CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH); - ByteArray bytes = parser::ForBwuWifiDirectPathAvailable( + std::string bytes = parser::ForBwuWifiDirectPathAvailable( /*ssid=*/"", /*password=*/"", /*port=*/2143, /*frequency=*/2412, /*supports_disabling_encryption=*/false, - /*gateway=*/"123.234.23.1", /*service_name=*/"NC-WifiDirectTest", + /*gateway=*/"123.234.23.1", /*device_name=*/"NC-WifiDirectTest", /*pin=*/"b592f7d3"); - frame.ParseFromString(std::string(bytes)); + frame.ParseFromString(bytes); ::nearby::connections::V1Frame* v1_frame = frame.mutable_v1(); ::nearby::connections::BandwidthUpgradeNegotiationFrame* sub_frame = @@ -938,7 +936,7 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnDisconnect_WifiDirect) { upgrade_path_info = sub_frame->mutable_upgrade_path_info(); upgrade_path_info->set_supports_client_introduction_ack(false); bwu_manager_->OnIncomingFrame(frame, std::string(kEndpointId1), &client_, - Medium::BLUETOOTH, packet_meta_data_); + Medium::BLUETOOTH); CountDownLatch latch(1); bwu_manager_->OnEndpointDisconnect(&client_, (std::string)kServiceIdA, std::string(kEndpointId1), latch, @@ -973,7 +971,7 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnDisconnect_Hotspot) { upgrade_path_info->set_supports_client_introduction_ack(false); upgrade_path_info->set_supports_disabling_encryption(true); bwu_manager_->OnIncomingFrame(frame, std::string(kEndpointId1), &client_, - Medium::BLUETOOTH, packet_meta_data_); + Medium::BLUETOOTH); CountDownLatch latch(1); bwu_manager_->OnEndpointDisconnect(&client_, (std::string)kServiceIdA, std::string(kEndpointId1), latch, @@ -1001,7 +999,7 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnDisconnect_Wlan) { upgrade_path_info->set_supports_client_introduction_ack(false); bwu_manager_->OnIncomingFrame(frame, std::string(kEndpointId1), &client_, - Medium::BLUETOOTH, packet_meta_data_); + Medium::BLUETOOTH); CountDownLatch latch(1); bwu_manager_->OnEndpointDisconnect(&client_, (std::string)kServiceIdA, std::string(kEndpointId1), latch, @@ -1040,7 +1038,7 @@ TEST_F(BwuManagerTest, BlockBwuFrameBeforeAccept) { upgrade_path_info2->set_supports_client_introduction_ack(false); upgrade_path_info2->set_supports_disabling_encryption(true); bwu_manager_->OnIncomingFrame(frame2, std::string(kEndpointId2), &client_, - Medium::BLUETOOTH, packet_meta_data_); + Medium::BLUETOOTH); CountDownLatch latch2(1); // The BWU frame should be drop, so the inProgressUpgrades should be empty. ASSERT_EQ(bwu_manager_->IsUpgradeOngoing(std::string(kEndpointId2)), false); @@ -1084,16 +1082,100 @@ TEST_F(BwuManagerTest, BlockBwuFrameFromAdvertiser) { EXPECT_TRUE(client_.IsConnectedToEndpoint(std::string(kEndpointId2))); bwu_manager_->OnIncomingFrame(frame, std::string(kEndpointId2), &client_, - Medium::BLUETOOTH, packet_meta_data_); + Medium::BLUETOOTH); CountDownLatch latch2(1); // The BWU frame should be drop, so the IsUpgradeOngoing should be empty. ASSERT_EQ(bwu_manager_->IsUpgradeOngoing(std::string(kEndpointId2)), false); UnRegisterChannelForEndpoint(kEndpointId2); } +TEST_F(BwuManagerTest, ReceiveUnexpectedSafeToClose_NoCrash) { + ExceptionOr safe_to_close_frame = + parser::FromBytes(parser::ForBwuSafeToClose()); + bwu_manager_->OnIncomingFrame(safe_to_close_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); +} + +TEST_F(BwuManagerTest, ReceiveUnexpectedLastWrite_NoCrashOrWedge) { + ExceptionOr last_write_frame = + parser::FromBytes(parser::ForBwuLastWrite()); + bwu_manager_->OnIncomingFrame(last_write_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); +} + +TEST_F(BwuManagerTest, ReceiveEarlyLastWrite_Success) { + CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH); + std::shared_ptr shared_initial_channel = + ecm_.GetChannelForEndpoint(std::string(kEndpointId1)); + + bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1), + Medium::WEB_RTC); + ASSERT_TRUE(bwu_manager_->IsUpgradeOngoing(std::string(kEndpointId1))); + + ExceptionOr last_write_frame = + parser::FromBytes(parser::ForBwuLastWrite()); + bwu_manager_->OnIncomingFrame(last_write_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); + + FakeEndpointChannel* upgraded_channel = + fake_web_rtc_bwu_handler_->NotifyBwuManagerOfIncomingConnection( + /*initialize_call_index=*/0u, bwu_manager_.get()); + + ExceptionOr safe_to_close_frame = + parser::FromBytes(parser::ForBwuSafeToClose()); + bwu_manager_->OnIncomingFrame(safe_to_close_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); + + auto old_channel = + dynamic_cast(shared_initial_channel.get()); + EXPECT_FALSE(upgraded_channel->IsPaused()); + EXPECT_TRUE(old_channel->is_closed()); + EXPECT_EQ(location::nearby::proto::connections::DisconnectionReason::UPGRADED, + old_channel->disconnection_reason()); + UnRegisterChannelForEndpoint(kEndpointId1); +} + +TEST_F(BwuManagerTest, ReceiveUnexpectedLastWriteBeforeUpgrade_NoWedge) { + ExceptionOr last_write_frame = + parser::FromBytes(parser::ForBwuLastWrite()); + bwu_manager_->OnIncomingFrame(last_write_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); + + CreateInitialEndpoint(&client_, kServiceIdA, kEndpointId1, Medium::BLUETOOTH); + std::shared_ptr shared_initial_channel = + ecm_.GetChannelForEndpoint(std::string(kEndpointId1)); + + bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1), + Medium::WEB_RTC); + + FakeEndpointChannel* upgraded_channel = + fake_web_rtc_bwu_handler_->NotifyBwuManagerOfIncomingConnection( + /*initialize_call_index=*/0u, bwu_manager_.get()); + + bwu_manager_->OnIncomingFrame(last_write_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); + + ExceptionOr safe_to_close_frame = + parser::FromBytes(parser::ForBwuSafeToClose()); + bwu_manager_->OnIncomingFrame(safe_to_close_frame.result(), + std::string(kEndpointId1), &client_, + Medium::BLUETOOTH); + + auto old_channel = + dynamic_cast(shared_initial_channel.get()); + EXPECT_FALSE(upgraded_channel->IsPaused()); + EXPECT_TRUE(old_channel->is_closed()); + UnRegisterChannelForEndpoint(kEndpointId1); +} + INSTANTIATE_TEST_SUITE_P(BwuManagerTestParam, BwuManagerTestParam, testing::Bool()); } // namespace -} // namespace connections -} // namespace nearby +} // namespace nearby::connections diff --git a/connections/implementation/client_proxy.cc b/connections/implementation/client_proxy.cc index d53e1a18..aa6c4557 100644 --- a/connections/implementation/client_proxy.cc +++ b/connections/implementation/client_proxy.cc @@ -38,12 +38,15 @@ #include "connections/discovery_options.h" #include "connections/implementation/analytics/advertising_metadata_params.h" #include "connections/implementation/analytics/analytics_recorder.h" +#include "connections/implementation/analytics/connection_attempt_metadata_params.h" #include "connections/implementation/analytics/discovery_metadata_params.h" +#include "connections/implementation/analytics/operation_result_with_medium.h" #include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/implementation/mediums/advertisements/dct_advertisement.h" #include "connections/listeners.h" #include "connections/medium_selector.h" #include "connections/payload.h" +#include "connections/payload_type.h" #include "connections/status.h" #include "connections/strategy.h" #include "connections/v3/bandwidth_info.h" @@ -52,7 +55,6 @@ #include "connections/v3/connections_device.h" #include "connections/v3/connections_device_provider.h" #include "connections/v3/listeners.h" -#include "internal/analytics/event_logger.h" #include "internal/base/file_path.h" #include "internal/base/files.h" #include "internal/flags/nearby_flags.h" @@ -60,7 +62,9 @@ #include "internal/platform/byte_array.h" #include "internal/platform/cancelable_alarm.h" #include "internal/platform/cancellation_flag.h" -#include "internal/platform/device_info_impl.h" +#ifndef NEARBY_CHROMIUM +#include "internal/platform/implementation/device_info.h" +#endif #include "internal/platform/error_code_params.h" #include "internal/platform/error_code_recorder.h" #include "internal/platform/feature_flags.h" @@ -77,9 +81,9 @@ namespace nearby::connections { namespace { -using ::location::nearby::analytics::proto::ConnectionsLog; using ::location::nearby::connections::MediumRole; using ::location::nearby::connections::OsInfo; +using ::nearby::analytics::AnalyticsRecorder; constexpr char kEndpointIdChars[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', @@ -95,11 +99,154 @@ constexpr absl::string_view kAdvertisingTimestamp = "nc.advertising.timestamp"; constexpr absl::Duration kAdvertisingKeepAliveDuration = absl::Seconds(30); +class NoOpAnalyticsRecorder : public AnalyticsRecorder { + public: + NoOpAnalyticsRecorder() = default; + ~NoOpAnalyticsRecorder() override = default; + + // Advertising phase + void OnStartAdvertising( + connections::Strategy strategy, + const std::vector& mediums, + AdvertisingMetadataParams* advertising_metadata_params) override {} + void OnStopAdvertising() override {} + + int GetNextAdvertisingUpdateIndex() override { return 0; } + + // Connection listening + void OnStartedIncomingConnectionListening( + connections::Strategy strategy) override {} + void OnStoppedIncomingConnectionListening() override {} + + // Discovery phase + void OnStartDiscovery( + connections::Strategy strategy, + const std::vector& mediums, + DiscoveryMetadataParams* discovery_metadata_params) override {} + void OnStopDiscovery() override {} + + int GetNextDiscoveryUpdateIndex() override { return 0; } + void OnEndpointFound( + location::nearby::proto::connections::Medium medium) override {} + + // Connection request + void OnRequestConnection(const connections::Strategy& strategy, + const std::string& endpoint_id) override {} + + void OnConnectionRequestReceived( + const std::string& remote_endpoint_id) override {} + void OnConnectionRequestSent( + const std::string& remote_endpoint_id) override {} + void OnRemoteEndpointAccepted( + const std::string& remote_endpoint_id) override {} + void OnLocalEndpointAccepted( + const std::string& remote_endpoint_id) override {} + void OnRemoteEndpointRejected( + const std::string& remote_endpoint_id) override {} + void OnLocalEndpointRejected( + const std::string& remote_endpoint_id) override {} + + // Connection attempt + void OnIncomingConnectionAttempt( + location::nearby::proto::connections::ConnectionAttemptType type, + location::nearby::proto::connections::Medium medium, + location::nearby::proto::connections::ConnectionAttemptResult result, + absl::Duration duration, const std::string& connection_token, + ConnectionAttemptMetadataParams* connection_attempt_metadata_params) + override {} + void OnOutgoingConnectionAttempt( + const std::string& remote_endpoint_id, + location::nearby::proto::connections::ConnectionAttemptType type, + location::nearby::proto::connections::Medium medium, + location::nearby::proto::connections::ConnectionAttemptResult result, + absl::Duration duration, const std::string& connection_token, + ConnectionAttemptMetadataParams* connection_attempt_metadata_params) + override {} + + // Connection established + void OnConnectionEstablished( + const std::string& endpoint_id, + location::nearby::proto::connections::Medium medium, + const std::string& connection_token) override {} + void OnConnectionClosed( + const std::string& endpoint_id, + location::nearby::proto::connections::Medium medium, + location::nearby::proto::connections::DisconnectionReason reason, + nearby::analytics::SafeDisconnectionResult result) override {} + + // Payload + void OnIncomingPayloadStarted(const std::string& endpoint_id, + std::int64_t payload_id, + connections::PayloadType type, + std::int64_t total_size_bytes) override {} + void OnPayloadChunkReceived(const std::string& endpoint_id, + std::int64_t payload_id, + std::int64_t chunk_size_bytes) override {} + void OnIncomingPayloadDone( + const std::string& endpoint_id, std::int64_t payload_id, + location::nearby::proto::connections::PayloadStatus status, + location::nearby::proto::connections::OperationResultCode + operation_result_code) override {} + void OnOutgoingPayloadStarted( + const std::vector& endpoint_ids, std::int64_t payload_id, + connections::PayloadType type, std::int64_t total_size_bytes) override {} + void OnPayloadChunkSent(const std::string& endpoint_id, + std::int64_t payload_id, + std::int64_t chunk_size_bytes) override {} + void OnOutgoingPayloadDone( + const std::string& endpoint_id, std::int64_t payload_id, + location::nearby::proto::connections::PayloadStatus status, + location::nearby::proto::connections::OperationResultCode + operation_result_code) override {} + + // BandwidthUpgrade + void OnBandwidthUpgradeStarted( + const std::string& endpoint_id, + location::nearby::proto::connections::Medium from_medium, + location::nearby::proto::connections::Medium to_medium, + location::nearby::proto::connections::ConnectionAttemptDirection + direction, + const std::string& connection_token) override {} + void UpdateBwUpgradeNetworkInfo(const std::string& endpoint_id, + int num_interfaces, + int num_ipv6_only_interfaces) override {} + void OnBandwidthUpgradeError( + const std::string& endpoint_id, + location::nearby::proto::connections::BandwidthUpgradeResult result, + location::nearby::proto::connections::BandwidthUpgradeErrorStage + error_stage, + location::nearby::proto::connections::OperationResultCode + operation_result_code) override {} + void OnBandwidthUpgradeSuccess(const std::string& endpoint_id) override {} + + // Error Code + void OnErrorCode(const ErrorCodeParams& params) override {} + + void LogStartSession() override {} + void LogSession() override {} + + bool IsSessionLogged() override { return false; } + + location::nearby::proto::connections::OperationResultCategory + GetOperationResultCategory( + location::nearby::proto::connections::OperationResultCode result_code) + override { + return location::nearby::proto::connections::OperationResultCategory:: + CATEGORY_UNKNOWN; + } + + void Sync() override {} +}; + } // namespace -ClientProxy::ClientProxy(::nearby::analytics::EventLogger* event_logger) - : client_id_(Prng().NextInt64()) { - VLOG(1) << "ClientProxy ctor event_logger=" << event_logger; +ClientProxy::ClientProxy(std::unique_ptr analytics_recorder) + : client_id_(Prng().NextInt64()), + analytics_recorder_(std::move(analytics_recorder)) { + if (analytics_recorder_ == nullptr) { + analytics_recorder_ = std::make_unique(); + } + if (NearbyFlags::GetInstance().GetBoolFlag( config_package_nearby::nearby_connections_feature:: kEnableNearbyConnectionsPreferences)) { @@ -108,8 +255,6 @@ ClientProxy::ClientProxy(::nearby::analytics::EventLogger* event_logger) is_dct_enabled_ = NearbyFlags::GetInstance().GetBoolFlag( config_package_nearby::nearby_connections_feature::kEnableDct); - analytics_recorder_ = - std::make_unique(event_logger); error_code_recorder_ = std::make_unique( [this](const ErrorCodeParams& params) { analytics_recorder_->OnErrorCode(params); @@ -119,14 +264,11 @@ ClientProxy::ClientProxy(::nearby::analytics::EventLogger* event_logger) supports_safe_to_disconnect_ = NearbyFlags::GetInstance().GetBoolFlag( config_package_nearby::nearby_connections_feature:: kEnableSafeToDisconnect); - support_auto_reconnect_ = NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature::kEnableAutoReconnect); - local_safe_to_disconnect_version_ = NearbyFlags::GetInstance().GetInt64Flag( - config_package_nearby::nearby_connections_feature:: - kSafeToDisconnectVersion); LOG(INFO) << "[safe-to-disconnect]: Local enabled: " - << supports_safe_to_disconnect_ - << "; Version: " << local_safe_to_disconnect_version_; + << supports_safe_to_disconnect_ << "; Version: " + << NearbyFlags::GetInstance().GetInt64Flag( + config_package_nearby::nearby_connections_feature:: + kSafeToDisconnectVersion); // Generate a 7 bits dedup value. absl::BitGen bitgen; dct_dedup_ = absl::Uniform(bitgen, 0, 1 << 7); @@ -189,6 +331,7 @@ const NearbyDevice* ClientProxy::GetLocalDevice() { } std::string ClientProxy::GetConnectionToken(const std::string& endpoint_id) { + MutexLock lock(&mutex_); ConnectionPair* item = LookupConnection(endpoint_id); if (item != nullptr) { return item->first.connection_token; @@ -219,6 +362,7 @@ std::string ClientProxy::GetSavePath( std::optional ClientProxy::GetBluetoothMacAddress( const std::string& endpoint_id) { + MutexLock lock(&mutex_); auto item = bluetooth_mac_addresses_.find(endpoint_id); if (item != bluetooth_mac_addresses_.end()) return item->second; return std::nullopt; @@ -226,6 +370,7 @@ std::optional ClientProxy::GetBluetoothMacAddress( void ClientProxy::SetBluetoothMacAddress(const std::string& endpoint_id, MacAddress bluetooth_mac_address) { + MutexLock lock(&mutex_); bluetooth_mac_addresses_[endpoint_id] = bluetooth_mac_address; } @@ -263,7 +408,7 @@ void ClientProxy::StartedAdvertising( const std::string& service_id, Strategy strategy, const ConnectionListener& listener, absl::Span mediums, - const std::vector& + const std::vector& operation_result_with_mediums, const AdvertisingOptions& advertising_options) { MutexLock lock(&mutex_); @@ -284,9 +429,9 @@ void ClientProxy::StartedAdvertising( mediums.begin(), mediums.end()); std::unique_ptr advertising_metadata_params; advertising_metadata_params = - GetAnalyticsRecorder().BuildAdvertisingMetadataParams(); + AnalyticsRecorder::BuildAdvertisingMetadataParams(); advertising_metadata_params->operation_result_with_mediums = - std::move(operation_result_with_mediums); + operation_result_with_mediums; analytics_recorder_->OnStartAdvertising(strategy, medium_vector, advertising_metadata_params.get()); } @@ -402,7 +547,7 @@ void ClientProxy::StartedDiscovery( const std::string& service_id, Strategy strategy, DiscoveryListener listener, absl::Span mediums, - const std::vector& + const std::vector& operation_result_with_mediums, const DiscoveryOptions& discovery_options) { MutexLock lock(&mutex_); @@ -412,10 +557,9 @@ void ClientProxy::StartedDiscovery( const std::vector medium_vector( mediums.begin(), mediums.end()); std::unique_ptr discovery_metadata_params; - discovery_metadata_params = - GetAnalyticsRecorder().BuildDiscoveryMetadataParams(); + discovery_metadata_params = AnalyticsRecorder::BuildDiscoveryMetadataParams(); discovery_metadata_params->operation_result_with_mediums = - std::move(operation_result_with_mediums); + operation_result_with_mediums; analytics_recorder_->OnStartDiscovery(strategy, medium_vector, discovery_metadata_params.get()); } @@ -878,16 +1022,19 @@ bool ClientProxy::IsConnectionRejected(const std::string& endpoint_id) const { } bool ClientProxy::LocalConnectionIsAccepted(std::string endpoint_id) const { + MutexLock lock(&mutex_); return ConnectionStatusesContains( endpoint_id, ClientProxy::Connection::kLocalEndpointAccepted); } bool ClientProxy::RemoteConnectionIsAccepted(std::string endpoint_id) const { + MutexLock lock(&mutex_); return ConnectionStatusesContains( endpoint_id, ClientProxy::Connection::kRemoteEndpointAccepted); } bool ClientProxy::AutoUpgradeBandwidth() const { + MutexLock lock(&mutex_); bool result = false; if (IsAdvertising() && (GetAdvertisingOptions().strategy.IsNone() || GetAdvertisingOptions().auto_upgrade_bandwidth)) { @@ -902,6 +1049,7 @@ bool ClientProxy::AutoUpgradeBandwidth() const { } bool ClientProxy::ShouldEnforceTopologyConstraints() const { + MutexLock lock(&mutex_); bool result = false; if (IsAdvertising() && (GetAdvertisingOptions().strategy.IsNone() || @@ -922,6 +1070,7 @@ void ClientProxy::AddCancellationFlag(const std::string& endpoint_id) { return; } + MutexLock lock(&mutex_); auto item = cancellation_flags_.find(endpoint_id); if (item != cancellation_flags_.end()) { // A new flag may be added to the map with the same endpoint, even if a @@ -936,19 +1085,21 @@ void ClientProxy::AddCancellationFlag(const std::string& endpoint_id) { return; } cancellation_flags_.emplace(endpoint_id, - std::make_unique()); + std::make_shared()); } -CancellationFlag* ClientProxy::GetCancellationFlag( +std::shared_ptr ClientProxy::GetCancellationFlag( const std::string& endpoint_id) { + MutexLock lock(&mutex_); const auto item = cancellation_flags_.find(endpoint_id); if (item == cancellation_flags_.end()) { - return default_cancellation_flag_.get(); + return default_cancellation_flag_; } - return item->second.get(); + return item->second; } void ClientProxy::CancelEndpoint(const std::string& endpoint_id) { + MutexLock lock(&mutex_); const auto item = cancellation_flags_.find(endpoint_id); if (item != cancellation_flags_.end()) { item->second->Cancel(); @@ -959,6 +1110,7 @@ const OsInfo& ClientProxy::GetLocalOsInfo() const { return local_os_info_; } std::optional ClientProxy::GetRemoteOsInfo( absl::string_view endpoint_id) const { + MutexLock lock(&mutex_); const ConnectionPair* item = LookupConnection(endpoint_id); if (item != nullptr) { return item->first.os_info; @@ -968,11 +1120,13 @@ std::optional ClientProxy::GetRemoteOsInfo( void ClientProxy::SetLocalOsType( const location::nearby::connections::OsInfo::OsType& os_type) { + MutexLock lock(&mutex_); local_os_info_.set_type(os_type); } void ClientProxy::SetRemoteOsInfo(absl::string_view endpoint_id, const OsInfo& remote_os_info) { + MutexLock lock(&mutex_); ConnectionPair* item = LookupConnection(endpoint_id); if (item != nullptr) { item->first.os_info.emplace(remote_os_info); @@ -1008,14 +1162,6 @@ bool ClientProxy::IsSafeToDisconnectEnabled(absl::string_view endpoint_id) { .min_nc_version_supports_safe_to_disconnect); } -bool ClientProxy::IsAutoReconnectEnabled(absl::string_view endpoint_id) { - return IsSupportAutoReconnect() && - GetRemoteSafeToDisconnectVersion(endpoint_id).has_value() && - (GetRemoteSafeToDisconnectVersion(endpoint_id) >= - FeatureFlags::GetInstance() - .GetFlags() - .min_nc_version_supports_auto_reconnect); -} bool ClientProxy::IsPayloadReceivedAckEnabled(absl::string_view endpoint_id) { return IsSupportSafeToDisconnect() && @@ -1027,8 +1173,9 @@ bool ClientProxy::IsPayloadReceivedAckEnabled(absl::string_view endpoint_id) { } void ClientProxy::CancelAllEndpoints() { + MutexLock lock(&mutex_); for (const auto& item : cancellation_flags_) { - CancellationFlag* cancellation_flag = item.second.get(); + std::shared_ptr cancellation_flag = item.second; if (cancellation_flag->Cancelled()) { continue; } @@ -1131,14 +1278,17 @@ void ClientProxy::AppendConnectionStatus(const std::string& endpoint_id, } AdvertisingOptions ClientProxy::GetAdvertisingOptions() const { + MutexLock lock(&mutex_); return advertising_options_; } DiscoveryOptions ClientProxy::GetDiscoveryOptions() const { + MutexLock lock(&mutex_); return discovery_options_; } v3::ConnectionListeningOptions ClientProxy::GetListeningOptions() const { + MutexLock lock(&mutex_); return listening_options_; } @@ -1215,30 +1365,9 @@ OsInfo::OsType ClientProxy::OSNameToOsInfoType(api::OSName osName) { } } -std::int32_t ClientProxy::GetLocalMultiplexSocketBitmask() const { - if (NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplex)) { - std::int32_t multiplex_bitmask = - (NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexBluetooth) - ? kBtMultiplexEnabled - : 0) | - (NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexWifiLan) - ? kWifiLanMultiplexEnabled - : 0); - LOG(INFO) << "ClientProxy [GetLocalMultiplexSocketBitmask]: " - << multiplex_bitmask; - return multiplex_bitmask; - } - return 0; -} - void ClientProxy::SetRemoteMultiplexSocketBitmask( absl::string_view endpoint_id, int remote_multiplex_socket_bitmask) { + MutexLock lock(&mutex_); ConnectionPair* item = LookupConnection(endpoint_id); if (item != nullptr) { item->first.remote_multiplex_socket_bitmask = @@ -1248,62 +1377,31 @@ void ClientProxy::SetRemoteMultiplexSocketBitmask( } } -bool ClientProxy::IsLocalMultiplexSocketSupported(Medium medium) { - int bitmask = GetLocalMultiplexSocketBitmask(); - switch (medium) { - case Medium::BLUETOOTH: - LOG(INFO) << "ClientProxy [IsLocalMultiplexSocketSupported]: " - << (bitmask & kBtMultiplexEnabled); - return (bitmask & kBtMultiplexEnabled) != 0; - case Medium::WIFI_LAN: - return (bitmask & kWifiLanMultiplexEnabled) != 0; - default: - return false; - } +bool ClientProxy::GetWebRtcNonCellular() { + MutexLock lock(&mutex_); + return webrtc_non_cellular_; } -std::optional ClientProxy::GetRemoteMultiplexSocketBitmask( - absl::string_view endpoint_id) const { - const ConnectionPair* item = LookupConnection(endpoint_id); - if (item != nullptr) { - return item->first.remote_multiplex_socket_bitmask; - } - return std::nullopt; -} - -bool ClientProxy::IsMultiplexSocketSupported(absl::string_view endpoint_id, - Medium medium) { - ConnectionPair* item = LookupConnection(endpoint_id); - if (item == nullptr) { - return false; - } - int combined_result = GetLocalMultiplexSocketBitmask() & - item->first.remote_multiplex_socket_bitmask; - - switch (medium) { - case Medium::BLUETOOTH: - return (combined_result & kBtMultiplexEnabled) != 0; - case Medium::WIFI_LAN: - return (combined_result & kWifiLanMultiplexEnabled) != 0; - default: - return false; - } -} - -bool ClientProxy::GetWebRtcNonCellular() { return webrtc_non_cellular_; } - void ClientProxy::SetWebRtcNonCellular(bool webrtc_non_cellular) { + MutexLock lock(&mutex_); VLOG(1) << "ClientProxy: client=" << GetClientId() << (webrtc_non_cellular ? " disallow" : " allow") << " to use mobile data."; webrtc_non_cellular_ = webrtc_non_cellular; } -bool ClientProxy::IsDctEnabled() const { return is_dct_enabled_; } +bool ClientProxy::IsDctEnabled() const { + MutexLock lock(&mutex_); + return is_dct_enabled_; +} -uint8_t ClientProxy::GetDctDedup() const { return dct_dedup_; } +uint8_t ClientProxy::GetDctDedup() const { + MutexLock lock(&mutex_); + return dct_dedup_; +} void ClientProxy::UpdateDctDeviceName(absl::string_view device_name) { + MutexLock lock(&mutex_); if (!dct_device_name_.empty() && dct_device_name_ != device_name) { // Need to update dedup value if device name is changed. absl::BitGen bitgen; @@ -1325,6 +1423,7 @@ void ClientProxy::UpdateDctDeviceName(absl::string_view device_name) { std::optional ClientProxy::GetMediumRole( absl::string_view endpoint_id) const { + MutexLock lock(&mutex_); const ConnectionPair* item = LookupConnection(endpoint_id); if (item != nullptr) { return item->first.connection_options.connection_info.medium_role; @@ -1333,6 +1432,7 @@ std::optional ClientProxy::GetMediumRole( } std::optional ClientProxy::GetEndpointIdForDct() const { + MutexLock lock(&mutex_); if (dct_endpoint_id_.empty()) { return std::nullopt; } @@ -1340,13 +1440,20 @@ std::optional ClientProxy::GetEndpointIdForDct() const { return dct_endpoint_id_; } +#ifdef NEARBY_CHROMIUM +void ClientProxy::InitializePreferencesManager() { + // This method is not currently used by Chromium. + NOTREACHED(); +} +#else void ClientProxy::InitializePreferencesManager() { LOG(INFO) << "ClientProxy [InitializePreferencesManager]: client=" << GetClientId(); - auto device_info_ = std::make_unique(); + std::unique_ptr device_info_ = + nearby::api::ImplementationPlatform::CreateDeviceInfo(); FilePath preferences_path = - device_info_->GetAppDataPath().append(FilePath(kPreferencesFilePath)); + device_info_->GetLocalAppDataPath(FilePath(kPreferencesFilePath)); if (!Files::FileExists(preferences_path)) { Files::CreateDirectories(preferences_path); @@ -1361,6 +1468,7 @@ void ClientProxy::InitializePreferencesManager() { << GetClientId(); } } +#endif void ClientProxy::SaveClientInfoToPreferences() { MutexLock lock(&mutex_); diff --git a/connections/implementation/client_proxy.h b/connections/implementation/client_proxy.h index 05406a27..e40ba901 100644 --- a/connections/implementation/client_proxy.h +++ b/connections/implementation/client_proxy.h @@ -32,6 +32,7 @@ #include "connections/connection_options.h" #include "connections/discovery_options.h" #include "connections/implementation/analytics/analytics_recorder.h" +#include "connections/implementation/analytics/operation_result_with_medium.h" #include "connections/implementation/proto/offline_wire_formats.pb.h" #include "connections/listeners.h" #include "connections/medium_selector.h" @@ -41,7 +42,6 @@ #include "connections/v3/connection_listening_options.h" #include "connections/v3/connections_device_provider.h" #include "connections/v3/listeners.h" -#include "internal/analytics/event_logger.h" #include "internal/interop/device.h" #include "internal/interop/device_provider.h" #include "internal/platform/byte_array.h" @@ -54,7 +54,6 @@ #include "internal/platform/mutex.h" #include "internal/platform/os_name.h" #include "internal/platform/scheduled_executor.h" -#include "internal/proto/analytics/connections_log.pb.h" namespace nearby::connections { @@ -66,8 +65,8 @@ class ClientProxy final { static constexpr absl::Duration kHighPowerAdvertisementEndpointIdCacheTimeout = absl::Seconds(30); - explicit ClientProxy( - ::nearby::analytics::EventLogger* event_logger = nullptr); + explicit ClientProxy(std::unique_ptr + analytics_recorder = nullptr); ~ClientProxy(); ClientProxy(ClientProxy&&) = default; ClientProxy& operator=(ClientProxy&&) = default; @@ -109,8 +108,7 @@ class ClientProxy final { const std::string& service_id, Strategy strategy, const ConnectionListener& connection_lifecycle_listener, absl::Span mediums, - const std::vector& + const std::vector& operation_result_with_medium, const AdvertisingOptions& advertising_options = AdvertisingOptions{}); // Marks this client as not advertising. @@ -134,8 +132,7 @@ class ClientProxy final { const std::string& service_id, Strategy strategy, DiscoveryListener discovery_listener, absl::Span mediums, - const std::vector& + const std::vector& operation_result_with_medium, const DiscoveryOptions& discovery_options = DiscoveryOptions{}); // Marks this client as not discovering at all. @@ -264,7 +261,8 @@ class ClientProxy final { // Adds a CancellationFlag for endpoint id. void AddCancellationFlag(const std::string& endpoint_id); // Returns the CancellationFlag for endpoint id, - CancellationFlag* GetCancellationFlag(const std::string& endpoint_id); + std::shared_ptr GetCancellationFlag( + const std::string& endpoint_id); // Sets the CancellationFlag to true for endpoint id. void CancelEndpoint(const std::string& endpoint_id); // Cancels all CancellationFlags. @@ -303,33 +301,17 @@ class ClientProxy final { return supports_safe_to_disconnect_; } - bool IsSupportAutoReconnect() const { return support_auto_reconnect_; } - - const std::int32_t& GetLocalSafeToDisconnectVersion() const { - return local_safe_to_disconnect_version_; - } std::optional GetRemoteSafeToDisconnectVersion( absl::string_view endpoint_id) const; void SetRemoteSafeToDisconnectVersion( absl::string_view endpoint_id, const std::int32_t& safe_to_disconnect_version); bool IsSafeToDisconnectEnabled(absl::string_view endpoint_id); - bool IsAutoReconnectEnabled(absl::string_view endpoint_id); bool IsPayloadReceivedAckEnabled(absl::string_view endpoint_id); - // Returns the multiplex socket supports status for local device. - std::int32_t GetLocalMultiplexSocketBitmask() const; // Sets the multiplex socket supports status for remote device. void SetRemoteMultiplexSocketBitmask(absl::string_view endpoint_id, int remote_multiplex_socket_bitmask); - // Returns true if the multiplex socket is supported for the given medium. - bool IsLocalMultiplexSocketSupported(Medium medium); - - // Gets the multiplex socket supports status for remote device. - std::optional GetRemoteMultiplexSocketBitmask( - absl::string_view endpoint_id) const; - // Returns true if the multiplex socket is supported for the given medium. - bool IsMultiplexSocketSupported(absl::string_view endpoint_id, Medium medium); // Gets the WebRTC non cellular network status. bool GetWebRtcNonCellular(); @@ -351,18 +333,6 @@ class ClientProxy final { std::optional GetMediumRole( absl::string_view endpoint_id) const; - /** Bitmask for bt multiplex connection support. */ - // Note. Deprecates the first and second bit of BT_MULTIPLEX_ENABLED and - // WIFI_LAN_MULTIPLEX_ENABLED and shift them to the third and the forth bit. - // The reason is we need to escape the (0, 1) bit which has been set in some - // devices without salt enabled. If accompany with the devices with salted - // enabled, the frames passed cannot be decrypted and the connection shall be - // failed. Please refer to b/295925531#comment#14 for the details. - enum MultiplexSocketBitmask : uint32_t { - kBtMultiplexEnabled = 1 << 2, - kWifiLanMultiplexEnabled = 1 << 3, - }; - // Forces client to regenerate a new local endpoint id. void ClearCachedLocalEndpointId(); @@ -525,11 +495,11 @@ class ClientProxy final { // Maps endpoint_id to CancellationFlag. CancellationFlags are passed around // as raw pointers to other classes in Nearby Connections, so it is important // that objects in this map are not cleared, even if they are cancelled. - absl::flat_hash_map> + absl::flat_hash_map> cancellation_flags_; // A default cancellation flag with isCancelled set be true. - std::unique_ptr default_cancellation_flag_ = - std::make_unique(true); + std::shared_ptr default_cancellation_flag_ = + std::make_shared(true); // An app lifecycle monitor for monitoring the app lifecycle state. std::unique_ptr app_lifecycle_monitor_; @@ -549,8 +519,6 @@ class ClientProxy final { // For Nearby Connections' own device provider. std::unique_ptr connections_device_provider_; bool supports_safe_to_disconnect_; - bool support_auto_reconnect_; - std::int32_t local_safe_to_disconnect_version_; // Allowed to use WebRTC over non-cellular networks. bool webrtc_non_cellular_ = false; // Whether DCT is enabled. diff --git a/connections/implementation/client_proxy_test.cc b/connections/implementation/client_proxy_test.cc index 172a58d8..2e3a5ac6 100644 --- a/connections/implementation/client_proxy_test.cc +++ b/connections/implementation/client_proxy_test.cc @@ -14,6 +14,7 @@ #include "connections/implementation/client_proxy.h" +#include #include #include #include @@ -32,17 +33,18 @@ #include "connections/advertising_options.h" #include "connections/connection_options.h" #include "connections/discovery_options.h" +#include "connections/implementation/analytics/mock_analytics_recorder.h" #include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/listeners.h" #include "connections/medium_selector.h" #include "connections/payload.h" #include "connections/status.h" #include "connections/strategy.h" +#include "connections/v3/bandwidth_info.h" #include "connections/v3/connection_listening_options.h" #include "connections/v3/connection_result.h" #include "connections/v3/connections_device_provider.h" #include "connections/v3/listeners.h" -#include "internal/analytics/mock_event_logger.h" #include "internal/flags/nearby_flags.h" #include "internal/interop/device.h" #include "internal/interop/device_provider.h" @@ -51,19 +53,15 @@ #include "internal/platform/count_down_latch.h" #include "internal/platform/feature_flags.h" #include "internal/platform/medium_environment.h" -#include "internal/platform/mutex.h" -#include "internal/platform/mutex_lock.h" +#include "internal/platform/single_thread_executor.h" #include "proto/connections_enums.pb.h" namespace nearby { namespace connections { namespace { -using ::location::nearby::analytics::proto::ConnectionsLog; using ::location::nearby::connections::OsInfo; -using ::location::nearby::proto::connections::CLIENT_SESSION; -using ::location::nearby::proto::connections::START_CLIENT_SESSION; -using ::location::nearby::proto::connections::STOP_CLIENT_SESSION; +using ::testing::_; using ::testing::IsEmpty; using ::testing::MockFunction; using ::testing::StrictMock; @@ -77,46 +75,6 @@ constexpr FeatureFlags::Flags kTestCases[] = { }, }; -class FakeEventLogger : public ::nearby::analytics::MockEventLogger { - public: - explicit FakeEventLogger() = default; - - void Log(const ConnectionsLog& message) override { - MutexLock lock(&mutex_); - logs_.push_back(message); - } - - int GetCompleteClientSessionCount() { - MutexLock lock(&mutex_); - bool has_start_client_session = false; - bool has_client_session = false; - int session_count = 0; - // We expect series of START_CLIENT_SESSION, CLIENT_SESSION and - // STOP_CLIENT_SESSION events, possibly interleaved with other events. - for (const auto& log : logs_) { - if (log.event_type() == START_CLIENT_SESSION) { - EXPECT_FALSE(has_start_client_session); - EXPECT_FALSE(has_client_session); - has_start_client_session = true; - } else if (log.event_type() == CLIENT_SESSION) { - EXPECT_TRUE(has_start_client_session); - EXPECT_FALSE(has_client_session); - has_client_session = true; - } else if (log.event_type() == STOP_CLIENT_SESSION) { - EXPECT_TRUE(has_start_client_session); - EXPECT_TRUE(has_client_session); - has_start_client_session = false; - has_client_session = false; - ++session_count; - } - } - return session_count; - } - - Mutex mutex_; - std::vector logs_; -}; - class MockDeviceProvider : public nearby::NearbyDeviceProvider { public: MOCK_METHOD((const NearbyDevice*), GetLocalDevice, (), (override)); @@ -165,8 +123,14 @@ class ClientProxyTest : public ::testing::TestWithParam { /*use_simulated_clock=*/true, /*use_temporary_directory_for_app_path=*/true}; env_.Start(config); - client1_ = std::make_unique(&event_logger1_); - client2_ = std::make_unique(&event_logger2_); + auto analytics_recorder1 = + std::make_unique(); + mock_analytics_recorder1_ptr_ = analytics_recorder1.get(); + client1_ = std::make_unique(std::move(analytics_recorder1)); + auto analytics_recorder2 = + std::make_unique(); + mock_analytics_recorder2_ptr_ = analytics_recorder2.get(); + client2_ = std::make_unique(std::move(analytics_recorder2)); } void TearDown() override { @@ -360,10 +324,7 @@ class ClientProxyTest : public ::testing::TestWithParam { ClientProxy* client2() { return client2_.get(); } void FastForward(absl::Duration duration) { - (*env_.GetSimulatedClock()) - ->FastForward( - ClientProxy::kHighPowerAdvertisementEndpointIdCacheTimeout + - absl::Milliseconds(100)); + env_.FastForward(duration); // make sure the timer based callback is executed. absl::SleepFor(absl::Milliseconds(100)); } @@ -383,8 +344,8 @@ class ClientProxyTest : public ::testing::TestWithParam { MediumEnvironment& env_ = MediumEnvironment::Instance(); Strategy strategy_{Strategy::kP2pPointToPoint}; const std::string service_id_{"service"}; - FakeEventLogger event_logger1_; - FakeEventLogger event_logger2_; + analytics::MockAnalyticsRecorder* mock_analytics_recorder1_ptr_; + analytics::MockAnalyticsRecorder* mock_analytics_recorder2_ptr_; std::unique_ptr client1_; std::unique_ptr client2_; std::string auth_token_ = "auth_token"; @@ -437,7 +398,7 @@ TEST_P(ClientProxyTest, CanCancelEndpoint) { // `CancellationFlag` pointers are passed to other classes in Nearby // Connections, and by using the pointers directly, we test their // consumption of `CancellationFlag` pointers. - CancellationFlag* cancellation_flag = + std::shared_ptr cancellation_flag = client2()->GetCancellationFlag(advertising_endpoint.id); EXPECT_FALSE( @@ -473,7 +434,7 @@ TEST_P(ClientProxyTest, CanCancelAllEndpoints) { // `CancellationFlag` pointers are passed to other classes in Nearby // Connections, and by using the pointers directly, we test their // consumption of `CancellationFlag` pointers. - CancellationFlag* cancellation_flag = + std::shared_ptr cancellation_flag = client2()->GetCancellationFlag(advertising_endpoint.id); EXPECT_FALSE( @@ -540,6 +501,26 @@ TEST_P(ClientProxyTest, CanCancelAllEndpointsWithDifferentEndpoint) { } } +TEST_P(ClientProxyTest, GetCancellationFlagRace) { + std::string endpoint_id = "test_endpoint"; + client1()->AddCancellationFlag(endpoint_id); + + std::atomic run{true}; + SingleThreadExecutor executor; + executor.Execute([&]() { + while (run) { + client1()->GetCancellationFlag(endpoint_id); + } + }); + + for (int i = 0; i < 10000; ++i) { + client1()->Reset(); + client1()->AddCancellationFlag(endpoint_id); + } + + run = false; +} + INSTANTIATE_TEST_SUITE_P(ParametrisedClientProxyTest, ClientProxyTest, ::testing::ValuesIn(kTestCases)); @@ -1161,11 +1142,13 @@ TEST_F(ClientProxyTest, NotLogSessionForStoppedAdvertisingWithConnection) { // After StopAdvertising(client1()); // No Advertising - EXPECT_EQ(event_logger1_.GetCompleteClientSessionCount(), 0); + EXPECT_CALL(*mock_analytics_recorder1_ptr_, LogSession()).Times(1); } TEST_F(ClientProxyTest, LogSessionForStoppedAdvertisingWhenNoConnectionsAndNoDiscovering) { + EXPECT_CALL(*mock_analytics_recorder1_ptr_, + OnStartAdvertising(strategy_, mediums_, _)); Endpoint advertising_endpoint = StartAdvertising(client1(), advertising_connection_listener_); @@ -1174,36 +1157,47 @@ TEST_F(ClientProxyTest, advertising_endpoint.id)); // No Connections EXPECT_FALSE(client1()->IsDiscovering()); // No Discovery EXPECT_TRUE(client1()->IsAdvertising()); // Advertising - EXPECT_EQ(event_logger1_.GetCompleteClientSessionCount(), 0); // After + EXPECT_CALL(*mock_analytics_recorder1_ptr_, OnStopAdvertising()); StopAdvertising(client1()); - EXPECT_GT(event_logger1_.GetCompleteClientSessionCount(), 0); } TEST_F(ClientProxyTest, NotLogSessionForStoppedDiscoveryWithConnection) { + EXPECT_CALL(*mock_analytics_recorder1_ptr_, + OnStartAdvertising(strategy_, mediums_, _)); Endpoint advertising_endpoint = StartAdvertising(client1(), advertising_connection_listener_); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnStartDiscovery(strategy_, mediums_, _)); StartDiscovery(client2(), GetDiscoveryListener()); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnEndpointFound(Medium::BLUETOOTH)); OnDiscoveryEndpointFound(client2(), advertising_endpoint); // Before + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnConnectionRequestReceived(advertising_endpoint.id)); OnDiscoveryConnectionInitiated( client2(), advertising_endpoint); // Connections are available EXPECT_FALSE(client2()->IsAdvertising()); // No Advertising EXPECT_TRUE(client2()->IsDiscovering()); // Discovering // After + EXPECT_CALL(*mock_analytics_recorder2_ptr_, OnStopDiscovery()); StopDiscovery(client2()); - EXPECT_EQ(event_logger2_.GetCompleteClientSessionCount(), 0); } TEST_F(ClientProxyTest, NotLogSessionForStoppedDiscoveryWithoutConnectionsAndAdvertising) { + EXPECT_CALL(*mock_analytics_recorder1_ptr_, + OnStartAdvertising(strategy_, mediums_, _)); Endpoint advertising_endpoint = StartAdvertising(client1(), advertising_connection_listener_); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnStartDiscovery(strategy_, mediums_, _)); StartDiscovery(client2(), GetDiscoveryListener()); // Before @@ -1213,30 +1207,40 @@ TEST_F(ClientProxyTest, advertising_endpoint.id)); // No Connections // After + EXPECT_CALL(*mock_analytics_recorder2_ptr_, OnStopDiscovery()); StopDiscovery(client2()); - EXPECT_GT(event_logger2_.GetCompleteClientSessionCount(), 0); } TEST_F(ClientProxyTest, LogSessionOnDisconnectedWithOneConnection) { + EXPECT_CALL(*mock_analytics_recorder1_ptr_, + OnStartAdvertising(strategy_, mediums_, _)); Endpoint advertising_endpoint = StartAdvertising(client1(), advertising_connection_listener_); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnStartDiscovery(strategy_, mediums_, _)); StartDiscovery(client2(), GetDiscoveryListener()); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnEndpointFound(Medium::BLUETOOTH)); OnDiscoveryEndpointFound(client2(), advertising_endpoint); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnConnectionRequestReceived(advertising_endpoint.id)); OnDiscoveryConnectionInitiated(client2(), advertising_endpoint); // Before EXPECT_FALSE(client2()->IsAdvertising()); // No Advertising + EXPECT_CALL(*mock_analytics_recorder2_ptr_, OnStopDiscovery()); StopDiscovery(client2()); // No Discovery EXPECT_TRUE(client2()->HasPendingConnectionToEndpoint( advertising_endpoint.id)); // One Connection // After OnDiscoveryConnectionDisconnected(client2(), advertising_endpoint); - EXPECT_GT(event_logger2_.GetCompleteClientSessionCount(), 0); } TEST_F(ClientProxyTest, NotLogSessionOnDisconnectedWithoutConnectionsDiscoveringAdvertising) { + EXPECT_CALL(*mock_analytics_recorder1_ptr_, + OnStartAdvertising(strategy_, mediums_, _)); Endpoint advertising_endpoint = StartAdvertising(client1(), advertising_connection_listener_); @@ -1248,13 +1252,16 @@ TEST_F(ClientProxyTest, // After client2()->OnDisconnected(advertising_endpoint.id, /*notify=*/false); - EXPECT_EQ(event_logger2_.GetCompleteClientSessionCount(), 0); } TEST_F(ClientProxyTest, NotLogSessionOnDisconnectedWhenMoreThanOneConnection) { ClientProxy client3; + EXPECT_CALL(*mock_analytics_recorder1_ptr_, + OnStartAdvertising(strategy_, mediums_, _)); Endpoint advertising_endpoint_1 = StartAdvertising(client1(), advertising_connection_listener_); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnStartAdvertising(strategy_, mediums_, _)); Endpoint advertising_endpoint_2 = StartAdvertising(client2(), advertising_connection_listener_); StartDiscovery(&client3, GetDiscoveryListener()); @@ -1275,15 +1282,22 @@ TEST_F(ClientProxyTest, NotLogSessionOnDisconnectedWhenMoreThanOneConnection) { // After client2()->OnDisconnected(advertising_endpoint_1.id, /*notify=*/false); - EXPECT_EQ(event_logger2_.GetCompleteClientSessionCount(), 0); } TEST_F(ClientProxyTest, NotLogSessionOnDisconnectedForDiscoveringWithOnlyOneConnection) { + EXPECT_CALL(*mock_analytics_recorder1_ptr_, + OnStartAdvertising(strategy_, mediums_, _)); Endpoint advertising_endpoint = StartAdvertising(client1(), advertising_connection_listener_); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnStartDiscovery(strategy_, mediums_, _)); StartDiscovery(client2(), GetDiscoveryListener()); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnEndpointFound(Medium::BLUETOOTH)); OnDiscoveryEndpointFound(client2(), advertising_endpoint); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnConnectionRequestReceived(advertising_endpoint.id)); OnDiscoveryConnectionInitiated(client2(), advertising_endpoint); // Before @@ -1294,26 +1308,27 @@ TEST_F(ClientProxyTest, // After OnDiscoveryConnectionDisconnected(client2(), advertising_endpoint); - // Since we are no longer checking IsDiscovering(), we complete sessions now - // solely based on advertising. - EXPECT_EQ(event_logger2_.GetCompleteClientSessionCount(), 1); } TEST_F(ClientProxyTest, LogSessionForResetClientProxy) { + EXPECT_CALL(*mock_analytics_recorder1_ptr_, + OnStartAdvertising(strategy_, mediums_, _)); Endpoint advertising_endpoint = StartAdvertising(client1(), advertising_connection_listener_); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnStartDiscovery(strategy_, mediums_, _)); StartDiscovery(client2(), GetDiscoveryListener()); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnEndpointFound(Medium::BLUETOOTH)); OnDiscoveryEndpointFound(client2(), advertising_endpoint); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, + OnConnectionRequestReceived(advertising_endpoint.id)); OnDiscoveryConnectionInitiated(client2(), advertising_endpoint); - EXPECT_EQ(event_logger1_.GetCompleteClientSessionCount(), 0); + EXPECT_CALL(*mock_analytics_recorder1_ptr_, OnStopAdvertising()); client1()->Reset(); - // TODO(b/290936886): Why are there more than one complete sessions? - EXPECT_GT(event_logger1_.GetCompleteClientSessionCount(), 0); - - EXPECT_EQ(event_logger2_.GetCompleteClientSessionCount(), 0); + EXPECT_CALL(*mock_analytics_recorder2_ptr_, OnStopDiscovery()); client2()->Reset(); - EXPECT_GT(event_logger2_.GetCompleteClientSessionCount(), 0); } TEST_F(ClientProxyTest, GetLocalInfoCorrect) { @@ -1483,91 +1498,12 @@ TEST_F(ClientProxyTest, TestAutoBwuWhenListeningWithAutoBwu) { EXPECT_TRUE(client1()->AutoUpgradeBandwidth()); } -TEST_F(ClientProxyTest, TestMultiplexSocketBitmask) { - if (!NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplex)) { - EXPECT_EQ(client1()->GetLocalMultiplexSocketBitmask(), 0); - } - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature::kEnableMultiplex, - true); - EXPECT_EQ(client1()->GetLocalMultiplexSocketBitmask(), 0); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexBluetooth, - true); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexWifiLan, - true); - EXPECT_EQ( - client1()->GetLocalMultiplexSocketBitmask(), - ClientProxy::kBtMultiplexEnabled | ClientProxy::kWifiLanMultiplexEnabled); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature::kEnableMultiplex, - false); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexBluetooth, - false); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexWifiLan, - false); -} - -TEST_F(ClientProxyTest, TestRemoteMultiplexSocketBitmask) { - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature::kEnableMultiplex, - true); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexBluetooth, - true); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexWifiLan, - true); - Endpoint advertising_endpoint = - StartAdvertising(client1(), advertising_connection_listener_); - OnAdvertisingConnectionInitiated(client1(), advertising_endpoint); - client1()->SetRemoteMultiplexSocketBitmask( - advertising_endpoint.id, - ClientProxy::kBtMultiplexEnabled | ClientProxy::kWifiLanMultiplexEnabled); - ASSERT_TRUE(client1() - ->GetRemoteMultiplexSocketBitmask(advertising_endpoint.id) - .has_value()); - EXPECT_EQ( - client1() - ->GetRemoteMultiplexSocketBitmask(advertising_endpoint.id) - .value(), - ClientProxy::kBtMultiplexEnabled | ClientProxy::kWifiLanMultiplexEnabled); - EXPECT_TRUE(client1()->IsMultiplexSocketSupported(advertising_endpoint.id, - Medium::BLUETOOTH)); - EXPECT_TRUE(client1()->IsMultiplexSocketSupported(advertising_endpoint.id, - Medium::WIFI_LAN)); - EXPECT_FALSE(client1()->IsMultiplexSocketSupported(advertising_endpoint.id, - Medium::WIFI_AWARE)); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature::kEnableMultiplex, - false); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexBluetooth, - false); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexWifiLan, - false); -} - TEST_F(ClientProxyTest, SaveClientInfoFromPreferences) { NearbyFlags::GetInstance().OverrideBoolFlagValue( config_package_nearby::nearby_connections_feature:: kEnableNearbyConnectionsPreferences, true); - client1_ = std::make_unique(&event_logger1_); + client1_ = std::make_unique(); Endpoint advertising_endpoint = StartAdvertising(client1(), advertising_connection_listener_); std::string endpoint_id = advertising_endpoint.id; @@ -1575,7 +1511,7 @@ TEST_F(ClientProxyTest, SaveClientInfoFromPreferences) { // Destroy the client and create a new one. client1_.reset(); - client1_ = std::make_unique(&event_logger1_); + client1_ = std::make_unique(); // The new client should load the same endpoint ID. EXPECT_EQ(client1()->GetLocalEndpointId(), endpoint_id); @@ -1590,7 +1526,7 @@ TEST_F(ClientProxyTest, NotLoadClientInfoFromPreferencesOnExpired) { config_package_nearby::nearby_connections_feature:: kEnableNearbyConnectionsPreferences, true); - client1_ = std::make_unique(&event_logger1_); + client1_ = std::make_unique(); Endpoint advertising_endpoint = StartAdvertising(client1(), advertising_connection_listener_); std::string endpoint_id = advertising_endpoint.id; @@ -1600,7 +1536,7 @@ TEST_F(ClientProxyTest, NotLoadClientInfoFromPreferencesOnExpired) { client1_.reset(); FastForward(absl::Hours(25)); - client1_ = std::make_unique(&event_logger1_); + client1_ = std::make_unique(); // The new client should load the same endpoint ID. EXPECT_NE(client1()->GetLocalEndpointId(), endpoint_id); diff --git a/connections/implementation/connections_authentication_transport.cc b/connections/implementation/connections_authentication_transport.cc index c98edbbe..f4bb001d 100644 --- a/connections/implementation/connections_authentication_transport.cc +++ b/connections/implementation/connections_authentication_transport.cc @@ -14,30 +14,30 @@ #include "connections/implementation/connections_authentication_transport.h" +#include #include +#include #include "absl/strings/string_view.h" #include "connections/implementation/endpoint_channel.h" -#include "internal/platform/byte_array.h" #include "internal/platform/logging.h" namespace nearby { namespace connections { ConnectionsAuthenticationTransport::ConnectionsAuthenticationTransport( - const EndpointChannel& channel) { - channel_ = const_cast(&channel); -} + std::shared_ptr channel) + : channel_(std::move(channel)) {} void ConnectionsAuthenticationTransport::WriteMessage( absl::string_view message) const { - // channel_ should never be null. + // channel_ is guaranteed valid by shared_ptr ownership CHECK(channel_ != nullptr); - channel_->Write(ByteArray(message.data(), message.size())); + channel_->Write(message); } std::string ConnectionsAuthenticationTransport::ReadMessage() const { - // channel_ should never be null. + // channel_ is guaranteed valid by shared_ptr ownership CHECK(channel_ != nullptr); auto response = channel_->Read(); if (response.ok()) { diff --git a/connections/implementation/connections_authentication_transport.h b/connections/implementation/connections_authentication_transport.h index ab7886d4..a4bcafac 100644 --- a/connections/implementation/connections_authentication_transport.h +++ b/connections/implementation/connections_authentication_transport.h @@ -15,6 +15,7 @@ #ifndef THIRD_PARTY_NEARBY_CONNECTIONS_IMPLEMENTATION_CONNECTIONS_AUTHENTICATION_TRANSPORT_H_ #define THIRD_PARTY_NEARBY_CONNECTIONS_IMPLEMENTATION_CONNECTIONS_AUTHENTICATION_TRANSPORT_H_ +#include #include #include "absl/strings/string_view.h" @@ -30,12 +31,13 @@ namespace connections { class ConnectionsAuthenticationTransport : public nearby::AuthenticationTransport { public: - explicit ConnectionsAuthenticationTransport(const EndpointChannel& channel); + explicit ConnectionsAuthenticationTransport( + std::shared_ptr channel); void WriteMessage(absl::string_view message) const override; std::string ReadMessage() const override; private: - EndpointChannel* channel_; + std::shared_ptr channel_; }; } // namespace connections diff --git a/connections/implementation/connections_authentication_transport_test.cc b/connections/implementation/connections_authentication_transport_test.cc index 11ebd2eb..333a4da2 100644 --- a/connections/implementation/connections_authentication_transport_test.cc +++ b/connections/implementation/connections_authentication_transport_test.cc @@ -14,7 +14,6 @@ #include "connections/implementation/connections_authentication_transport.h" -#include #include #include #include @@ -23,12 +22,9 @@ #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" #include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "connections/implementation/analytics/analytics_recorder.h" -#include "connections/implementation/endpoint_channel.h" +#include "connections/implementation/mock_endpoint_channel.h" #include "internal/platform/byte_array.h" #include "internal/platform/exception.h" -#include "proto/connections_enums.pb.h" namespace nearby { namespace connections { @@ -36,84 +32,40 @@ namespace { using ::testing::_; -class MockEndpointChannel : public EndpointChannel { - public: - MOCK_METHOD(ExceptionOr, Read, (), (override)); - MOCK_METHOD(ExceptionOr, Read, (PacketMetaData&), (override)); - MOCK_METHOD(Exception, Write, (const ByteArray& data), (override)); - MOCK_METHOD(Exception, Write, (absl::string_view data, PacketMetaData&), - (override)); - MOCK_METHOD(void, Close, (), (override)); - MOCK_METHOD( - void, Close, - (location::nearby::proto::connections::DisconnectionReason reason), - (override)); - MOCK_METHOD(void, Close, - (location::nearby::proto::connections::DisconnectionReason reason, - location::nearby::analytics::proto::ConnectionsLog:: - EstablishedConnection::SafeDisconnectionResult result), - (override)); - MOCK_METHOD(bool, IsClosed, (), (const, override)); - MOCK_METHOD(std::string, GetType, (), (const, override)); - MOCK_METHOD(std::string, GetServiceId, (), (const, override)); - MOCK_METHOD(std::string, GetName, (), (const, override)); - MOCK_METHOD(location::nearby::proto::connections::Medium, GetMedium, (), - (const, override)); - MOCK_METHOD(location::nearby::proto::connections::ConnectionTechnology, - GetTechnology, (), (const, override)); - MOCK_METHOD(location::nearby::proto::connections::ConnectionBand, GetBand, (), - (const, override)); - MOCK_METHOD(int, GetFrequency, (), (const, override)); - MOCK_METHOD(int, GetTryCount, (), (const, override)); - MOCK_METHOD(int, GetMaxTransmitPacketSize, (), (const, override)); - MOCK_METHOD(void, EnableEncryption, (std::shared_ptr), - (override)); - MOCK_METHOD(void, DisableEncryption, (), (override)); - MOCK_METHOD(bool, IsEncrypted, (), (override)); - MOCK_METHOD(ExceptionOr, TryDecrypt, (const ByteArray& data), - (override)); - MOCK_METHOD(bool, IsPaused, (), (const, override)); - MOCK_METHOD(void, Pause, (), (override)); - MOCK_METHOD(void, Resume, (), (override)); - MOCK_METHOD(absl::Time, GetLastReadTimestamp, (), (const, override)); - MOCK_METHOD(absl::Time, GetLastWriteTimestamp, (), (const, override)); - MOCK_METHOD(uint32_t, GetNextKeepAliveSeqNo, (), (const, override)); - MOCK_METHOD(void, SetAnalyticsRecorder, - (analytics::AnalyticsRecorder*, const std::string&), (override)); - - std::vector messages_; -}; - TEST(ConnectionsAuthenticationTransportTest, TestWriteMessage) { - MockEndpointChannel channel; + std::vector messages; + auto channel = std::make_shared(); ConnectionsAuthenticationTransport transport(channel); - EXPECT_CALL(channel, Write(_)).WillOnce([&channel](const ByteArray& data) { - channel.messages_.push_back(data.string_data()); - return Exception{ - .value = Exception::Value::kSuccess, - }; - }); + EXPECT_CALL(*channel, Write(_)) + .WillOnce([&messages](absl::string_view data) { + messages.push_back(std::string(data)); + return Exception{ + .value = Exception::Value::kSuccess, + }; + }); transport.WriteMessage("hello world"); - EXPECT_THAT(channel.messages_, testing::ElementsAre("hello world")); + EXPECT_THAT(messages, testing::ElementsAre("hello world")); } TEST(ConnectionsAuthenticationTransportTest, TestReadMessage) { - MockEndpointChannel channel; + std::vector messages; + auto channel = std::make_shared(); ConnectionsAuthenticationTransport transport(channel); - channel.messages_.push_back("hello world"); - EXPECT_CALL(channel, Read()).WillOnce([&channel]() { - std::string ret = channel.messages_[0]; - channel.messages_.erase(channel.messages_.begin()); + messages.push_back("hello world"); + EXPECT_CALL(*channel, Read()).WillOnce([&messages]() { + std::string ret = messages[0]; + messages.erase(messages.begin()); return ExceptionOr(ByteArray(ret)); }); EXPECT_EQ(transport.ReadMessage(), "hello world"); } TEST(ConnectionsAuthenticationTransportTest, TestReadMessageFail) { - MockEndpointChannel channel; + std::vector messages; + auto channel = std::make_shared(); ConnectionsAuthenticationTransport transport(channel); - channel.messages_.push_back("hello world"); - EXPECT_CALL(channel, Read()).WillOnce([]() { + messages.push_back("hello world"); + EXPECT_CALL(*channel, Read()).WillOnce([]() { return ExceptionOr(Exception::Value::kIo); }); EXPECT_EQ(transport.ReadMessage(), ""); diff --git a/connections/implementation/encryption_runner.cc b/connections/implementation/encryption_runner.cc index a74a4b8e..a3ade421 100644 --- a/connections/implementation/encryption_runner.cc +++ b/connections/implementation/encryption_runner.cc @@ -14,7 +14,6 @@ #include "connections/implementation/encryption_runner.h" -#include #include #include #include @@ -22,7 +21,6 @@ #include "securegcm/ukey2_handshake.h" #include "absl/strings/ascii.h" -#include "absl/time/clock.h" #include "absl/time/time.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" @@ -70,9 +68,9 @@ bool HandleEncryptionSuccess(const std::string& endpoint_id, return true; } -void CancelableAlarmRunnable(ClientProxy* client, - const std::string& endpoint_id, - EndpointChannel* endpoint_channel) { +void CancelableAlarmRunnable( + ClientProxy* client, const std::string& endpoint_id, + std::shared_ptr endpoint_channel) { LOG(INFO) << "Timing out encryption for client " << client->GetClientId() << " to endpoint_id=" << endpoint_id << " after " << absl::FormatDuration(kTimeout); @@ -82,18 +80,31 @@ void CancelableAlarmRunnable(ClientProxy* client, class ServerRunnable final { public: ServerRunnable(ClientProxy* client, ScheduledExecutor* alarm_executor, - const std::string& endpoint_id, EndpointChannel* channel, + const std::string& endpoint_id, + std::shared_ptr channel, EncryptionRunner::ResultListener listener) : client_(client), alarm_executor_(alarm_executor), endpoint_id_(endpoint_id), - channel_(channel), + weak_channel_(channel), listener_(std::move(listener)) {} void operator()() { + // Lock the weak pointer. If it fails, the channel was freed. + auto channel = weak_channel_.lock(); + // The IsClosed() check is to provide an early exit if channel has been + // closed. Otherwise the Read() and Write() calls on the channel below will + // return error and exit. + if (!channel || channel->IsClosed()) { + return; + } CancelableAlarm timeout_alarm( "EncryptionRunner.StartServer() timeout", - [this]() { CancelableAlarmRunnable(client_, endpoint_id_, channel_); }, + [this, weak_channel = weak_channel_]() { + if (auto channel = weak_channel.lock()) { + CancelableAlarmRunnable(client_, endpoint_id_, channel); + } + }, kTimeout, alarm_executor_); std::unique_ptr server = @@ -105,7 +116,7 @@ class ServerRunnable final { } // Message 1 (Client Init) - ExceptionOr client_init = channel_->Read(); + ExceptionOr client_init = channel->Read(); if (!client_init.ok()) { LogException(); HandleHandshakeOrIoException(&timeout_alarm); @@ -119,7 +130,7 @@ class ServerRunnable final { if (!parse_result.success) { LogException(); if (parse_result.alert_to_send != nullptr) { - HandleAlertException(parse_result); + HandleAlertException(parse_result, channel); } HandleHandshakeOrIoException(&timeout_alarm); return; @@ -139,8 +150,7 @@ class ServerRunnable final { return; } - Exception write_exception = - channel_->Write(ByteArray(std::move(*server_init))); + Exception write_exception = channel->Write(*server_init); if (!write_exception.Ok()) { LogException(); HandleHandshakeOrIoException(&timeout_alarm); @@ -151,7 +161,7 @@ class ServerRunnable final { << endpoint_id_ << ")."; // Message 3 (Client Finish) - ExceptionOr client_finish = channel_->Read(); + ExceptionOr client_finish = channel->Read(); if (!client_finish.ok()) { LogException(); @@ -166,7 +176,7 @@ class ServerRunnable final { if (!parse_result.success) { LogException(); if (parse_result.alert_to_send != nullptr) { - HandleAlertException(parse_result); + HandleAlertException(parse_result, channel); } HandleHandshakeOrIoException(&timeout_alarm); return; @@ -192,13 +202,13 @@ class ServerRunnable final { void HandleHandshakeOrIoException(CancelableAlarm* timeout_alarm) { timeout_alarm->Cancel(); - listener_.CallFailureCallback(endpoint_id_, channel_); + listener_.CallFailureCallback(endpoint_id_); } void HandleAlertException( - const securegcm::UKey2Handshake::ParseResult& parse_result) const { - Exception write_exception = - channel_->Write(ByteArray(*parse_result.alert_to_send)); + const securegcm::UKey2Handshake::ParseResult& parse_result, + std::shared_ptr channel) const { + Exception write_exception = channel->Write(*parse_result.alert_to_send); if (!write_exception.Ok()) { LOG(WARNING) << "In StartServer(), client " << client_->GetClientId() << " failed to pass the alert error message to endpoint(id=" @@ -209,25 +219,39 @@ class ServerRunnable final { ClientProxy* client_; ScheduledExecutor* alarm_executor_; const std::string endpoint_id_; - EndpointChannel* channel_; + std::weak_ptr weak_channel_; EncryptionRunner::ResultListener listener_; }; class ClientRunnable final { public: ClientRunnable(ClientProxy* client, ScheduledExecutor* alarm_executor, - const std::string& endpoint_id, EndpointChannel* channel, + const std::string& endpoint_id, + std::shared_ptr channel, EncryptionRunner::ResultListener listener) : client_(client), alarm_executor_(alarm_executor), endpoint_id_(endpoint_id), - channel_(channel), + weak_channel_(channel), listener_(std::move(listener)) {} void operator()() { + // Lock the weak pointer. If it fails, the channel was freed. + auto channel = weak_channel_.lock(); + // The IsClosed() check is to provide an early exit if channel has been + // closed. Otherwise the Read() and Write() calls on the channel below will + // return error and exit. + if (!channel || channel->IsClosed()) { + return; + } + CancelableAlarm timeout_alarm( "EncryptionRunner.StartClient() timeout", - [this]() { CancelableAlarmRunnable(client_, endpoint_id_, channel_); }, + [this, weak_channel = weak_channel_]() { + if (auto channel = weak_channel.lock()) { + CancelableAlarmRunnable(client_, endpoint_id_, channel); + } + }, kTimeout, alarm_executor_); std::unique_ptr crypto = @@ -251,7 +275,7 @@ class ClientRunnable final { return; } - Exception write_init_exception = channel_->Write(ByteArray(*client_init)); + Exception write_init_exception = channel->Write(*client_init); if (!write_init_exception.Ok()) { LogException(); HandleHandshakeOrIoException(&timeout_alarm); @@ -262,7 +286,7 @@ class ClientRunnable final { << endpoint_id_ << ")."; // Message 2 (Server Init) - ExceptionOr server_init = channel_->Read(); + ExceptionOr server_init = channel->Read(); if (!server_init.ok()) { LogException(); @@ -277,7 +301,7 @@ class ClientRunnable final { if (!parse_result.success) { LogException(); if (parse_result.alert_to_send != nullptr) { - HandleAlertException(parse_result); + HandleAlertException(parse_result, channel); } HandleHandshakeOrIoException(&timeout_alarm); return; @@ -297,8 +321,7 @@ class ClientRunnable final { return; } - Exception write_finish_exception = - channel_->Write(ByteArray(*client_finish)); + Exception write_finish_exception = channel->Write(*client_finish); if (!write_finish_exception.Ok()) { LogException(); HandleHandshakeOrIoException(&timeout_alarm); @@ -325,13 +348,13 @@ class ClientRunnable final { void HandleHandshakeOrIoException(CancelableAlarm* timeout_alarm) { timeout_alarm->Cancel(); - listener_.CallFailureCallback(endpoint_id_, channel_); + listener_.CallFailureCallback(endpoint_id_); } void HandleAlertException( - const securegcm::UKey2Handshake::ParseResult& parse_result) const { - Exception write_exception = - channel_->Write(ByteArray(*parse_result.alert_to_send)); + const securegcm::UKey2Handshake::ParseResult& parse_result, + std::shared_ptr channel) const { + Exception write_exception = channel->Write(*parse_result.alert_to_send); if (!write_exception.Ok()) { LOG(WARNING) << "In StartClient(), client " << client_->GetClientId() << " failed to pass the alert error message to endpoint(id=" @@ -342,7 +365,7 @@ class ClientRunnable final { ClientProxy* client_; ScheduledExecutor* alarm_executor_; const std::string endpoint_id_; - EndpointChannel* channel_; + std::weak_ptr weak_channel_; EncryptionRunner::ResultListener listener_; }; @@ -350,19 +373,19 @@ class ClientRunnable final { EncryptionRunner::~EncryptionRunner() { Shutdown(); } -void EncryptionRunner::StartServer(ClientProxy* client, - const std::string& endpoint_id, - EndpointChannel* endpoint_channel, - EncryptionRunner::ResultListener listener) { +void EncryptionRunner::StartServer( + ClientProxy* client, const std::string& endpoint_id, + std::shared_ptr endpoint_channel, + EncryptionRunner::ResultListener listener) { ServerRunnable runnable(client, &alarm_executor_, endpoint_id, endpoint_channel, std::move(listener)); server_executor_.Execute("encryption-server", std::move(runnable)); } -void EncryptionRunner::StartClient(ClientProxy* client, - const std::string& endpoint_id, - EndpointChannel* endpoint_channel, - EncryptionRunner::ResultListener listener) { +void EncryptionRunner::StartClient( + ClientProxy* client, const std::string& endpoint_id, + std::shared_ptr endpoint_channel, + EncryptionRunner::ResultListener listener) { ClientRunnable runnable(client, &alarm_executor_, endpoint_id, endpoint_channel, std::move(listener)); client_executor_.Execute("encryption-client", std::move(runnable)); @@ -391,9 +414,9 @@ void EncryptionRunner::ResultListener::CallSuccessCallback( } void EncryptionRunner::ResultListener::CallFailureCallback( - const std::string& endpoint_id, EndpointChannel* channel) { + const std::string& endpoint_id) { if (on_failure_cb) { - std::move(on_failure_cb)(endpoint_id, channel); + std::move(on_failure_cb)(endpoint_id); } Reset(); } diff --git a/connections/implementation/encryption_runner.h b/connections/implementation/encryption_runner.h index e8e8c186..6e2236d7 100644 --- a/connections/implementation/encryption_runner.h +++ b/connections/implementation/encryption_runner.h @@ -45,8 +45,7 @@ class EncryptionRunner { std::unique_ptr ukey2, const std::string& auth_token, const ByteArray& raw_auth_token); - void CallFailureCallback(const std::string& endpoint_id, - EndpointChannel* channel); + void CallFailureCallback(const std::string& endpoint_id); void Reset(); // @EncryptionRunnerThread @@ -56,27 +55,19 @@ class EncryptionRunner { const ByteArray& raw_auth_token) &&> on_success_cb; - // Encryption has failed. The remote_endpoint_id and channel are given so - // that any pending state can be cleaned up. - // - // We return the EndpointChannel because, at this stage, simultaneous - // connections are a possibility. Use this channel to verify that the state - // you're cleaning up is for this EndpointChannel, and not state for another - // channel to the same endpoint. + // Encryption has failed. // // @EncryptionRunnerThread - absl::AnyInvocable - on_failure_cb; + absl::AnyInvocable on_failure_cb; }; // @AnyThread void StartServer(ClientProxy* client, const std::string& endpoint_id, - EndpointChannel* endpoint_channel, + std::shared_ptr endpoint_channel, ResultListener result_listener); // @AnyThread void StartClient(ClientProxy* client, const std::string& endpoint_id, - EndpointChannel* endpoint_channel, + std::shared_ptr endpoint_channel, ResultListener result_listener); // @AnyThread diff --git a/connections/implementation/encryption_runner_test.cc b/connections/implementation/encryption_runner_test.cc index c5ec1964..743c1658 100644 --- a/connections/implementation/encryption_runner_test.cc +++ b/connections/implementation/encryption_runner_test.cc @@ -35,8 +35,7 @@ #include "proto/connections_enums.pb.h" #include "third_party/ukey2/src/main/cpp/include/securegcm/ukey2_handshake.h" -namespace nearby { -namespace connections { +namespace nearby::connections { namespace { using ::location::nearby::proto::connections::Medium; @@ -52,16 +51,8 @@ class FakeEndpointChannel : public EndpointChannel { read_timestamp_ = SystemClock::ElapsedRealtime(); return in_ ? in_->Read(kChunkSize) : ExceptionOr{Exception::kIo}; } - ExceptionOr Read(PacketMetaData& packet_meta_data) override { - read_timestamp_ = SystemClock::ElapsedRealtime(); - return in_ ? in_->Read(kChunkSize) : ExceptionOr{Exception::kIo}; - } - Exception Write(const ByteArray& data) override { - write_timestamp_ = SystemClock::ElapsedRealtime(); - return out_ ? out_->Write(data.AsStringView()) : Exception{Exception::kIo}; - } - Exception Write(absl::string_view data, - PacketMetaData& packet_meta_data) override { + + Exception Write(absl::string_view data) override { write_timestamp_ = SystemClock::ElapsedRealtime(); return out_ ? out_->Write(data) : Exception{Exception::kIo}; } @@ -73,10 +64,8 @@ class FakeEndpointChannel : public EndpointChannel { override { Close(); } - void Close( - location::nearby::proto::connections::DisconnectionReason reason, - location::nearby::analytics::proto::ConnectionsLog:: - EstablishedConnection::SafeDisconnectionResult result) override { + void Close(location::nearby::proto::connections::DisconnectionReason reason, + nearby::analytics::SafeDisconnectionResult result) override { Close(); } bool IsClosed() const override { return false; } @@ -124,21 +113,24 @@ class FakeEndpointChannel : public EndpointChannel { }; struct User { - User(InputStream* reader, OutputStream* writer) : channel(reader, writer) {} + User(InputStream* reader, OutputStream* writer) + : channel(std::make_shared(reader, writer)) {} - FakeEndpointChannel channel; + std::shared_ptr channel; EncryptionRunner crypto; ClientProxy client; }; struct Response { + Response() : latch(2) {} + explicit Response(int count) : latch(count) {} enum class Status { kUnknown = 0, kDone = 1, kFailed = 2, }; - CountDownLatch latch{2}; + CountDownLatch latch; Status server_status = Status::kUnknown; Status client_status = Status::kUnknown; }; @@ -155,7 +147,7 @@ TEST(EncryptionRunnerTest, ReadWrite) { Response response; user_a.crypto.StartServer( - &user_a.client, "endpoint_id", &user_a.channel, + &user_a.client, "endpoint_id", user_a.channel, { .on_success_cb = [&response](const std::string& endpoint_id, @@ -166,15 +158,14 @@ TEST(EncryptionRunnerTest, ReadWrite) { response.latch.CountDown(); }, .on_failure_cb = - [&response](const std::string& endpoint_id, - EndpointChannel* channel) { - channel->Close(); + [&response, &user_a](const std::string& endpoint_id) { + user_a.channel->Close(); response.server_status = Response::Status::kFailed; response.latch.CountDown(); }, }); user_b.crypto.StartClient( - &user_b.client, "endpoint_id", &user_b.channel, + &user_b.client, "endpoint_id", user_b.channel, { .on_success_cb = [&response](const std::string& endpoint_id, @@ -185,9 +176,8 @@ TEST(EncryptionRunnerTest, ReadWrite) { response.latch.CountDown(); }, .on_failure_cb = - [&response](const std::string& endpoint_id, - EndpointChannel* channel) { - channel->Close(); + [&response, &user_b](const std::string& endpoint_id) { + user_b.channel->Close(); response.client_status = Response::Status::kFailed; response.latch.CountDown(); }, @@ -204,14 +194,13 @@ TEST(EncryptionRunnerTest, ClientWriteFails) { /*writer=*/from_a_to_b.second.get()); User user_b(/*reader=*/from_a_to_b.first.get(), /*writer=*/from_b_to_a.second.get()); - Response response; - response.latch = CountDownLatch(1); + Response response(1); // Close server's input stream, so client can't write to it. from_b_to_a.first->Close(); user_b.crypto.StartClient( - &user_b.client, "endpoint_id", &user_b.channel, + &user_b.client, "endpoint_id", user_b.channel, { .on_success_cb = [&response](const std::string& endpoint_id, @@ -222,9 +211,8 @@ TEST(EncryptionRunnerTest, ClientWriteFails) { response.latch.CountDown(); }, .on_failure_cb = - [&response](const std::string& endpoint_id, - EndpointChannel* channel) { - channel->Close(); + [&response, &user_a](const std::string& endpoint_id) { + user_a.channel->Close(); response.client_status = Response::Status::kFailed; response.latch.CountDown(); }, @@ -240,14 +228,13 @@ TEST(EncryptionRunnerTest, ServerWriteFails) { /*writer=*/from_a_to_b.second.get()); User user_b(/*reader=*/from_a_to_b.first.get(), /*writer=*/from_b_to_a.second.get()); - Response response; - response.latch = CountDownLatch(1); + Response response(1); // Close client's input stream, so server can't write to it. from_a_to_b.first->Close(); user_a.crypto.StartServer( - &user_a.client, "endpoint_id", &user_a.channel, + &user_a.client, "endpoint_id", user_a.channel, { .on_success_cb = [&response](const std::string& endpoint_id, @@ -258,24 +245,22 @@ TEST(EncryptionRunnerTest, ServerWriteFails) { response.latch.CountDown(); }, .on_failure_cb = - [&response](const std::string& endpoint_id, - EndpointChannel* channel) { - channel->Close(); + [&response, &user_a](const std::string& endpoint_id) { + user_a.channel->Close(); response.server_status = Response::Status::kFailed; response.latch.CountDown(); }, }); user_b.crypto.StartClient( - &user_b.client, "endpoint_id", &user_b.channel, + &user_b.client, "endpoint_id", user_b.channel, { - .on_success_cb = - [](const std::string& endpoint_id, - std::unique_ptr ukey2, - const std::string& auth_token, - const ByteArray& raw_auth_token) {}, + .on_success_cb = [](const std::string& endpoint_id, + std::unique_ptr ukey2, + const std::string& auth_token, + const ByteArray& raw_auth_token) {}, .on_failure_cb = - [](const std::string& endpoint_id, EndpointChannel* channel) { - channel->Close(); + [&user_b](const std::string& endpoint_id) { + user_b.channel->Close(); }, }); EXPECT_TRUE(response.latch.Await(absl::Milliseconds(5000)).result()); @@ -287,11 +272,10 @@ TEST(EncryptionRunnerTest, ClientSendsGarbageMessage1) { auto from_client_to_server = CreatePipe(); User user_a(/*reader=*/from_client_to_server.first.get(), /*writer=*/from_server_to_client.second.get()); - Response response; - response.latch = CountDownLatch(1); + Response response(1); user_a.crypto.StartServer( - &user_a.client, "endpoint_id", &user_a.channel, + &user_a.client, "endpoint_id", user_a.channel, { .on_success_cb = [&response](const std::string& endpoint_id, @@ -302,9 +286,8 @@ TEST(EncryptionRunnerTest, ClientSendsGarbageMessage1) { response.latch.CountDown(); }, .on_failure_cb = - [&response](const std::string& endpoint_id, - EndpointChannel* channel) { - channel->Close(); + [&response, &user_a](const std::string& endpoint_id) { + user_a.channel->Close(); response.server_status = Response::Status::kFailed; response.latch.CountDown(); }, @@ -328,11 +311,10 @@ TEST(EncryptionRunnerTest, ServerSendsGarbageMessage2) { auto from_client_to_server = CreatePipe(); User user_b(/*reader=*/from_server_to_client.first.get(), /*writer=*/from_client_to_server.second.get()); - Response response; - response.latch = CountDownLatch(1); + Response response(1); user_b.crypto.StartClient( - &user_b.client, "endpoint_id", &user_b.channel, + &user_b.client, "endpoint_id", user_b.channel, { .on_success_cb = [&response](const std::string& endpoint_id, @@ -343,9 +325,8 @@ TEST(EncryptionRunnerTest, ServerSendsGarbageMessage2) { response.latch.CountDown(); }, .on_failure_cb = - [&response](const std::string& endpoint_id, - EndpointChannel* channel) { - channel->Close(); + [&response, &user_b](const std::string& endpoint_id) { + user_b.channel->Close(); response.client_status = Response::Status::kFailed; response.latch.CountDown(); }, @@ -374,11 +355,10 @@ TEST(EncryptionRunnerTest, ClientSendsGarbageMessage3) { /*writer=*/from_server_to_client.second.get()); User user_b(/*reader=*/from_server_to_client.first.get(), /*writer=*/from_client_to_server.second.get()); - Response response; - response.latch = CountDownLatch(1); + Response response(1); user_a.crypto.StartServer( - &user_a.client, "endpoint_id", &user_a.channel, + &user_a.client, "endpoint_id", user_a.channel, { .on_success_cb = [&response](const std::string& endpoint_id, @@ -389,9 +369,8 @@ TEST(EncryptionRunnerTest, ClientSendsGarbageMessage3) { response.latch.CountDown(); }, .on_failure_cb = - [&response](const std::string& endpoint_id, - EndpointChannel* channel) { - channel->Close(); + [&response, &user_a](const std::string& endpoint_id) { + user_a.channel->Close(); response.server_status = Response::Status::kFailed; response.latch.CountDown(); }, @@ -411,7 +390,8 @@ TEST(EncryptionRunnerTest, ClientSendsGarbageMessage3) { EXPECT_TRUE(server_init.ok()); // Client crypto parses message 2. - client_crypto->ParseHandshakeMessage(std::string(server_init.result())); + client_crypto->ParseHandshakeMessage( + std::string(server_init.result().data(), server_init.result().size())); // Client sends garbage instead of message 3 from_client_to_server.second->Write("Garbage"); @@ -427,5 +407,4 @@ TEST(EncryptionRunnerTest, ClientSendsGarbageMessage3) { } } // namespace -} // namespace connections -} // namespace nearby +} // namespace nearby::connections diff --git a/connections/implementation/endpoint_channel.h b/connections/implementation/endpoint_channel.h index b4e9cbe3..1b286475 100644 --- a/connections/implementation/endpoint_channel.h +++ b/connections/implementation/endpoint_channel.h @@ -23,14 +23,10 @@ #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "connections/implementation/analytics/analytics_recorder.h" -#include "connections/implementation/analytics/packet_meta_data.h" #include "internal/platform/byte_array.h" #include "internal/platform/exception.h" -namespace nearby { -namespace connections { - -using analytics::PacketMetaData; +namespace nearby::connections { class EndpointChannel { public: @@ -41,15 +37,9 @@ class EndpointChannel { virtual ExceptionOr Read() = 0; // throws Exception::IO, Exception::INTERRUPTED - virtual ExceptionOr Read(PacketMetaData& packet_meta_data) = 0; + virtual Exception Write(absl::string_view data) = 0; // throws Exception::IO - virtual Exception Write(const ByteArray& data) = 0; // throws Exception::IO - - virtual Exception Write( - absl::string_view data, - PacketMetaData& packet_meta_data) = 0; // throws Exception::IO // Closes this EndpointChannel, without tracking the closure in analytics. - virtual void Close() = 0; // Closes this EndpointChannel and records the closure with the given reason. @@ -60,8 +50,7 @@ class EndpointChannel { // and safe disconnection result. virtual void Close( location::nearby::proto::connections::DisconnectionReason reason, - location::nearby::analytics::proto::ConnectionsLog:: - EstablishedConnection::SafeDisconnectionResult result) = 0; + nearby::analytics::SafeDisconnectionResult result) = 0; // True if the EndpointChannel is currently closed. virtual bool IsClosed() const = 0; @@ -150,7 +139,6 @@ inline bool operator!=(const EndpointChannel& lhs, const EndpointChannel& rhs) { return !(lhs == rhs); } -} // namespace connections -} // namespace nearby +} // namespace nearby::connections #endif // CORE_INTERNAL_ENDPOINT_CHANNEL_H_ diff --git a/connections/implementation/endpoint_channel_manager.cc b/connections/implementation/endpoint_channel_manager.cc index b093b6f3..ab2af35d 100644 --- a/connections/implementation/endpoint_channel_manager.cc +++ b/connections/implementation/endpoint_channel_manager.cc @@ -29,10 +29,7 @@ #include "internal/platform/mutex.h" #include "internal/platform/mutex_lock.h" -namespace nearby { -namespace connections { -using ::location::nearby::analytics::proto::ConnectionsLog; - +namespace nearby::connections { namespace { const absl::Duration kDataTransferDelay = absl::Milliseconds(500); } @@ -46,7 +43,7 @@ EndpointChannelManager::~EndpointChannelManager() { void EndpointChannelManager::RegisterChannelForEndpoint( ClientProxy* client, const std::string& endpoint_id, - std::unique_ptr channel) { + std::shared_ptr channel) { MutexLock lock(&mutex_); LOG(INFO) << "EndpointChannelManager registered channel of type " @@ -59,7 +56,7 @@ void EndpointChannelManager::RegisterChannelForEndpoint( void EndpointChannelManager::ReplaceChannelForEndpoint( ClientProxy* client, const std::string& endpoint_id, - std::unique_ptr channel, bool enable_encryption) { + std::shared_ptr channel, bool enable_encryption) { MutexLock lock(&mutex_); if (client->IsSafeToDisconnectEnabled(endpoint_id) && channel_state_.IsWaitingForSafeToDisconnectTimeout(endpoint_id)) { @@ -106,7 +103,7 @@ std::shared_ptr EndpointChannelManager::GetChannelForEndpoint( void EndpointChannelManager::SetActiveEndpointChannel( ClientProxy* client, const std::string& endpoint_id, - std::unique_ptr channel, bool enable_encryption) { + std::shared_ptr channel, bool enable_encryption) { // Update the channel first, then encrypt this new channel, if // crypto context is present. channel->SetAnalyticsRecorder(&client->GetAnalyticsRecorder(), endpoint_id); @@ -183,13 +180,13 @@ void EndpointChannelManager::ChannelState::DestroyAll() { for (auto& item : endpoints_) { RemoveEndpoint(item.first, DisconnectionReason::SHUTDOWN, /* safe_to_disconnect_enabled */ false, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + SafeDisconnectionResult::kSafeDisconnection); } endpoints_.clear(); } void EndpointChannelManager::ChannelState::UpdateChannelForEndpoint( - const std::string& endpoint_id, std::unique_ptr channel) { + const std::string& endpoint_id, std::shared_ptr channel) { // Create EndpointData instance, if necessary, and populate channel. endpoints_[endpoint_id].channel = std::move(channel); } @@ -365,5 +362,4 @@ bool EndpointChannelManager::UnregisterChannelForEndpoint( return true; } -} // namespace connections -} // namespace nearby +} // namespace nearby::connections diff --git a/connections/implementation/endpoint_channel_manager.h b/connections/implementation/endpoint_channel_manager.h index cbc5556a..47878023 100644 --- a/connections/implementation/endpoint_channel_manager.h +++ b/connections/implementation/endpoint_channel_manager.h @@ -21,18 +21,16 @@ #include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" #include "absl/time/time.h" +#include "connections/implementation/analytics/analytics_recorder.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" #include "internal/platform/mutex.h" -#include "internal/proto/analytics/connections_log.pb.h" #include "proto/connections_enums.pb.h" -namespace nearby { -namespace connections { +namespace nearby::connections { using DisconnectionReason = ::location::nearby::proto::connections::DisconnectionReason; -using SafeDisconnectionResult = ::location::nearby::analytics::proto:: - ConnectionsLog::EstablishedConnection::SafeDisconnectionResult; +using SafeDisconnectionResult = nearby::analytics::SafeDisconnectionResult; // NOTE(std::string): // All the strings in internal class public interfaces should be exchanged as @@ -59,7 +57,7 @@ class EndpointChannelManager final { // be closed before continuing the registration. void RegisterChannelForEndpoint(ClientProxy* client, const std::string& endpoint_id, - std::unique_ptr channel) + std::shared_ptr channel) ABSL_LOCKS_EXCLUDED(mutex_); // Replaces the EndpointChannel to be associated with an endpoint from here on @@ -67,7 +65,7 @@ class EndpointChannelManager final { // to the newly-provided EndpointChannel. void ReplaceChannelForEndpoint(ClientProxy* client, const std::string& endpoint_id, - std::unique_ptr channel, + std::shared_ptr channel, bool enable_encryption) ABSL_LOCKS_EXCLUDED(mutex_); @@ -168,7 +166,7 @@ class EndpointChannelManager final { // Stores a new EndpointChannel for the endpoint. // Prevoius one is destroyed, if it existed. void UpdateChannelForEndpoint(const std::string& endpoint_id, - std::unique_ptr channel); + std::shared_ptr channel); // Stores a new EncryptionContext for the endpoint. // Prevoius one is destroyed, if it existed. @@ -207,7 +205,7 @@ class EndpointChannelManager final { void SetActiveEndpointChannel(ClientProxy* client, const std::string& endpoint_id, - std::unique_ptr channel, + std::shared_ptr channel, bool enable_encryption) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); @@ -215,7 +213,6 @@ class EndpointChannelManager final { ChannelState channel_state_; }; -} // namespace connections -} // namespace nearby +} // namespace nearby::connections #endif // CORE_INTERNAL_ENDPOINT_CHANNEL_MANAGER_H_ diff --git a/connections/implementation/endpoint_channel_manager_test.cc b/connections/implementation/endpoint_channel_manager_test.cc index 23f61bd4..0bdbb6d1 100644 --- a/connections/implementation/endpoint_channel_manager_test.cc +++ b/connections/implementation/endpoint_channel_manager_test.cc @@ -27,6 +27,7 @@ #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" +#include "connections/implementation/analytics/analytics_recorder.h" #include "connections/implementation/base_endpoint_channel.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/encryption_runner.h" @@ -39,16 +40,14 @@ #include "internal/platform/multi_thread_executor.h" #include "internal/platform/output_stream.h" #include "internal/platform/pipe.h" -#include "internal/proto/analytics/connections_log.pb.h" #include "proto/connections_enums.pb.h" -namespace nearby { -namespace connections { +namespace nearby::connections { namespace { -using ::location::nearby::analytics::proto::ConnectionsLog; using ::location::nearby::proto::connections::DisconnectionReason; using ::location::nearby::proto::connections::Medium; +using ::nearby::analytics::SafeDisconnectionResult; using EncryptionContext = BaseEndpointChannel::EncryptionContext; constexpr size_t kChunkSize = 64 * 1024; @@ -110,8 +109,8 @@ std::function MakeDataMonitor(absl::string_view label, std::pair, std::unique_ptr> -DoDhKeyExchange(BaseEndpointChannel* channel_a, - BaseEndpointChannel* channel_b) { +DoDhKeyExchange(std::shared_ptr channel_a, + std::shared_ptr channel_b) { std::unique_ptr context_a; std::unique_ptr context_b; EncryptionRunner crypto_a; @@ -136,8 +135,7 @@ DoDhKeyExchange(BaseEndpointChannel* channel_a, latch.CountDown(); }, .on_failure_cb = - [&latch](const std::string& endpoint_id, - EndpointChannel* channel) { + [&latch](const std::string& endpoint_id) { LOG(INFO) << "client-A side key negotiation failed"; latch.CountDown(); }, @@ -159,8 +157,7 @@ DoDhKeyExchange(BaseEndpointChannel* channel_a, latch.CountDown(); }, .on_failure_cb = - [&latch](const std::string& endpoint_id, - EndpointChannel* channel) { + [&latch](const std::string& endpoint_id) { LOG(INFO) << "client-B side key negotiation failed"; latch.CountDown(); }, @@ -185,9 +182,9 @@ TEST(BaseEndpointChannelManagerTest, RegisterChannelEncryptedReadwrite) { // to server "b". auto server_b = CreatePipe(); // Data pump "b" reads from client "b", writes // to server "a". - auto channel_a = std::make_unique(server_a.first.get(), + auto channel_a = std::make_shared(server_a.first.get(), client_a.second.get()); - auto channel_b = std::make_unique(server_b.first.get(), + auto channel_b = std::make_shared(server_b.first.get(), client_b.second.get()); auto channel_a_raw = channel_a.get(); auto channel_b_raw = channel_b.get(); @@ -208,7 +205,7 @@ TEST(BaseEndpointChannelManagerTest, RegisterChannelEncryptedReadwrite) { MakeDataMonitor(kMonitorB, &capture_b, &mutex))); // Run DH key exchange; setup encryption contexts for channels. - auto context = DoDhKeyExchange(channel_a.get(), channel_b.get()); + auto context = DoDhKeyExchange(channel_a, channel_b); ASSERT_NE(context.first, nullptr); ASSERT_NE(context.second, nullptr); @@ -227,12 +224,12 @@ TEST(BaseEndpointChannelManagerTest, RegisterChannelEncryptedReadwrite) { EXPECT_EQ(channel_a_raw->GetType(), "ENCRYPTED_BLUETOOTH"); EXPECT_EQ(channel_b_raw->GetType(), "ENCRYPTED_BLUETOOTH"); - ByteArray tx_message{"data message"}; + absl::string_view tx_message = "data message"; channel_a_raw->Write(tx_message); ByteArray rx_message = std::move(channel_b_raw->Read().result()); // Verify expectations. - EXPECT_EQ(rx_message, tx_message); + EXPECT_EQ(rx_message.AsStringView(), tx_message); { absl::MutexLock lock(mutex); std::string message{tx_message}; @@ -245,10 +242,10 @@ TEST(BaseEndpointChannelManagerTest, RegisterChannelEncryptedReadwrite) { channel_b_raw->Close(DisconnectionReason::REMOTE_DISCONNECTION); ecm_a.UnregisterChannelForEndpoint( std::string(kEndpointId), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + SafeDisconnectionResult::kSafeDisconnection); ecm_b.UnregisterChannelForEndpoint( std::string(kEndpointId), DisconnectionReason::REMOTE_DISCONNECTION, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + SafeDisconnectionResult::kSafeDisconnection); } TEST(BaseEndpointChannelManagerTest, ReplaceChannelNoEncrypted) { @@ -266,9 +263,9 @@ TEST(BaseEndpointChannelManagerTest, ReplaceChannelNoEncrypted) { // to server "b". auto server_b = CreatePipe(); // Data pump "b" reads from client "b", writes // to server "a". - auto channel_a = std::make_unique(server_a.first.get(), + auto channel_a = std::make_shared(server_a.first.get(), client_a.second.get()); - auto channel_b = std::make_unique(server_b.first.get(), + auto channel_b = std::make_shared(server_b.first.get(), client_b.second.get()); auto channel_a_raw = channel_a.get(); auto channel_b_raw = channel_b.get(); @@ -289,7 +286,7 @@ TEST(BaseEndpointChannelManagerTest, ReplaceChannelNoEncrypted) { MakeDataMonitor(kMonitorB, &capture_b, &mutex))); // Run DH key exchange; setup encryption contexts for channels. - auto context = DoDhKeyExchange(channel_a.get(), channel_b.get()); + auto context = DoDhKeyExchange(channel_a, channel_b); ASSERT_NE(context.first, nullptr); ASSERT_NE(context.second, nullptr); @@ -313,12 +310,11 @@ TEST(BaseEndpointChannelManagerTest, ReplaceChannelNoEncrypted) { channel_b_raw->Close(DisconnectionReason::REMOTE_DISCONNECTION); ecm_a.UnregisterChannelForEndpoint( std::string(kEndpointId), DisconnectionReason::LOCAL_DISCONNECTION, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + SafeDisconnectionResult::kSafeDisconnection); ecm_b.UnregisterChannelForEndpoint( std::string(kEndpointId), DisconnectionReason::REMOTE_DISCONNECTION, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + SafeDisconnectionResult::kSafeDisconnection); } } // namespace -} // namespace connections -} // namespace nearby +} // namespace nearby::connections diff --git a/connections/implementation/endpoint_manager.cc b/connections/implementation/endpoint_manager.cc index ecde9b08..31b4454b 100644 --- a/connections/implementation/endpoint_manager.cc +++ b/connections/implementation/endpoint_manager.cc @@ -24,8 +24,7 @@ #include "absl/functional/any_invocable.h" #include "absl/time/time.h" #include "connections/connection_options.h" -#include "connections/implementation/analytics/packet_meta_data.h" -#include "connections/implementation/analytics/throughput_recorder.h" +#include "connections/implementation/analytics/analytics_recorder.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" #include "connections/implementation/endpoint_channel_manager.h" @@ -34,7 +33,6 @@ #include "connections/implementation/service_id_constants.h" #include "connections/listeners.h" #include "connections/medium_selector.h" -#include "connections/payload_type.h" #include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/exception.h" @@ -45,20 +43,17 @@ #include "internal/platform/mutex_lock.h" #include "internal/platform/runnable.h" #include "internal/platform/single_thread_executor.h" -#include "internal/proto/analytics/connections_log.pb.h" #include "proto/connections_enums.pb.h" -namespace nearby { -namespace connections { +namespace nearby::connections { namespace { -using ::location::nearby::analytics::proto::ConnectionsLog; using ::location::nearby::connections::KeepAliveFrame; using ::location::nearby::connections::OfflineFrame; using ::location::nearby::connections::PayloadTransferFrame; using ::location::nearby::connections::V1Frame; using ::location::nearby::proto::connections::DisconnectionReason; -using ::nearby::analytics::PacketMetaData; +using ::nearby::analytics::SafeDisconnectionResult; // We set this to 11s to provide sufficient time for an in-progress WebRTC // bandwidth upgrade to resolve. This is chosen to be slightly longer than the @@ -207,7 +202,7 @@ ExceptionOr EndpointManager::TryDecryptFrame( if (decrypted.ok()) { VLOG(1) << "Message decrypted after " << SystemClock::ElapsedRealtime() - start_time; - return parser::FromBytes(decrypted.result()); + return parser::FromBytes(decrypted.result().AsStringView()); } if (decrypted.exception() == Exception::kExecution) { return decrypted.exception(); @@ -235,8 +230,7 @@ ExceptionOr EndpointManager::HandleData( // a replacement for this endpoint since we last checked with the // EndpointChannelManager. while (true) { - PacketMetaData packet_meta_data; - ExceptionOr bytes = endpoint_channel->Read(packet_meta_data); + ExceptionOr bytes = endpoint_channel->Read(); if (!bytes.ok()) { LOG(INFO) << "Stop reading on read-time exception: " << bytes.exception(); // Treat kNoData as kIo. @@ -245,7 +239,8 @@ ExceptionOr EndpointManager::HandleData( } return ExceptionOr(bytes.exception()); } - ExceptionOr wrapped_frame = parser::FromBytes(bytes.result()); + ExceptionOr wrapped_frame = + parser::FromBytes(bytes.result().AsStringView()); if (!wrapped_frame.ok() && try_decrypting) { // Workaround for a race condition where the remote party has sent an // encrypted message but our end was still configured as unencrypted when @@ -316,8 +311,7 @@ ExceptionOr EndpointManager::HandleData( } frame_processor->OnIncomingFrame(frame, endpoint_id, client, - endpoint_channel->GetMedium(), - packet_meta_data); + endpoint_channel->GetMedium()); } } @@ -456,7 +450,6 @@ EndpointManager::~EndpointManager() { MutexLock lock(&mutex_); is_shutdown_ = true; } - analytics::ThroughputRecorderContainer::GetInstance().Shutdown(); CountDownLatch latch(1); RunOnEndpointManagerThread("bring-down-endpoints", [this, &latch]() { LOG(INFO) << "Bringing down endpoints"; @@ -539,103 +532,94 @@ void EndpointManager::RegisterEndpoint( ClientProxy* client, const std::string& endpoint_id, const ConnectionResponseInfo& info, const ConnectionOptions& connection_options, - std::unique_ptr channel, + std::shared_ptr channel, const ConnectionListener& listener, const std::string& connection_token) { CountDownLatch latch(1); - // NOTE (unique_ptr<> capture): - // std::unique_ptr<> is not copyable, so we can not pass it to - // lambda capture, because lambda eventually is converted to - // std::function<>. Instead, we release() a pointer, and pass a raw pointer, - // which is copyalbe. We ignore the risk of job not scheduled (and an - // associated risk of memory leak), because this may only happen during - // service shutdown. - RunOnEndpointManagerThread( - "register-endpoint", - [this, client, channel = channel.release(), &endpoint_id, &info, - &connection_options, &listener, &connection_token, &latch]() { - if (endpoints_.contains(endpoint_id)) { - LOG(WARNING) << "Registering duplicate endpoint " << endpoint_id; - // We must remove old endpoint state before registering a new one - // for the same endpoint_id. - RemoveEndpointState(endpoint_id); - } + RunOnEndpointManagerThread("register-endpoint", [this, client, channel, + &endpoint_id, &info, + &connection_options, + &listener, &connection_token, + &latch]() { + if (endpoints_.contains(endpoint_id)) { + LOG(WARNING) << "Registering duplicate endpoint " << endpoint_id; + // We must remove old endpoint state before registering a new one + // for the same endpoint_id. + RemoveEndpointState(endpoint_id); + } - absl::Duration keep_alive_interval = - absl::Milliseconds(connection_options.keep_alive_interval_millis); - absl::Duration keep_alive_timeout = - absl::Milliseconds(connection_options.keep_alive_timeout_millis); - LOG(INFO) << "Registering endpoint " << endpoint_id << " for client " - << client->GetClientId() - << " with keep-alive frame as interval=" - << absl::FormatDuration(keep_alive_interval) - << ", timeout=" << absl::FormatDuration(keep_alive_timeout); + absl::Duration keep_alive_interval = + absl::Milliseconds(connection_options.keep_alive_interval_millis); + absl::Duration keep_alive_timeout = + absl::Milliseconds(connection_options.keep_alive_timeout_millis); + LOG(INFO) << "Registering endpoint " << endpoint_id << " for client " + << client->GetClientId() << " with keep-alive frame as interval=" + << absl::FormatDuration(keep_alive_interval) + << ", timeout=" << absl::FormatDuration(keep_alive_timeout); - // Pass ownership of channel to EndpointChannelManager - LOG(INFO) << "Registering endpoint with channel manager: endpoint " - << endpoint_id; - channel_manager_->RegisterChannelForEndpoint( - client, endpoint_id, std::unique_ptr(channel)); + // Pass ownership of channel to EndpointChannelManager + LOG(INFO) << "Registering endpoint with channel manager: endpoint " + << endpoint_id; + channel_manager_->RegisterChannelForEndpoint(client, endpoint_id, channel); - EndpointState& endpoint_state = - endpoints_ - .emplace(endpoint_id, - EndpointState(endpoint_id, channel_manager_)) - .first->second; + EndpointState& endpoint_state = + endpoints_ + .emplace(endpoint_id, EndpointState(endpoint_id, channel_manager_)) + .first->second; - LOG(INFO) << "Starting workers: endpoint " << endpoint_id; - // For every endpoint, there's normally only one Read handler instance - // running on a dedicated thread. This instance reads data from the - // endpoint and delegates incoming frames to various FrameProcessors. - // Once the frame has been properly handled, it starts reading again - // for the next frame. If the handler fails its read and no other - // EndpointChannels are available for this endpoint, a disconnection - // will be initiated. - endpoint_state.StartEndpointReader([this, client, endpoint_id]() { + LOG(INFO) << "Starting workers: endpoint " << endpoint_id; + // For every endpoint, there's normally only one Read handler instance + // running on a dedicated thread. This instance reads data from the + // endpoint and delegates incoming frames to various FrameProcessors. + // Once the frame has been properly handled, it starts reading again + // for the next frame. If the handler fails its read and no other + // EndpointChannels are available for this endpoint, a disconnection + // will be initiated. + endpoint_state.StartEndpointReader([this, client, endpoint_id]() { + EndpointChannelLoopRunnable( + "Read", client, endpoint_id, + [this, client, endpoint_id](EndpointChannel* channel) { + return HandleData(endpoint_id, client, channel); + }); + }); + + // For every endpoint, there's only one KeepAliveManager instance + // running on a dedicated thread. This instance will periodically send + // out a ping* to the endpoint while listening for an incoming pong**. + // If it fails to send the ping, or if no pong is heard within + // keep_alive_timeout, it initiates a disconnection. + // + // (*) Bluetooth requires a constant outgoing stream of messages. If + // there's silence, Android will break the socket. This is why we + // ping. + // (**) Wifi Hotspots can fail to notice a connection has been lost, + // and they will happily keep writing to /dev/null. This is why we + // listen for the pong. + VLOG(1) << "EndpointManager enabling KeepAlive for endpoint " + << endpoint_id; + endpoint_state.StartEndpointKeepAliveManager( + [this, client, endpoint_id, keep_alive_interval, keep_alive_timeout]( + Mutex* keep_alive_waiter_mutex, + ConditionVariable* keep_alive_waiter) { EndpointChannelLoopRunnable( - "Read", client, endpoint_id, - [this, client, endpoint_id](EndpointChannel* channel) { - return HandleData(endpoint_id, client, channel); + "KeepAliveManager", client, endpoint_id, + [this, keep_alive_interval, keep_alive_timeout, + keep_alive_waiter_mutex, + keep_alive_waiter](EndpointChannel* channel) { + return HandleKeepAlive( + channel, keep_alive_interval, keep_alive_timeout, + keep_alive_waiter_mutex, keep_alive_waiter); }); }); + LOG(INFO) << "Registering endpoint " << endpoint_id + << ", workers started and notifying client."; - // For every endpoint, there's only one KeepAliveManager instance - // running on a dedicated thread. This instance will periodically send - // out a ping* to the endpoint while listening for an incoming pong**. - // If it fails to send the ping, or if no pong is heard within - // keep_alive_timeout, it initiates a disconnection. - // - // (*) Bluetooth requires a constant outgoing stream of messages. If - // there's silence, Android will break the socket. This is why we - // ping. - // (**) Wifi Hotspots can fail to notice a connection has been lost, - // and they will happily keep writing to /dev/null. This is why we - // listen for the pong. - VLOG(1) << "EndpointManager enabling KeepAlive for endpoint " - << endpoint_id; - endpoint_state.StartEndpointKeepAliveManager( - [this, client, endpoint_id, keep_alive_interval, - keep_alive_timeout](Mutex* keep_alive_waiter_mutex, - ConditionVariable* keep_alive_waiter) { - EndpointChannelLoopRunnable( - "KeepAliveManager", client, endpoint_id, - [this, keep_alive_interval, keep_alive_timeout, - keep_alive_waiter_mutex, - keep_alive_waiter](EndpointChannel* channel) { - return HandleKeepAlive( - channel, keep_alive_interval, keep_alive_timeout, - keep_alive_waiter_mutex, keep_alive_waiter); - }); - }); - LOG(INFO) << "Registering endpoint " << endpoint_id - << ", workers started and notifying client."; - - // It's now time to let the client know of this new connection so that - // they can accept or reject it. - client->OnConnectionInitiated(endpoint_id, info, connection_options, - listener, connection_token); - latch.CountDown(); - }); + // It's now time to let the client know of this new connection so that + // they can accept or reject it. + client->OnConnectionInitiated(endpoint_id, info, connection_options, + listener, connection_token); + latch.CountDown(); + }); latch.Await(); } @@ -666,17 +650,15 @@ int EndpointManager::GetMaxTransmitPacketSize(const std::string& endpoint_id) { std::vector EndpointManager::SendPayloadChunk( const PayloadTransferFrame::PayloadHeader& payload_header, const PayloadTransferFrame::PayloadChunk& payload_chunk, - const std::vector& endpoint_ids, - PacketMetaData& packet_meta_data) { - ByteArray bytes = + const std::vector& endpoint_ids) { + std::string bytes = parser::ForDataPayloadTransfer(payload_header, payload_chunk); return SendTransferFrameBytes( endpoint_ids, bytes, payload_header.id(), /*offset=*/payload_chunk.offset(), /*packet_type=*/ - PayloadTransferFrame::PacketType_Name(PayloadTransferFrame::DATA), - packet_meta_data); + PayloadTransferFrame::PacketType_Name(PayloadTransferFrame::DATA)); } // Designed to run asynchronously. It is called from IO thread pools, and @@ -722,7 +704,7 @@ void EndpointManager::DiscardEndpoint(ClientProxy* client, // of `serial_executor_` and will still have access to a valid // `is_shutdown_`. // - // TODO(b/280653613): Develop a more robost solution to prevent + // TODO(b/280653613): Develop a more robust solution to prevent // accessing an already destroyed `ClientProxy` during destruction. { MutexLock lock(&mutex_); @@ -743,15 +725,13 @@ std::vector EndpointManager::SendControlMessage( const PayloadTransferFrame::PayloadHeader& header, const PayloadTransferFrame::ControlMessage& control, const std::vector& endpoint_ids) { - ByteArray bytes = parser::ForControlPayloadTransfer(header, control); - PacketMetaData packet_meta_data; + std::string bytes = parser::ForControlPayloadTransfer(header, control); return SendTransferFrameBytes( endpoint_ids, bytes, header.id(), /*offset=*/control.offset(), /*packet_type=*/ - PayloadTransferFrame::PacketType_Name(PayloadTransferFrame::CONTROL), - packet_meta_data); + PayloadTransferFrame::PacketType_Name(PayloadTransferFrame::CONTROL)); } // @EndpointManagerThread @@ -762,7 +742,7 @@ void EndpointManager::RemoveEndpoint(ClientProxy* client, << ", reason: " << reason; SafeDisconnectionResult safe_disconnect_result = - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION; + SafeDisconnectionResult::kSafeDisconnection; // Grab the service ID before we destroy the channel. EndpointChannel* channel = @@ -775,17 +755,15 @@ void EndpointManager::RemoveEndpoint(ClientProxy* client, bool is_safe_disconnection = ApplySafeToDisconnect(endpoint_id, channel, reason); safe_disconnect_result = - is_safe_disconnection - ? ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION - : ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION; + is_safe_disconnection ? SafeDisconnectionResult::kSafeDisconnection + : SafeDisconnectionResult::kUnsafeDisconnection; LOG(INFO) << "[safe-to-disconnect] safe_disconnect_result:" - << (safe_disconnect_result ? "true" : "false"); + << (safe_disconnect_result == + SafeDisconnectionResult::kSafeDisconnection + ? "true" + : "false"); } } - if (safe_disconnect_result == - ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION) { - // TODO(b/297259496): Autoreconnect - } // Unregistering from channel_manager_ will also serve to terminate // the dedicated handler and KeepAlive threads we started when we registered @@ -924,21 +902,19 @@ CountDownLatch EndpointManager::NotifyFrameProcessorsOnEndpointDisconnect( std::vector EndpointManager::SendPayloadAck( std::int64_t payload_id, const std::vector& endpoint_ids) { - ByteArray bytes = parser::ForPayloadAckPayloadTransfer(payload_id); - PacketMetaData packet_meta_data; + std::string bytes = parser::ForPayloadAckPayloadTransfer(payload_id); return SendTransferFrameBytes( endpoint_ids, bytes, payload_id, /* offset= */ -1, /*packet_type=*/ - PayloadTransferFrame::PacketType_Name(PayloadTransferFrame::PAYLOAD_ACK), - packet_meta_data); + PayloadTransferFrame::PacketType_Name(PayloadTransferFrame::PAYLOAD_ACK)); } std::vector EndpointManager::SendTransferFrameBytes( - const std::vector& endpoint_ids, const ByteArray& bytes, + const std::vector& endpoint_ids, const std::string& bytes, std::int64_t payload_id, std::int64_t offset, - const std::string& packet_type, PacketMetaData& packet_meta_data) { + const std::string& packet_type) { std::vector failed_endpoint_ids; for (const std::string& endpoint_id : endpoint_ids) { std::shared_ptr channel = @@ -957,16 +933,12 @@ std::vector EndpointManager::SendTransferFrameBytes( continue; } - Exception write_exception = - channel->Write(bytes.AsStringView(), packet_meta_data); + Exception write_exception = channel->Write(bytes); if (!write_exception.Ok()) { failed_endpoint_ids.push_back(endpoint_id); LOG(INFO) << "Failed to send packet; endpoint_id=" << endpoint_id; continue; } - analytics::ThroughputRecorderContainer::GetInstance() - .GetTPRecorder(payload_id, PayloadDirection::OUTGOING_PAYLOAD) - ->OnFrameSent(channel->GetMedium(), packet_meta_data); } return failed_endpoint_ids; @@ -982,7 +954,7 @@ EndpointManager::EndpointState::~EndpointState() { VLOG(1) << "EndpointState destructor " << endpoint_id_; channel_manager_->UnregisterChannelForEndpoint( endpoint_id_, DisconnectionReason::SHUTDOWN, - ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + SafeDisconnectionResult::kSafeDisconnection); } // Make sure the KeepAlive thread isn't blocking shutdown. @@ -1011,5 +983,4 @@ void EndpointManager::RunOnEndpointManagerThread(const std::string& name, serial_executor_->Execute(name, std::move(runnable)); } -} // namespace connections -} // namespace nearby +} // namespace nearby::connections diff --git a/connections/implementation/endpoint_manager.h b/connections/implementation/endpoint_manager.h index 8b2b0b81..2250a958 100644 --- a/connections/implementation/endpoint_manager.h +++ b/connections/implementation/endpoint_manager.h @@ -16,7 +16,6 @@ #define CORE_INTERNAL_ENDPOINT_MANAGER_H_ #include -#include #include #include #include @@ -24,10 +23,9 @@ #include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" #include "absl/functional/any_invocable.h" #include "absl/time/time.h" -#include "connections/implementation/analytics/packet_meta_data.h" +#include "connections/connection_options.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" #include "connections/implementation/endpoint_channel_manager.h" @@ -36,6 +34,8 @@ #include "internal/platform/byte_array.h" #include "internal/platform/condition_variable.h" #include "internal/platform/count_down_latch.h" +#include "internal/platform/exception.h" +#include "internal/platform/mutex.h" #include "internal/platform/runnable.h" #include "internal/platform/single_thread_executor.h" @@ -79,8 +79,7 @@ class EndpointManager { virtual void OnIncomingFrame( location::nearby::connections::OfflineFrame& offline_frame, const std::string& from_endpoint_id, ClientProxy* to_client, - location::nearby::proto::connections::Medium current_medium, - analytics::PacketMetaData& packet_meta_data) = 0; + location::nearby::proto::connections::Medium current_medium) = 0; // Implementations must call barrier.CountDown() once // they're done. This parallelizes the disconnection event across all frame @@ -114,7 +113,7 @@ class EndpointManager { void RegisterEndpoint(ClientProxy* client, const std::string& endpoint_id, const ConnectionResponseInfo& info, const ConnectionOptions& connection_options, - std::unique_ptr channel, + std::shared_ptr channel, const ConnectionListener& listener, const std::string& connection_token); // Called when a client explicitly asks to disconnect from this endpoint. In @@ -133,8 +132,7 @@ class EndpointManager { payload_header, const location::nearby::connections::PayloadTransferFrame::PayloadChunk& payload_chunk, - const std::vector& endpoint_ids, - analytics::PacketMetaData& packet_meta_data); + const std::vector& endpoint_ids); std::vector SendControlMessage( const location::nearby::connections::PayloadTransferFrame::PayloadHeader& payload_header, @@ -283,9 +281,8 @@ class EndpointManager { std::vector SendTransferFrameBytes( const std::vector& endpoint_ids, - const ByteArray& payload_transfer_frame_bytes, std::int64_t payload_id, - std::int64_t offset, const std::string& packet_type, - analytics::PacketMetaData& packet_meta_data); + const std::string& payload_transfer_frame_bytes, std::int64_t payload_id, + std::int64_t offset, const std::string& packet_type); // Executes all jobs sequentially, on a serial_executor_. void RunOnEndpointManagerThread(const std::string& name, Runnable runnable); diff --git a/connections/implementation/endpoint_manager_test.cc b/connections/implementation/endpoint_manager_test.cc index 0f906e3c..70a85949 100644 --- a/connections/implementation/endpoint_manager_test.cc +++ b/connections/implementation/endpoint_manager_test.cc @@ -29,11 +29,10 @@ #include "absl/time/clock.h" #include "absl/time/time.h" #include "connections/connection_options.h" -#include "connections/implementation/analytics/analytics_recorder.h" #include "connections/implementation/client_proxy.h" -#include "connections/implementation/endpoint_channel.h" #include "connections/implementation/endpoint_channel_manager.h" #include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "connections/implementation/mock_endpoint_channel.h" #include "connections/implementation/offline_frames.h" #include "connections/listeners.h" #include "connections/status.h" @@ -63,68 +62,12 @@ using ::testing::MockFunction; using ::testing::Return; using ::testing::StrictMock; -class MockEndpointChannel : public EndpointChannel { - public: - MOCK_METHOD(ExceptionOr, Read, (), (override)); - MOCK_METHOD(ExceptionOr, Read, (PacketMetaData & packet_meta_data), - (override)); - MOCK_METHOD(Exception, Write, (const ByteArray& data), (override)); - MOCK_METHOD(Exception, Write, - (absl::string_view data, PacketMetaData& packet_meta_data), - (override)); - MOCK_METHOD(void, Close, (), (override)); - MOCK_METHOD(void, Close, (DisconnectionReason reason), (override)); - MOCK_METHOD(void, Close, - (DisconnectionReason reason, - location::nearby::analytics::proto::ConnectionsLog:: - EstablishedConnection::SafeDisconnectionResult result), - (override)); - MOCK_METHOD(location::nearby::proto::connections::ConnectionTechnology, - GetTechnology, (), (const, override)); - MOCK_METHOD(location::nearby::proto::connections::ConnectionBand, GetBand, (), - (const, override)); - MOCK_METHOD(int, GetFrequency, (), (const, override)); - MOCK_METHOD(int, GetTryCount, (), (const, override)); - MOCK_METHOD(std::string, GetType, (), (const, override)); - MOCK_METHOD(std::string, GetServiceId, (), (const, override)); - MOCK_METHOD(std::string, GetName, (), (const, override)); - MOCK_METHOD(Medium, GetMedium, (), (const, override)); - MOCK_METHOD(int, GetMaxTransmitPacketSize, (), (const, override)); - MOCK_METHOD(void, EnableEncryption, - (std::shared_ptr context), (override)); - MOCK_METHOD(void, DisableEncryption, (), (override)); - MOCK_METHOD(bool, IsPaused, (), (const, override)); - MOCK_METHOD(bool, IsEncrypted, (), (override)); - MOCK_METHOD(ExceptionOr, TryDecrypt, (const ByteArray& data), - (override)); - MOCK_METHOD(void, Pause, (), (override)); - MOCK_METHOD(void, Resume, (), (override)); - MOCK_METHOD(absl::Time, GetLastReadTimestamp, (), (const, override)); - MOCK_METHOD(absl::Time, GetLastWriteTimestamp, (), (const, override)); - MOCK_METHOD(uint32_t, GetNextKeepAliveSeqNo, (), (const, override)); - MOCK_METHOD(void, SetAnalyticsRecorder, - (analytics::AnalyticsRecorder*, const std::string&), (override)); - - bool IsClosed() const override { - absl::MutexLock lock(mutex_); - return closed_; - } - void DoClose() { - absl::MutexLock lock(mutex_); - closed_ = true; - } - - private: - mutable absl::Mutex mutex_; - bool closed_ = false; -}; - class MockFrameProcessor : public EndpointManager::FrameProcessor { public: MOCK_METHOD(void, OnIncomingFrame, (OfflineFrame & offline_frame, const std::string& from_endpoint_id, ClientProxy* to_client, - Medium current_medium, PacketMetaData& packet_meta_data), + Medium current_medium), (override)); MOCK_METHOD(void, OnEndpointDisconnect, @@ -136,16 +79,13 @@ class MockFrameProcessor : public EndpointManager::FrameProcessor { class SetSafeToDisconnect { public: - SetSafeToDisconnect(bool safe_to_disconnect, bool auto_reconnect, + SetSafeToDisconnect(bool safe_to_disconnect, bool payload_received_ack, std::int32_t safe_to_disconnect_version) { NearbyFlags::GetInstance().OverrideBoolFlagValue( config_package_nearby::nearby_connections_feature:: kEnableSafeToDisconnect, safe_to_disconnect); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature::kEnableAutoReconnect, - auto_reconnect); NearbyFlags::GetInstance().OverrideBoolFlagValue( config_package_nearby::nearby_connections_feature:: kEnablePayloadReceivedAck, @@ -187,7 +127,7 @@ class EndpointManagerTest : public ::testing::Test { EXPECT_TRUE(done.Await(absl::Milliseconds(1000)).result()); } } - SetSafeToDisconnect set_safe_to_disconnect_{true, false, true, 5}; + SetSafeToDisconnect set_safe_to_disconnect_{true, true, 5}; std::unique_ptr client_ = std::make_unique(); ConnectionOptions connection_options_{ .keep_alive_interval_millis = 5000, @@ -280,11 +220,12 @@ TEST_F(EndpointManagerTest, RegisterFrameProcessorWorks) { 0 /*keep_alive_interval_millis*/, 0 /*keep_alive_timeout_millis*/}; - auto read_data = parser::ForConnectionRequestConnections({}, connection_info); + std::string read_data = + parser::ForConnectionRequestConnections({}, connection_info); EXPECT_CALL(*connect_request, OnIncomingFrame); EXPECT_CALL(*connect_request, OnEndpointDisconnect); - EXPECT_CALL(*endpoint_channel, Read(_)) - .WillOnce(Return(ExceptionOr(read_data))) + EXPECT_CALL(*endpoint_channel, Read()) + .WillOnce(Return(ExceptionOr(ByteArray(read_data)))) .WillRepeatedly(Return(ExceptionOr(Exception::kIo))); EXPECT_CALL(*endpoint_channel, Write(_)) .WillRepeatedly(Return(Exception{Exception::kSuccess})); @@ -321,6 +262,8 @@ TEST_F(EndpointManagerTest, UnregisterFrameProcessorWorks) { TEST_F(EndpointManagerTest, SendControlMessageAndPayloadAckWorks) { auto endpoint_channel = std::make_unique(); + absl::Mutex close_mutex; + bool closed = false; PayloadTransferFrame::PayloadHeader header; PayloadTransferFrame::ControlMessage control; header.set_id(12345); @@ -329,22 +272,24 @@ TEST_F(EndpointManagerTest, SendControlMessageAndPayloadAckWorks) { control.set_offset(150); control.set_event(PayloadTransferFrame::ControlMessage::PAYLOAD_CANCELED); - ON_CALL(*endpoint_channel, Read(_)) - .WillByDefault([channel = endpoint_channel.get()]() { - if (channel->IsClosed()) return ExceptionOr(Exception::kIo); + ON_CALL(*endpoint_channel, Read()) + .WillByDefault([&, channel = endpoint_channel.get()]() { + absl::MutexLock lock(close_mutex); + if (closed) return ExceptionOr(Exception::kIo); LOG(INFO) << "Simulate read delay: wait"; absl::SleepFor(absl::Milliseconds(100)); LOG(INFO) << "Simulate read delay: done"; - if (channel->IsClosed()) return ExceptionOr(Exception::kIo); + if (closed) return ExceptionOr(Exception::kIo); return ExceptionOr(ByteArray{}); }); ON_CALL(*endpoint_channel, Close(_)) .WillByDefault( - [channel = endpoint_channel.get()](DisconnectionReason reason) { - channel->DoClose(); + [&, channel = endpoint_channel.get()](DisconnectionReason reason) { + absl::MutexLock lock(close_mutex); + closed = true; LOG(INFO) << "Channel closed"; }); - EXPECT_CALL(*endpoint_channel, Write(_, _)) + EXPECT_CALL(*endpoint_channel, Write(_)) .WillRepeatedly(Return(Exception{Exception::kSuccess})); RegisterEndpoint(std::move(endpoint_channel), false); @@ -361,7 +306,7 @@ TEST_F(EndpointManagerTest, SendControlMessageAndPayloadAckWorks) { TEST_F(EndpointManagerTest, SingleReadOnReadError) { auto endpoint_channel = std::make_unique(); - EXPECT_CALL(*endpoint_channel, Read(_)) + EXPECT_CALL(*endpoint_channel, Read()) .WillOnce( Return(ExceptionOr(Exception::kInvalidProtocolBuffer))); EXPECT_CALL(*endpoint_channel, Write(_)) @@ -379,7 +324,7 @@ TEST_F(EndpointManagerTest, ReadInvalidUnencryptedPayloadIgnoresFrame) { CountDownLatch latch(1); const ByteArray payload("not a valid frame"); auto endpoint_channel = std::make_unique(); - EXPECT_CALL(*endpoint_channel, Read(_)) + EXPECT_CALL(*endpoint_channel, Read()) .WillOnce(Return(ExceptionOr(payload))) .WillRepeatedly(Return(ExceptionOr(Exception::kIo))); EXPECT_CALL(*endpoint_channel, TryDecrypt(Eq(payload))) @@ -403,7 +348,7 @@ class EndpointManagerFuzzTest // too. // 4. Invalid frame is ignored. No bad side effects. auto endpoint_channel = std::make_unique(); - EXPECT_CALL(*endpoint_channel, Read(_)) + EXPECT_CALL(*endpoint_channel, Read()) .WillOnce(Return(ExceptionOr(payload))) .WillRepeatedly(Return(ExceptionOr(Exception::kIo))); EXPECT_CALL(*endpoint_channel, TryDecrypt(Eq(payload))) @@ -419,7 +364,7 @@ class EndpointManagerFuzzTest // 2. EndpointManager receives an invalid encrypted frame. // 3. No calls to TryDecrypt. auto endpoint_channel = std::make_unique(); - EXPECT_CALL(*endpoint_channel, Read(_)) + EXPECT_CALL(*endpoint_channel, Read()) .WillOnce(Return(ExceptionOr(payload))) .WillRepeatedly(Return(ExceptionOr(Exception::kIo))); EXPECT_CALL(*endpoint_channel, IsEncrypted()).WillRepeatedly(Return(true)); @@ -432,7 +377,9 @@ class EndpointManagerFuzzTest auto InvalidPayloadDomain() { return Filter( - [](ByteArray payload) { return !parser::FromBytes(payload).ok(); }, + [](ByteArray payload) { + return !parser::FromBytes(payload.AsStringView()).ok(); + }, Map([](std::string payloadString) { return ByteArray(payloadString); }, String())); } @@ -464,16 +411,16 @@ TEST_F(EndpointManagerTest, TryDecrypt) { std::vector{Medium::BLE} /*supported_mediums*/, 0 /*keep_alive_interval_millis*/, 0 /*keep_alive_timeout_millis*/}; - ByteArray decrypted_data = + std::string decrypted_data = parser::ForConnectionRequestConnections({}, connection_info); EXPECT_CALL(*connect_request, OnIncomingFrame); EXPECT_CALL(*connect_request, OnEndpointDisconnect); - EXPECT_CALL(*endpoint_channel, Read(_)) + EXPECT_CALL(*endpoint_channel, Read()) .WillOnce(Return(ExceptionOr(payload))) .WillRepeatedly(Return(ExceptionOr(Exception::kIo))); EXPECT_CALL(*endpoint_channel, TryDecrypt(Eq(payload))) .WillOnce(Return(ExceptionOr(Exception::kFailed))) - .WillOnce(Return(ExceptionOr(decrypted_data))); + .WillOnce(Return(ExceptionOr(ByteArray(decrypted_data)))); EXPECT_CALL(*endpoint_channel, Write(_)) .WillRepeatedly(Return(Exception{Exception::kSuccess})); em_.RegisterFrameProcessor(V1Frame::CONNECTION_REQUEST, diff --git a/connections/implementation/fake_bwu_handler.h b/connections/implementation/fake_bwu_handler.h index c26a91e6..dee9168b 100644 --- a/connections/implementation/fake_bwu_handler.h +++ b/connections/implementation/fake_bwu_handler.h @@ -87,9 +87,9 @@ class FakeBwuHandler : public BaseBwuHandler { medium_, *handle_initialize_calls_[initialize_call_index].service_id); FakeEndpointChannel* upgraded_channel_raw = upgraded_channel.get(); upgraded_channel->set_read_output( - ExceptionOr(parser::ForBwuIntroduction( + ExceptionOr(ByteArray(parser::ForBwuIntroduction( *handle_initialize_calls_[initialize_call_index].endpoint_id, - false /* supports_disabling_encryption */))); + false /* supports_disabling_encryption */)))); auto connection = std::make_unique(); connection->channel = std::move(upgraded_channel); @@ -133,7 +133,7 @@ class FakeBwuHandler : public BaseBwuHandler { } // BaseBwuHandler: - ByteArray HandleInitializeUpgradedMediumForEndpoint( + std::string HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, const std::string& endpoint_id) final { handle_initialize_calls_.push_back({.client = client, @@ -178,7 +178,7 @@ class FakeBwuHandler : public BaseBwuHandler { return parser::ForBwuWifiDirectPathAvailable( /*ssid=*/"", /*password=*/"", /*port=*/2143, /*frequency=*/2412, /*supports_disabling_encryption=*/false, - /*gateway=*/"123.234.23.1", /*service_name=*/"NC-WifiDirectTest", + /*gateway=*/"123.234.23.1", /*device_name=*/"NC-WifiDirectTest", /*pin=*/"b592f7d3"); case location::nearby::proto::connections::UNKNOWN_MEDIUM: case location::nearby::proto::connections::MDNS: @@ -188,7 +188,7 @@ class FakeBwuHandler : public BaseBwuHandler { case location::nearby::proto::connections::BLE_L2CAP: case location::nearby::proto::connections::USB: case location::nearby::proto::connections::AWDL: - return ByteArray{}; + return {}; } } diff --git a/connections/implementation/fake_endpoint_channel.h b/connections/implementation/fake_endpoint_channel.h index fa1bc3f7..01bb92fe 100644 --- a/connections/implementation/fake_endpoint_channel.h +++ b/connections/implementation/fake_endpoint_channel.h @@ -27,8 +27,7 @@ #include "internal/platform/exception.h" #include "internal/platform/implementation/system_clock.h" -namespace nearby { -namespace connections { +namespace nearby::connections { // An endpoint channel implementation used for testing. The read and write // output can be set. @@ -46,16 +45,7 @@ class FakeEndpointChannel : public EndpointChannel { read_timestamp_ = SystemClock::ElapsedRealtime(); return read_output_; } - ExceptionOr Read(PacketMetaData& packet_meta_data) override { - read_timestamp_ = SystemClock::ElapsedRealtime(); - return read_output_; - } - Exception Write(const ByteArray& data) override { - write_timestamp_ = SystemClock::ElapsedRealtime(); - return write_output_; - } - Exception Write(absl::string_view data, - PacketMetaData& packet_meta_data) override { + Exception Write(absl::string_view data) override { write_timestamp_ = SystemClock::ElapsedRealtime(); return write_output_; } @@ -65,10 +55,8 @@ class FakeEndpointChannel : public EndpointChannel { is_closed_ = true; disconnection_reason_ = reason; } - void Close( - location::nearby::proto::connections::DisconnectionReason reason, - location::nearby::analytics::proto::ConnectionsLog:: - EstablishedConnection::SafeDisconnectionResult result) override { + void Close(location::nearby::proto::connections::DisconnectionReason reason, + nearby::analytics::SafeDisconnectionResult result) override { Close(reason); } bool IsClosed() const override { return is_closed_; } @@ -128,7 +116,6 @@ class FakeEndpointChannel : public EndpointChannel { mutable uint32_t next_keep_alive_seq_no_ = 0; }; -} // namespace connections -} // namespace nearby +} // namespace nearby::connections #endif // NEARBY_CONNECTIONS_IMPLEMENTATION_FAKE_ENDPOINT_CHANNEL_H_ diff --git a/connections/implementation/flags/nearby_connections_feature_flags.h b/connections/implementation/flags/nearby_connections_feature_flags.h old mode 100644 new mode 100755 index 8dbe6dbc..d6cd22d4 --- a/connections/implementation/flags/nearby_connections_feature_flags.h +++ b/connections/implementation/flags/nearby_connections_feature_flags.h @@ -31,15 +31,9 @@ namespace nearby_connections_feature { // The timeout in millis to report peripheral device lost. constexpr auto kBlePeripheralLostTimeoutMillis = flags::Flag(kConfigPackage, "45411439", 12000); -// Disable instant on lost on BLE without extended feature. -constexpr auto kDisableInstantOnLostOnBleWithoutExtended = - flags::Flag(kConfigPackage, "45687098", true); // When true, enable advertising for instant on lost feature. constexpr auto kEnableAdvertisingForInstantOnLost = flags::Flag(kConfigPackage, "45708614", true); -// Enable/Disable auto_reconnect feature. -constexpr auto kEnableAutoReconnect = - flags::Flag(kConfigPackage, "45427690", false); // Enable/Disable AWDL in Nearby connections SDK. constexpr auto kEnableAwdl = flags::Flag(kConfigPackage, "45690762", false); @@ -58,34 +52,25 @@ constexpr auto kEnableDynamicRoleSwitch = // Enable/Disable GATT client disconnection. constexpr auto kEnableGattClientDisconnection = flags::Flag(kConfigPackage, "45698964", false); -// When true, enable instant on lost feature. -// When true, enable multiplexing in NC. -constexpr auto kEnableMultiplex = - flags::Flag(kConfigPackage, "45647946", false); -// Enable/disable multiplex in NC for AWDL. -constexpr auto kEnableMultiplexAwdl = - flags::Flag(kConfigPackage, "45690761", false); // When true, enable multiplexing in NC for Bluetooth. constexpr auto kEnableMultiplexBluetooth = flags::Flag(kConfigPackage, "45676646", false); -// When true, enable multiplexing in NC for Wifi. -constexpr auto kEnableMultiplexWifiLan = - flags::Flag(kConfigPackage, "45676647", false); // Enable/Disable preferences for Nearby Connections. constexpr auto kEnableNearbyConnectionsPreferences = flags::Flag(kConfigPackage, "45732423", false); -// Enable/Disable payload manager to skip chunk update. -constexpr auto kEnablePayloadManagerToSkipChunkUpdate = - flags::Flag(kConfigPackage, "45415729", true); // Enable/Disable payload-received-ack feature. constexpr auto kEnablePayloadReceivedAck = flags::Flag(kConfigPackage, "45425840", false); // Enable/Disable safe-to-disconnect feature. constexpr auto kEnableSafeToDisconnect = flags::Flag(kConfigPackage, "45425789", false); -// When true, enable scanning for instant on lost feature. -constexpr auto kEnableScanningForInstantOnLost = - flags::Flag(kConfigPackage, "45708613", true); +// Enable/Disable usage of shared CBPeripheralManager for GATT and L2CAP +// servers. +constexpr auto kEnableSharedPeripheralManager = + flags::Flag(kConfigPackage, "45770787", false); +// Enable/Disable single copy read/write for input/output buffers. +constexpr auto kEnableSingleCopy = + flags::Flag(kConfigPackage, "45782646", true); // Stop BLE_V2 scanning when upgrading to WIFI Hotspot or WFD. constexpr auto kEnableStopBleScanningOnWifiUpgrade = flags::Flag(kConfigPackage, "45687902", false); @@ -95,6 +80,9 @@ constexpr auto kEnableWifiDirect = // by default, enable Wi-Fi Hotspot client. constexpr auto kEnableWifiHotspotClient = flags::Flag(kConfigPackage, "45648734", true); +// When true, fix the BleServerSocket deadlock/use-after-free (b/494335036). +constexpr auto kFixBleServerSocketDeadlock = + flags::Flag(kConfigPackage, "45782647", true); // Default max transmit packet size for medium. constexpr auto kMediumDefaultMaxTransmitPacketSize = flags::Flag(kConfigPackage, "45669529", 65536); @@ -105,7 +93,8 @@ constexpr auto kMediumMaxAllowedReadBytes = constexpr auto kRefactorBleL2cap = flags::Flag(kConfigPackage, "45737079", false); // Set the safe-to-disconnect version. -// 0. Disabled all. 1. safe-to-disconnect 2. reserved 3. auto-reconnect +// 0. Disabled all. 1. safe-to-disconnect 2. reserved 3. +// auto-reconnect(deprecated) // 4. auto-resume 5. non-distance-constraint-recovery 6. payload_ack constexpr auto kSafeToDisconnectVersion = flags::Flag(kConfigPackage, "45425841", 0); diff --git a/connections/implementation/fuzzers/BUILD b/connections/implementation/fuzzers/BUILD index 2ef4e0d5..490dac2f 100644 --- a/connections/implementation/fuzzers/BUILD +++ b/connections/implementation/fuzzers/BUILD @@ -24,11 +24,10 @@ cc_test( ], tags = ["componentid:148515"], deps = [ - "//connections/implementation:internal", - "//internal/platform:base", + "//connections/implementation:offline_frames", "//internal/platform/implementation/g3", - "//testing/fuzzing:fuzztest", "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/strings:string_view", "@com_google_googletest//:gtest_main", ], ) diff --git a/connections/implementation/fuzzers/offline_frames_fuzzer.cc b/connections/implementation/fuzzers/offline_frames_fuzzer.cc index 256f112c..5f3aaafe 100644 --- a/connections/implementation/fuzzers/offline_frames_fuzzer.cc +++ b/connections/implementation/fuzzers/offline_frames_fuzzer.cc @@ -12,13 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "absl/strings/string_view.h" #include "connections/implementation/offline_frames.h" -#include "internal/platform/byte_array.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - nearby::ByteArray byte_array; - byte_array.SetData(reinterpret_cast(data), size); - + absl::string_view byte_array(reinterpret_cast(data), size); nearby::connections::parser::FromBytes(byte_array); return 0; diff --git a/connections/implementation/mediums/BUILD b/connections/implementation/mediums/BUILD index e12c6e42..d533c0ae 100644 --- a/connections/implementation/mediums/BUILD +++ b/connections/implementation/mediums/BUILD @@ -21,46 +21,75 @@ cc_library( name = "mediums", srcs = [ "awdl.cc", + "awdl_bwu_handler.cc", + "awdl_endpoint_channel.cc", "ble.cc", + "ble_endpoint_channel.cc", + "ble_l2cap_endpoint_channel.cc", + "bluetooth_bwu_handler.cc", "bluetooth_classic.cc", + "bluetooth_endpoint_channel.cc", "bluetooth_radio.cc", "mediums.cc", - "webrtc.cc", - "webrtc_stub.cc", "wifi_direct.cc", + "wifi_direct_bwu_handler.cc", + "wifi_direct_endpoint_channel.cc", "wifi_hotspot.cc", + "wifi_hotspot_bwu_handler.cc", + "wifi_hotspot_endpoint_channel.cc", "wifi_lan.cc", + "wifi_lan_bwu_handler.cc", + "wifi_lan_endpoint_channel.cc", ], hdrs = [ "awdl.h", + "awdl_bwu_handler.h", + "awdl_endpoint_channel.h", "ble.h", + "ble_endpoint_channel.h", + "ble_l2cap_endpoint_channel.h", + "bluetooth_bwu_handler.h", "bluetooth_classic.h", + "bluetooth_endpoint_channel.h", "bluetooth_radio.h", "mediums.h", - "webrtc.h", - "webrtc_stub.h", "wifi.h", "wifi_direct.h", + "wifi_direct_bwu_handler.h", + "wifi_direct_endpoint_channel.h", "wifi_hotspot.h", + "wifi_hotspot_bwu_handler.h", + "wifi_hotspot_endpoint_channel.h", "wifi_lan.h", + "wifi_lan_bwu_handler.h", + "wifi_lan_endpoint_channel.h", ], - copts = ["-DNO_WEBRTC"], + local_defines = select({ + "//:webrtc_enabled": [], + "//conditions:default": ["NO_WEBRTC"], + }), visibility = [ "//connections/implementation:__subpackages__", ], deps = [ ":utils", - ":webrtc_utils", + ":webrtc", + ":webrtc_peer_id", + ":webrtc_socket", "//connections:core_types", + "//connections/implementation:bwu_handler", + "//connections/implementation:client_proxy", + "//connections/implementation:endpoint_channel", + "//connections/implementation:offline_frames", + "//connections/implementation:service_id_constants", "//connections/implementation:types", "//connections/implementation/flags:connections_flags", "//connections/implementation/mediums/ble", "//connections/implementation/mediums/ble:ble_advertisement_header", "//connections/implementation/mediums/ble:ble_socket", "//connections/implementation/mediums/ble:bloom_filter", - "//connections/implementation/mediums/multiplex", - "//connections/implementation/mediums/webrtc", "//connections/implementation/proto:offline_wire_formats_cc_proto", + "//internal/base:masker", "//internal/flags:nearby_flags", "//internal/platform:base", "//internal/platform:cancellation_flag", @@ -73,9 +102,8 @@ cc_library( "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", "//internal/platform/implementation:wifi_utils", - "//proto/mediums:web_rtc_signaling_frames_cc_proto", - # TODO: Support WebRTC "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/container:btree", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", @@ -86,21 +114,18 @@ cc_library( "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time", "@com_google_absl//absl/types:optional", - ], + ] + select({ + "//:webrtc_enabled": [ + "//connections/implementation/mediums/webrtc:webrtc_impl", + ], + "//conditions:default": [], + }), ) cc_library( - name = "webrtc_utils", - srcs = [ - "webrtc_peer_id.cc", - "webrtc_peer_id_stub.cc", - ], - hdrs = [ - "webrtc_peer_id.h", - "webrtc_peer_id_stub.h", - "webrtc_socket.h", - "webrtc_socket_stub.h", - ], + name = "webrtc_peer_id", + srcs = ["webrtc_peer_id.cc"], + hdrs = ["webrtc_peer_id.h"], visibility = [ "//connections/implementation:__pkg__", "//connections/implementation/mediums:__pkg__", @@ -108,7 +133,6 @@ cc_library( ], deps = [ ":utils", - "//connections/implementation/mediums/webrtc:data_types", "//internal/platform:base", "@com_google_absl//absl/strings", ], @@ -130,12 +154,10 @@ cc_library( name = "utils", srcs = ["utils.cc"], hdrs = ["utils.h"], - copts = ["-DNO_WEBRTC"], visibility = [ "//connections/implementation:__pkg__", "//connections/implementation/mediums/advertisements:__pkg__", "//connections/implementation/mediums/ble:__subpackages__", - "//connections/implementation/mediums/multiplex:__pkg__", "//internal/platform/implementation/windows:__pkg__", ], deps = [ @@ -144,6 +166,31 @@ cc_library( ], ) +cc_library( + name = "webrtc_socket", + hdrs = ["webrtc_socket.h"], + visibility = ["//connections/implementation:__subpackages__"], + deps = [ + "//internal/platform:base", + "@com_google_absl//absl/strings:string_view", + ], +) + +cc_library( + name = "webrtc", + hdrs = ["webrtc.h"], + visibility = ["//connections/implementation:__subpackages__"], + deps = [ + ":webrtc_peer_id", + ":webrtc_socket", + "//connections/implementation:bwu_handler", + "//connections/implementation/proto:offline_wire_formats_cc_proto", + "//internal/platform:base", + "//internal/platform:cancellation_flag", + "@com_google_absl//absl/functional:any_invocable", + ], +) + cc_test( name = "core_internal_mediums_test", size = "small", @@ -179,19 +226,53 @@ cc_test( "//internal/platform/implementation:types", "//internal/platform/implementation/g3", # build_cleaner: keep "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", ], ) +cc_test( + name = "bwu_handler_test", + size = "small", + srcs = [ + "awdl_bwu_handler_test.cc", + "bluetooth_bwu_handler_test.cc", + "wifi_direct_bwu_handler_test.cc", + "wifi_hotspot_bwu_handler_test.cc", + "wifi_lan_bwu_handler_test.cc", + ], + deps = [ + ":mediums", + "//connections/implementation:bwu_handler", + "//connections/implementation:client_proxy", + "//connections/implementation:endpoint_channel", + "//connections/implementation:offline_frames", + "//connections/implementation/flags:connections_flags", + "//internal/flags:nearby_flags", + "//internal/platform:base", + "//internal/platform:cancellation_flag", + "//internal/platform:comm", + "//internal/platform:logging", + "//internal/platform:mock_platform", + "//internal/platform:test_util", + "//internal/platform:types", + "//internal/platform/flags:platform_flags", + "//internal/platform/implementation:comm", + "//internal/platform/implementation:platform", + "//internal/platform/implementation/g3", # build_cleaner: keep + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "core_internal_mediums_webrtc_test", size = "small", srcs = [ "webrtc_peer_id_test.cc", - "webrtc_test.cc", ], shard_count = 16, tags = [ @@ -199,8 +280,8 @@ cc_test( "requires-net:external", ], deps = [ - ":mediums", - ":webrtc_utils", + ":webrtc_peer_id", + ":webrtc_socket", "//internal/platform:base", "//internal/platform:cancellation_flag", "//internal/platform:comm", diff --git a/connections/implementation/mediums/awdl.cc b/connections/implementation/mediums/awdl.cc index cf380fcb..ba2539f1 100644 --- a/connections/implementation/mediums/awdl.cc +++ b/connections/implementation/mediums/awdl.cc @@ -23,28 +23,23 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" -#include "connections/implementation/mediums/multiplex/multiplex_socket.h" +#include "connections/implementation/bwu_handler.h" +#include "connections/implementation/mediums/awdl_bwu_handler.h" #include "connections/implementation/mediums/utils.h" -#include "connections/medium_selector.h" #include "internal/platform/awdl.h" -#include "internal/platform/base64_utils.h" #include "internal/platform/byte_array.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/exception.h" #include "internal/platform/expected.h" #include "internal/platform/implementation/psk_info.h" -#include "internal/platform/implementation/wifi_utils.h" #include "internal/platform/logging.h" #include "internal/platform/mutex_lock.h" #include "internal/platform/nsd_service_info.h" -#include "internal/platform/socket.h" -#include "internal/platform/types.h" namespace nearby { namespace connections { namespace { -using MultiplexSocket = mediums::multiplex::MultiplexSocket; using location::nearby::proto::connections::OperationResultCode; constexpr absl::string_view kAwdlServiceIdSuffixForServiceType = "_AWDL"; @@ -479,5 +474,11 @@ ErrorOr Awdl::InternalConnect( return socket; } +std::unique_ptr Awdl::CreateBwuHandler( + BwuHandler::IncomingConnectionCallback incoming_connection_callback) { + return std::make_unique( + this, std::move(incoming_connection_callback)); +} + } // namespace connections } // namespace nearby diff --git a/connections/implementation/mediums/awdl.h b/connections/implementation/mediums/awdl.h index b2a620d5..cb4f1910 100644 --- a/connections/implementation/mediums/awdl.h +++ b/connections/implementation/mediums/awdl.h @@ -16,6 +16,7 @@ #define CORE_INTERNAL_MEDIUMS_AWDL_H_ #include +#include #include #include #include @@ -24,12 +25,9 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/functional/any_invocable.h" -#include "connections/implementation/flags/nearby_connections_feature_flags.h" -#include "connections/implementation/mediums/multiplex/multiplex_socket.h" -#include "internal/flags/nearby_flags.h" +#include "connections/implementation/bwu_handler.h" #include "internal/platform/awdl.h" #include "internal/platform/cancellation_flag.h" -#include "internal/platform/exception.h" #include "internal/platform/expected.h" #include "internal/platform/implementation/psk_info.h" #include "internal/platform/multi_thread_executor.h" @@ -133,6 +131,9 @@ class Awdl { AwdlCredential GetCredentials(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_); + std::unique_ptr CreateBwuHandler( + BwuHandler::IncomingConnectionCallback incoming_connection_callback); + private: struct AdvertisingInfo { bool Empty() const { return nsd_service_infos.empty(); } diff --git a/connections/implementation/awdl_bwu_handler.cc b/connections/implementation/mediums/awdl_bwu_handler.cc similarity index 94% rename from connections/implementation/awdl_bwu_handler.cc rename to connections/implementation/mediums/awdl_bwu_handler.cc index 45baead2..8f8e5287 100644 --- a/connections/implementation/awdl_bwu_handler.cc +++ b/connections/implementation/mediums/awdl_bwu_handler.cc @@ -12,29 +12,30 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/awdl_bwu_handler.h" +#include "connections/implementation/mediums/awdl_bwu_handler.h" #include #include #include +#include "absl/base/nullability.h" #include "absl/functional/bind_front.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" -#include "connections/implementation/awdl_endpoint_channel.h" #include "connections/implementation/base_bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" #include "connections/implementation/mediums/awdl.h" -#include "connections/implementation/mediums/mediums.h" +#include "connections/implementation/mediums/awdl_endpoint_channel.h" #include "connections/implementation/mediums/utils.h" #include "connections/implementation/offline_frames.h" #include "connections/implementation/service_id_constants.h" #include "internal/base/masker.h" #include "internal/platform/awdl.h" #include "internal/platform/byte_array.h" +#include "internal/platform/cancellation_flag.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/expected.h" #include "internal/platform/implementation/psk_info.h" @@ -56,9 +57,10 @@ constexpr absl::string_view kAwdlServiceIdSuffixForServiceType = "_AWDL"; } // namespace AwdlBwuHandler::AwdlBwuHandler( - Mediums& mediums, IncomingConnectionCallback incoming_connection_callback) + Awdl* absl_nonnull awdl_medium, + IncomingConnectionCallback incoming_connection_callback) : BaseBwuHandler(std::move(incoming_connection_callback)), - mediums_(mediums) {} + awdl_medium_(*awdl_medium) {} // Called by BWU target. Retrieves a new medium info from incoming message, // and establishes connection over AWDL using this info. @@ -148,9 +150,10 @@ AwdlBwuHandler::CreateUpgradedEndpointChannel( << service_name << ", service_type:" << service_type << ") for endpoint " << endpoint_id; - ErrorOr socket_result = - awdl_medium_.Connect(upgrade_service_id, nsd_service_info, psk_info, - client->GetCancellationFlag(endpoint_id)); + std::shared_ptr cancellation_flag = + client->GetCancellationFlag(endpoint_id); + ErrorOr socket_result = awdl_medium_.Connect( + upgrade_service_id, nsd_service_info, psk_info, cancellation_flag.get()); if (socket_result.has_error()) { LOG(ERROR) << "Failed to connect to the AWDL service (service_name:" << service_name << ", service_type:" << service_type @@ -185,7 +188,7 @@ AwdlBwuHandler::CreateUpgradedEndpointChannel( // Called by BWU initiator. Set up AWDL upgraded medium for this endpoint, // and returns a upgrade path info (service_name, port) for remote party to // perform discovery. -ByteArray AwdlBwuHandler::HandleInitializeUpgradedMediumForEndpoint( +std::string AwdlBwuHandler::HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, const std::string& endpoint_id) { if (!awdl_medium_.IsAcceptingConnections(upgrade_service_id)) { diff --git a/connections/implementation/awdl_bwu_handler.h b/connections/implementation/mediums/awdl_bwu_handler.h similarity index 87% rename from connections/implementation/awdl_bwu_handler.h rename to connections/implementation/mediums/awdl_bwu_handler.h index 25e921ec..56a89290 100644 --- a/connections/implementation/awdl_bwu_handler.h +++ b/connections/implementation/mediums/awdl_bwu_handler.h @@ -12,21 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CORE_INTERNAL_AWDL_BWU_HANDLER_H_ -#define CORE_INTERNAL_AWDL_BWU_HANDLER_H_ +#ifndef CORE_INTERNAL_MEDIUMS_AWDL_BWU_HANDLER_H_ +#define CORE_INTERNAL_MEDIUMS_AWDL_BWU_HANDLER_H_ #include #include -#include +#include "absl/base/nullability.h" #include "connections/implementation/base_bwu_handler.h" #include "connections/implementation/bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" #include "connections/implementation/mediums/awdl.h" -#include "connections/implementation/mediums/mediums.h" #include "internal/platform/awdl.h" -#include "internal/platform/byte_array.h" #include "internal/platform/expected.h" #include "internal/platform/nsd_service_info.h" @@ -37,8 +35,8 @@ namespace connections { // per-Medium-specific operations needed to upgrade an EndpointChannel. class AwdlBwuHandler : public BaseBwuHandler { public: - explicit AwdlBwuHandler( - Mediums& mediums, + AwdlBwuHandler( + Awdl* absl_nonnull awdl_medium, IncomingConnectionCallback incoming_connection_callback); private: @@ -68,7 +66,7 @@ class AwdlBwuHandler : public BaseBwuHandler { const std::string& endpoint_id) final {} // BaseBwuHandler implementation: - ByteArray HandleInitializeUpgradedMediumForEndpoint( + std::string HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, const std::string& endpoint_id) final; void HandleRevertInitiatorStateForService( @@ -82,12 +80,11 @@ class AwdlBwuHandler : public BaseBwuHandler { std::string GenerateServiceName(); std::string GeneratePassword(); - Mediums& mediums_; - Awdl& awdl_medium_{mediums_.GetAwdl()}; + Awdl& awdl_medium_; NsdServiceInfo nsd_service_info_; }; } // namespace connections } // namespace nearby -#endif // CORE_INTERNAL_AWDL_BWU_HANDLER_H_ +#endif // CORE_INTERNAL_MEDIUMS_AWDL_BWU_HANDLER_H_ diff --git a/connections/implementation/mediums/awdl_bwu_handler_test.cc b/connections/implementation/mediums/awdl_bwu_handler_test.cc new file mode 100644 index 00000000..0080fec0 --- /dev/null +++ b/connections/implementation/mediums/awdl_bwu_handler_test.cc @@ -0,0 +1,495 @@ +// Copyright 2026 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/awdl_bwu_handler.h" + +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "connections/implementation/bwu_handler.h" +#include "connections/implementation/client_proxy.h" +#include "connections/implementation/mediums/awdl.h" +#include "connections/implementation/mediums/awdl_endpoint_channel.h" +#include "connections/implementation/mediums/mediums.h" +#include "internal/platform/awdl.h" +#include "internal/platform/cancellation_flag.h" +#include "internal/platform/count_down_latch.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/awdl.h" +#include "internal/platform/implementation/platform.h" +#include "internal/platform/implementation/psk_info.h" +#include "internal/platform/input_stream.h" +#include "internal/platform/medium_environment.h" +#include "internal/platform/mock_input_stream.h" +#include "internal/platform/mock_output_stream.h" +#include "internal/platform/nsd_service_info.h" +#include "internal/platform/output_stream.h" + +namespace nearby { + +class MockAwdlSocket : public api::AwdlSocket { + public: + MOCK_METHOD(InputStream&, GetInputStream, (), (override)); + MOCK_METHOD(OutputStream&, GetOutputStream, (), (override)); + MOCK_METHOD(Exception, Close, (), (override)); +}; + +class MockAwdlServerSocket : public api::AwdlServerSocket { + public: + MOCK_METHOD(std::string, GetIPAddress, (), (const, override)); + MOCK_METHOD(int, GetPort, (), (const, override)); + MOCK_METHOD(std::unique_ptr, Accept, (), (override)); + MOCK_METHOD(Exception, Close, (), (override)); +}; + +class MockAwdlMedium : public api::AwdlMedium { + public: + MOCK_METHOD(bool, IsNetworkConnected, (), (const, override)); + MOCK_METHOD(bool, StartAdvertising, (const NsdServiceInfo& nsd_service_info), + (override)); + MOCK_METHOD(bool, StopAdvertising, (const NsdServiceInfo& nsd_service_info), + (override)); + MOCK_METHOD(bool, StartDiscovery, + (const std::string& service_type, + DiscoveredServiceCallback callback), + (override)); + MOCK_METHOD(bool, StopDiscovery, (const std::string& service_type), + (override)); + MOCK_METHOD(std::unique_ptr, ConnectToService, + (const NsdServiceInfo& remote_service_info, + CancellationFlag* cancellation_flag), + (override)); + MOCK_METHOD(std::unique_ptr, ConnectToService, + (const NsdServiceInfo& remote_service_info, + const api::PskInfo& psk_info, + CancellationFlag* cancellation_flag), + (override)); + MOCK_METHOD(std::unique_ptr, ListenForService, + (int port), (override)); + MOCK_METHOD(std::unique_ptr, ListenForService, + (const api::PskInfo& psk_info, int port), (override)); + MOCK_METHOD((std::optional>), + GetDynamicPortRange, (), (override)); +}; + +MockAwdlMedium* awdl_medium_mock = nullptr; + +namespace connections { +namespace { + +using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame; +using ::location::nearby::connections::OfflineFrame; +using ::location::nearby::connections::V1Frame; +using ::location::nearby::proto::connections::OperationResultCode; +using ::testing::_; +using ::testing::ByMove; +using ::protobuf_matchers::EqualsProto; +using ::testing::MockFunction; +using ::testing::Return; +using ::testing::ReturnRef; +using ::testing::StrictMock; + +constexpr absl::string_view kServiceId{"service_id"}; +constexpr absl::string_view kEndpointId{"endpoint_id"}; +constexpr absl::string_view kServiceName{"awdl_srv"}; +constexpr absl::string_view kServiceType{"_awdl._tcp"}; +constexpr absl::string_view kPassword{"password123"}; +constexpr absl::string_view kChannelName{"channel_name"}; + +class AwdlBwuHandlerTest : public ::testing::Test { + protected: + AwdlBwuHandlerTest() + : handler_(&mediums_.GetAwdl(), + incoming_connection_callback_.AsStdFunction()) {} + + void SetUp() override { + // By default, network is connected. + ON_CALL(*awdl_medium_mock, IsNetworkConnected()) + .WillByDefault(Return(true)); + } + + Mediums mediums_; + MockFunction)> + incoming_connection_callback_; + AwdlBwuHandler handler_; + MockInputStream mock_input_stream_; + MockOutputStream mock_output_stream_; +}; + +TEST_F(AwdlBwuHandlerTest, + CreateUpgradedEndpointChannel_InvalidCredentials_Fails) { + ClientProxy client; + BandwidthUpgradeNegotiationFrame::UpgradePathInfo path_info; + path_info.mutable_awdl_credentials(); // Empty credentials + + auto result = + static_cast(&handler_)->CreateUpgradedEndpointChannel( + &client, "service_id", "endpoint_id", path_info); + + ASSERT_TRUE(result.has_error()); + EXPECT_EQ(result.error().operation_result_code().value(), + OperationResultCode::CONNECTIVITY_AWDL_INVALID_CREDENTIAL); +} + +TEST_F(AwdlBwuHandlerTest, CreateUpgradedEndpointChannel_Success) { + ClientProxy client; + client.AddCancellationFlag(std::string(kEndpointId)); + MockInputStream input_stream; + MockOutputStream output_stream; + auto awdl_socket = std::make_unique(); + EXPECT_CALL(*awdl_socket, GetInputStream()) + .WillRepeatedly(ReturnRef(input_stream)); + EXPECT_CALL(*awdl_socket, GetOutputStream()) + .WillRepeatedly(ReturnRef(output_stream)); + + EXPECT_CALL(*awdl_medium_mock, StartDiscovery(_, _)) + .WillOnce([](const std::string& service_type, + api::AwdlMedium::DiscoveredServiceCallback callback) { + NsdServiceInfo service_info; + service_info.SetServiceName(std::string(kServiceName)); + service_info.SetServiceType(service_type); + if (callback.service_discovered_cb) { + NsdServiceInfo service_info_copy = service_info; + callback.service_discovered_cb(service_info_copy); + } + return true; + }); + EXPECT_CALL(*awdl_medium_mock, StopDiscovery(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(*awdl_medium_mock, ConnectToService(_, _, _)) + .WillOnce(Return(ByMove(std::move(awdl_socket)))); + + BandwidthUpgradeNegotiationFrame::UpgradePathInfo path_info; + auto* credentials = path_info.mutable_awdl_credentials(); + credentials->set_service_name(kServiceName); + credentials->set_service_type(kServiceType); + credentials->set_password(kPassword); + + auto result = + static_cast(&handler_)->CreateUpgradedEndpointChannel( + &client, std::string(kServiceId), std::string(kEndpointId), + path_info); + + EXPECT_TRUE(result.has_value()); +} + +TEST_F(AwdlBwuHandlerTest, + InitializeUpgradedMediumForEndpoint_StartAcceptingConnectionsFails) { + MediumEnvironment::Instance().Start({.use_simulated_clock = true}); + ClientProxy client; + client.AddCancellationFlag(std::string(kEndpointId)); + + EXPECT_CALL(*awdl_medium_mock, ListenForService(_, 0)) + .WillOnce(Return(ByMove(nullptr))); + + std::string result = handler_.InitializeUpgradedMediumForEndpoint( + &client, std::string(kServiceId), std::string(kEndpointId)); + + EXPECT_TRUE(result.empty()); + MediumEnvironment::Instance().Stop(); +} + +TEST_F(AwdlBwuHandlerTest, InitializeUpgradedMediumForEndpoint_Success) { + MediumEnvironment::Instance().Start({.use_simulated_clock = true}); + // The reason for putting ClientProxy inside a C++ { } block so it destructs + // before the simulated clock is restored. Otherwise, the simulated clock + // would stopped before ClientProxy went out of scope, causing its destructor + // to log the session duration using the real system clock. If 1 or more + // real-world milliseconds elapsed between the test start and test end, this + // duration evaluated to something > 0. + { + ClientProxy client; + client.AddCancellationFlag(std::string(kEndpointId)); + + auto awdl_server_socket = std::make_unique(); + auto* awdl_server_socket_ptr = awdl_server_socket.get(); + EXPECT_CALL(*awdl_server_socket_ptr, GetPort()) + .WillRepeatedly(Return(8080)); + EXPECT_CALL(*awdl_server_socket_ptr, Accept()) + .WillOnce(Return(ByMove(nullptr))); + EXPECT_CALL(*awdl_server_socket_ptr, Close()) + .WillRepeatedly(Return(Exception{Exception::kSuccess})); + + EXPECT_CALL(*awdl_medium_mock, ListenForService(_, 0)) + .WillOnce(Return(ByMove(std::move(awdl_server_socket)))); + + std::string captured_service_name; + std::string captured_service_type; + EXPECT_CALL(*awdl_medium_mock, StartAdvertising(_)) + .WillOnce([&](const NsdServiceInfo& nsd_service_info) { + captured_service_name = nsd_service_info.GetServiceName(); + captured_service_type = nsd_service_info.GetServiceType(); + return true; + }); + + std::string result = handler_.InitializeUpgradedMediumForEndpoint( + &client, std::string(kServiceId), std::string(kEndpointId)); + + EXPECT_FALSE(result.empty()); + OfflineFrame expected_frame; + expected_frame.set_version(OfflineFrame::V1); + expected_frame.mutable_v1()->set_type( + V1Frame::BANDWIDTH_UPGRADE_NEGOTIATION); + auto* bwu_frame = + expected_frame.mutable_v1()->mutable_bandwidth_upgrade_negotiation(); + bwu_frame->set_event_type( + BandwidthUpgradeNegotiationFrame::UPGRADE_PATH_AVAILABLE); + auto* upgrade_path_info = bwu_frame->mutable_upgrade_path_info(); + upgrade_path_info->set_medium( + BandwidthUpgradeNegotiationFrame::UpgradePathInfo::AWDL); + upgrade_path_info->set_supports_client_introduction_ack(true); + upgrade_path_info->set_supports_disabling_encryption(true); + auto* awdl_credentials = upgrade_path_info->mutable_awdl_credentials(); + awdl_credentials->set_service_name(captured_service_name); + awdl_credentials->set_service_type(captured_service_type); + + // The password is automatically generated and set in the handle start, we + // can obtain it from the credential Since we mock StartAcceptingConnections + // instead of using real awdl, GetPskInfo won't work perfectly. However + // InitializeUpgradedMediumForEndpoint internally calls + // parser::ForBwuAwdlPathAvailable which puts the generated password. We + // will extract it from result directly to build expected frame. + OfflineFrame result_frame; + EXPECT_TRUE(result_frame.ParseFromString(result)); + awdl_credentials->set_password(result_frame.v1() + .bandwidth_upgrade_negotiation() + .upgrade_path_info() + .awdl_credentials() + .password()); + + EXPECT_THAT(result_frame, EqualsProto(expected_frame)); + + handler_.RevertInitiatorState(); + } + MediumEnvironment::Instance().Stop(); +} + +TEST_F(AwdlBwuHandlerTest, OnIncomingAwdlConnection_Success) { + MediumEnvironment::Instance().Start({.use_simulated_clock = true}); + ClientProxy client; + client.AddCancellationFlag(std::string(kEndpointId)); + + auto awdl_server_socket = std::make_unique(); + auto* awdl_server_socket_ptr = awdl_server_socket.get(); + EXPECT_CALL(*awdl_server_socket_ptr, Close()) + .WillRepeatedly(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(*awdl_server_socket_ptr, Accept()) + .WillOnce([this]() { + auto awdl_socket = std::make_unique(); + EXPECT_CALL(*awdl_socket, GetInputStream()) + .WillRepeatedly(ReturnRef(mock_input_stream_)); + EXPECT_CALL(*awdl_socket, GetOutputStream()) + .WillRepeatedly(ReturnRef(mock_output_stream_)); + return awdl_socket; + }) + .WillRepeatedly([]() { + absl::SleepFor(absl::Seconds(5)); + return nullptr; + }); + + EXPECT_CALL(*awdl_medium_mock, ListenForService(_, 0)) + .WillOnce(Return(ByMove(std::move(awdl_server_socket)))); + EXPECT_CALL(*awdl_medium_mock, StartAdvertising(_)).WillOnce(Return(true)); + + CountDownLatch latch(1); + EXPECT_CALL(incoming_connection_callback_, Call(&client, _)) + .WillOnce([&latch](ClientProxy* client, + std::unique_ptr + connection) { latch.CountDown(); }); + + std::string result = handler_.InitializeUpgradedMediumForEndpoint( + &client, std::string(kServiceId), std::string(kEndpointId)); + EXPECT_FALSE(result.empty()); + + auto await_result = latch.Await(absl::Seconds(5)); + EXPECT_TRUE(await_result.ok()); + + handler_.RevertInitiatorState(); + MediumEnvironment::Instance().Stop(); +} + +TEST_F(AwdlBwuHandlerTest, AwdlIncomingSocket_ToStringAndClose) { + MediumEnvironment::Instance().Start({.use_simulated_clock = true}); + ClientProxy client; + client.AddCancellationFlag(std::string(kEndpointId)); + + auto awdl_server_socket = std::make_unique(); + auto* awdl_server_socket_ptr = awdl_server_socket.get(); + EXPECT_CALL(*awdl_server_socket_ptr, Close()) + .WillRepeatedly(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(*awdl_server_socket_ptr, Accept()) + .WillOnce([this]() { + auto awdl_socket = std::make_unique(); + EXPECT_CALL(*awdl_socket, GetInputStream()) + .WillRepeatedly(ReturnRef(mock_input_stream_)); + EXPECT_CALL(*awdl_socket, GetOutputStream()) + .WillRepeatedly(ReturnRef(mock_output_stream_)); + EXPECT_CALL(*awdl_socket, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + return awdl_socket; + }) + .WillRepeatedly([]() { + absl::SleepFor(absl::Seconds(5)); + return nullptr; + }); + + EXPECT_CALL(*awdl_medium_mock, ListenForService(_, 0)) + .WillOnce(Return(ByMove(std::move(awdl_server_socket)))); + EXPECT_CALL(*awdl_medium_mock, StartAdvertising(_)).WillOnce(Return(true)); + + CountDownLatch latch(1); + EXPECT_CALL(incoming_connection_callback_, Call(&client, _)) + .WillOnce([&latch](ClientProxy* client, + std::unique_ptr + connection) { + EXPECT_FALSE(connection->socket->ToString().empty()); + connection->socket->Close(); + latch.CountDown(); + }); + + std::string result = handler_.InitializeUpgradedMediumForEndpoint( + &client, std::string(kServiceId), std::string(kEndpointId)); + EXPECT_FALSE(result.empty()); + + auto await_result = latch.Await(absl::Seconds(5)); + EXPECT_TRUE(await_result.ok()); + + handler_.RevertInitiatorState(); + MediumEnvironment::Instance().Stop(); +} + +TEST_F(AwdlBwuHandlerTest, HandleRevertInitiatorStateForService_Success) { + MediumEnvironment::Instance().Start({.use_simulated_clock = true}); + ClientProxy client; + client.AddCancellationFlag(std::string(kEndpointId)); + + auto awdl_server_socket = std::make_unique(); + auto* awdl_server_socket_ptr = awdl_server_socket.get(); + EXPECT_CALL(*awdl_server_socket_ptr, Close()) + .WillRepeatedly(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(*awdl_server_socket_ptr, Accept()).WillRepeatedly([]() { + absl::SleepFor(absl::Seconds(10)); + return nullptr; + }); + + EXPECT_CALL(*awdl_medium_mock, ListenForService(_, 0)) + .WillOnce(Return(ByMove(std::move(awdl_server_socket)))); + EXPECT_CALL(*awdl_medium_mock, StartAdvertising(_)).WillOnce(Return(true)); + EXPECT_CALL(*awdl_medium_mock, StopAdvertising(_)).WillOnce(Return(true)); + + handler_.InitializeUpgradedMediumForEndpoint(&client, std::string(kServiceId), + std::string(kEndpointId)); + + handler_.RevertInitiatorState(); + MediumEnvironment::Instance().Stop(); +} + +TEST_F(AwdlBwuHandlerTest, GetUpgradeMedium_ReturnsAwdl) { + auto* bwu_handler = static_cast(&handler_); + EXPECT_EQ(bwu_handler->GetUpgradeMedium(), + location::nearby::proto::connections::Medium::AWDL); +} + +TEST_F(AwdlBwuHandlerTest, OnEndpointDisconnect_DoesNotCrash) { + ClientProxy client; + auto* bwu_handler = static_cast(&handler_); + // This method is a no-op, just verifying it doesn't crash. + bwu_handler->OnEndpointDisconnect(&client, std::string(kEndpointId)); +} + +class AwdlEndpointChannelTest : public ::testing::Test { + protected: + void SetUp() override { + ON_CALL(*awdl_medium_mock, IsNetworkConnected()) + .WillByDefault(Return(true)); + mock_socket_ = std::make_unique>(); + EXPECT_CALL(*mock_socket_, GetInputStream()) + .WillRepeatedly(ReturnRef(mock_input_stream_)); + EXPECT_CALL(*mock_socket_, GetOutputStream()) + .WillRepeatedly(ReturnRef(mock_output_stream_)); + } + + std::unique_ptr> mock_socket_; + StrictMock mock_input_stream_; + StrictMock mock_output_stream_; + Awdl awdl_medium_; +}; + +TEST_F(AwdlEndpointChannelTest, CloseImpl_StopsDiscoveryIfOutgoing) { + EXPECT_CALL(*mock_socket_, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(mock_input_stream_, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(mock_output_stream_, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(*awdl_medium_mock, StartDiscovery(_, _)).WillOnce(Return(true)); + EXPECT_CALL(*awdl_medium_mock, StopDiscovery(_)).WillOnce(Return(true)); + awdl_medium_.StartDiscovery(std::string(kServiceId), {}); + + AwdlEndpointChannel channel( + std::string(kServiceId), std::string(kChannelName), + AwdlSocket(std::move(mock_socket_)), &awdl_medium_, + /*is_outgoing=*/true); + + channel.Close(location::nearby::proto::connections::DisconnectionReason:: + UNKNOWN_DISCONNECTION_REASON); +} + +TEST_F(AwdlEndpointChannelTest, CloseImpl_DoesNotStopDiscoveryIfIncoming) { + EXPECT_CALL(*mock_socket_, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(mock_input_stream_, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + EXPECT_CALL(mock_output_stream_, Close()) + .WillOnce(Return(Exception{Exception::kSuccess})); + + AwdlEndpointChannel channel( + std::string(kServiceId), std::string(kChannelName), + AwdlSocket(std::move(mock_socket_)), &awdl_medium_, + /*is_outgoing=*/false); + + channel.Close(location::nearby::proto::connections::DisconnectionReason:: + UNKNOWN_DISCONNECTION_REASON); +} + +TEST_F(AwdlEndpointChannelTest, EnableMultiplexSocket_CallsSocket) { + AwdlEndpointChannel channel( + std::string(kServiceId), std::string(kChannelName), + AwdlSocket(std::move(mock_socket_)), &awdl_medium_, + /*is_outgoing=*/true); + + EXPECT_TRUE(channel.EnableMultiplexSocket()); +} + +} // namespace +} // namespace connections +namespace api { + +std::unique_ptr ImplementationPlatform::CreateAwdlMedium() { + auto medium = std::make_unique(); + awdl_medium_mock = medium.get(); + return medium; +} + +} // namespace api +} // namespace nearby diff --git a/connections/implementation/awdl_endpoint_channel.cc b/connections/implementation/mediums/awdl_endpoint_channel.cc similarity index 97% rename from connections/implementation/awdl_endpoint_channel.cc rename to connections/implementation/mediums/awdl_endpoint_channel.cc index 74ac3564..46cac068 100644 --- a/connections/implementation/awdl_endpoint_channel.cc +++ b/connections/implementation/mediums/awdl_endpoint_channel.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/awdl_endpoint_channel.h" +#include "connections/implementation/mediums/awdl_endpoint_channel.h" #include #include diff --git a/connections/implementation/awdl_endpoint_channel.h b/connections/implementation/mediums/awdl_endpoint_channel.h similarity index 88% rename from connections/implementation/awdl_endpoint_channel.h rename to connections/implementation/mediums/awdl_endpoint_channel.h index 1cd8da5b..ff22735a 100644 --- a/connections/implementation/awdl_endpoint_channel.h +++ b/connections/implementation/mediums/awdl_endpoint_channel.h @@ -12,13 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CORE_INTERNAL_AWDL_ENDPOINT_CHANNEL_H_ -#define CORE_INTERNAL_AWDL_ENDPOINT_CHANNEL_H_ +#ifndef CORE_INTERNAL_MEDIUMS_AWDL_ENDPOINT_CHANNEL_H_ +#define CORE_INTERNAL_MEDIUMS_AWDL_ENDPOINT_CHANNEL_H_ #include #include "connections/implementation/base_endpoint_channel.h" #include "connections/implementation/mediums/awdl.h" +#include "internal/platform/awdl.h" namespace nearby { namespace connections { @@ -48,4 +49,4 @@ class AwdlEndpointChannel final : public BaseEndpointChannel { } // namespace connections } // namespace nearby -#endif // CORE_INTERNAL_AWDL_ENDPOINT_CHANNEL_H_ +#endif // CORE_INTERNAL_MEDIUMS_AWDL_ENDPOINT_CHANNEL_H_ diff --git a/connections/implementation/mediums/ble/BUILD b/connections/implementation/mediums/ble/BUILD index 619672c2..a4f1a0a4 100644 --- a/connections/implementation/mediums/ble/BUILD +++ b/connections/implementation/mediums/ble/BUILD @@ -70,7 +70,6 @@ cc_library( "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:string_view", - "@com_google_absl//absl/synchronization", ], ) diff --git a/connections/implementation/mediums/ble/ble_socket.cc b/connections/implementation/mediums/ble/ble_socket.cc index 97c29e32..dc068c9c 100644 --- a/connections/implementation/mediums/ble/ble_socket.cc +++ b/connections/implementation/mediums/ble/ble_socket.cc @@ -207,22 +207,28 @@ Medium BleSocket::GetMediumLocked() const { } ExceptionOr BleSocket::DispatchPacket() { - MutexLock lock(&mutex_); - if (!ble_input_stream_) { - return Exception::kFailed; + std::shared_ptr input_stream; + { + MutexLock lock(&mutex_); + if (!ble_input_stream_) { + return Exception::kFailed; + } + input_stream = ble_input_stream_; } ExceptionOr read_bytes = - ble_input_stream_->Read(BlePacket::kServiceIdHashLength); + input_stream->Read(BlePacket::kServiceIdHashLength); while (read_bytes.ok()) { ByteArray read_bytes_result = read_bytes.result(); if (BlePacket::IsControlPacketBytes(read_bytes_result)) { - ExceptionOr handle_result = ProcessBleControlPacketLocked(); + ExceptionOr handle_result = + ProcessBleControlPacket(input_stream); if (!handle_result.ok()) { return handle_result; } - read_bytes = ble_input_stream_->Read(BlePacket::kServiceIdHashLength); + read_bytes = input_stream->Read(BlePacket::kServiceIdHashLength); } else { + MutexLock lock(&mutex_); if (read_bytes_result != service_id_hash_) { LOG(WARNING) << "Received data packet with incorrect service ID hash. Expected: " @@ -239,20 +245,22 @@ ExceptionOr BleSocket::DispatchPacket() { ExceptionOr BleSocket::ReadPayloadLength() { int payload_length = 0; + std::shared_ptr input_stream; { MutexLock lock(&mutex_); if (!ble_input_stream_) { return {Exception::kIo}; } - - ExceptionOr read_bytes = - ble_input_stream_->Read(sizeof(std::int32_t)); - if (!read_bytes.ok()) { - return read_bytes.exception(); - } - - payload_length = byte_utils::BytesToInt(std::move(read_bytes.result())); + input_stream = ble_input_stream_; } + + ExceptionOr read_bytes = input_stream->Read(sizeof(std::int32_t)); + if (!read_bytes.ok()) { + return read_bytes.exception(); + } + + payload_length = byte_utils::BytesToInt(std::move(read_bytes.result())); + Exception send_ack_result = SendPacketAcknowledgement(payload_length); if (!send_ack_result.Ok()) { LOG(WARNING) << "Failed to send packet acknowledgement."; @@ -268,9 +276,10 @@ Exception BleSocket::WritePayloadLength(int payload_length) { return ble_output_stream_->WritePayloadLength(payload_length); } -ExceptionOr BleSocket::ProcessBleControlPacketLocked() { +ExceptionOr BleSocket::ProcessBleControlPacket( + std::shared_ptr input_stream) { // Read the first 4 bytes (packet block 1). - ExceptionOr read_bytes = ble_input_stream_->Read(4); + ExceptionOr read_bytes = input_stream->Read(4); if (!read_bytes.ok()) { return read_bytes; } @@ -282,7 +291,7 @@ ExceptionOr BleSocket::ProcessBleControlPacketLocked() { // Read the length from the 3rd byte of the packet block (0-indexed). int packet_block_2_size = packet_block_1.data()[3]; // Read the left bytes for the packet block 2). - read_bytes = ble_input_stream_->Read(packet_block_2_size); + read_bytes = input_stream->Read(packet_block_2_size); if (!read_bytes.ok()) { return read_bytes; } diff --git a/connections/implementation/mediums/ble/ble_socket.h b/connections/implementation/mediums/ble/ble_socket.h index 4aba0ca1..85aa9fb0 100644 --- a/connections/implementation/mediums/ble/ble_socket.h +++ b/connections/implementation/mediums/ble/ble_socket.h @@ -375,8 +375,8 @@ class BleSocket final { * payload, the `ByteArray` may be empty. Returns an `Exception` if a * protocol error occurs or the read operation fails. */ - ExceptionOr ProcessBleControlPacketLocked() - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + ExceptionOr ProcessBleControlPacket( + std::shared_ptr input_stream); /** * Sends a raw L2CAP packet over the socket. @@ -407,9 +407,9 @@ class BleSocket final { SingleThreadExecutor serial_executor_; const ByteArray service_id_hash_; - std::unique_ptr ble_input_stream_ + std::shared_ptr ble_input_stream_ ABSL_GUARDED_BY(mutex_) = nullptr; - std::unique_ptr ble_output_stream_ + std::shared_ptr ble_output_stream_ ABSL_GUARDED_BY(mutex_) = nullptr; nearby::BleSocket ble_socket_ ABSL_GUARDED_BY(mutex_) = nearby::BleSocket(); nearby::BleL2capSocket l2cap_socket_ ABSL_GUARDED_BY(mutex_) = diff --git a/connections/implementation/mediums/ble/discovered_peripheral_tracker.cc b/connections/implementation/mediums/ble/discovered_peripheral_tracker.cc index 4eda44c6..51e2dd53 100644 --- a/connections/implementation/mediums/ble/discovered_peripheral_tracker.cc +++ b/connections/implementation/mediums/ble/discovered_peripheral_tracker.cc @@ -228,6 +228,7 @@ bool DiscoveredPeripheralTracker::HandleOnLostAdvertisementLocked( return false; } + std::vector advertisements_to_clear; for (const auto& hash : on_lost_advertisement->hashes()) { for (const auto& it : gatt_advertisement_infos_) { if (it.second.instant_on_lost_hash.string_data() == hash) { @@ -247,30 +248,25 @@ bool DiscoveredPeripheralTracker::HandleOnLostAdvertisementLocked( BlePeripheral lost_peripheral = it.second.peripheral; lost_peripheral.SetId(ByteArray(gatt_advertisement)); if (gatt_advertisement.IsValid()) { - if (NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kEnableScanningForInstantOnLost)) { - AddInstantLostAdvertisement(it.second.advertisement_header); - discovery_cb_it->second.discovered_peripheral_callback - .instant_lost_cb(lost_peripheral, it.second.service_id, - gatt_advertisement.GetData(), - gatt_advertisement.IsFastAdvertisement()); - } else { - discovery_cb_it->second.discovered_peripheral_callback - .peripheral_lost_cb(lost_peripheral, it.second.service_id, - gatt_advertisement.GetData(), - gatt_advertisement.IsFastAdvertisement()); - } + AddInstantLostAdvertisement(it.second.advertisement_header); + discovery_cb_it->second.discovered_peripheral_callback + .instant_lost_cb(lost_peripheral, it.second.service_id, + gatt_advertisement.GetData(), + gatt_advertisement.IsFastAdvertisement()); LOG(INFO) << __func__ << ": OnLost triggered for service_id " << it.second.service_id; } - ClearGattAdvertisement(gatt_advertisement); + advertisements_to_clear.push_back(gatt_advertisement); } break; } } } + + for (const auto& advertisement : advertisements_to_clear) { + ClearGattAdvertisement(advertisement); + } return true; } diff --git a/connections/implementation/mediums/ble/discovered_peripheral_tracker_test.cc b/connections/implementation/mediums/ble/discovered_peripheral_tracker_test.cc index 79fd120f..52fb547f 100644 --- a/connections/implementation/mediums/ble/discovered_peripheral_tracker_test.cc +++ b/connections/implementation/mediums/ble/discovered_peripheral_tracker_test.cc @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -53,7 +52,6 @@ #include "internal/platform/mutex.h" #include "internal/platform/mutex_lock.h" #include "internal/platform/uuid.h" -#include "internal/test/fake_clock.h" namespace nearby { namespace connections { @@ -225,6 +223,42 @@ class DiscoveredPeripheralTrackerTest adapter_peripheral_->GetAddress().address()); } + void SetupMultipleAdvertisementsState( + const BleAdvertisementHeader& header, + const BleAdvertisement& advertisement_1, + const BleAdvertisement& advertisement_2) { + MutexLock lock(&discovered_peripheral_tracker_->mutex_); + ByteArray advertisement_bytes_1 = advertisement_1.ByteArrayWithExtraField(); + ByteArray advertisement_bytes_2 = advertisement_2.ByteArrayWithExtraField(); + std::vector gatt_advertisement_bytes_list = { + &advertisement_bytes_1, &advertisement_bytes_2}; + + discovered_peripheral_tracker_->HandleRawGattAdvertisements( + CreateBlePeripheral(), header, gatt_advertisement_bytes_list, + /*service_uuid=*/{}); + } + + void RegisterServiceIdCallback(const std::string& service_id, + CountDownLatch& lost_latch) { + discovered_peripheral_tracker_->StartTracking( + service_id, /*include_dct_advertisement=*/false, Pcp::kP2pPointToPoint, + { + .instant_lost_cb = + [&lost_latch]( + BlePeripheral peripheral, const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { lost_latch.CountDown(); }, + }, + /*fast_advertisement_service_uuid=*/{}); + } + + bool CallHandleOnLostAdvertisementLocked( + const api::ble::BleAdvertisementData& advertisement_data) { + MutexLock lock(&discovered_peripheral_tracker_->mutex_); + return discovered_peripheral_tracker_->HandleOnLostAdvertisementLocked( + advertisement_data); + } + // Simulates to see a fast advertisement. void FindFastAdvertisement( const api::ble::BleAdvertisementData& advertisement_data, @@ -1269,6 +1303,58 @@ TEST_P(DiscoveredPeripheralTrackerTest, InstantLostPeripheralForInstantOnLost) { EXPECT_TRUE(lost_latch.Await(kWaitDuration).result()); } +TEST_P(DiscoveredPeripheralTrackerTest, + InstantLostPeripheralForInstantOnLost_MultipleAdvertisements) { + ByteArray advertisement_hash = GenerateRandomAdvertisementHash(); + BleAdvertisementHeader header(BleAdvertisementHeader::Version::kV2, + /*extended_advertisement=*/false, + /*num_slots=*/1, ByteArray{}, // bloom filter + advertisement_hash, + BleAdvertisementHeader::kDefaultPsmValue); + + ByteArray advertisement_bytes_1 = CreateBleAdvertisement( + std::string(kServiceIdA), ByteArray(std::string(kData)), + ByteArray(std::string(kDeviceToken))); + ByteArray advertisement_bytes_2 = CreateBleAdvertisement( + std::string(kServiceIdB), ByteArray(std::string(kData2)), + ByteArray(std::string(kDeviceToken))); + + auto adv_status_or_1 = + BleAdvertisement::CreateBleAdvertisement(advertisement_bytes_1); + ASSERT_OK(adv_status_or_1); + BleAdvertisement advertisement_1 = adv_status_or_1.value(); + + auto adv_status_or_2 = + BleAdvertisement::CreateBleAdvertisement(advertisement_bytes_2); + ASSERT_OK(adv_status_or_2); + BleAdvertisement advertisement_2 = adv_status_or_2.value(); + + // Register callbacks for both Service A and Service B. + CountDownLatch lost_latch(2); + RegisterServiceIdCallback(std::string(kServiceIdA), lost_latch); + RegisterServiceIdCallback(std::string(kServiceIdB), lost_latch); + + // Use helper method to set up state manually. + SetupMultipleAdvertisementsState(header, advertisement_1, advertisement_2); + + // Create OnLost advertisement for advertisement_1's hash. + auto advertisement = InstantOnLostAdvertisement::CreateFromHashes( + std::list({std::string(bleutils::GenerateAdvertisementHash( + advertisement_1.ByteArrayWithExtraField()))})); + ASSERT_OK(advertisement); + + api::ble::BleAdvertisementData loss_advertisement_data{}; + loss_advertisement_data.service_data.insert( + {bleutils::kCopresenceServiceUuid, ByteArray(advertisement->ToBytes())}); + + // Call HandleOnLostAdvertisementLocked using helper. + bool result = CallHandleOnLostAdvertisementLocked(loss_advertisement_data); + EXPECT_TRUE(result); + + // Verify that both are lost (callback triggered twice). + EXPECT_TRUE(lost_latch.Await(kWaitDuration).result()); +} + TEST_P(DiscoveredPeripheralTrackerTest, IgnoreFoundAdvertisementForInstantOnLost) { std::vector service_ids = {std::string(kServiceIdA)}; @@ -1628,9 +1714,6 @@ TEST_P(DiscoveredPeripheralTrackerTest, TEST_P(DiscoveredPeripheralTrackerTest, OnlyGattAdvertisementReceivedOnDeviceWithExtended) { - std::optional fake_clock = - MediumEnvironment::Instance().GetSimulatedClock(); - std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_hash = GenerateRandomAdvertisementHash(); ByteArray advertisement_header_bytes = @@ -1668,7 +1751,7 @@ TEST_P(DiscoveredPeripheralTrackerTest, // 2. Receive GATT advertisement data again after 4 seconds, it should access // GATT server. - (*fake_clock)->FastForward(absl::Seconds(4)); + MediumEnvironment::Instance().FastForward(absl::Seconds(4)); FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); // We should receive a client callback of a peripheral discovery. @@ -1678,9 +1761,6 @@ TEST_P(DiscoveredPeripheralTrackerTest, } TEST_P(DiscoveredPeripheralTrackerTest, SkipExpiredGattAdvertisement) { - std::optional fake_clock = - MediumEnvironment::Instance().GetSimulatedClock(); - std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_hash = GenerateRandomAdvertisementHash(); ByteArray advertisement_header_bytes = @@ -1718,7 +1798,7 @@ TEST_P(DiscoveredPeripheralTrackerTest, SkipExpiredGattAdvertisement) { // 2. The GATT advertisement is already queued and will be skipped. FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); - (*fake_clock)->FastForward(absl::Seconds(20)); + MediumEnvironment::Instance().FastForward(absl::Seconds(20)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); // We should not receive a client callback of a peripheral discovery. @@ -1729,9 +1809,6 @@ TEST_P(DiscoveredPeripheralTrackerTest, SkipExpiredGattAdvertisement) { TEST_P(DiscoveredPeripheralTrackerTest, DiscoveredOnceWhenGattAndExtendedAdvertisementReceived) { - std::optional fake_clock = - MediumEnvironment::Instance().GetSimulatedClock(); - std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_hash = GenerateRandomAdvertisementHash(); ByteArray advertisement_header_bytes = @@ -1756,7 +1833,7 @@ TEST_P(DiscoveredPeripheralTrackerTest, }, }, bleutils::kCopresenceServiceUuid); - (*fake_clock)->FastForward(absl::Seconds(4)); + MediumEnvironment::Instance().FastForward(absl::Seconds(4)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); // 1. Received extended advertisement. @@ -1784,8 +1861,6 @@ TEST_P(DiscoveredPeripheralTrackerTest, TEST_P(DiscoveredPeripheralTrackerTest, FindGattAdvertisementInHigherPriorityThanExtendedGattAdvertisement) { - std::optional fake_clock = - MediumEnvironment::Instance().GetSimulatedClock(); std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_hash_a = GenerateRandomAdvertisementHash(); ByteArray advertisement_header_a = @@ -1823,7 +1898,7 @@ TEST_P(DiscoveredPeripheralTrackerTest, }, }, bleutils::kCopresenceServiceUuid); - (*fake_clock)->FastForward(absl::Seconds(4)); + MediumEnvironment::Instance().FastForward(absl::Seconds(4)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); // 1. Find peripheral A with GATT advertisement. diff --git a/connections/implementation/mediums/ble/instant_on_lost_manager.cc b/connections/implementation/mediums/ble/instant_on_lost_manager.cc index 6fd17c46..8a477d21 100644 --- a/connections/implementation/mediums/ble/instant_on_lost_manager.cc +++ b/connections/implementation/mediums/ble/instant_on_lost_manager.cc @@ -206,10 +206,7 @@ bool InstantOnLostManager::StartInstantOnLostAdvertisement() { StopOnLostAdvertising(); - if (NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kDisableInstantOnLostOnBleWithoutExtended) && - !ble_medium_.IsExtendedAdvertisementsAvailable()) { + if (!ble_medium_.IsExtendedAdvertisementsAvailable()) { LOG(WARNING) << __func__ << ": Disabling instant on lost on BLE without extended advertising."; diff --git a/connections/implementation/ble_endpoint_channel.cc b/connections/implementation/mediums/ble_endpoint_channel.cc similarity index 98% rename from connections/implementation/ble_endpoint_channel.cc rename to connections/implementation/mediums/ble_endpoint_channel.cc index 4146b7ac..a261ab89 100644 --- a/connections/implementation/ble_endpoint_channel.cc +++ b/connections/implementation/mediums/ble_endpoint_channel.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/ble_endpoint_channel.h" +#include "connections/implementation/mediums/ble_endpoint_channel.h" #include #include diff --git a/connections/implementation/ble_endpoint_channel.h b/connections/implementation/mediums/ble_endpoint_channel.h similarity index 91% rename from connections/implementation/ble_endpoint_channel.h rename to connections/implementation/mediums/ble_endpoint_channel.h index 7ad7fc4e..82805a98 100644 --- a/connections/implementation/ble_endpoint_channel.h +++ b/connections/implementation/mediums/ble_endpoint_channel.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CONNECTIONS_IMPLEMENTATION_BLE_ENDPOINT_CHANNEL_H_ -#define CONNECTIONS_IMPLEMENTATION_BLE_ENDPOINT_CHANNEL_H_ +#ifndef CORE_INTERNAL_MEDIUMS_BLE_ENDPOINT_CHANNEL_H_ +#define CORE_INTERNAL_MEDIUMS_BLE_ENDPOINT_CHANNEL_H_ #include #include @@ -57,4 +57,4 @@ class BleEndpointChannel final : public BaseEndpointChannel { } // namespace connections } // namespace nearby -#endif // CONNECTIONS_IMPLEMENTATION_BLE_ENDPOINT_CHANNEL_H_ +#endif // CORE_INTERNAL_MEDIUMS_BLE_ENDPOINT_CHANNEL_H_ diff --git a/connections/implementation/ble_l2cap_endpoint_channel.cc b/connections/implementation/mediums/ble_l2cap_endpoint_channel.cc similarity index 98% rename from connections/implementation/ble_l2cap_endpoint_channel.cc rename to connections/implementation/mediums/ble_l2cap_endpoint_channel.cc index 33056d69..6f92010e 100644 --- a/connections/implementation/ble_l2cap_endpoint_channel.cc +++ b/connections/implementation/mediums/ble_l2cap_endpoint_channel.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/ble_l2cap_endpoint_channel.h" +#include "connections/implementation/mediums/ble_l2cap_endpoint_channel.h" #include #include @@ -22,11 +22,11 @@ #include "connections/implementation/base_endpoint_channel.h" #include "connections/implementation/mediums/ble/ble_socket.h" #include "internal/platform/ble.h" +#include "internal/platform/byte_array.h" #include "internal/platform/exception.h" #include "internal/platform/input_stream.h" #include "internal/platform/logging.h" #include "internal/platform/output_stream.h" -#include "internal/platform/byte_array.h" namespace nearby { namespace connections { diff --git a/connections/implementation/ble_l2cap_endpoint_channel.h b/connections/implementation/mediums/ble_l2cap_endpoint_channel.h similarity index 91% rename from connections/implementation/ble_l2cap_endpoint_channel.h rename to connections/implementation/mediums/ble_l2cap_endpoint_channel.h index 292d67ea..00b84c8b 100644 --- a/connections/implementation/ble_l2cap_endpoint_channel.h +++ b/connections/implementation/mediums/ble_l2cap_endpoint_channel.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CORE_INTERNAL_BLE_L2CAP_ENDPOINT_CHANNEL_H_ -#define CORE_INTERNAL_BLE_L2CAP_ENDPOINT_CHANNEL_H_ +#ifndef CORE_INTERNAL_MEDIUMS_BLE_L2CAP_ENDPOINT_CHANNEL_H_ +#define CORE_INTERNAL_MEDIUMS_BLE_L2CAP_ENDPOINT_CHANNEL_H_ #include #include @@ -60,4 +60,4 @@ class BleL2capEndpointChannel final : public BaseEndpointChannel { } // namespace connections } // namespace nearby -#endif // CORE_INTERNAL_BLE_L2CAP_ENDPOINT_CHANNEL_H_ +#endif // CORE_INTERNAL_MEDIUMS_BLE_L2CAP_ENDPOINT_CHANNEL_H_ diff --git a/connections/implementation/bluetooth_bwu_handler.cc b/connections/implementation/mediums/bluetooth_bwu_handler.cc similarity index 88% rename from connections/implementation/bluetooth_bwu_handler.cc rename to connections/implementation/mediums/bluetooth_bwu_handler.cc index f6734597..0edaae99 100644 --- a/connections/implementation/bluetooth_bwu_handler.cc +++ b/connections/implementation/mediums/bluetooth_bwu_handler.cc @@ -12,22 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/bluetooth_bwu_handler.h" +#include "connections/implementation/mediums/bluetooth_bwu_handler.h" #include #include #include +#include "absl/base/nullability.h" #include "absl/functional/bind_front.h" #include "connections/implementation/base_bwu_handler.h" -#include "connections/implementation/bluetooth_endpoint_channel.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" -#include "connections/implementation/mediums/mediums.h" +#include "connections/implementation/mediums/bluetooth_classic.h" +#include "connections/implementation/mediums/bluetooth_endpoint_channel.h" +#include "connections/implementation/mediums/bluetooth_radio.h" #include "connections/implementation/offline_frames.h" #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/bluetooth_classic.h" -#include "internal/platform/byte_array.h" +#include "internal/platform/cancellation_flag.h" #include "internal/platform/expected.h" #include "internal/platform/logging.h" #include "internal/platform/mac_address.h" @@ -44,9 +46,12 @@ using ::location::nearby::proto::connections::OperationResultCode; } // namespace BluetoothBwuHandler::BluetoothBwuHandler( - Mediums& mediums, IncomingConnectionCallback incoming_connection_callback) + BluetoothRadio* absl_nonnull bluetooth_radio, + BluetoothClassic* absl_nonnull bluetooth_medium, + IncomingConnectionCallback incoming_connection_callback) : BaseBwuHandler(std::move(incoming_connection_callback)), - mediums_(mediums) {} + bluetooth_radio_(*bluetooth_radio), + bluetooth_medium_(*bluetooth_medium) {} // Called by BWU target. Retrieves a new medium info from incoming message, // and establishes connection over BT using this info. @@ -85,8 +90,10 @@ BluetoothBwuHandler::CreateUpgradedEndpointChannel( OperationResultCode::CONNECTIVITY_BLUETOOTH_DEVICE_OBTAIN_FAILURE)}; } - ErrorOr socket_result = bluetooth_medium_.Connect( - device, service_id, client->GetCancellationFlag(endpoint_id)); + std::shared_ptr cancellation_flag = + client->GetCancellationFlag(endpoint_id); + ErrorOr socket_result = + bluetooth_medium_.Connect(device, service_name, cancellation_flag.get()); if (socket_result.has_error()) { LOG(ERROR) << "BluetoothBwuHandler failed to connect to the Bluetooth device (" @@ -116,7 +123,7 @@ BluetoothBwuHandler::CreateUpgradedEndpointChannel( return {std::move(channel)}; } -ByteArray BluetoothBwuHandler::HandleInitializeUpgradedMediumForEndpoint( +std::string BluetoothBwuHandler::HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, const std::string& endpoint_id) { MacAddress mac_address = bluetooth_medium_.GetAddress(); diff --git a/connections/implementation/bluetooth_bwu_handler.h b/connections/implementation/mediums/bluetooth_bwu_handler.h similarity index 84% rename from connections/implementation/bluetooth_bwu_handler.h rename to connections/implementation/mediums/bluetooth_bwu_handler.h index ae409310..b1902362 100644 --- a/connections/implementation/bluetooth_bwu_handler.h +++ b/connections/implementation/mediums/bluetooth_bwu_handler.h @@ -12,21 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CORE_INTERNAL_BLUETOOTH_BWU_HANDLER_H_ -#define CORE_INTERNAL_BLUETOOTH_BWU_HANDLER_H_ +#ifndef CORE_INTERNAL_MEDIUMS_BLUETOOTH_BWU_HANDLER_H_ +#define CORE_INTERNAL_MEDIUMS_BLUETOOTH_BWU_HANDLER_H_ #include #include +#include "absl/base/nullability.h" #include "connections/implementation/base_bwu_handler.h" +#include "connections/implementation/bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" #include "connections/implementation/mediums/bluetooth_classic.h" #include "connections/implementation/mediums/bluetooth_radio.h" -#include "connections/implementation/mediums/mediums.h" #include "connections/medium_selector.h" #include "internal/platform/bluetooth_classic.h" -#include "internal/platform/byte_array.h" #include "internal/platform/expected.h" namespace nearby { @@ -36,8 +36,9 @@ namespace connections { // per-Medium-specific operations needed to upgrade an EndpointChannel. class BluetoothBwuHandler : public BaseBwuHandler { public: - explicit BluetoothBwuHandler( - Mediums& mediums, + BluetoothBwuHandler( + BluetoothRadio* absl_nonnull bluetooth_radio, + BluetoothClassic* absl_nonnull bluetooth_medium, IncomingConnectionCallback incoming_connection_callback); private: @@ -66,7 +67,7 @@ class BluetoothBwuHandler : public BaseBwuHandler { const std::string& endpoint_id) final {} // BaseBwuHandler implementation: - ByteArray HandleInitializeUpgradedMediumForEndpoint( + std::string HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, const std::string& endpoint_id) final; void HandleRevertInitiatorStateForService( @@ -76,12 +77,11 @@ class BluetoothBwuHandler : public BaseBwuHandler { const std::string& upgrade_service_id, BluetoothSocket socket); - Mediums& mediums_; - BluetoothRadio& bluetooth_radio_{mediums_.GetBluetoothRadio()}; - BluetoothClassic& bluetooth_medium_{mediums_.GetBluetoothClassic()}; + BluetoothRadio& bluetooth_radio_; + BluetoothClassic& bluetooth_medium_; }; } // namespace connections } // namespace nearby -#endif // CORE_INTERNAL_BLUETOOTH_BWU_HANDLER_H_ +#endif // CORE_INTERNAL_MEDIUMS_BLUETOOTH_BWU_HANDLER_H_ diff --git a/connections/implementation/bluetooth_bwu_test.cc b/connections/implementation/mediums/bluetooth_bwu_handler_test.cc similarity index 69% rename from connections/implementation/bluetooth_bwu_test.cc rename to connections/implementation/mediums/bluetooth_bwu_handler_test.cc index a60f8333..55c6c021 100644 --- a/connections/implementation/bluetooth_bwu_test.cc +++ b/connections/implementation/mediums/bluetooth_bwu_handler_test.cc @@ -12,18 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "connections/implementation/mediums/bluetooth_bwu_handler.h" + #include +#include #include #include "gtest/gtest.h" #include "absl/time/time.h" -#include "connections/implementation/bluetooth_bwu_handler.h" #include "connections/implementation/bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" #include "connections/implementation/mediums/mediums.h" #include "connections/implementation/offline_frames.h" -#include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/exception.h" #include "internal/platform/expected.h" @@ -43,17 +44,27 @@ constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000); class BluetoothBwuTest : public testing::Test { protected: - BluetoothBwuTest() { env_.Start(); } - ~BluetoothBwuTest() override { env_.Stop(); } + BluetoothBwuTest() { + original_flags_ = FeatureFlags::GetInstance().GetFlags(); + env_.Start(); + } + ~BluetoothBwuTest() override { + FeatureFlags::GetMutableInstanceForTesting().SetFlags(original_flags_); + env_.Stop(); + } + + void RunSTACreateEndpointChannelTest(bool enable_cancellation); MediumEnvironment& env_{MediumEnvironment::Instance()}; + FeatureFlags::Flags original_flags_; }; TEST_F(BluetoothBwuTest, CanCreateBwuHandler) { ClientProxy client; Mediums mediums; - auto handler = std::make_unique(mediums, nullptr); + auto handler = std::make_unique( + &mediums.GetBluetoothRadio(), &mediums.GetBluetoothClassic(), nullptr); handler->InitializeUpgradedMediumForEndpoint(&client, /*service_id=*/"B", /*endpoint_id=*/"2"); @@ -62,7 +73,12 @@ TEST_F(BluetoothBwuTest, CanCreateBwuHandler) { handler.reset(); } -TEST_F(BluetoothBwuTest, SoftAPBWUInit_STACreateEndpointChannel) { +void BluetoothBwuTest::RunSTACreateEndpointChannelTest( + bool enable_cancellation) { + FeatureFlags::Flags flags = original_flags_; + flags.enable_cancellation_flag = enable_cancellation; + FeatureFlags::GetMutableInstanceForTesting().SetFlags(flags); + CountDownLatch start_latch(1); CountDownLatch accept_latch(1); CountDownLatch end_latch(1); @@ -71,10 +87,14 @@ TEST_F(BluetoothBwuTest, SoftAPBWUInit_STACreateEndpointChannel) { Mediums mediums_1, mediums_2; ExceptionOr upgrade_frame; + EXPECT_TRUE(mediums_1.GetBluetoothRadio().Enable()); + EXPECT_TRUE(mediums_2.GetBluetoothRadio().Enable()); + auto handler_1 = std::make_unique( - mediums_1, [&](ClientProxy* client, - std::unique_ptr - mutable_connection) { + &mediums_1.GetBluetoothRadio(), &mediums_1.GetBluetoothClassic(), + [&](ClientProxy* client, + std::unique_ptr + mutable_connection) { LOG(WARNING) << "Server socket connection accept call back"; accept_latch.CountDown(); EXPECT_TRUE(end_latch.Await(kWaitDuration).result()); @@ -83,11 +103,11 @@ TEST_F(BluetoothBwuTest, SoftAPBWUInit_STACreateEndpointChannel) { // client_1 works as Bluetooth Server Device SingleThreadExecutor server_executor; server_executor.Execute([&]() { - ByteArray upgrade_path_available_frame = + std::string upgrade_path_available_frame = handler_1->InitializeUpgradedMediumForEndpoint(&client_1, /*service_id=*/"A", /*endpoint_id=*/"1"); - EXPECT_FALSE(upgrade_path_available_frame.Empty()); + EXPECT_FALSE(upgrade_path_available_frame.empty()); upgrade_frame = parser::FromBytes(upgrade_path_available_frame); start_latch.CountDown(); @@ -98,7 +118,9 @@ TEST_F(BluetoothBwuTest, SoftAPBWUInit_STACreateEndpointChannel) { // Wait till client_1 started as Bluetooth and then connect to it EXPECT_TRUE(start_latch.Await(kWaitDuration).result()); std::unique_ptr handler_2 = - std::make_unique(mediums_2, nullptr); + std::make_unique( + &mediums_2.GetBluetoothRadio(), &mediums_2.GetBluetoothClassic(), + nullptr); client_executor.Execute([&]() { auto bwu_frame = @@ -108,7 +130,7 @@ TEST_F(BluetoothBwuTest, SoftAPBWUInit_STACreateEndpointChannel) { handler_2->CreateUpgradedEndpointChannel(&client_2, /*service_id=*/"A", /*endpoint_id=*/"1", bwu_frame.upgrade_path_info()); - if (!FeatureFlags::GetInstance().GetFlags().enable_cancellation_flag) { + if (!enable_cancellation) { ASSERT_TRUE(result.has_value()); std::unique_ptr new_channel = std::move(result.value()); EXPECT_TRUE(accept_latch.Await(kWaitDuration).result()); @@ -117,9 +139,9 @@ TEST_F(BluetoothBwuTest, SoftAPBWUInit_STACreateEndpointChannel) { } else { EXPECT_FALSE(result.has_value()); EXPECT_TRUE(result.has_error()); - EXPECT_EQ( - result.error().operation_result_code(), - OperationResultCode::CONNECTIVITY_BLUETOOTH_DEVICE_OBTAIN_FAILURE); + EXPECT_EQ(result.error().operation_result_code(), + OperationResultCode:: + CLIENT_CANCELLATION_CANCEL_BT_OUTGOING_CONNECTION); accept_latch.CountDown(); } EXPECT_TRUE(mediums_2.GetBluetoothClassic().GetAddress().IsSet()); @@ -131,5 +153,15 @@ TEST_F(BluetoothBwuTest, SoftAPBWUInit_STACreateEndpointChannel) { EXPECT_TRUE(end_latch.Await(kWaitDuration).result()); } +TEST_F(BluetoothBwuTest, + SoftAPBWUInit_STACreateEndpointChannel_WithCancellation) { + RunSTACreateEndpointChannelTest(true); +} + +TEST_F(BluetoothBwuTest, + SoftAPBWUInit_STACreateEndpointChannel_NoCancellation) { + RunSTACreateEndpointChannelTest(false); +} + } // namespace connections } // namespace nearby diff --git a/connections/implementation/mediums/bluetooth_classic.cc b/connections/implementation/mediums/bluetooth_classic.cc index 2ee55c30..d0dc386d 100644 --- a/connections/implementation/mediums/bluetooth_classic.cc +++ b/connections/implementation/mediums/bluetooth_classic.cc @@ -18,11 +18,9 @@ #include #include -#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "connections/implementation/bwu_handler.h" #include "connections/implementation/mediums/bluetooth_radio.h" -#include "connections/implementation/mediums/multiplex/multiplex_socket.h" -#include "connections/medium_selector.h" -#include "internal/flags/nearby_flags.h" +#include "connections/implementation/mediums/bluetooth_bwu_handler.h" #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/bluetooth_classic.h" #include "internal/platform/cancellation_flag.h" @@ -30,8 +28,6 @@ #include "internal/platform/logging.h" #include "internal/platform/mac_address.h" #include "internal/platform/mutex_lock.h" -#include "internal/platform/socket.h" -#include "internal/platform/types.h" #include "internal/platform/uuid.h" namespace nearby { @@ -54,8 +50,6 @@ std::string ScanModeToString(BluetoothAdapter::ScanMode mode) { } } // namespace -using MultiplexSocket = mediums::multiplex::MultiplexSocket; - BluetoothClassic::BluetoothClassic(BluetoothRadio& radio) : BluetoothClassic(radio, std::make_unique( radio.GetBluetoothAdapter())) {} @@ -64,15 +58,7 @@ BluetoothClassic::BluetoothClassic( BluetoothRadio& radio, std::unique_ptr medium) : radio_(radio), adapter_(radio_.GetBluetoothAdapter()), - medium_(std::move(medium)) { - is_multiplex_enabled_ = - NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplex) && - NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexBluetooth); -} + medium_(std::move(medium)) {} BluetoothClassic::~BluetoothClassic() { // Destructor is not taking locks, but methods it is calling are. @@ -82,20 +68,6 @@ BluetoothClassic::~BluetoothClassic() { } TurnOffDiscoverability(); - { - MutexLock lock(&mutex_); - LOG(INFO) << "Closing multiplex sockets for " << multiplex_sockets_.size() - << " devices"; - if (is_multiplex_enabled_) { - for (auto& [bt_mac, multiplex_socket] : multiplex_sockets_) { - LOG(INFO) << "Closing multiplex sockets for " - << GetRemoteDevice(bt_mac).GetName(); - multiplex_socket->Shutdown(); - } - } - multiplex_sockets_.clear(); - } - // All the AcceptLoopRunnable objects in here should already have gotten an // opportunity to shut themselves down cleanly in the calls to // StopAcceptingConnections() above. @@ -384,24 +356,13 @@ ErrorOr BluetoothClassic::StartAcceptingConnections( auto owned_socket = server_sockets_.emplace(service_id, std::move(socket)).first->second; - if (is_multiplex_enabled_) { - MultiplexSocket::ListenForIncomingConnection( - service_id, Medium::BLUETOOTH, - [&callback](const std::string& listening_service_id, - MediumSocket* virtual_socket) mutable { - if (callback) { - callback(listening_service_id, - *(down_cast(virtual_socket))); - } - }); - } // Start the accept loop on a dedicated thread - this stays alive and // listening for new incoming connections until StopAcceptingConnections() // is invoked. accept_loops_runner_.Execute("bt-accept", [callback = std::move(callback), server_socket = std::move(owned_socket), - service_id, this]() mutable { + service_id]() mutable { while (true) { BluetoothSocket client_socket = server_socket.Accept(); if (!client_socket.IsValid()) { @@ -411,34 +372,6 @@ ErrorOr BluetoothClassic::StartAcceptingConnections( } LOG(INFO) << "Accepted connection for " << service_id; bool callback_called = false; - { - MutexLock lock(&mutex_); - if (is_multiplex_enabled_) { - BluetoothSocket client_socket_bak = client_socket; - auto physical_socket_ptr = - std::make_shared(client_socket_bak); - MultiplexSocket* multiplex_socket = - MultiplexSocket::CreateIncomingSocket(physical_socket_ptr, - service_id, 0); - - if (multiplex_socket != nullptr && - multiplex_socket->GetVirtualSocket(service_id)) { - multiplex_sockets_.emplace( - client_socket.GetRemoteDevice().GetAddress(), - multiplex_socket); - MultiplexSocket::StopListeningForIncomingConnection( - service_id, Medium::BLUETOOTH); - LOG(INFO) << "Multiplex virtaul socket created for " - << client_socket.GetRemoteDevice().GetName(); - if (callback) { - callback(service_id, - *(down_cast( - multiplex_socket->GetVirtualSocket(service_id)))); - callback_called = true; - } - } - } - } if (callback && !callback_called) { LOG(INFO) << "Call back triggered for physical socket."; callback(service_id, std::move(client_socket)); @@ -475,10 +408,6 @@ bool BluetoothClassic::StopAcceptingConnections(const std::string& service_id) { << " because it was never started."; return false; } - if (is_multiplex_enabled_) { - MultiplexSocket::StopListeningForIncomingConnection(service_id, - Medium::BLUETOOTH); - } // Closing the BluetoothServerSocket will kick off the suicide of the thread // in accept_loops_thread_pool_ that blocks on @@ -507,29 +436,6 @@ bool BluetoothClassic::StopAcceptingConnections(const std::string& service_id) { ErrorOr BluetoothClassic::Connect( BluetoothDevice& bluetooth_device, const std::string& service_id, CancellationFlag* cancellation_flag) { - { - MutexLock lock(&mutex_); - if (is_multiplex_enabled_) { - LOG(INFO) << "multiplex_sockets_ size:" << multiplex_sockets_.size(); - auto it = multiplex_sockets_.find(bluetooth_device.GetAddress()); - if (it != multiplex_sockets_.end()) { - MultiplexSocket* multiplex_socket = it->second; - if (multiplex_socket->IsEnabled()) { - auto* virtual_socket = - multiplex_socket->EstablishVirtualSocket(service_id); - // Should not happen. - auto* bluetooth_socket = down_cast(virtual_socket); - if (bluetooth_socket == nullptr) { - LOG(INFO) << "Failed to cast to BluetoothSocket for " << service_id - << " with " << bluetooth_device.GetName(); - return {Error(OperationResultCode:: - NEARBY_BT_VIRTUAL_SOCKET_CREATION_FAILURE)}; - } - return *bluetooth_socket; - } - } - } - } service_id_to_connect_attempts_count_map_[service_id] = 1; while (service_id_to_connect_attempts_count_map_[service_id] <= kConnectAttemptsLimit) { @@ -605,29 +511,6 @@ ErrorOr BluetoothClassic::AttemptToConnect( return {Error( OperationResultCode::CONNECTIVITY_BT_CLIENT_SOCKET_CREATION_FAILURE)}; } - - if (is_multiplex_enabled_) { - // New MultiplexSocket but default disabled, should be enabled after - // negotiated - auto physical_socket_ptr = std::make_shared(socket); - MultiplexSocket* multiplex_socket = MultiplexSocket::CreateOutgoingSocket( - std::move(physical_socket_ptr), service_id); - - auto* virtual_socket = multiplex_socket->GetVirtualSocket(service_id); - // Should not happen. - auto* bluetooth_socket = down_cast(virtual_socket); - if (bluetooth_socket == nullptr) { - LOG(INFO) << "Failed to cast to BluetoothSocket for " << service_id - << " with " << bluetooth_device.GetName(); - return {Error( - OperationResultCode::NEARBY_BT_VIRTUAL_SOCKET_CREATION_FAILURE)}; - } - LOG(INFO) << "Multiplex socket created for " << bluetooth_device.GetName(); - multiplex_sockets_.emplace(bluetooth_device.GetAddress(), - multiplex_socket); - return *bluetooth_socket; - } - return socket; } @@ -684,5 +567,12 @@ std::string BluetoothClassic::GenerateUuidFromString(const std::string& data) { return std::string(Uuid(data)); } +std::unique_ptr BluetoothClassic::CreateBwuHandler( + BwuHandler::IncomingConnectionCallback incoming_connection_callback) { + MutexLock lock(&mutex_); + return std::make_unique( + &radio_, this, std::move(incoming_connection_callback)); +} + } // namespace connections } // namespace nearby diff --git a/connections/implementation/mediums/bluetooth_classic.h b/connections/implementation/mediums/bluetooth_classic.h index d4221a1c..63ae1e68 100644 --- a/connections/implementation/mediums/bluetooth_classic.h +++ b/connections/implementation/mediums/bluetooth_classic.h @@ -22,10 +22,8 @@ #include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" #include "absl/functional/any_invocable.h" -#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "connections/implementation/bwu_handler.h" #include "connections/implementation/mediums/bluetooth_radio.h" -#include "connections/implementation/mediums/multiplex/multiplex_socket.h" -#include "internal/flags/nearby_flags.h" #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/bluetooth_classic.h" #include "internal/platform/cancellation_flag.h" @@ -129,6 +127,9 @@ class BluetoothClassic { bool IsDiscovering(const std::string& serviceId) const ABSL_LOCKS_EXCLUDED(mutex_); + std::unique_ptr CreateBwuHandler( + BwuHandler::IncomingConnectionCallback incoming_connection_callback); + protected: // Use for unit tests only to inject a BluetoothClassicMedium. BluetoothClassic(BluetoothRadio& radio, @@ -234,14 +235,6 @@ class BluetoothClassic { mutable Mutex discovery_callbacks_mutex_; absl::flat_hash_map discovery_callbacks_ ABSL_GUARDED_BY(discovery_callbacks_mutex_); - - // Whether the multiplex feature is enabled. - bool is_multiplex_enabled_ = NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature::kEnableMultiplex); - - // A map of Bluetooth MacAddress -> MultiplexSocket. - absl::flat_hash_map - multiplex_sockets_ ABSL_GUARDED_BY(mutex_); }; } // namespace connections diff --git a/connections/implementation/bluetooth_endpoint_channel.cc b/connections/implementation/mediums/bluetooth_endpoint_channel.cc similarity index 97% rename from connections/implementation/bluetooth_endpoint_channel.cc rename to connections/implementation/mediums/bluetooth_endpoint_channel.cc index 73d09d9c..643a65c7 100644 --- a/connections/implementation/bluetooth_endpoint_channel.cc +++ b/connections/implementation/mediums/bluetooth_endpoint_channel.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/bluetooth_endpoint_channel.h" +#include "connections/implementation/mediums/bluetooth_endpoint_channel.h" #include #include diff --git a/connections/implementation/bluetooth_endpoint_channel.h b/connections/implementation/mediums/bluetooth_endpoint_channel.h similarity index 88% rename from connections/implementation/bluetooth_endpoint_channel.h rename to connections/implementation/mediums/bluetooth_endpoint_channel.h index b2176388..a28a7cdf 100644 --- a/connections/implementation/bluetooth_endpoint_channel.h +++ b/connections/implementation/mediums/bluetooth_endpoint_channel.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CORE_INTERNAL_BLUETOOTH_ENDPOINT_CHANNEL_H_ -#define CORE_INTERNAL_BLUETOOTH_ENDPOINT_CHANNEL_H_ +#ifndef CORE_INTERNAL_MEDIUMS_BLUETOOTH_ENDPOINT_CHANNEL_H_ +#define CORE_INTERNAL_MEDIUMS_BLUETOOTH_ENDPOINT_CHANNEL_H_ #include @@ -46,4 +46,4 @@ class BluetoothEndpointChannel final : public BaseEndpointChannel { } // namespace connections } // namespace nearby -#endif // CORE_INTERNAL_BLUETOOTH_ENDPOINT_CHANNEL_H_ +#endif // CORE_INTERNAL_MEDIUMS_BLUETOOTH_ENDPOINT_CHANNEL_H_ diff --git a/connections/implementation/mediums/mediums.cc b/connections/implementation/mediums/mediums.cc index 8dd6dccd..4e0ff7bf 100644 --- a/connections/implementation/mediums/mediums.cc +++ b/connections/implementation/mediums/mediums.cc @@ -14,10 +14,15 @@ #include "connections/implementation/mediums/mediums.h" +#include + #include "connections/implementation/mediums/awdl.h" #include "connections/implementation/mediums/ble.h" #include "connections/implementation/mediums/bluetooth_classic.h" #include "connections/implementation/mediums/bluetooth_radio.h" +#ifndef NO_WEBRTC +#include "connections/implementation/mediums/webrtc/webrtc_impl.h" +#endif #include "connections/implementation/mediums/webrtc.h" #include "connections/implementation/mediums/wifi.h" #include "connections/implementation/mediums/wifi_direct.h" @@ -27,6 +32,14 @@ namespace nearby { namespace connections { +Mediums::Mediums() { +#ifndef NO_WEBRTC + webrtc_ = std::make_unique(); +#else + webrtc_ = std::make_unique(); +#endif +} + BluetoothRadio& Mediums::GetBluetoothRadio() { return bluetooth_radio_; } BluetoothClassic& Mediums::GetBluetoothClassic() { return bluetooth_classic_; } @@ -41,7 +54,7 @@ WifiHotspot& Mediums::GetWifiHotspot() { return wifi_hotspot_; } WifiDirect& Mediums::GetWifiDirect() { return wifi_direct_; } -mediums::WebRtc& Mediums::GetWebRtc() { return webrtc_; } +mediums::WebRtc& Mediums::GetWebRtc() { return *webrtc_; } Awdl& Mediums::GetAwdl() { return awdl_; } diff --git a/connections/implementation/mediums/mediums.h b/connections/implementation/mediums/mediums.h index 9cacfc31..bc125964 100644 --- a/connections/implementation/mediums/mediums.h +++ b/connections/implementation/mediums/mediums.h @@ -15,15 +15,13 @@ #ifndef CORE_INTERNAL_MEDIUMS_MEDIUMS_H_ #define CORE_INTERNAL_MEDIUMS_MEDIUMS_H_ +#include + #include "connections/implementation/mediums/awdl.h" #include "connections/implementation/mediums/ble.h" #include "connections/implementation/mediums/bluetooth_classic.h" #include "connections/implementation/mediums/bluetooth_radio.h" -#ifdef NO_WEBRTC -#include "connections/implementation/mediums/webrtc_stub.h" -#else #include "connections/implementation/mediums/webrtc.h" -#endif #include "connections/implementation/mediums/wifi.h" #include "connections/implementation/mediums/wifi_direct.h" #include "connections/implementation/mediums/wifi_hotspot.h" @@ -35,7 +33,7 @@ namespace connections { // Facilitates convenient and reliable usage of various wireless mediums. class Mediums { public: - Mediums() = default; + Mediums(); ~Mediums() = default; // Returns a handle to the Bluetooth radio. @@ -81,7 +79,7 @@ class Mediums { WifiLan wifi_lan_; WifiHotspot wifi_hotspot_; WifiDirect wifi_direct_; - mediums::WebRtc webrtc_; + std::unique_ptr webrtc_; Awdl awdl_; }; diff --git a/connections/implementation/mediums/multiplex/BUILD b/connections/implementation/mediums/multiplex/BUILD deleted file mode 100644 index fc1a59ff..00000000 --- a/connections/implementation/mediums/multiplex/BUILD +++ /dev/null @@ -1,72 +0,0 @@ -load("@rules_cc//cc:cc_library.bzl", "cc_library") -load("@rules_cc//cc:cc_test.bzl", "cc_test") - -# Copyright 2024 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. -licenses(["notice"]) - -cc_library( - name = "multiplex", - srcs = [ - "multiplex_frames.cc", - "multiplex_output_stream.cc", - "multiplex_socket.cc", - ], - hdrs = [ - "multiplex_frames.h", - "multiplex_output_stream.h", - "multiplex_socket.h", - ], - visibility = [ - "//connections/implementation:__subpackages__", - ], - deps = [ - "//connections:core_types", - "//connections/implementation/mediums:utils", - "//internal/platform:base", - "//internal/platform:logging", - "//internal/platform:types", - "//proto:connections_enums_cc_proto", - "//proto/mediums:multiplex_frames_cc_proto", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - ], -) - -cc_test( - name = "multiplex_test", - srcs = [ - "multiplex_frames_test.cc", - "multiplex_output_stream_test.cc", - "multiplex_socket_test.cc", - ], - deps = [ - ":multiplex", - "//connections/implementation:internal", - "//internal/platform:base", - "//internal/platform:logging", - "//internal/platform:types", - "//internal/platform/implementation/g3", # buildcleaner: keep - "//proto:connections_enums_cc_proto", - "//proto/mediums:multiplex_frames_cc_proto", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/strings:string_view", - "@com_google_absl//absl/time", - "@com_google_googletest//:gtest_main", - ], -) diff --git a/connections/implementation/mediums/multiplex/multiplex_frames.cc b/connections/implementation/mediums/multiplex/multiplex_frames.cc deleted file mode 100644 index 3eea2aa1..00000000 --- a/connections/implementation/mediums/multiplex/multiplex_frames.cc +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2024 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/multiplex/multiplex_frames.h" - -#include -#include - -#include "absl/strings/string_view.h" -#include "connections/implementation/mediums/utils.h" -#include "internal/platform/base64_utils.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/exception.h" -#include "internal/platform/logging.h" - -namespace nearby { -namespace connections { -namespace mediums { -namespace multiplex { - -using ::location::nearby::mediums::ConnectionResponseFrame; -using ::location::nearby::mediums::MultiplexControlFrame; -using ::location::nearby::mediums::MultiplexFrame; - -ByteArray GenerateServiceIdHash(const std::string& service_id) { - return Utils::Sha256Hash(service_id, kServiceIdHashLength); -} - -ByteArray GenerateServiceIdHashWithSalt(const std::string& service_id, - std::string salt) { - if (salt.empty()) { - return GenerateServiceIdHash(service_id); - } - - return Utils::Sha256Hash(service_id + salt, kServiceIdHashLength); -} - -std::string GenerateServiceIdHashKey(const ByteArray& service_id_hash) { - return Base64Utils::Encode(service_id_hash); -} - -std::string GenerateServiceIdHashKey(const std::string& service_id) { - return GenerateServiceIdHashKey(GenerateServiceIdHash(service_id)); -} - -std::string GenerateServiceIdHashKeyWithSalt(const std::string& service_id, - std::string salt) { - return GenerateServiceIdHashKey( - GenerateServiceIdHashWithSalt(service_id, salt)); -} - -ByteArray ToBytes(MultiplexFrame&& frame) { - ByteArray bytes(frame.ByteSizeLong()); - frame.SerializeToArray(bytes.data(), bytes.size()); - return bytes; -} - -ByteArray ForConnectionRequest(const std::string& service_id, - const std::string& service_id_hash_salt) { - MultiplexFrame frame; - - frame.set_frame_type(MultiplexFrame::CONTROL_FRAME); - auto* header = frame.mutable_header(); - header->set_salted_service_id_hash(std::string( - GenerateServiceIdHashWithSalt(service_id, service_id_hash_salt))); - header->set_service_id_hash_salt(service_id_hash_salt); - - auto* control_frame = frame.mutable_control_frame(); - control_frame->set_control_frame_type( - MultiplexControlFrame::CONNECTION_REQUEST); - - return ToBytes(std::move(frame)); -} - -ByteArray ForConnectionResponse( - const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt, - ConnectionResponseFrame::ConnectionResponseCode response_code) { - MultiplexFrame frame; - - frame.set_frame_type(MultiplexFrame::CONTROL_FRAME); - auto* header = frame.mutable_header(); - header->set_salted_service_id_hash(std::string(salted_service_id_hash)); - header->set_service_id_hash_salt(service_id_hash_salt); - - auto* control_frame = frame.mutable_control_frame(); - control_frame->set_control_frame_type( - MultiplexControlFrame::CONNECTION_RESPONSE); - - auto* response_frame = control_frame->mutable_connection_response_frame(); - response_frame->set_connection_response_code(response_code); - - return ToBytes(std::move(frame)); -} - -ByteArray ForDisconnection(const std::string& service_id, - const std::string& service_id_hash_salt) { - MultiplexFrame frame; - - frame.set_frame_type(MultiplexFrame::CONTROL_FRAME); - auto* header = frame.mutable_header(); - header->set_salted_service_id_hash(std::string( - GenerateServiceIdHashWithSalt(service_id, service_id_hash_salt))); - header->set_service_id_hash_salt(service_id_hash_salt); - - auto* control_frame = frame.mutable_control_frame(); - control_frame->set_control_frame_type(MultiplexControlFrame::DISCONNECTION); - - return ToBytes(std::move(frame)); -} - -ByteArray ForData(const std::string& service_id, - const std::string& service_id_hash_salt, - bool should_pass_salt, absl::string_view data) { - MultiplexFrame frame; - - frame.set_frame_type(MultiplexFrame::DATA_FRAME); - auto* header = frame.mutable_header(); - header->set_salted_service_id_hash(std::string( - GenerateServiceIdHashWithSalt(service_id, service_id_hash_salt))); - if (should_pass_salt) { - header->set_service_id_hash_salt(service_id_hash_salt); - } - - auto* data_frame = frame.mutable_data_frame(); - data_frame->set_data(data); - - return ToBytes(std::move(frame)); -} - -ExceptionOr FromBytes(const ByteArray& multiplex_frame_bytes) { - MultiplexFrame frame; - - if (frame.ParseFromString(std::string(multiplex_frame_bytes))) { - if (!IsValid(frame)) { - return ExceptionOr(Exception::kInvalidProtocolBuffer); - } - return ExceptionOr(std::move(frame)); - } else { - return ExceptionOr(Exception::kInvalidProtocolBuffer); - } -} - -bool IsControlFrame(MultiplexFrame::MultiplexFrameType frame_type) { - return frame_type == MultiplexFrame::CONTROL_FRAME; -} - -bool IsDataFrame(MultiplexFrame::MultiplexFrameType frame_type) { - return frame_type == MultiplexFrame::DATA_FRAME; -} - -bool IsValid(const MultiplexFrame& frame) { - switch (frame.frame_type()) { - case MultiplexFrame::CONTROL_FRAME: - return IsValidControlFrame(frame); - case MultiplexFrame::DATA_FRAME: - return IsValidDataFrame(frame); - default: - return false; - } -} - -bool IsValidControlFrame(const MultiplexFrame& frame) { - if (!frame.has_control_frame()) { - return false; - } - - switch (frame.control_frame().control_frame_type()) { - case MultiplexControlFrame::CONNECTION_REQUEST: - case MultiplexControlFrame::CONNECTION_RESPONSE: - case MultiplexControlFrame::DISCONNECTION: - if (frame.header().salted_service_id_hash().size() == - kServiceIdHashLength) { - return true; - } - break; - default: - break; - } - - return false; -} - -bool IsValidDataFrame(const MultiplexFrame& frame) { - return frame.has_data_frame() && - frame.header().salted_service_id_hash().size() == kServiceIdHashLength; -} - -bool IsMultiplexFrame(const ByteArray& data) { - ExceptionOr frame = FromBytes(data); - if (!frame.ok()) { - return false; - } else { - LOG(INFO) << "Checked data is a multiplex frame. Is Control ? " - << frame.result().has_control_frame() << ", is data ? " - << frame.result().has_data_frame(); - return true; - } -} - -} // namespace multiplex -} // namespace mediums -} // namespace connections -} // namespace nearby diff --git a/connections/implementation/mediums/multiplex/multiplex_frames.h b/connections/implementation/mediums/multiplex/multiplex_frames.h deleted file mode 100644 index 283697a4..00000000 --- a/connections/implementation/mediums/multiplex/multiplex_frames.h +++ /dev/null @@ -1,112 +0,0 @@ - -// Copyright 2024 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 CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_FRAMES_H_ -#define CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_FRAMES_H_ - -#include - -#include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/exception.h" -#include "proto/mediums/multiplex_frames.pb.h" - -namespace nearby { -namespace connections { -namespace mediums { -namespace multiplex { - -constexpr int kServiceIdHashLength = 4; - -// Serialize/Deserialize MultiplexFrame messages. - -// Parses incoming MultiplexFrame message. -// Returns MultiplexFrame if parser was able to understand it, or -// Exception::kInvalidProtocolBuffer, if parser failed. - -// Generates a service ID hash bytes with {@link -// MultiplexFrames#SERVICE_ID_HASH_LENGTH}. -ByteArray GenerateServiceIdHash(const std::string& service_id); - -// Generates a service ID hash bytes with salt and {@link -// MultiplexFrames#SERVICE_ID_HASH_LENGTH}. -ByteArray GenerateServiceIdHashWithSalt(const std::string& service_id, - std::string salt); - -// Converts the service Id hash bytes to a Base64 encoded string to be used as a -// {@code Map} key. -std::string GenerateServiceIdHashKey(const ByteArray& service_id_hash); - -// Generates a service ID hash bytes with {@link -// MultiplexFrames#SERVICE_ID_HASH_LENGTH} and converts to a Base64 encoded -// string to be used as a {@code Map} key. -std::string GenerateServiceIdHashKey(const std::string& service_id); - -// Generates a service ID hash bytes with salt and {@link -// MultiplexFrames#SERVICE_ID_HASH_LENGTH} and converts to a Base64 encoded -// string to be used as a { @code Map } key. -std::string GenerateServiceIdHashKeyWithSalt(const std::string& service_id, - std::string salt); - -// Build a MultiplexFrame Connection Request frame Bytes stream. -// @param service_id The service ID of the connection. -// @param service_id_hash_salt The salt used to generate the service ID hash. -ByteArray ForConnectionRequest(const std::string& service_id, - const std::string& service_id_hash_salt); - -// Build a MultiplexFrame Connection Response frame Bytes stream. -// @param salted_service_id_hash The salted service ID hash. -// @param service_id_hash_salt The salt used to generate the service ID hash. -// @param response_code The response code of the connection. -ByteArray ForConnectionResponse( - const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt, - location::nearby::mediums::ConnectionResponseFrame::ConnectionResponseCode - response_code); - -// Build a MultiplexFrame Disconnection frame Bytes stream. -// @param service_id The service ID of the connection. -// @param service_id_hash_salt The salt used to generate the service ID hash. -ByteArray ForDisconnection(const std::string& service_id, - const std::string& service_id_hash_salt); - -// Build a MultiplexFrame Data frame Bytes stream. -// @param service_id The service ID of the connection. -// @param service_id_hash_salt The salt used to generate the service ID hash. -// @param should_pass_salt Whether to pass the salt in the data frame. -// @param data The data to send. -ByteArray ForData(const std::string& service_id, - const std::string& service_id_hash_salt, - bool should_pass_salt, absl::string_view data); - -ExceptionOr FromBytes( - const ByteArray& multiplex_frame_bytes); - -bool IsControlFrame( - location::nearby::mediums::MultiplexFrame::MultiplexFrameType frame_type); -bool IsDataFrame( - location::nearby::mediums::MultiplexFrame::MultiplexFrameType frame_type); -bool IsValid(const location::nearby::mediums::MultiplexFrame& frame); -bool IsValidControlFrame( - const location::nearby::mediums::MultiplexFrame& frame); -bool IsValidDataFrame(const location::nearby::mediums::MultiplexFrame& frame); -bool IsMultiplexFrame(const ByteArray& data); - -} // namespace multiplex -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif // CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_FRAMES_H_ diff --git a/connections/implementation/mediums/multiplex/multiplex_frames_test.cc b/connections/implementation/mediums/multiplex/multiplex_frames_test.cc deleted file mode 100644 index 95a66803..00000000 --- a/connections/implementation/mediums/multiplex/multiplex_frames_test.cc +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2024 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/multiplex/multiplex_frames.h" -#include -#include - -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" - -namespace nearby { -namespace connections { -namespace mediums { -namespace multiplex { - -using ::location::nearby::mediums::MultiplexFrame; -using ::location::nearby::mediums::MultiplexControlFrame; -using ::location::nearby::mediums::ConnectionResponseFrame; - -constexpr absl::string_view kServiceId_1 = "serviceId_1"; -constexpr absl::string_view kServiceId_2 = "serviceId_2"; - -TEST(MultiplexFrameTest, FrameValidation) { - const ByteArray data("abcdefghijklmnopqrstuvwxyz"); - MultiplexFrame frame; - EXPECT_FALSE(IsValid(frame)); - frame.set_frame_type(MultiplexFrame::CONTROL_FRAME); - EXPECT_FALSE(IsValidControlFrame(frame)); - - auto* control_frame = frame.mutable_control_frame(); - control_frame->set_control_frame_type( - MultiplexControlFrame::UNKNOWN_CONTROL_FRAME_TYPE); - EXPECT_FALSE(IsValidControlFrame(frame)); - auto* header = frame.mutable_header(); - header->set_salted_service_id_hash(std::string( - GenerateServiceIdHashWithSalt(std::string(kServiceId_1), "1234"))); - control_frame->set_control_frame_type( - MultiplexControlFrame::CONNECTION_REQUEST); - EXPECT_TRUE(IsValidControlFrame(frame)); - EXPECT_TRUE(IsValid(frame)); - control_frame->set_control_frame_type( - MultiplexControlFrame::CONNECTION_RESPONSE); - EXPECT_TRUE(IsValidControlFrame(frame)); - EXPECT_TRUE(IsValid(frame)); - control_frame->set_control_frame_type( - MultiplexControlFrame::DISCONNECTION); - EXPECT_TRUE(IsValidControlFrame(frame)); - EXPECT_TRUE(IsValid(frame)); - - EXPECT_FALSE(IsValidDataFrame(frame)); - frame.set_frame_type(MultiplexFrame::DATA_FRAME); - auto* data_frame = frame.mutable_data_frame(); - data_frame->set_data(std::string(std::move(data))); - EXPECT_TRUE(IsValidDataFrame(frame)); - EXPECT_TRUE(IsValid(frame)); - - frame.set_frame_type(MultiplexFrame::UNKNOWN_FRAME_TYPE); - EXPECT_FALSE(IsValid(frame)); - - frame.set_frame_type(MultiplexFrame::DATA_FRAME); - auto serialized_bytes = ByteArray(frame.SerializeAsString()); - EXPECT_TRUE(IsMultiplexFrame(std::move(serialized_bytes))); - - EXPECT_TRUE(IsControlFrame(MultiplexFrame::CONTROL_FRAME)); - EXPECT_FALSE(IsControlFrame(MultiplexFrame::DATA_FRAME)); - EXPECT_TRUE(IsDataFrame(MultiplexFrame::DATA_FRAME)); - EXPECT_FALSE(IsDataFrame(MultiplexFrame::UNKNOWN_FRAME_TYPE)); -} - -TEST(MultiplexFrameTest, HashValidtion) { - auto service_id_hash_1 = GenerateServiceIdHash(std::string(kServiceId_1)); - EXPECT_EQ(service_id_hash_1.size(), kServiceIdHashLength); - auto service_id_hash_2 = GenerateServiceIdHash(std::string(kServiceId_2)); - EXPECT_NE(service_id_hash_1, service_id_hash_2); - - auto hash_key_1 = GenerateServiceIdHashKey(service_id_hash_1); - auto hash_key_2 = GenerateServiceIdHashKey(service_id_hash_2); - EXPECT_NE(hash_key_1, hash_key_2); - - auto service_id_hash_with_salt_1 = - GenerateServiceIdHashWithSalt(std::string(kServiceId_1), "1234"); - EXPECT_EQ(service_id_hash_with_salt_1.size(), kServiceIdHashLength); - auto service_id_hash_with_salt_2 = - GenerateServiceIdHashWithSalt(std::string(kServiceId_2), "1234"); - EXPECT_NE(service_id_hash_with_salt_1, service_id_hash_with_salt_2); - service_id_hash_with_salt_2 = - GenerateServiceIdHashWithSalt(std::string(kServiceId_1), "abcd"); - EXPECT_NE(service_id_hash_with_salt_1, service_id_hash_with_salt_2); - - auto hash_key_with_salt_1 = - GenerateServiceIdHashKeyWithSalt(std::string(kServiceId_1), "1234"); - auto hash_key_with_salt_2 = - GenerateServiceIdHashKeyWithSalt(std::string(kServiceId_2), "1234"); - EXPECT_NE(hash_key_with_salt_1, hash_key_with_salt_2); -} - -TEST(MultiplexFrameTest, CanGenerateConnectionRequest) { - ByteArray bytes = ForConnectionRequest(std::string(kServiceId_1), "1234"); - auto request = FromBytes(bytes); - ASSERT_TRUE(request.ok()); - auto frame = request.result(); - EXPECT_EQ(frame.control_frame().control_frame_type(), - MultiplexControlFrame::CONNECTION_REQUEST); - EXPECT_EQ(frame.header().salted_service_id_hash(), - std::string(GenerateServiceIdHashWithSalt(std::string(kServiceId_1), - "1234"))); -} - -TEST(MultiplexFrameTest, CanGenerateConnectionRespons) { - auto service_id_hash_with_salt_2 = - GenerateServiceIdHashWithSalt(std::string(kServiceId_2), "1234"); - ByteArray bytes = - ForConnectionResponse(service_id_hash_with_salt_2, "1234", - ConnectionResponseFrame::CONNECTION_ACCEPTED); - auto response = FromBytes(bytes); - ASSERT_TRUE(response.ok()); - auto frame = response.result(); - EXPECT_EQ(frame.control_frame().control_frame_type(), - MultiplexControlFrame::CONNECTION_RESPONSE); - EXPECT_EQ(frame.header().salted_service_id_hash(), - std::string(service_id_hash_with_salt_2)); - EXPECT_EQ(frame.control_frame() - .connection_response_frame() - .connection_response_code(), - ConnectionResponseFrame::CONNECTION_ACCEPTED); -} - -TEST(MultiplexFrameTest, CanGenerateDisconnection) { - ByteArray bytes = ForDisconnection(std::string(kServiceId_1), "1234"); - auto response = FromBytes(bytes); - ASSERT_TRUE(response.ok()); - auto frame = response.result(); - EXPECT_EQ(frame.control_frame().control_frame_type(), - MultiplexControlFrame::DISCONNECTION); - EXPECT_EQ(frame.header().salted_service_id_hash(), - std::string(GenerateServiceIdHashWithSalt(std::string(kServiceId_1), - "1234"))); -} - -TEST(MultiplexFrameTest, CanGenerateData) { - absl::string_view data = "abcdefghijklmnopqrstuvwxyz"; - ByteArray bytes = - ForData(std::string(kServiceId_1), "1234", true, data); - auto response = FromBytes(bytes); - ASSERT_TRUE(response.ok()); - auto frame = response.result(); - EXPECT_EQ(frame.frame_type(), MultiplexFrame::DATA_FRAME); - EXPECT_EQ(frame.header().salted_service_id_hash(), - std::string(GenerateServiceIdHashWithSalt(std::string(kServiceId_1), - "1234"))); - EXPECT_EQ(frame.data_frame().data(), - std::string("abcdefghijklmnopqrstuvwxyz")); -} - -} // namespace multiplex -} // namespace mediums -} // namespace connections -} // namespace nearby diff --git a/connections/implementation/mediums/multiplex/multiplex_output_stream.cc b/connections/implementation/mediums/multiplex/multiplex_output_stream.cc deleted file mode 100644 index 0cdc2d52..00000000 --- a/connections/implementation/mediums/multiplex/multiplex_output_stream.cc +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright 2024 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/multiplex/multiplex_output_stream.h" - -#include -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "connections/implementation/mediums/multiplex/multiplex_frames.h" -#include "internal/platform/array_blocking_queue.h" -#include "internal/platform/atomic_boolean.h" -#include "internal/platform/base64_utils.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/exception.h" -#include "internal/platform/feature_flags.h" -#include "internal/platform/future.h" -#include "internal/platform/logging.h" -#include "internal/platform/mutex_lock.h" -#include "internal/platform/output_stream.h" - -namespace nearby { -namespace connections { -namespace mediums { -namespace multiplex { -namespace { -using ::location::nearby::mediums::ConnectionResponseFrame; - -constexpr absl::string_view kFakeSalt = "RECEIVER_CONDIMENT"; -} // namespace - -// Implementation for class MultiplexOutputStream -MultiplexOutputStream::MultiplexOutputStream(OutputStream* physical_writer, - AtomicBoolean& is_enabled) - : is_enabled_(is_enabled), - physical_writer_(physical_writer), - multiplex_writer_{physical_writer} {} - -Exception MultiplexOutputStream::WaitForResult(const std::string& method_name, - Future* future) { - if (!future) { - LOG(INFO) << "No future to wait for; return with error."; - return {Exception::kFailed}; - } - LOG(INFO) << "Waiting for future to complete: " << method_name; - ExceptionOr result = - future->Get(FeatureFlags::GetInstance() - .GetFlags() - .mediums_frame_write_timeout_millis); - if (!result.ok()) { - LOG(INFO) << "Future:[" << method_name - << "] completed with exception:" << result.exception(); - return {Exception::kFailed}; - } - if (result.result()) { - LOG(INFO) << "Future:[" << method_name << "] completed with success."; - return {Exception::kSuccess}; - } - LOG(INFO) << "Future:[" << method_name << "] completed with failure."; - return {Exception::kFailed}; -} - -bool MultiplexOutputStream::WriteConnectionRequestFrame( - const std::string& service_id, const std::string& service_id_hash_salt) { - if (!is_enabled_.Get()) { - return false; - } - Future future; - multiplex_writer_.EnqueueToSend( - &future, ForConnectionRequest(service_id, service_id_hash_salt), - "MultiplexFrame::CONNECTION_REQUEST"); - if (WaitForResult("MultiplexFrame::CONNECTION_REQUEST", &future).Ok()) - return true; - return false; -} - -bool MultiplexOutputStream::WriteConnectionResponseFrame( - const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt, - ConnectionResponseFrame::ConnectionResponseCode response_code) { - if (!is_enabled_.Get()) { - return false; - } - Future future; - multiplex_writer_.EnqueueToSend( - &future, - ForConnectionResponse(salted_service_id_hash, service_id_hash_salt, - response_code), - "MultiplexFrame::CONNECTION_RESPONSE"); - if (WaitForResult("MultiplexFrame::CONNECTION_RESPONSE", &future).Ok()) - return true; - return false; -} - -bool MultiplexOutputStream::Close(const std::string& service_id) { - auto item = virtual_output_streams_.find(service_id); - if (item == virtual_output_streams_.end()) { - LOG(INFO) << "Don't need to close VirtualOutputStream(" << service_id - << ") because it's already gone."; - return false; - } - - item->second->Close(); - if (is_enabled_.Get()) { - Future future; - multiplex_writer_.EnqueueToSend( - &future, - ForDisconnection(service_id, item->second->GetServiceIdHashSalt()), - "MultiplexFrame::DISCONNECTION"); - WaitForResult("MultiplexFrame::DISCONNECTION", &future); - } - virtual_output_streams_.erase(service_id); - if (virtual_output_streams_.empty()) { - physical_writer_->Close(); - multiplex_writer_.Close(); - } - return true; -} - -void MultiplexOutputStream::CloseAll() { - for (auto& [service_id, virtual_output_stream] : virtual_output_streams_) { - if (is_enabled_.Get()) { - Future future; - multiplex_writer_.EnqueueToSend( - &future, - ForDisconnection(service_id, - virtual_output_stream->GetServiceIdHashSalt()), - "MultiplexFrame::DISCONNECTION"); - WaitForResult("MultiplexFrame::DISCONNECTION", &future); - } - virtual_output_stream->Close(); - } - virtual_output_streams_.clear(); - physical_writer_->Close(); - multiplex_writer_.Close(); -} - -OutputStream* -MultiplexOutputStream::CreateVirtualOutputStreamForFirstVirtualSocket( - const std::string& service_id, const std::string& service_id_hash_salt) { - return virtual_output_streams_ - .emplace(service_id, - std::make_unique( - service_id, service_id_hash_salt, physical_writer_, - multiplex_writer_, - VirtualOutputStreamType::kFirstVirtualSocket, *this)) - .first->second.get(); -} - -OutputStream* MultiplexOutputStream::CreateVirtualOutputStream( - const std::string& service_id, const std::string& service_id_hash_salt) { - return virtual_output_streams_ - .emplace(service_id, - std::make_unique( - service_id, service_id_hash_salt, physical_writer_, - multiplex_writer_, - VirtualOutputStreamType::kNormalVirtualSocket, *this)) - .first->second.get(); -} - -std::string MultiplexOutputStream::GetServiceIdHashSalt( - const std::string& service_id) { - auto item = virtual_output_streams_.find(service_id); - if (item != virtual_output_streams_.end()) { - return item->second->GetServiceIdHashSalt(); - } - return {}; -} - -void MultiplexOutputStream::Shutdown() { - physical_writer_->Close(); - multiplex_writer_.Close(); -} - -// Implementation for class MultiplexOutputStream::MultiplexWriter -MultiplexOutputStream::MultiplexWriter::MultiplexWriter( - OutputStream* physical_writer) - : physical_writer_(physical_writer) {} - -MultiplexOutputStream::MultiplexWriter::~MultiplexWriter() { - Close(); - physical_writer_ = nullptr; -} - -void MultiplexOutputStream::MultiplexWriter::EnqueueToSend( - Future* future, const ByteArray& data, - const std::string& frame_name) { - MutexLock lock(&writing_mutex_); - data_queue_.Put(EnqueuedFrame(future, data)); - - if (is_writing_) { - return; - } - is_writing_ = true; - is_writing_cond_.Notify(); - if (!is_write_loop_running_) { - is_write_loop_running_ = true; - writer_thread_.Execute("Start writing", [this] { StartWriting(); }); - } -} - -void MultiplexOutputStream::MultiplexWriter::StartWriting() { - LOG(INFO) << "Writing loop started."; - while (true) { - auto enqueued_frame = data_queue_.TryTake(); - if (enqueued_frame != std::nullopt) { - Write(enqueued_frame.value()); - continue; - } - { - MutexLock lock(&writing_mutex_); - if (data_queue_.Empty() && is_writing_ && !is_closed_) { - is_writing_ = false; - LOG(INFO) << "Waiting for data_queue_ has data."; - Exception wait_succeeded = is_writing_cond_.Wait(); - if (!wait_succeeded.Ok()) { - LOG(WARNING) << "Failure waiting to wait: " << wait_succeeded.value; - return; - } - } - if (is_closed_) { - LOG(INFO) << "Notify to close_writing_thread"; - MutexLock lock(&close_writing_thread_mutex_); - close_writing_thread_cond_.Notify(); - break; - } - } - } - LOG(INFO) << "Writing loop stopped."; -} - -void MultiplexOutputStream::MultiplexWriter::Write( - EnqueuedFrame& enqueued_frame) { - MutexLock lock(&writer_mutex_); - if (!Base64Utils::WriteInt(physical_writer_, enqueued_frame.data_.size()) - .Ok()) { - enqueued_frame.future_->SetException({Exception::kIo}); - return; - }; - if (!physical_writer_->Write(enqueued_frame.data_.AsStringView()).Ok()) { - enqueued_frame.future_->SetException({Exception::kIo}); - return; - }; - if (!physical_writer_->Flush().Ok()) { - enqueued_frame.future_->SetException({Exception::kIo}); - return; - }; - enqueued_frame.future_->Set(true); -} - -void MultiplexOutputStream::MultiplexWriter::Close() { - if (is_closed_) { - LOG(INFO) << "MultiplexWriter is already closed."; - return; - } - LOG(INFO) << "Stop writing loop and Shutdown writer thread."; - { - MutexLock lock(&writing_mutex_); - is_closed_ = true; - if (!is_write_loop_running_) { - writer_thread_.Shutdown(); - return; - } - is_write_loop_running_ = false; - is_writing_cond_.Notify(); - } - LOG(INFO) << "Wait to close_writing_thread"; - { - MutexLock lock(&close_writing_thread_mutex_); - close_writing_thread_cond_.Wait(absl::Milliseconds(20)); - LOG(INFO) << "Shutdown writer thread."; - writer_thread_.Shutdown(); - } -} - -MultiplexOutputStream::VirtualOutputStream::VirtualOutputStream( - std::string service_id, std::string service_id_hash_salt, - OutputStream* physical_writer, MultiplexWriter& multiplex_writer, - VirtualOutputStreamType virtual_output_stream_type, - MultiplexOutputStream& multiplex_output_stream) - : service_id_(service_id), - service_id_hash_salt_(service_id_hash_salt), - physical_writer_(physical_writer), - multiplex_writer_(multiplex_writer), - virtual_output_stream_type_(virtual_output_stream_type), - multiplex_output_stream_(multiplex_output_stream) {} - -Exception MultiplexOutputStream::VirtualOutputStream::Write( - absl::string_view data) { - if (is_closed_.Get()) { - LOG(WARNING) << "Failed to write data because the VirtualOutputStream for " - << service_id_ << " closed"; - return {Exception::kIo}; - } - if (multiplex_output_stream_.is_enabled_.Get()) { - bool should_pass_salt = false; - if (IsFirstVirtualOutputStream()) { - if (!first_frame_sent_for_first_virtual_output_stream_) { - first_frame_sent_for_first_virtual_output_stream_ = true; - should_pass_salt = true; - } - // Fixes b/290724590, b/290983930 which can't get the correct socket - // from the virtualSockets map. NS receiver side will pass 2 - // DATA_FRAMEs continuously to the remote sender side but originally - // impl will only consider the 1st one. Add below fix to handle 2nd - // frame which the salt is still fake one and change shouldPassSalt to - // true to let the remote handle correctly. - if ((service_id_hash_salt_ == kFakeSalt) && !should_pass_salt) { - should_pass_salt = true; - LOG(INFO) << "service_idHashSalt is still a fake one and " - "not changed yet; continue to pass salt."; - } - } - ByteArray data_frame = - ForData(service_id_, service_id_hash_salt_, should_pass_salt, data); - Future future; - multiplex_writer_.EnqueueToSend(&future, data_frame, - "MultiplexFrame::DATA_FRAME"); - return multiplex_output_stream_.WaitForResult("MultiplexFrame::DATA_FRAME", - &future); - } else { - if (!physical_writer_->Write(data).Ok()) { - return {Exception::kIo}; - }; - if (!physical_writer_->Flush().Ok()) { - return {Exception::kIo}; - }; - } - - return {Exception::kSuccess}; -} - -Exception MultiplexOutputStream::VirtualOutputStream::Flush() { - return {Exception::kSuccess}; -} - -Exception MultiplexOutputStream::VirtualOutputStream::Close() { - LOG(INFO) << "MultiplexOutputStream::VirtualOutputStream::Close"; - is_closed_.Set(true); - return {Exception::kSuccess}; -} - -} // namespace multiplex -} // namespace mediums -} // namespace connections -} // namespace nearby diff --git a/connections/implementation/mediums/multiplex/multiplex_output_stream.h b/connections/implementation/mediums/multiplex/multiplex_output_stream.h deleted file mode 100644 index 22067d71..00000000 --- a/connections/implementation/mediums/multiplex/multiplex_output_stream.h +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2024 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 CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_OUTPUT_STREAM_H_ -#define CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_OUTPUT_STREAM_H_ - -#include -#include - -#include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" -#include "absl/strings/string_view.h" -#include "internal/platform/array_blocking_queue.h" -#include "internal/platform/atomic_boolean.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/exception.h" -#include "internal/platform/feature_flags.h" -#include "internal/platform/future.h" -#include "internal/platform/mutex.h" -#include "internal/platform/output_stream.h" -#include "internal/platform/single_thread_executor.h" -#include "proto/mediums/multiplex_frames.pb.h" - -namespace nearby { -namespace connections { -namespace mediums { -namespace multiplex { -/** - * A helper class to send out the {@code MultiplexControlFrame} and the outgoing - * data from clients. It schedules control and data frames with priority below - * - *

{@link MultiplexControlFrameType#CONNECTION_REQUEST} and {@link - * MultiplexControlFrameType#CONNECTION_RESPONSE} have the highest priority - * - *

All {@link MultiplexDataFrame} has the medium priority. If there's - * multiple clients send data at the same time, should poll every client's - * outgoing data in sequence. For example, client A and B send data at the same - * time, the outgoing data sequence should like A-Frame-1, B-Frame-1, A-Frame-2, - * B-Frame-2,... - * - *

{@link MultiplexControlFrameType#DISCONNECTION} has the same priority with - * {@link MultiplexDataFrame} because the disconnect should not make the already - * enqueued data failed to send out, so put it in the same priority queue with - * the MultiplexDataFrame. - */ -class MultiplexOutputStream { - public: - enum class VirtualOutputStreamType { - // The type of virtual socket established for the physical socket is - // created. - kFirstVirtualSocket = 0, - // The others except FIRST_VIRTUAL_SCOKET type. - kNormalVirtualSocket = 1, - }; - - MultiplexOutputStream(OutputStream* physical_writer, - AtomicBoolean& is_enabled); - ~MultiplexOutputStream() = default; - - // Writes the connection request frame to the physical output stream. - bool WriteConnectionRequestFrame(const std::string& service_id, - const std::string& service_id_hash_salt); - - // Writes the connection response frame to the physical output stream. - bool WriteConnectionResponseFrame( - const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt, - ::location::nearby::mediums::ConnectionResponseFrame:: - ConnectionResponseCode response_code); - - // Closes the virtual output stream. - bool Close(const std::string& service_id); - - // Closes all virtual output streams. - void CloseAll(); - - // Waits for the result of the future. - Exception WaitForResult(const std::string& method_name, Future* future); - - // Creates the virtual output stream for the first virtual socket. - OutputStream* CreateVirtualOutputStreamForFirstVirtualSocket( - const std::string& service_id, const std::string& service_id_hash_salt); - - // Creates the virtual output stream. - OutputStream* CreateVirtualOutputStream( - const std::string& service_id, const std::string& service_id_hash_salt); - - // Gets the service id hash salt. - std::string GetServiceIdHashSalt(const std::string& service_id); - - // Shuts down the multiplex output stream. - void Shutdown(); - - class EnqueuedFrame { - public: - EnqueuedFrame(Future* future, ByteArray data) - : future_(future), data_(data) {} - ~EnqueuedFrame() = default; - - Future* future_; - ByteArray data_; - }; - - class MultiplexWriter { - public: - explicit MultiplexWriter(OutputStream* physical_writer); - ~MultiplexWriter(); - - // Enqueues the frame to be sent out. - void EnqueueToSend(Future* future, const ByteArray& data, - const std::string& frame_name); - // Closes the writer. - void Close(); - - private: - // Starts the writer thread. - void StartWriting(); - - // Writes the enqueued frame. - void Write(EnqueuedFrame& enqueued_frame); - - Mutex writer_mutex_; - OutputStream* physical_writer_ ABSL_PT_GUARDED_BY(writer_mutex_); - - ArrayBlockingQueue data_queue_{ - FeatureFlags::GetInstance() - .GetFlags() - .multiplex_socket_middle_priority_queue_capacity}; - mutable Mutex writing_mutex_; - ConditionVariable is_writing_cond_{&writing_mutex_}; - bool is_writing_ ABSL_GUARDED_BY(writing_mutex_) = false; - bool is_closed_ = false; - mutable Mutex close_writing_thread_mutex_; - ConditionVariable close_writing_thread_cond_{&close_writing_thread_mutex_}; - - // The single thread to write all enqueued frames. - SingleThreadExecutor writer_thread_; - bool is_write_loop_running_ = false; - }; - - class VirtualOutputStream : public OutputStream { - public: - VirtualOutputStream(std::string service_id, - std::string service_id_hash_salt, - OutputStream* physical_writer, - MultiplexWriter& multiplex_writer, - VirtualOutputStreamType virtual_output_stream_type, - MultiplexOutputStream& multiplex_output_stream); - ~VirtualOutputStream() override = default; - - // Returns true if the virtual output stream is the first virtual output - // stream. - bool IsFirstVirtualOutputStream() { - return virtual_output_stream_type_ == - VirtualOutputStreamType::kFirstVirtualSocket; - } - - // Returns the service id hash salt. - std::string GetServiceIdHashSalt() { return service_id_hash_salt_; } - - // Sets the service id hash salt. - void SetserviceIdHashSalt(std::string service_id_hash_salt) { - service_id_hash_salt_ = service_id_hash_salt; - } - - // Writes the data to the physical output stream. - Exception Write(absl::string_view data) override; - // Flushes the physical output stream. - Exception Flush() override; - // Closes the virtual output stream. - Exception Close() override; - - private: - AtomicBoolean is_closed_{false}; - - std::string service_id_; - std::string service_id_hash_salt_; - OutputStream* physical_writer_; - MultiplexWriter& multiplex_writer_; - VirtualOutputStreamType virtual_output_stream_type_; - bool first_frame_sent_for_first_virtual_output_stream_ = false; - MultiplexOutputStream& multiplex_output_stream_; - }; - - private: - AtomicBoolean& is_enabled_; - OutputStream* physical_writer_; - absl::flat_hash_map> - virtual_output_streams_; - MultiplexWriter multiplex_writer_; -}; - -} // namespace multiplex -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif // CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_OUTPUT_STREAM_H_ diff --git a/connections/implementation/mediums/multiplex/multiplex_output_stream_test.cc b/connections/implementation/mediums/multiplex/multiplex_output_stream_test.cc deleted file mode 100644 index 4a5ae130..00000000 --- a/connections/implementation/mediums/multiplex/multiplex_output_stream_test.cc +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2024 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/multiplex/multiplex_output_stream.h" - -#include -#include -#include -#include - -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "connections/implementation/mediums/multiplex/multiplex_frames.h" -#include "internal/platform/atomic_boolean.h" -#include "internal/platform/base64_utils.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/exception.h" -#include "internal/platform/input_stream.h" -#include "internal/platform/logging.h" -#include "internal/platform/multi_thread_executor.h" -#include "internal/platform/output_stream.h" -#include "internal/platform/pipe.h" -#include "proto/mediums/multiplex_frames.pb.h" - -namespace nearby { -namespace connections { -namespace mediums { -namespace multiplex { - -constexpr absl::string_view kServiceId_1 = "serviceId_1"; -constexpr absl::string_view kServiceId_2 = "serviceId_2"; -constexpr absl::string_view kNoSalt = ""; -constexpr absl::string_view kSalt_1 = "DNFG"; -constexpr absl::string_view kSalt_2 = "YFRT"; - -using ::location::nearby::mediums::ConnectionResponseFrame; -using ::location::nearby::mediums::MultiplexControlFrame; -using ::location::nearby::mediums::MultiplexFrame; - -class MultiplexOutputStreamTest : public ::testing::Test { - protected: - ExceptionOr ReadFrame() { - ExceptionOr read_int = Base64Utils::ReadInt(reader_.get()); - if (!read_int.ok()) return read_int.GetException(); - if (read_int.result() <= 0) return {Exception::kFailed}; - - ExceptionOr received_data = - reader_->ReadExactly(read_int.result()); - if (!received_data.ok()) return received_data.GetException(); - auto bytes = std::move(received_data.result()); - return FromBytes(bytes); - } - - AtomicBoolean enabled_{true}; - std::pair, std::unique_ptr> pipe_ = - CreatePipe(); - - std::unique_ptr reader_ = std::move(pipe_.first); - std::unique_ptr writer_ = std::move(pipe_.second); - std::unique_ptr multiplex_output_stream_; -}; - -TEST_F(MultiplexOutputStreamTest, SendConnectionRequestFrame) { - multiplex_output_stream_ = - std::make_unique(writer_.get(), enabled_); - EXPECT_TRUE(multiplex_output_stream_->WriteConnectionRequestFrame( - std::string(kServiceId_1), std::string(kNoSalt))); - - auto request = ReadFrame(); - ASSERT_TRUE(request.ok()); - auto frame = request.result(); - EXPECT_EQ(frame.control_frame().control_frame_type(), - MultiplexControlFrame::CONNECTION_REQUEST); - EXPECT_EQ(frame.header().salted_service_id_hash(), - std::string(GenerateServiceIdHashWithSalt(std::string(kServiceId_1), - std::string(kNoSalt)))); - - multiplex_output_stream_->Shutdown(); -} - -TEST_F(MultiplexOutputStreamTest, SendConnectionRequestFrameDisabled) { - enabled_.Set(false); - multiplex_output_stream_ = - std::make_unique(writer_.get(), enabled_); - EXPECT_FALSE(multiplex_output_stream_->WriteConnectionRequestFrame( - std::string(kServiceId_1), std::string(kNoSalt))); - - multiplex_output_stream_->Shutdown(); -} - -TEST_F(MultiplexOutputStreamTest, SendConnectionResponseFrame) { - multiplex_output_stream_ = - std::make_unique(writer_.get(), enabled_); - EXPECT_TRUE(multiplex_output_stream_->WriteConnectionResponseFrame( - GenerateServiceIdHash(std::string(kServiceId_1)), std::string(kNoSalt), - ConnectionResponseFrame::CONNECTION_ACCEPTED)); - - auto response = ReadFrame(); - ASSERT_TRUE(response.ok()); - auto frame = response.result(); - EXPECT_EQ(frame.control_frame().control_frame_type(), - MultiplexControlFrame::CONNECTION_RESPONSE); - EXPECT_EQ(frame.header().salted_service_id_hash(), - std::string(GenerateServiceIdHashWithSalt(std::string(kServiceId_1), - std::string(kNoSalt)))); - EXPECT_EQ(frame.control_frame() - .connection_response_frame() - .connection_response_code(), - ConnectionResponseFrame::CONNECTION_ACCEPTED); - - multiplex_output_stream_->Shutdown(); -} - -TEST_F(MultiplexOutputStreamTest, SendConnectionResponseFrameDisabled) { - enabled_.Set(false); - multiplex_output_stream_ = - std::make_unique(writer_.get(), enabled_); - EXPECT_FALSE(multiplex_output_stream_->WriteConnectionResponseFrame( - GenerateServiceIdHash(std::string(kServiceId_1)), std::string(kNoSalt), - ConnectionResponseFrame::CONNECTION_ACCEPTED)); - - multiplex_output_stream_->Shutdown(); -} - -TEST_F(MultiplexOutputStreamTest, CloseVirtualStreamFailed) { - multiplex_output_stream_ = - std::make_unique(writer_.get(), enabled_); - EXPECT_FALSE(multiplex_output_stream_->Close(std::string(kServiceId_1))); - - multiplex_output_stream_->Shutdown(); -} - -TEST_F(MultiplexOutputStreamTest, CloseVirtualStreamSuccess) { - multiplex_output_stream_ = - std::make_unique(writer_.get(), enabled_); - EXPECT_FALSE(multiplex_output_stream_->Close(std::string(kServiceId_1))); - - multiplex_output_stream_->CreateVirtualOutputStream(std::string(kServiceId_1), - std::string(kNoSalt)); - EXPECT_TRUE(multiplex_output_stream_->Close(std::string(kServiceId_1))); - - auto request = ReadFrame(); - ASSERT_TRUE(request.ok()); - auto frame = request.result(); - EXPECT_EQ(frame.control_frame().control_frame_type(), - MultiplexControlFrame::DISCONNECTION); - EXPECT_EQ(frame.header().salted_service_id_hash(), - std::string(GenerateServiceIdHashWithSalt(std::string(kServiceId_1), - std::string(kNoSalt)))); - - multiplex_output_stream_->Shutdown(); -} - -TEST_F(MultiplexOutputStreamTest, CreateVirtualStream_SendData) { - multiplex_output_stream_ = - std::make_unique(writer_.get(), enabled_); - - auto virtual_output_stream = - multiplex_output_stream_->CreateVirtualOutputStream( - std::string(kServiceId_1), std::string(kSalt_1)); - - absl::string_view data = "abcdefghijklmnopqrstuvwxyz"; - virtual_output_stream->Write(data); - virtual_output_stream->Flush(); - auto frame_data = ReadFrame(); - ASSERT_TRUE(frame_data.ok()); - auto frame = frame_data.result(); - EXPECT_EQ(frame.frame_type(), MultiplexFrame::DATA_FRAME); - EXPECT_EQ(frame.header().salted_service_id_hash(), - std::string(GenerateServiceIdHashWithSalt(std::string(kServiceId_1), - std::string(kSalt_1)))); - EXPECT_EQ(frame.data_frame().data(), std::string(data)); - - multiplex_output_stream_->Shutdown(); -} - -TEST_F(MultiplexOutputStreamTest, CreateTwoVirtualStreams_SendData) { - multiplex_output_stream_ = - std::make_unique(writer_.get(), enabled_); - - auto virtual_output_stream_1 = - multiplex_output_stream_->CreateVirtualOutputStreamForFirstVirtualSocket( - std::string(kServiceId_1), std::string(kSalt_1)); - auto virtual_output_stream_2 = - multiplex_output_stream_->CreateVirtualOutputStreamForFirstVirtualSocket( - std::string(kServiceId_2), std::string(kSalt_2)); - - absl::string_view data_1("abcdefg"); - absl::string_view data_2("hijklmn"); - MultiThreadExecutor executor(2); - CountDownLatch latch(2); - executor.Execute([&virtual_output_stream_1, &latch, &data_1]() { - absl::SleepFor(absl::Milliseconds(100)); - virtual_output_stream_1->Write(data_1); - virtual_output_stream_1->Flush(); - latch.CountDown(); - }); - executor.Execute([&virtual_output_stream_2, &latch, &data_2]() { - virtual_output_stream_2->Write(data_2); - virtual_output_stream_2->Flush(); - latch.CountDown(); - }); - EXPECT_TRUE(latch.Await(absl::Milliseconds(5000)).result()); - - auto frame_data = ReadFrame(); - ASSERT_TRUE(frame_data.ok()); - auto frame = frame_data.result(); - EXPECT_EQ(frame.frame_type(), MultiplexFrame::DATA_FRAME); - bool first_frame_is_data_1 = true; - if (frame.header().salted_service_id_hash() == - std::string(GenerateServiceIdHashWithSalt(std::string(kServiceId_1), - std::string(kSalt_1)))) { - EXPECT_EQ(frame.data_frame().data(), std::string(data_1)); - LOG(INFO) << "Read first virtual stream frame first."; - } else { - EXPECT_EQ(frame.header().salted_service_id_hash(), - std::string(GenerateServiceIdHashWithSalt( - std::string(kServiceId_2), std::string(kSalt_2)))); - EXPECT_EQ(frame.data_frame().data(), std::string(data_2)); - first_frame_is_data_1 = false; - LOG(INFO) << "Read second virtual stream frame first."; - } - - frame_data = ReadFrame(); - ASSERT_TRUE(frame_data.ok()); - frame = frame_data.result(); - EXPECT_EQ(frame.frame_type(), MultiplexFrame::DATA_FRAME); - if (first_frame_is_data_1) { - EXPECT_EQ(frame.data_frame().data(), std::string(data_2)); - } else { - EXPECT_EQ(frame.data_frame().data(), std::string(data_1)); - } - multiplex_output_stream_->Shutdown(); -} - -} // namespace multiplex -} // namespace mediums -} // namespace connections -} // namespace nearby diff --git a/connections/implementation/mediums/multiplex/multiplex_socket.cc b/connections/implementation/mediums/multiplex/multiplex_socket.cc deleted file mode 100644 index befe36e9..00000000 --- a/connections/implementation/mediums/multiplex/multiplex_socket.cc +++ /dev/null @@ -1,807 +0,0 @@ -// Copyright 2024 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/multiplex/multiplex_socket.h" - -#include -#include -#include -#include -#include -#include - -#include "absl/container/flat_hash_map.h" -#include "absl/functional/any_invocable.h" -#include "absl/strings/string_view.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "connections/implementation/mediums/multiplex/multiplex_frames.h" -#include "connections/implementation/mediums/multiplex/multiplex_output_stream.h" -#include "connections/implementation/mediums/utils.h" -#include "internal/platform/atomic_boolean.h" -#include "internal/platform/base64_utils.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/exception.h" -#include "internal/platform/feature_flags.h" -#include "internal/platform/future.h" -#include "internal/platform/logging.h" -#include "internal/platform/mutex.h" -#include "internal/platform/mutex_lock.h" -#include "internal/platform/socket.h" -#include "internal/platform/types.h" -#include "proto/connections_enums.pb.h" - -namespace nearby { -namespace connections { -namespace mediums { -namespace multiplex { - -namespace { -// It is defined for the receiver which send the first packet to the sender -// without getting salt from it yet. The fake salt reminds sender to get the -// correct socket from `virtualSockets` without remapping it. -constexpr absl::string_view kFakeSalt = "RECEIVER_CONDIMENT"; - -// The max duration to wait for the reader thread to stop. -constexpr absl::Duration kTimeoutForReaderThreadStop = absl::Milliseconds(100); - -} // namespace - -using ::location::nearby::mediums::ConnectionResponseFrame; -using ::location::nearby::mediums::MultiplexControlFrame; -using ::location::nearby::mediums::MultiplexDataFrame; -using ::location::nearby::mediums::MultiplexFrame; -using ::location::nearby::proto::connections::Medium; -using ::location::nearby::proto::connections::Medium_Name; -using ConnectionResponseCode = ConnectionResponseFrame::ConnectionResponseCode; - -// AtomicBoolean is trivial destructible, so it is safe to use it as a static -// variable. -AtomicBoolean MultiplexSocket::is_shutting_down_{false}; // NOLINT - -void MultiplexSocket::ListenForIncomingConnection( - const std::string& service_id, Medium type, - MultiplexIncomingConnectionCb incoming_connection_cb) { - GetIncomingConnectionCallbacks().emplace( - std::pair(service_id, type), - std::move(incoming_connection_cb)); -} - -void MultiplexSocket::StopListeningForIncomingConnection( - const std::string& service_id, Medium type) { - GetIncomingConnectionCallbacks().erase( - std::pair(service_id, type)); -} - -MultiplexSocket::MultiplexSocket(std::shared_ptr physical_socket) - : physical_socket_ptr_(physical_socket), - multiplex_output_stream_{&physical_socket_ptr_->GetOutputStream(), - enabled_}, - physical_reader_(&physical_socket_ptr_->GetInputStream()), - medium_(physical_socket_ptr_->GetMedium()) {} - -absl::flat_hash_map, - MultiplexIncomingConnectionCb>& -MultiplexSocket::GetIncomingConnectionCallbacks() { - using MapType = absl::flat_hash_map, - MultiplexIncomingConnectionCb>; - alignas(MapType) static char storage[sizeof(MapType)]; - static MapType* incoming_connection_callbacks = new (&storage) MapType(); - - return *incoming_connection_callbacks; -} - -MultiplexSocket* MultiplexSocket::CreateIncomingSocket( - std::shared_ptr physical_socket, - const std::string& service_id, std::int32_t first_frame_len) { - while (is_shutting_down_.Get()) { - LOG(WARNING) - << "Shutting down is going on, wait for 2ms to create incoming socket"; - absl::SleepFor(absl::Milliseconds(2)); - } - - MultiplexSocket* multiplex_incoming_socket = nullptr; - static MultiplexSocket* multiplex_incoming_socket_bt = nullptr; - static MultiplexSocket* multiplex_incoming_socket_wlan = nullptr; - - switch (physical_socket->GetMedium()) { - case Medium::BLUETOOTH: - if (multiplex_incoming_socket_bt != nullptr) { - LOG(INFO) << "Multiplex incoming socket already exists for BT"; - return multiplex_incoming_socket_bt; - } - alignas(MultiplexSocket) static char storage_bt[sizeof(MultiplexSocket)]; - multiplex_incoming_socket_bt = - new (&storage_bt) MultiplexSocket(physical_socket); - multiplex_incoming_socket = multiplex_incoming_socket_bt; - break; - case Medium::WIFI_LAN: - case Medium::AWDL: - if (multiplex_incoming_socket_wlan != nullptr) { - LOG(INFO) << "Multiplex incoming socket already exists for WLAN"; - return multiplex_incoming_socket_wlan; - } - alignas( - MultiplexSocket) static char storage_wlan[sizeof(MultiplexSocket)]; - multiplex_incoming_socket_wlan = - new (&storage_wlan) MultiplexSocket(physical_socket); - multiplex_incoming_socket = multiplex_incoming_socket_wlan; - break; - default: - LOG(ERROR) << __func__ - << "Unsupported medium: " << physical_socket->GetMedium(); - multiplex_incoming_socket = nullptr; - return multiplex_incoming_socket; - } - LOG(INFO) << "CreateIncomingSocket with serviceId=" << service_id - << ", serviceIdHashSalt=" << kFakeSalt - << " for medium=" << Medium_Name(physical_socket->GetMedium()); - - multiplex_incoming_socket->CreateFirstVirtualSocket(service_id, - (std::string)kFakeSalt); - multiplex_incoming_socket->StartReaderThread(first_frame_len); - - return multiplex_incoming_socket; -} - -MultiplexSocket* MultiplexSocket::CreateOutgoingSocket( - std::shared_ptr physical_socket, - const std::string& service_id, const std::string& service_id_hash_salt) { - while (is_shutting_down_.Get()) { - LOG(WARNING) - << "Shutting down is going on, wait for 2ms to create outgoing socket"; - absl::SleepFor(absl::Milliseconds(2)); - } - - MultiplexSocket* multiplex_outgoing_socket = nullptr; - static MultiplexSocket* multiplex_outgoing_socket_bt = nullptr; - static MultiplexSocket* multiplex_outgoing_socket_wlan = nullptr; - - switch (physical_socket->GetMedium()) { - case Medium::BLUETOOTH: - if (multiplex_outgoing_socket_bt != nullptr) { - LOG(INFO) << "Multiplex outgoing socket already exists for BT"; - return multiplex_outgoing_socket_bt; - } - alignas(MultiplexSocket) static char storage_bt[sizeof(MultiplexSocket)]; - multiplex_outgoing_socket_bt = - new (&storage_bt) MultiplexSocket(physical_socket); - multiplex_outgoing_socket = multiplex_outgoing_socket_bt; - break; - case Medium::WIFI_LAN: - case Medium::AWDL: - if (multiplex_outgoing_socket_wlan != nullptr) { - LOG(INFO) << "Multiplex outgoing socket already exists for WLAN"; - return multiplex_outgoing_socket_wlan; - } - alignas( - MultiplexSocket) static char storage_wlan[sizeof(MultiplexSocket)]; - multiplex_outgoing_socket_wlan = - new (&storage_wlan) MultiplexSocket(physical_socket); - multiplex_outgoing_socket = multiplex_outgoing_socket_wlan; - break; - default: - LOG(ERROR) << __func__ - << "Unsupported medium: " << physical_socket->GetMedium(); - multiplex_outgoing_socket = nullptr; - return multiplex_outgoing_socket; - } - LOG(INFO) << "CreateOutgoingSocket with serviceId=" << service_id - << ", serviceIdHashSalt=" << service_id_hash_salt - << " for medium=" << Medium_Name(physical_socket->GetMedium()); - - multiplex_outgoing_socket->CreateFirstVirtualSocket(service_id, - service_id_hash_salt); - multiplex_outgoing_socket->StartReaderThread(0); - return multiplex_outgoing_socket; -} - -MultiplexSocket* MultiplexSocket::CreateOutgoingSocket( - std::shared_ptr physical_socket, - const std::string& service_id) { - return CreateOutgoingSocket(physical_socket, service_id, - Utils::GenerateSalt()); -} - -MediumSocket* MultiplexSocket::CreateFirstVirtualSocket( - const std::string& service_id, const std::string& service_id_hash_salt) { - auto output_stream = - multiplex_output_stream_.CreateVirtualOutputStreamForFirstVirtualSocket( - service_id, service_id_hash_salt); - - MutexLock lock(&virtual_socket_mutex_); - std::string salted_service_id_hash_key = - GenerateServiceIdHashKeyWithSalt(service_id, service_id_hash_salt); - LOG(INFO) << __func__ << " for service_id=" << service_id - << ", salt=" << service_id_hash_salt - << ", salted_service_id_hash_key=" << salted_service_id_hash_key; - MediumSocket* virtual_socket = physical_socket_ptr_->CreateVirtualSocket( - salted_service_id_hash_key, output_stream, medium_, &virtual_sockets_); - - virtual_socket->AddOnSocketClosedListener( - std::make_unique>( - [this, service_id]() { OnVirtualSocketClosed(service_id); })); - - if (!IsEnabled()) { - LOG(INFO) << __func__ << ": Register multiplex enabled callback"; - virtual_socket->RegisterMultiplexEnabledCallback(enable_cb_); - } - - return virtual_socket; -} - -MediumSocket* MultiplexSocket::CreateVirtualSocket( - const std::string& service_id, const std::string& service_id_hash_salt) { - auto output_stream = multiplex_output_stream_.CreateVirtualOutputStream( - service_id, service_id_hash_salt); - MutexLock lock(&virtual_socket_mutex_); - std::string salted_service_id_hash_key = - GenerateServiceIdHashKeyWithSalt(service_id, service_id_hash_salt); - - LOG(INFO) << __func__ << "service_id=" << service_id - << ", salt=" << service_id_hash_salt - << ", salted_service_id_hash_key=" << salted_service_id_hash_key; - - MediumSocket* virtual_socket = physical_socket_ptr_->CreateVirtualSocket( - salted_service_id_hash_key, output_stream, medium_, &virtual_sockets_); - - virtual_socket->AddOnSocketClosedListener( - std::make_unique>( - [this, service_id]() { OnVirtualSocketClosed(service_id); })); - - return virtual_socket; -} - -MediumSocket* MultiplexSocket::GetVirtualSocket(const std::string& service_id) { - MutexLock lock(&virtual_socket_mutex_); - LOG(INFO) << __func__ << " service_id=" << service_id << ", Salt=" - << multiplex_output_stream_.GetServiceIdHashSalt(service_id) - << ", virtual_sockets_.size()=" << virtual_sockets_.size(); - auto item = virtual_sockets_.find(GenerateServiceIdHashKeyWithSalt( - service_id, multiplex_output_stream_.GetServiceIdHashSalt(service_id))); - if (item == virtual_sockets_.end()) { - LOG(INFO) << "Not found!"; - return nullptr; - } - return item->second.get(); -} - -int MultiplexSocket::GetVirtualSocketCount() { - MutexLock lock(&virtual_socket_mutex_); - return virtual_sockets_.size(); -} - -void MultiplexSocket::ListVirtualSocket() { - LOG(INFO) << __func__ - << " virtual_sockets_.size()=" << virtual_sockets_.size(); - for (auto& [service_id_hash_key, virtual_socket] : virtual_sockets_) { - LOG(INFO) << __func__ << " service_id_hash_key=" << service_id_hash_key - << ", virtual_socket=" << virtual_socket; - } -} - -std::shared_ptr> -MultiplexSocket::RegisterConnectionResponse(const std::string& service_id) { - auto future = std::make_shared>(); - connection_response_futures_.emplace(service_id, future); - - return future; -} - -void MultiplexSocket::UnRegisterConnectionResponse( - const std::string& service_id) { - connection_response_futures_.erase(service_id); -} - -MediumSocket* MultiplexSocket::EstablishVirtualSocket( - const std::string& service_id) { - if (!IsEnabled()) { - LOG(ERROR) - << "MultiplexSocket is disabled, cannot establish virtual socket."; - return nullptr; - } - - std::string service_id_hash_salt = Utils::GenerateSalt(); - auto future = RegisterConnectionResponse(service_id); - - multiplex_output_stream_.WriteConnectionRequestFrame(service_id, - service_id_hash_salt); - auto result = - future->Get(FeatureFlags::GetInstance() - .GetFlags() - .multiplex_socket_connection_response_timeout_millis); - if (!result.ok()) { - LOG(ERROR) << __func__ - << "EstablishVirtualSocket failed with response code=" - << result.exception(); - return nullptr; - } - - ConnectionResponseCode response_code = result.GetResult(); - switch (response_code) { - case ConnectionResponseFrame::CONNECTION_ACCEPTED: - LOG(INFO) << "EstablishVirtualSocket after remote response to" - " accept the connection with service_id=" - << service_id - << ", service_id_hash_salt=" << service_id_hash_salt; - return CreateVirtualSocket(service_id, service_id_hash_salt); - case ConnectionResponseFrame::NOT_LISTENING: - LOG(ERROR) << "EstablishVirtualSocket failed for service_id=" - << service_id - << ", service_id_hash_salt=" << service_id_hash_salt - << " with response code=NOT_LISTENING"; - break; - default: - LOG(ERROR) << "EstablishVirtualSocket failed for service_id=" - << service_id - << ", service_id_hash_salt=" << service_id_hash_salt - << " with response code=UNKNOWN_RESPONSE_CODE"; - break; - } - return nullptr; -} - -void MultiplexSocket::StartReaderThread(std::int32_t first_frame_len) { - if (is_shutdown_) { - LOG(WARNING) << "Stop to start reader thread since socket is " - "shutdown."; - return; - } - reader_thread_shutdown_barrier_ = std::make_unique(1); - physical_reader_thread_.Execute([this, first_frame_len]() { - LOG(INFO) << __func__ << " Reader thread starts."; - auto first_frame_len_copy = first_frame_len; - while (!is_shutdown_) { - bool fail = false; - ExceptionOr bytes; - ExceptionOr read_int; - if (first_frame_len_copy > 0) { - read_int = ExceptionOr(first_frame_len); - first_frame_len_copy = 0; - } else { - read_int = Base64Utils::ReadInt(physical_reader_); - } - if (!read_int.ok()) { - LOG(WARNING) << __func__ - << "Failed to read. Exception:" << read_int.exception(); - fail = true; - } else { - auto length = read_int.result(); - VLOG(1) << __func__ << " length:" << length; - - if (length < 0 || length > FeatureFlags::GetInstance() - .GetFlags() - .connection_max_frame_length) { - // Ignore the failure because not only one client use this - // connection. - LOG(WARNING) << __func__ - << "Failed to read because received a invalid length " - << length << ", but continue to read."; - continue; - } - - bytes = physical_reader_->ReadExactly(length); - if (!bytes.ok()) { - LOG(WARNING) << __func__ - << "Read data exception:" << bytes.exception(); - fail = true; - } - } - if (fail) { - reader_thread_shutdown_barrier_->CountDown(); - return; - } - - ExceptionOr frame_exc = - multiplex::FromBytes(bytes.result()); - if (!frame_exc.ok()) { - HandleOfflineFrame(bytes.result()); - continue; - } - - if (!IsEnabled()) { - // The reader thread will only be enabled when local device - // supports multiplex if we received a multiplex frame from - // the remote, it means that the remote and the local both - // support multiplex as well. So it is safe to just turn on - // the feature at this point. - LOG(INFO) << __func__ - << " Received a multiplex frame while not enabled, enable " - "multiplex."; - Enable(); - } - const auto& frame = frame_exc.result(); - auto salted_service_id_hash = - ByteArray{std::move(frame.header().salted_service_id_hash())}; - auto service_id_hash_salt = frame.header().has_service_id_hash_salt() - ? frame.header().service_id_hash_salt() - : ""; - switch (frame.frame_type()) { - case MultiplexFrame::CONTROL_FRAME: - HandleControlFrame(salted_service_id_hash, service_id_hash_salt, - frame.control_frame()); - break; - case MultiplexFrame::DATA_FRAME: - VLOG(1) << "service_id_hash_salt: " << service_id_hash_salt; - HandleDataFrame(salted_service_id_hash, service_id_hash_salt, - frame.data_frame()); - break; - default: - LOG(WARNING) << __func__ - << " Received MultiplexFrame with unknown frame type " - << frame.frame_type(); - } - } - }); -} - -void MultiplexSocket::HandleOfflineFrame(const ByteArray& bytes) { - MutexLock lock(&virtual_socket_mutex_); - LOG(INFO) << __func__ << " Virtual_socket num:" << virtual_sockets_.size(); - if (virtual_sockets_.size() == 1) { - auto item = virtual_sockets_.begin(); - if (item->second == nullptr) { - LOG(WARNING) << "Expected one live socket, but found null."; - return; - } - LOG(INFO) << __func__ << "FeedIncomingData:" << std::string(bytes); - item->second->FeedIncomingData(Base64Utils::IntToBytes(bytes.size())); - item->second->FeedIncomingData(bytes); - } -} - -void MultiplexSocket::HandleControlFrame( - const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt, - const MultiplexControlFrame& frame) { - switch (frame.control_frame_type()) { - case MultiplexControlFrame::CONNECTION_REQUEST: - RunOffloadThread("CONNECTION_REQUEST", [this, salted_service_id_hash, - service_id_hash_salt] { - HandleConnectionRequest(salted_service_id_hash, service_id_hash_salt); - }); - break; - case MultiplexControlFrame::CONNECTION_RESPONSE: - LOG(INFO) << __func__ << "Received an CONNECTION_RESPONSE frame." - << " salted_service_id_hash: " - << std::string(salted_service_id_hash) - << ", service_id_hash_salt: " << service_id_hash_salt - << ", ConnectionResponseCode: " - << frame.connection_response_frame().connection_response_code(); - - RunOffloadThread("CONNECTION_RESPONSE", [this, salted_service_id_hash, - service_id_hash_salt, - frame = frame] { - HandleConnectionResponse(salted_service_id_hash, service_id_hash_salt, - frame.connection_response_frame()); - }); - break; - case MultiplexControlFrame::DISCONNECTION: - // The virtual socket will be closed in the offload thread, so don't run - // the thread here. - HandleDisconnection(salted_service_id_hash); - break; - default: - LOG(WARNING) << __func__ << "Received an unknown frame type " - << frame.control_frame_type(); - break; - } -} - -void MultiplexSocket::HandleConnectionRequest( - const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt) { - if (!IsEnabled()) { - LOG(WARNING) << "Received a CONNECTION_REQUEST frame on medium " - << Medium_Name(medium_) - << " but status is disabled, ignore it."; - return; - } - - std::string salted_service_id_hash_key = - GenerateServiceIdHashKey(salted_service_id_hash); - MultiplexIncomingConnectionCb* incoming_connection_callback = nullptr; - std::string listening_service_id = ""; - for (auto& [service_id_medium_pair, callback] : - GetIncomingConnectionCallbacks()) { - if (GenerateServiceIdHashWithSalt(service_id_medium_pair.first, - service_id_hash_salt) == - salted_service_id_hash) { - incoming_connection_callback = &callback; - listening_service_id = service_id_medium_pair.first; - } - } - - if (incoming_connection_callback == nullptr || listening_service_id.empty()) { - LOG(INFO) << "There's no client listening for hash salt : " - << service_id_hash_salt - << ", hash key : " << salted_service_id_hash_key << " on medium " - << Medium_Name(medium_); - - LOG(INFO) << "The size of incomingConnectionCallbacks : " - << GetIncomingConnectionCallbacks().size(); - if (!multiplex_output_stream_.WriteConnectionResponseFrame( - salted_service_id_hash, service_id_hash_salt, - ConnectionResponseFrame::NOT_LISTENING)) { - LOG(INFO) << __func__ << "Failed to write NOT_LISTENING frame."; - } - return; - } - LOG(INFO) << "Accept new virtual socket request service ID : " - << listening_service_id << ", hash salt : " << service_id_hash_salt - << ", hash key : " << salted_service_id_hash_key << " on medium " - << Medium_Name(medium_); - - if (!multiplex_output_stream_.WriteConnectionResponseFrame( - salted_service_id_hash, service_id_hash_salt, - ConnectionResponseFrame::CONNECTION_ACCEPTED)) { - LOG(INFO) << "Failed to write CONNECTION_ACCEPTED frame."; - return; - } - - LOG(INFO) - << "EstablishVirtualSocket after local device accept the connection " - "with serviceId=" - << listening_service_id; - MediumSocket* virtual_socket = - CreateVirtualSocket(listening_service_id, service_id_hash_salt); - (*incoming_connection_callback)(std::move(listening_service_id), - virtual_socket); -} - -void MultiplexSocket::HandleConnectionResponse( - const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt, - const ConnectionResponseFrame& frame) { - LOG(INFO) << __func__ - << "connection_response_code: " << frame.connection_response_code(); - for (auto& [service_id, future] : connection_response_futures_) { - if (GenerateServiceIdHashWithSalt(service_id, service_id_hash_salt) == - salted_service_id_hash) { - if (future != nullptr) { - future->Set(frame.connection_response_code()); - LOG(INFO) << __func__ << "Set the future for serviceId=" << service_id - << ", serviceIdHashSalt=" << service_id_hash_salt - << " with response code=" << frame.connection_response_code(); - return; - } - } - } - - LOG(WARNING) - << __func__ - << "Received a CONNECTION_RESPONSE frame but no client waiting for " - "service ID Hash Key" - << GenerateServiceIdHashKey(salted_service_id_hash); -} - -void MultiplexSocket::HandleDisconnection( - const ByteArray& salted_service_id_hash) { - std::string salted_service_id_hash_key = - GenerateServiceIdHashKey(salted_service_id_hash); - MediumSocket* virtual_socket_to_close = nullptr; - { - MutexLock lock(&virtual_socket_mutex_); - auto item = virtual_sockets_.find(salted_service_id_hash_key); - if (item != virtual_sockets_.end()) { - LOG(INFO) - << "Received a DISCONNECTION frame to disconnect virtual socket for " - "salted service ID Hash Key " - << salted_service_id_hash_key; - virtual_socket_to_close = item->second.get(); - } else { - LOG(WARNING) - << "Received a DISCONNECTION frame but there's no alive socket to " - "disconnect for service ID Hash Key " - << salted_service_id_hash_key; - } - } - // Close the virtual socket outside of the mutex lock because - // OnVirtualSocketClosed will lock the mutex. - if (virtual_socket_to_close != nullptr) { - virtual_socket_to_close->Close(); - } -} - -void MultiplexSocket::HandleDataFrame(const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt, - const MultiplexDataFrame& frame) { - std::string salted_service_id_hash_key = - GenerateServiceIdHashKey(salted_service_id_hash); - MediumSocket* virtual_socket = nullptr; - if (service_id_hash_salt.empty()) { - { - MutexLock lock(&virtual_socket_mutex_); - auto item = virtual_sockets_.find(salted_service_id_hash_key); - if (item != virtual_sockets_.end()) { - virtual_socket = item->second.get(); - } - } - } else { - virtual_socket = - ReMapAndGetVirtualSocket(salted_service_id_hash, service_id_hash_salt); - } - - if (virtual_socket != nullptr) { - VLOG(1) - << "Received a DATA frame to feed virtual socket for salted service ID " - "Hash Key " - << salted_service_id_hash_key; - virtual_socket->FeedIncomingData(ByteArray(frame.data())); - } else { - LOG(WARNING) - << "Received a DATA frame but there's no alive socket to feed for " - "salted service ID Hash Key " - << salted_service_id_hash_key; - } -} - -void MultiplexSocket::OnPhysicalSocketClosed() { - RunOffloadThread("Shutdown", [this]() { Shutdown(); }); -} - -void MultiplexSocket::OnVirtualSocketClosed(const std::string& service_id) { - LOG(INFO) << __func__ << " for service_id:" << service_id; - CountDownLatch latch(1); - bool shutdown = false; - RunOffloadThread( - "VirtualSocketClosed", [this, service_id, &latch, &shutdown]() { - LOG(INFO) << "Try to close Virtual socket: " << service_id; - MediumSocket* virtual_socket = GetVirtualSocket(service_id); - { - MutexLock lock(&virtual_socket_mutex_); - LOG(INFO) << "virtual_socket:" << virtual_socket; - if (virtual_socket != nullptr) { - auto salted_service_id_hash_key = GenerateServiceIdHashKeyWithSalt( - service_id, - multiplex_output_stream_.GetServiceIdHashSalt(service_id)); - multiplex_output_stream_.Close(service_id); - virtual_sockets_.erase(salted_service_id_hash_key); - LOG(INFO) << "Erase Virtual socket with service_id: " << service_id - << ", hash_key: " << salted_service_id_hash_key; - ListVirtualSocket(); - - if (virtual_sockets_.empty()) { - LOG(INFO) << "Close the physical socket because all virtual " - "sockets disconnected."; - is_shutting_down_.Set(true); - Shutdown(); - shutdown = true; - } - } else { - LOG(INFO) << "Virtual socket(" << service_id << ") not found"; - } - } - latch.CountDown(); - }); - - if (!latch.Await(absl::Milliseconds(1000)).result()) { - LOG(ERROR) << "Timeout to close virtual socket"; - } - - if (shutdown) { - LOG(INFO) - << "Shutdown single_thread_offloader_ and physical_reader_thread_"; - single_thread_offloader_.Shutdown(); - physical_reader_thread_.Shutdown(); - is_shutting_down_.Set(false); - } -} - -MediumSocket* MultiplexSocket::ReMapAndGetVirtualSocket( - const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt) { - std::string salted_service_id_hash_key = - GenerateServiceIdHashKey(salted_service_id_hash); - VLOG(1) << "ReMapAndGetVirtualSocket with serviceIdHashSalt=" - << service_id_hash_salt - << ", saltedServiceIdHashKey=" << salted_service_id_hash_key; - { - MutexLock lock(&virtual_socket_mutex_); - for (auto& [hash_key, virtual_socket] : virtual_sockets_) { - auto output_stream = - down_cast( - &(virtual_socket->GetOutputStream())); - if (output_stream == nullptr) { - continue; - } - if (!output_stream->IsFirstVirtualOutputStream()) { - continue; - } - if ((service_id_hash_salt == kFakeSalt) || - (hash_key == salted_service_id_hash_key)) { - return virtual_socket.get(); - } else { - LOG(INFO) << "Remap the virtualSockets."; - output_stream->SetserviceIdHashSalt(service_id_hash_salt); - auto virtual_socket_tmp = virtual_socket; - LOG(INFO) << "virtual_socket before:" << virtual_socket; - virtual_sockets_.erase(hash_key); - virtual_sockets_[salted_service_id_hash_key] = virtual_socket_tmp; - ListVirtualSocket(); - return virtual_socket_tmp.get(); - } - } - } - - LOG(INFO) << "Failed to remap the virtualSockets."; - return nullptr; -} - -void MultiplexSocket::RunOffloadThread(const std::string& name, - absl::AnyInvocable runnable) { - single_thread_offloader_.Execute(name, std::move(runnable)); -} - -void MultiplexSocket::Shutdown() { - LOG(INFO) << __func__ << " start"; - if (is_shutdown_) { - LOG(INFO) << __func__ << " Already shutdown"; - return; - } - - multiplex_output_stream_.Shutdown(); - physical_socket_ptr_->Close(); - - if (reader_thread_shutdown_barrier_) { - reader_thread_shutdown_barrier_->Await(kTimeoutForReaderThreadStop); - } - - GetIncomingConnectionCallbacks().clear(); - connection_response_futures_.clear(); - - is_shutdown_ = true; - enabled_.Set(false); - LOG(INFO) << __func__ << " end"; -} - -void MultiplexSocket::ShutdownAll() { - LOG(INFO) << __func__ << " start"; - if (is_shutdown_) { - LOG(WARNING) << __func__ << " Already shutdown"; - return; - } - - CountDownLatch latch(1); - RunOffloadThread("VirtualSocketClosed", [this, &latch]() { - { - MutexLock lock(&virtual_socket_mutex_); - multiplex_output_stream_.CloseAll(); - virtual_sockets_.clear(); - - Shutdown(); - } - latch.CountDown(); - }); - - if (!latch - .Await(FeatureFlags::GetInstance() - .GetFlags() - .mediums_frame_write_timeout_millis + - absl::Milliseconds(100)) - .result()) { - LOG(ERROR) << "Timeout to close virtual socket"; - } - - LOG(INFO) << "Shutdown single_thread_offloader_ and physical_reader_thread_"; - single_thread_offloader_.Shutdown(); - physical_reader_thread_.Shutdown(); - LOG(INFO) << __func__ << " end"; -} - -} // namespace multiplex -} // namespace mediums -} // namespace connections -} // namespace nearby diff --git a/connections/implementation/mediums/multiplex/multiplex_socket.h b/connections/implementation/mediums/multiplex/multiplex_socket.h deleted file mode 100644 index 5e65c5d4..00000000 --- a/connections/implementation/mediums/multiplex/multiplex_socket.h +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2024 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 CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_SOCKET_H_ -#define CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_SOCKET_H_ - -#include -#include -#include -#include - -#include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" -#include "absl/functional/any_invocable.h" -#include "connections/implementation/mediums/multiplex/multiplex_output_stream.h" -#include "connections/medium_selector.h" -#include "internal/platform/atomic_boolean.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/future.h" -#include "internal/platform/input_stream.h" -#include "internal/platform/logging.h" -#include "internal/platform/mutex.h" -#include "internal/platform/single_thread_executor.h" -#include "internal/platform/socket.h" -#include "proto/connections_enums.pb.h" -#include "proto/mediums/multiplex_frames.pb.h" - -namespace nearby { -namespace connections { -namespace mediums { -namespace multiplex { - -using MultiplexEnbaleCb = absl::AnyInvocable; -using MultiplexIncomingConnectionCb = absl::AnyInvocable; - -class MultiplexSocket { - public: - MultiplexSocket(const MultiplexSocket&) = delete; - MultiplexSocket& operator=(const MultiplexSocket&) = delete; - ~MultiplexSocket() { ShutdownAll(); }; - - // Creates a new incoming MultiplexSocket. - static MultiplexSocket* CreateIncomingSocket( - std::shared_ptr physical_socket, - const std::string& service_id, std::int32_t first_frame_len); - // Creates a new outgoing MultiplexSocket. - static MultiplexSocket* CreateOutgoingSocket( - std::shared_ptr physical_socket, - const std::string& service_id, const std::string& service_id_hash_salt); - - // Creates a new outgoing MultiplexSocket with default service_id_hash_salt. - static MultiplexSocket* CreateOutgoingSocket( - std::shared_ptr physical_socket, - const std::string& service_id); - - // A Table of service Id as row key, medium type as column key, and - // MultiplexIncomingConnectionCb as value. Non-empty while the client starts - // listening for incoming virtual socket. The MultiplexIncomingConnectionCb - // will be called when the incoming virtual socket is established. - static absl::flat_hash_map< - std::pair, - MultiplexIncomingConnectionCb>& - GetIncomingConnectionCallbacks(); - - // Listens for incoming connection through multiplex for specified {@code - // service_id} on medium - // {@code type}. Should register the callback before new the MultiplexSocket. - static void ListenForIncomingConnection( - const std::string& service_id, - ::location::nearby::proto::connections::Medium type, - absl::AnyInvocable - incoming_connection_cb); - - // Stops listening for incoming multiplex connection for {@code service_id} on - // medium {@code type}. - static void StopListeningForIncomingConnection( - const std::string& service_id, - ::location::nearby::proto::connections::Medium type); - - bool IsEnabled() { return enabled_.Get(); } - void Enable() { - LOG(INFO) << "Enable the Multiplex MediumSocket."; - enabled_.Set(true); - } - - // Gets the virtual socket by service id. - MediumSocket* GetVirtualSocket(const std::string& service_id); - // Gets the virtual socket count. - int GetVirtualSocketCount(); - - void ListVirtualSocket() - ABSL_EXCLUSIVE_LOCKS_REQUIRED(virtual_socket_mutex_); - - // Establishes the virtual socket by service id. - MediumSocket* EstablishVirtualSocket(const std::string& service_id); - // Shuts down the multiplex socket. - void Shutdown(); - bool IsShutdown() { return is_shutdown_; } - void SetShutdown(bool is_shutdown) { is_shutdown_ = is_shutdown; } - void ShutdownAll(); - - private: - explicit MultiplexSocket(std::shared_ptr physical_socket); - - // Creates the first virtual socket for the service id. The first virtual - // socket is created by the sender. - MediumSocket* CreateFirstVirtualSocket( - const std::string& service_id, const std::string& service_id_hash_salt); - // Creates the virtual socket for the service id. - MediumSocket* CreateVirtualSocket(const std::string& service_id, - const std::string& service_id_hash_salt); - // Registers the connection response future for the service id. - std::shared_ptr> - RegisterConnectionResponse(const std::string& service_id); - // Unregisters the connection response future for the service id. - void UnRegisterConnectionResponse(const std::string& service_id); - // Starts the reader thread to read the incoming MultiplexFrame from the - // physical socket. - void StartReaderThread(std::int32_t first_frame_len); - // Handles the offline frame from the physical socket. - void HandleOfflineFrame(const ByteArray& bytes); - // Handles the control frame from the physical socket. - void HandleControlFrame( - const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt, - const ::location::nearby::mediums::MultiplexControlFrame& frame); - // Handles the connection request frame from the physical socket. - void HandleConnectionRequest(const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt); - // Handles the connection response frame from the physical socket. - void HandleConnectionResponse( - const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt, - const ::location::nearby::mediums::ConnectionResponseFrame& frame); - // Handles the disconnection frame from the physical socket. - void HandleDisconnection(const ByteArray& salted_service_id_hash); - // Handles the data frame from the physical socket. - void HandleDataFrame( - const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt, - const ::location::nearby::mediums::MultiplexDataFrame& frame); - // Handles the physical socket closed. - void OnPhysicalSocketClosed(); - // Remaps and gets the virtual socket by service id hash. - MediumSocket* ReMapAndGetVirtualSocket( - const ByteArray& salted_service_id_hash, - const std::string& service_id_hash_salt); - // Handles the virtual socket closed. - void OnVirtualSocketClosed(const std::string& service_id); - // Runs the offload thread. - void RunOffloadThread(const std::string& name, - absl::AnyInvocable runnable); - - // The physical socket connect to the remote device. - std::shared_ptr physical_socket_ptr_; - - // The output stream to manage all outgoing frames from all clients. - MultiplexOutputStream multiplex_output_stream_; - // The {@link InputStream} of the physical socket. It is used to read the - // incoming MultiplexFrame from the physical socket. - InputStream* physical_reader_; - // The medium type of the physical socket. - Medium medium_; - - // The callback to enable the MultiplexSocket. - std::shared_ptr> enable_cb_ = - std::make_shared>([this]() { Enable(); }); - - // A map of service Id -> {@link SettableFuture} for waiting the - // ConnectionResponse. Non-empty while requesting the virtual socket. - absl::flat_hash_map>> - connection_response_futures_; - - // A map of service Id hash key -> virtual socket. Non-empty while at least - // one virtual socket alive. Class derived from "MediumSocket" should define a - // pointer to the virtual sockets map. When here's any virtual socket - // operation, it will be reflected in both derived MediumSocket class and - // MultiplexSocket object - mutable Mutex virtual_socket_mutex_; - absl::flat_hash_map> - virtual_sockets_ ABSL_GUARDED_BY(virtual_socket_mutex_); - - // The thread to receive incoming MultiplexFrame from the physical socket. - SingleThreadExecutor physical_reader_thread_; - // The single thread we throw the potentially blocking work on to. - SingleThreadExecutor single_thread_offloader_; - - // The status of the MultiplexSocket enabled or disabled, it depends on both - // Sender and Receiver supports MultiplexSocket or not. Default disabled and - // enable it once two devices negotiated finished. - AtomicBoolean enabled_{false}; - - // If the socket is already shutdown and no longer in use. - bool is_shutdown_ = false; - static AtomicBoolean is_shutting_down_; - std::unique_ptr reader_thread_shutdown_barrier_; -}; - -} // namespace multiplex -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif // CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_SOCKET_H_ diff --git a/connections/implementation/mediums/multiplex/multiplex_socket_test.cc b/connections/implementation/mediums/multiplex/multiplex_socket_test.cc deleted file mode 100644 index 3b523d20..00000000 --- a/connections/implementation/mediums/multiplex/multiplex_socket_test.cc +++ /dev/null @@ -1,451 +0,0 @@ -// Copyright 2024 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/multiplex/multiplex_socket.h" - -#include -#include -#include -#include - -#include "gtest/gtest.h" -#include "absl/container/flat_hash_map.h" -#include "absl/strings/string_view.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "connections/implementation/mediums/multiplex/multiplex_frames.h" -#include "connections/implementation/offline_frames.h" -#include "internal/platform/base64_utils.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/exception.h" -#include "internal/platform/feature_flags.h" -#include "internal/platform/future.h" -#include "internal/platform/input_stream.h" -#include "internal/platform/logging.h" -#include "internal/platform/output_stream.h" -#include "internal/platform/pipe.h" -#include "internal/platform/single_thread_executor.h" -#include "internal/platform/socket.h" -#include "proto/connections_enums.proto.h" - -namespace nearby { -namespace connections { -namespace mediums { -namespace multiplex { - -constexpr absl::string_view SERVICE_ID_1 = "serviceId_1"; -constexpr absl::string_view SERVICE_ID_2 = "serviceId_2"; - -using location::nearby::mediums::ConnectionResponseFrame; -using location::nearby::mediums::MultiplexControlFrame; -using location::nearby::mediums::MultiplexFrame; -using location::nearby::proto::connections::Medium; -using location::nearby::proto::connections::Medium_Name; - -// A fake socket for testing. -class FakeSocket : public MediumSocket { - public: - explicit FakeSocket(Medium medium) : MediumSocket(medium) { - pipe_1_ = CreatePipe(); - reader_1_ = std::move(pipe_1_.first); - writer_1_ = std::move(pipe_1_.second); - pipe_2_ = CreatePipe(); - reader_2_ = std::move(pipe_2_.first); - writer_2_ = std::move(pipe_2_.second); - LOG(WARNING) << "Physical Socket Medium:" << Medium_Name(GetMedium()); - }; - ~FakeSocket() override = default; - - FakeSocket(const FakeSocket&) = default; - FakeSocket& operator=(const FakeSocket&) = default; - - /** - * The constructor for a virtual socket which own the virtual {@link - * OutputStream} and {@link InputStream}. - */ - explicit FakeSocket(Medium medium, OutputStream* virtualOutputStream) - : MediumSocket(medium), - is_virtual_socket_(true), - virtual_output_stream_(virtualOutputStream) { - pipe_1_ = CreatePipe(); - reader_1_ = std::move(pipe_1_.first); - writer_1_ = std::move(pipe_1_.second); - pipe_2_ = CreatePipe(); - reader_2_ = std::move(pipe_2_.first); - writer_2_ = std::move(pipe_2_.second); - } - - InputStream& GetInputStream() override { return *reader_1_; } - OutputStream& GetOutputStream() override { - return IsVirtualSocket() ? *virtual_output_stream_ - : *writer_2_; - } Exception Close() override { - if (IsVirtualSocket()) { - LOG(INFO) << "Multiplex: Closing virtual socket: " << this; - CloseLocal(); - return {Exception::kSuccess}; - } - LOG(INFO) << "Multiplex: Closing physical socket: " << this; - reader_1_->Close(); - reader_2_->Close(); - writer_1_->Close(); - writer_2_->Close(); - return {Exception::kSuccess}; - } - - MediumSocket* CreateVirtualSocket( - const std::string& salted_service_id_hash_key, OutputStream* outputstream, - Medium medium, - absl::flat_hash_map>* - virtual_sockets_ptr) override { - if (IsVirtualSocket()) { - LOG(WARNING) - << "Creating the virtual socket on a virtual socket is not allowed."; - return nullptr; - } - - auto virtual_socket = std::make_shared(medium, outputstream); - LOG(WARNING) << "Created the virtual socket for Medium: " - << Medium_Name(virtual_socket->GetMedium()); - - if (virtual_sockets_ptr_ == nullptr) { - virtual_sockets_ptr_ = virtual_sockets_ptr; - } - - (*virtual_sockets_ptr_)[salted_service_id_hash_key] = virtual_socket; - LOG(INFO) << "virtual_sockets_ size: " << virtual_sockets_ptr_->size(); - return virtual_socket.get(); - } - - void FeedIncomingData(ByteArray data) override { - bytes_read_future_.Set(data); - LOG(INFO) << "FeedIncomingData. Size of receive data: " << data.size() - << ", bytes content:" << std::string(data); - } - - bool IsVirtualSocket() override { return is_virtual_socket_; } - Future& GetByteReadFuture() { return bytes_read_future_; } - - std::pair, std::unique_ptr> - pipe_1_; - std::unique_ptr reader_1_; - std::unique_ptr writer_1_; - std::pair, std::unique_ptr> - pipe_2_; - std::unique_ptr reader_2_; - std::unique_ptr writer_2_; - - private: - bool is_virtual_socket_ = false; - Future bytes_read_future_; - absl::flat_hash_map>* - virtual_sockets_ptr_ = nullptr; - OutputStream* virtual_output_stream_ = nullptr; -}; - -TEST(MultiplexSocketTest, CreateIncomingSocketSuccess) { - auto fake_socket_ptr = std::make_shared(Medium::BLUETOOTH); - MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_1), - Medium::BLUETOOTH); - MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_2), - Medium::BLUETOOTH); - MultiplexSocket::ListenForIncomingConnection( - std::string(SERVICE_ID_1), Medium::BLUETOOTH, - [](const std::string& service_id, MediumSocket* socket) { - LOG(INFO) << "Incoming connection for service_id: " << service_id; - }); - MultiplexSocket::ListenForIncomingConnection( - std::string(SERVICE_ID_2), Medium::BLUETOOTH, - [](const std::string& service_id, MediumSocket* socket) { - LOG(INFO) << "Incoming connection for service_id: " << service_id; - }); - - MultiplexSocket* multiplex_socket_incoming = - MultiplexSocket::CreateIncomingSocket( - fake_socket_ptr, std::string(SERVICE_ID_1), /*first_frame_len*/ 0); - ASSERT_NE(multiplex_socket_incoming, nullptr); - MultiplexSocket* multiplex_socket_incoming_2 = - MultiplexSocket::CreateIncomingSocket( - fake_socket_ptr, std::string(SERVICE_ID_2), /*first_frame_len*/ 0); - ASSERT_EQ(multiplex_socket_incoming_2, multiplex_socket_incoming); - - FakeSocket* virtual_socket = - (FakeSocket*)multiplex_socket_incoming->GetVirtualSocket( - std::string(SERVICE_ID_1)); - if (virtual_socket == nullptr) { - LOG(INFO) << "Virtual socket not found for " << SERVICE_ID_1; - return; - } - - SingleThreadExecutor executor; - FakeSocket* socket = fake_socket_ptr.get(); - executor.Execute([socket]() { - ByteArray connection_req_frame = parser::ForConnectionRequestConnections( - {}, { - .local_endpoint_id = "endpoint1", - .local_endpoint_info = ByteArray("endpoint1 info"), - }); - auto& writer = socket->writer_1_; - LOG(INFO) << "writer_1_ Write start"; - Base64Utils::WriteInt(writer.get(), connection_req_frame.size()); - writer->Write(connection_req_frame.AsStringView()); - writer->Flush(); - LOG(INFO) << "writer_1_ Write end"; - }); - - ExceptionOr result = virtual_socket->GetByteReadFuture().Get(); - if (!result.ok()) { - ADD_FAILURE() << "Read error: " << result.GetException().value; - } - ByteArray data = result.result(); - LOG(INFO) << "Received " << data.size() << " bytes of data."; - EXPECT_NE(data.size(), 0); - absl::SleepFor(absl::Milliseconds(100)); - - EXPECT_EQ(multiplex_socket_incoming->GetVirtualSocketCount(), 1); - virtual_socket->Close(); - EXPECT_EQ(multiplex_socket_incoming->GetVirtualSocketCount(), 0); - multiplex_socket_incoming->ShutdownAll(); -} - -TEST(MultiplexSocketTest, CreateFail_MediumNotSupport) { - auto fake_socket_ptr = std::make_shared(Medium::WEB_RTC); - MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_1), - Medium::WEB_RTC); - MultiplexSocket* multiplex_socket_incoming = - MultiplexSocket::CreateIncomingSocket( - fake_socket_ptr, std::string(SERVICE_ID_1), /*first_frame_len*/ 0); - - ASSERT_EQ(multiplex_socket_incoming, nullptr); -} - -TEST(MultiplexSocketTest, CreateIncomingVirtualSocketSuccess) { - auto fake_socket_ptr = std::make_shared(Medium::WIFI_LAN); - - MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_1), - Medium::WIFI_LAN); - MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_2), - Medium::WIFI_LAN); - MultiplexSocket::ListenForIncomingConnection( - std::string(SERVICE_ID_1), Medium::WIFI_LAN, - [](const std::string& service_id, MediumSocket* socket) { - LOG(INFO) << "Incoming connection for service_id: " << service_id; - }); - MultiplexSocket::ListenForIncomingConnection( - std::string(SERVICE_ID_2), Medium::WIFI_LAN, - [](const std::string& service_id, MediumSocket* socket) { - LOG(INFO) << "Incoming connection for service_id: " << service_id; - }); - - MultiplexSocket* multiplex_socket_incoming = - MultiplexSocket::CreateIncomingSocket( - fake_socket_ptr, std::string(SERVICE_ID_1), /*first_frame_len*/ 0); - ASSERT_NE(multiplex_socket_incoming, nullptr); - - FakeSocket* virtual_socket = - (FakeSocket*)multiplex_socket_incoming->GetVirtualSocket( - std::string(SERVICE_ID_1)); - if (virtual_socket == nullptr) { - LOG(INFO) << "Virtual socket not found for " << SERVICE_ID_1; - return; - } - - SingleThreadExecutor executor; - FakeSocket* socket = fake_socket_ptr.get(); - executor.Execute([socket]() { - ByteArray connection_req_frame = ForConnectionRequest( - std::string(SERVICE_ID_2), "J7frzSmHK-VBTHjCKpf4ew"); - auto& writer = socket->writer_1_; - LOG(INFO) << "writer_1_ Write start"; - Base64Utils::WriteInt(writer.get(), connection_req_frame.size()); - writer->Write(connection_req_frame.AsStringView()); - writer->Flush(); - LOG(INFO) << "writer_1_ Write end"; - }); - absl::SleepFor(absl::Milliseconds(100)); - - EXPECT_EQ(multiplex_socket_incoming->GetVirtualSocketCount(), 2); - virtual_socket->Close(); - EXPECT_EQ(multiplex_socket_incoming->GetVirtualSocketCount(), 1); - multiplex_socket_incoming->ShutdownAll(); -} - -TEST(MultiplexSocketTest, - EstablishVirtualSocket_Timeout_BecauseNoConnectionResponse) { - auto fake_socket_ptr = std::make_shared(Medium::WIFI_LAN); - MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_1), - Medium::WIFI_LAN); - MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_2), - Medium::WIFI_LAN); - MultiplexSocket* multiplex_socket = MultiplexSocket::CreateOutgoingSocket( - fake_socket_ptr, std::string(SERVICE_ID_1)); - ASSERT_NE(multiplex_socket, nullptr); - MultiplexSocket* multiplex_socket_2 = MultiplexSocket::CreateOutgoingSocket( - fake_socket_ptr, std::string(SERVICE_ID_2)); - ASSERT_EQ(multiplex_socket_2, multiplex_socket); - multiplex_socket->Enable(); - FakeSocket* virtual_socket = (FakeSocket*)multiplex_socket->GetVirtualSocket( - std::string(SERVICE_ID_1)); - if (virtual_socket == nullptr) { - LOG(INFO) << "Virtual socket not found for " << SERVICE_ID_1; - return; - } - - SingleThreadExecutor establish_socket_executor; - establish_socket_executor.Execute([&multiplex_socket]() { - LOG(INFO) << "EstablishVirtualSocket"; - MediumSocket* socket = - multiplex_socket->EstablishVirtualSocket(std::string(SERVICE_ID_2)); - LOG(INFO) << "EstablishVirtualSocket finished"; - EXPECT_EQ(socket, nullptr); - }); - - SingleThreadExecutor read_executor; - read_executor.Execute([&multiplex_socket, &fake_socket_ptr]() { - auto reader = fake_socket_ptr->reader_2_.get(); - LOG(INFO) << "reader_2_ Read start"; - ExceptionOr read_int = Base64Utils::ReadInt(reader); - if (!read_int.ok()) { - ADD_FAILURE() << "Failed to read. Exception:" << read_int.exception(); - } - auto length = read_int.result(); - LOG(INFO) << " length:" << length; - EXPECT_GT(length, 0); - EXPECT_EQ(multiplex_socket->GetVirtualSocket(std::string(SERVICE_ID_2)), - nullptr); - }); - - absl::SleepFor(absl::Milliseconds(300)); - EXPECT_EQ(multiplex_socket->GetVirtualSocketCount(), 1); - virtual_socket->Close(); - EXPECT_EQ(multiplex_socket->GetVirtualSocketCount(), 0); - multiplex_socket->ShutdownAll(); -} - -TEST(MultiplexSocketTest, EstablishVirtualSocket_RemoteAccepted) { - auto fake_socket_ptr = std::make_shared(Medium::BLUETOOTH); - MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_1), - Medium::BLUETOOTH); - MultiplexSocket::StopListeningForIncomingConnection(std::string(SERVICE_ID_2), - Medium::BLUETOOTH); - - MultiplexSocket* multiplex_socket = MultiplexSocket::CreateOutgoingSocket( - fake_socket_ptr, std::string(SERVICE_ID_1)); - ASSERT_NE(multiplex_socket, nullptr); - MultiplexSocket* multiplex_socket_2 = MultiplexSocket::CreateOutgoingSocket( - fake_socket_ptr, std::string(SERVICE_ID_2)); - ASSERT_EQ(multiplex_socket_2, multiplex_socket); - - SingleThreadExecutor executor; - CountDownLatch latch(1); - executor.Execute([&multiplex_socket, &latch]() { - LOG(INFO) << "EstablishVirtualSocket"; - MediumSocket* socket = - multiplex_socket->EstablishVirtualSocket(std::string(SERVICE_ID_2)); - EXPECT_EQ(socket, nullptr); - latch.CountDown(); - }); - latch.Await(); - - multiplex_socket->Enable(); - executor.Execute([&multiplex_socket]() { - LOG(INFO) << "EstablishVirtualSocket"; - MediumSocket* socket = - multiplex_socket->EstablishVirtualSocket(std::string(SERVICE_ID_2)); - EXPECT_NE(socket, nullptr); - }); - - auto reader = fake_socket_ptr->reader_2_.get(); - LOG(INFO) << "reader_2_ Waiting for CONNECTION_REQUEST frame."; - ExceptionOr read_int = Base64Utils::ReadInt(reader); - if (!read_int.ok()) { - ADD_FAILURE() << "Failed to read length.Exception:" << read_int.exception(); - } - auto length = read_int.result(); - if (length < 0 || - length > - FeatureFlags::GetInstance().GetFlags().connection_max_frame_length) { - ADD_FAILURE() << "Invalid length:" << length; - } - - auto bytes = reader->ReadExactly(length); - if (!bytes.ok()) { - ADD_FAILURE() << "Failed to read frame. Exception:" << bytes.exception(); - } - length = read_int.result(); - if (length < 0 || - length > - FeatureFlags::GetInstance().GetFlags().connection_max_frame_length) { - ADD_FAILURE() << "Invalid frame length:" << length; - } - - ExceptionOr frame_exc = multiplex::FromBytes(bytes.result()); - if (!frame_exc.ok()) { - ADD_FAILURE() << "Failed to parse MultiplexFrame. Exception:" - << frame_exc.exception(); - } - auto frame = frame_exc.result(); - auto salted_service_id_hash = - ByteArray{std::move(frame.header().salted_service_id_hash())}; - auto service_id_hash_salt = frame.header().has_service_id_hash_salt() - ? frame.header().service_id_hash_salt() - : ""; - ASSERT_EQ(frame.frame_type(), MultiplexFrame::CONTROL_FRAME); - auto control_frame = frame.control_frame(); - ASSERT_EQ(control_frame.control_frame_type(), - MultiplexControlFrame::CONNECTION_REQUEST); - LOG(INFO) << "Recieved MultiplexControlFrame::CONNECTION_REQUEST " - "frame, now send CONNECTION_RESPONSE frame."; - - ByteArray connection_response_frame = - ForConnectionResponse(salted_service_id_hash, service_id_hash_salt, - ConnectionResponseFrame::CONNECTION_ACCEPTED); - auto& writer = fake_socket_ptr->writer_1_; - LOG(INFO) << "writer_1_ Write start"; - Base64Utils::WriteInt(writer.get(), connection_response_frame.size()); - writer->Write(connection_response_frame.AsStringView()); - writer->Flush(); - LOG(INFO) << "writer_1_ Write end"; - absl::SleepFor(absl::Milliseconds(100)); - EXPECT_NE(multiplex_socket->GetVirtualSocket(std::string(SERVICE_ID_2)), - nullptr); - - EXPECT_EQ(multiplex_socket->GetVirtualSocketCount(), 2); - - LOG(INFO) << "Send Data frame on virtual socket for SERVICE_ID_2."; - ByteArray data_frame = - ForData(std::string(SERVICE_ID_2), service_id_hash_salt, - /*should_pass_salt=*/true, absl::string_view("data")); - Base64Utils::WriteInt(writer.get(), data_frame.size()); - writer->Write(data_frame.AsStringView()); - writer->Flush(); - absl::SleepFor(absl::Milliseconds(100)); - - LOG(INFO) << "Send disconnection frame on virtual socket for SERVICE_ID_2."; - ByteArray disconnect_frame = - ForDisconnection(std::string(SERVICE_ID_2), service_id_hash_salt); - Base64Utils::WriteInt(writer.get(), disconnect_frame.size()); - writer->Write(disconnect_frame.AsStringView()); - writer->Flush(); - absl::SleepFor(absl::Milliseconds(100)); - EXPECT_EQ(multiplex_socket->GetVirtualSocketCount(), 1); - - multiplex_socket->ShutdownAll(); -} - -} // namespace multiplex -} // namespace mediums -} // namespace connections -} // namespace nearby diff --git a/connections/implementation/mediums/webrtc.cc b/connections/implementation/mediums/webrtc.cc deleted file mode 100644 index c0fc4a56..00000000 --- a/connections/implementation/mediums/webrtc.cc +++ /dev/null @@ -1,788 +0,0 @@ -// 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. - -#ifndef NO_WEBRTC - -#include "connections/implementation/mediums/webrtc.h" - -#include -#include -#include -#include -#include - -#include "absl/container/flat_hash_set.h" -#include "absl/functional/bind_front.h" -#include "absl/time/time.h" -#include "connections/implementation/mediums/webrtc/connection_flow.h" -#include "connections/implementation/mediums/webrtc/session_description_wrapper.h" -#include "connections/implementation/mediums/webrtc/signaling_frames.h" -#include "connections/implementation/mediums/webrtc_peer_id.h" -#include "connections/implementation/mediums/webrtc_socket.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/cancelable_alarm.h" -#include "internal/platform/cancellation_flag.h" -#include "internal/platform/cancellation_flag_listener.h" -#include "internal/platform/exception.h" -#include "internal/platform/expected.h" -#include "internal/platform/feature_flags.h" -#include "internal/platform/future.h" -#include "internal/platform/logging.h" -#include "internal/platform/mutex_lock.h" -#include "internal/platform/runnable.h" -#include "internal/platform/webrtc.h" -#include "webrtc/api/jsep.h" - -namespace nearby { -namespace connections { -namespace mediums { - -namespace { -using ::location::nearby::connections::LocationHint; -using ::location::nearby::proto::connections::OperationResultCode; - -// The maximum amount of time to wait to connect to a data channel via WebRTC. -constexpr absl::Duration kDataChannelTimeout = absl::Seconds(10); - -// Delay between restarting signaling messenger to receive messages. -constexpr absl::Duration kRestartReceiveMessagesDuration = absl::Seconds(60); - -} // namespace - -WebRtc::WebRtc() : WebRtc(std::make_unique()) {} - -WebRtc::WebRtc(std::unique_ptr medium) - : medium_(std::move(medium)) {} - -WebRtc::~WebRtc() { - // This ensures that all pending callbacks are run before we reset the medium - // and we are not accepting new runnables. - single_thread_executor_.Shutdown(); - - // Stop accepting all connections - absl::flat_hash_set service_ids; - for (auto& item : accepting_connections_info_) { - service_ids.emplace(item.first); - } - for (const auto& service_id : service_ids) { - StopAcceptingConnections(service_id); - } -} - -std::string WebRtc::GetDefaultCountryCode() { - return medium_->GetDefaultCountryCode(); -} - -bool WebRtc::IsAvailable() { return medium_->IsValid(); } - -bool WebRtc::IsAcceptingConnections(const std::string& service_id) { - MutexLock lock(&mutex_); - return IsAcceptingConnectionsLocked(service_id); -} - -bool WebRtc::IsAcceptingConnectionsLocked(const std::string& service_id) { - return accepting_connections_info_.contains(service_id); -} - -bool WebRtc::StartAcceptingConnections(const std::string& service_id, - const WebrtcPeerId& self_peer_id, - const LocationHint& location_hint, - AcceptedConnectionCallback callback, - bool non_cellular) { - MutexLock lock(&mutex_); - if (!IsAvailable()) { - LOG(WARNING) << "Cannot start accepting WebRTC connections because " - "WebRTC is not available."; - return false; - } - - if (IsAcceptingConnectionsLocked(service_id)) { - LOG(WARNING) << "Cannot start accepting WebRTC connections because service " - << service_id << "is already accepting WebRTC connections."; - return false; - } - - // We'll track our state here, so that we're separated from the other services - // who may be also using WebRTC. - AcceptingConnectionsInfo info = AcceptingConnectionsInfo(); - info.self_peer_id = self_peer_id; - info.accepted_connection_callback = std::move(callback); - - medium_->SetNonCellular(non_cellular); - - // Create a new SignalingMessenger so that we can communicate w/ Tachyon. - info.signaling_messenger = - medium_->GetSignalingMessenger(self_peer_id.GetId(), location_hint); - if (!info.signaling_messenger->IsValid()) { - return false; - } - - // This registers ourselves w/ Tachyon, creating a room from the PeerId. - // This allows a remote device to message us over Tachyon. - if (!info.signaling_messenger->StartReceivingMessages( - absl::bind_front(&WebRtc::OnSignalingMessage, this, service_id), - absl::bind_front(&WebRtc::OnSignalingComplete, this, service_id))) { - info.signaling_messenger.reset(); - return false; - } - - // We'll automatically disconnect from Tachyon after 60sec. When this alarm - // fires, we'll recreate our room so we continue to receive messages. - info.restart_tachyon_receive_messages_alarm = - std::make_unique( - "restart_receiving_messages_webrtc", - std::bind(&WebRtc::ProcessRestartTachyonReceiveMessages, this, - service_id), - kRestartReceiveMessagesDuration, &single_thread_executor_); - - // Now that we're set up to receive messages, we'll save our state and return - // a successful result. - accepting_connections_info_.emplace(service_id, std::move(info)); - LOG(INFO) << "Started listening for WebRTC connections as " - << self_peer_id.GetId() << " on service " << service_id; - return true; -} - -void WebRtc::StopAcceptingConnections(const std::string& service_id) { - MutexLock lock(&mutex_); - if (!IsAcceptingConnectionsLocked(service_id)) { - LOG(WARNING) << "Cannot stop accepting WebRTC connections because service " - << service_id << "is not accepting WebRTC connections."; - return; - } - - // Grab our info from the map. - auto& info = accepting_connections_info_.find(service_id)->second; - - // Stop receiving messages from Tachyon. - info.signaling_messenger->StopReceivingMessages(); - info.signaling_messenger.reset(); - - // Cancel the scheduled alarm. - if (info.restart_tachyon_receive_messages_alarm && - info.restart_tachyon_receive_messages_alarm->IsValid()) { - info.restart_tachyon_receive_messages_alarm->Cancel(); - info.restart_tachyon_receive_messages_alarm.reset(); - } - - // If we had any in-progress connections that haven't materialized into full - // DataChannels yet, it's time to shut them down since they can't reach us - // anymore. - absl::flat_hash_set peer_ids; - for (auto& item : connection_flows_) { - peer_ids.emplace(item.first); - } - for (const auto& peer_id : peer_ids) { - const auto& entry = connection_flows_.find(peer_id); - // Skip outgoing connections in this step. Start/StopAcceptingConnections - // only deals with incoming connections. - if (requesting_connections_info_.contains(peer_id)) { - continue; - } - - // Skip fully connected connections in this step. If the connection was - // formed while we were accepting connections, then it will stay alive until - // it's explicitly closed. - if (!entry->second->CloseIfNotConnected()) { - continue; - } - - connection_flows_.erase(peer_id); - } - - // Clean up our state. We're now no longer listening for connections. - accepting_connections_info_.erase(service_id); - LOG(INFO) << "Stopped listening for WebRTC connections for service " - << service_id; -} - -ErrorOr WebRtc::Connect( - const std::string& service_id, const WebrtcPeerId& remote_peer_id, - const LocationHint& location_hint, CancellationFlag* cancellation_flag, - bool non_cellular) { - service_id_to_connect_attempts_count_map_[service_id] = 1; - medium_->SetNonCellular(non_cellular); - ErrorOr wrapper_result = { - Error(OperationResultCode::DETAIL_UNKNOWN)}; - while (service_id_to_connect_attempts_count_map_[service_id] <= - kConnectAttemptsLimit) { - if (cancellation_flag->Cancelled()) { - LOG(WARNING) << "Attempt #" - << service_id_to_connect_attempts_count_map_[service_id] - << ": Cannot Connect with WebRtc due to cancel."; - return { - Error(OperationResultCode:: - CLIENT_CANCELLATION_CANCEL_WEB_RTC_OUTGOING_CONNECTION)}; - } - - LOG(INFO) << "Attempt #" - << service_id_to_connect_attempts_count_map_[service_id] - << ": Beginning connection."; - wrapper_result = AttemptToConnect(service_id, remote_peer_id, location_hint, - cancellation_flag); - if (wrapper_result.has_value()) { - return std::move(wrapper_result.value()); - } - - service_id_to_connect_attempts_count_map_[service_id]++; - } - - LOG(WARNING) << "Giving up after " << kConnectAttemptsLimit << " attempts"; - return {Error(wrapper_result.error().operation_result_code().value())}; -} - -ErrorOr WebRtc::AttemptToConnect( - const std::string& service_id, const WebrtcPeerId& remote_peer_id, - const LocationHint& location_hint, CancellationFlag* cancellation_flag) { - ConnectionRequestInfo info = ConnectionRequestInfo(); - info.self_peer_id = WebrtcPeerId::FromRandom(); - Future socket_future = info.socket_future; - - // `listener` will go out of scope at the end of `AttemptToConnect`, and this - // is expected. This `listener` is tied to `socket_future` which we block on - // within this stack call, and will not go out of scope until the attempt - // is complete. - CancellationFlagListener listener( - cancellation_flag, [this, &service_id, &socket_future]() { - LOG(WARNING) << "Attempt # " - << service_id_to_connect_attempts_count_map_[service_id] - << " to connect with WebRtc stopped due to cancel."; - socket_future.SetException({Exception::kFailed}); - }); - - { - MutexLock lock(&mutex_); - if (!IsAvailable()) { - LOG(WARNING) << "Cannot connect to WebRTC peer " << remote_peer_id.GetId() - << " because WebRTC is not available."; - return { - Error(OperationResultCode::MEDIUM_UNAVAILABLE_WEB_RTC_NOT_AVAILABLE)}; - } - - // Create a new ConnectionFlow for this connection attempt. - std::unique_ptr connection_flow = - CreateConnectionFlow(service_id, remote_peer_id); - if (!connection_flow) { - LOG(INFO) << "Cannot connect to WebRTC peer " << remote_peer_id.GetId() - << " because we failed to create a ConnectionFlow."; - return {Error(OperationResultCode::NEARBY_WEB_RTC_CONNECTION_FLOW_NULL)}; - } - - // Create a new SignalingMessenger so that we can communicate over Tachyon. - info.signaling_messenger = medium_->GetSignalingMessenger( - info.self_peer_id.GetId(), location_hint); - if (!info.signaling_messenger->IsValid()) { - LOG(INFO) << "Cannot connect to WebRTC peer " << remote_peer_id.GetId() - << " because we failed to create a SignalingMessenger."; - return { - Error(OperationResultCode:: - MISCELLEANEOUS_WEB_RTC_TACHYON_SIGNALING_MESSENGER_NULL)}; - } - - // This registers ourselves w/ Tachyon, creating a room from the PeerId. - // This allows a remote device to message us over Tachyon. - auto signaling_complete_callback = [socket_future](bool success) mutable { - if (!success) { - socket_future.SetException({Exception::kFailed}); - } - }; - if (!info.signaling_messenger->StartReceivingMessages( - absl::bind_front(&WebRtc::OnSignalingMessage, this, service_id), - signaling_complete_callback)) { - LOG(INFO) - << "Cannot connect to WebRTC peer " << remote_peer_id.GetId() - << " because we failed to start receiving messages over Tachyon."; - info.signaling_messenger.reset(); - return {Error(OperationResultCode:: - MISCELLEANEOUS_WEB_RTC_FAILED_TO_RECEIVE_MESSAGE)}; - } - - // Poke the remote device. This will cause them to send us an Offer. - if (!info.signaling_messenger->SendMessage( - remote_peer_id.GetId(), - webrtc_frames::EncodeReadyForSignalingPoke(info.self_peer_id))) { - LOG(INFO) << "Cannot connect to WebRTC peer " << remote_peer_id.GetId() - << " because we failed to poke the peer over Tachyon."; - info.signaling_messenger.reset(); - return {Error(OperationResultCode:: - CONNECTIVITY_WEB_RTC_CONNECT_TO_TACHYON_FAILURE)}; - } - - // Create a new ConnectionRequest entry. This map will be used later to look - // up state as we negotiate the connection over Tachyon. - requesting_connections_info_.emplace(remote_peer_id.GetId(), - std::move(info)); - connection_flows_.emplace(remote_peer_id.GetId(), - std::move(connection_flow)); - } - - // Wait for the connection to go through. Don't hold the mutex here so that - // we're not blocking necessary operations. - ExceptionOr socket_result = - socket_future.Get(kDataChannelTimeout); - - { - MutexLock lock(&mutex_); - - // Reclaim our info, since we had released ownership while talking to - // Tachyon. - auto& info = - requesting_connections_info_.find(remote_peer_id.GetId())->second; - - // Verify that the connection went through. - if (!socket_result.ok()) { - LOG(INFO) << "Failed to connect to WebRTC peer " - << remote_peer_id.GetId(); - RemoveConnectionFlow(remote_peer_id); - info.signaling_messenger.reset(); - requesting_connections_info_.erase(remote_peer_id.GetId()); - return {Error(OperationResultCode:: - CONNECTIVITY_WEB_RTC_CLIENT_SOCKET_CREATION_FAILURE)}; - } - - // Clean up our ConnectionRequest. - info.signaling_messenger.reset(); - requesting_connections_info_.erase(remote_peer_id.GetId()); - - // Return the result. - return socket_result.GetResult(); - } -} - -void WebRtc::ProcessLocalIceCandidate( - const std::string& service_id, const WebrtcPeerId& remote_peer_id, - const location::nearby::mediums::IceCandidate ice_candidate) { - MutexLock lock(&mutex_); - - // Check first if we have an outgoing request w/ this peer. As this request is - // tied to a specific peer, it takes precedence. - const auto& connection_request_entry = - requesting_connections_info_.find(remote_peer_id.GetId()); - if (connection_request_entry != requesting_connections_info_.end()) { - // Pass the ice candidate to the remote side. - if (!connection_request_entry->second.signaling_messenger->SendMessage( - remote_peer_id.GetId(), - webrtc_frames::EncodeIceCandidates( - connection_request_entry->second.self_peer_id, - {ice_candidate}))) { - LOG(INFO) << "Failed to send ice candidate to " << remote_peer_id.GetId(); - } - - LOG(INFO) << "Sent ice candidate to " << remote_peer_id.GetId(); - return; - } - - // Check next if we're expecting incoming connection requests. - const auto& accepting_connection_entry = - accepting_connections_info_.find(service_id); - if (accepting_connection_entry != accepting_connections_info_.end()) { - // Pass the ice candidate to the remote side. - // TODO(xlythe) Consider not blocking here, since this can eat into the - // connection time - if (!accepting_connection_entry->second.signaling_messenger->SendMessage( - remote_peer_id.GetId(), - webrtc_frames::EncodeIceCandidates( - accepting_connection_entry->second.self_peer_id, - {ice_candidate}))) { - LOG(INFO) << "Failed to send ice candidate to " << remote_peer_id.GetId(); - } - - LOG(INFO) << "Sent ice candidate to " << remote_peer_id.GetId(); - return; - } - - LOG(INFO) << "Skipping restart listening for tachyon inbox messages " - "since we are not accepting connections for service " - << service_id; -} - -void WebRtc::OnSignalingMessage(const std::string& service_id, - const ByteArray& message) { - OffloadFromThread("rtc-on-signaling-message", [this, service_id, message]() { - ProcessTachyonInboxMessage(service_id, message); - }); -} - -void WebRtc::OnSignalingComplete(const std::string& service_id, bool success) { - LOG(INFO) << "Signaling completed with status: " << success; - if (success) { - return; - } - - OffloadFromThread("rtc-on-signaling-complete", [this, service_id]() { - MutexLock lock(&mutex_); - const auto& info_entry = accepting_connections_info_.find(service_id); - if (info_entry == accepting_connections_info_.end()) { - return; - } - - if (info_entry->second.restart_accept_connections_count < - kRestartAcceptConnectionsLimit) { - ++info_entry->second.restart_accept_connections_count; - } else { - return; - } - - RestartTachyonReceiveMessages(service_id); - }); -} - -void WebRtc::ProcessTachyonInboxMessage(const std::string& service_id, - const ByteArray& message) { - MutexLock lock(&mutex_); - - // Attempt to parse the incoming message as a WebRtcSignalingFrame. - location::nearby::mediums::WebRtcSignalingFrame frame; - if (!frame.ParseFromString(std::string(message))) { - LOG(WARNING) << "Failed to parse signaling message."; - return; - } - - // Ensure that the frame is valid (no missing fields). - if (!frame.has_sender_id()) { - LOG(WARNING) << "Invalid WebRTC frame: Sender ID is missing."; - return; - } - WebrtcPeerId remote_peer_id = WebrtcPeerId(frame.sender_id().id()); - - // Depending on the message type, we'll respond as appropriate. - if (requesting_connections_info_.contains(remote_peer_id.GetId())) { - // This is from a peer we have an outgoing connection request with, so we'll - // only process the Answer path. - if (frame.has_offer()) { - ReceiveOffer(remote_peer_id, - SessionDescriptionWrapper( - webrtc_frames::DecodeOffer(frame).release())); - SendAnswer(remote_peer_id); - } else if (frame.has_ice_candidates()) { - ReceiveIceCandidates(remote_peer_id, - webrtc_frames::DecodeIceCandidates(frame)); - } else { - LOG(INFO) << "Received unknown WebRTC frame: ignoring."; - } - } else if (IsAcceptingConnectionsLocked(service_id)) { - // We don't have an outgoing connection request with this peer, but we are - // accepting incoming requests so we'll only process the Offer path. - if (frame.has_ready_for_signaling_poke()) { - SendOffer(service_id, remote_peer_id); - } else if (frame.has_answer()) { - ReceiveAnswer(remote_peer_id, - SessionDescriptionWrapper( - webrtc_frames::DecodeAnswer(frame).release())); - } else if (frame.has_ice_candidates()) { - ReceiveIceCandidates(remote_peer_id, - webrtc_frames::DecodeIceCandidates(frame)); - } else { - LOG(INFO) << "Received unknown WebRTC frame: ignoring."; - } - } else { - LOG(INFO) - << "Ignoring Tachyon message since we are not accepting connections."; - } -} - -void WebRtc::SendOffer(const std::string& service_id, - const WebrtcPeerId& remote_peer_id) { - std::unique_ptr connection_flow = - CreateConnectionFlow(service_id, remote_peer_id); - if (!connection_flow) { - LOG(INFO) << "Unable to send offer. Failed to create a ConnectionFlow."; - return; - } - - SessionDescriptionWrapper offer = connection_flow->CreateOffer(); - if (!offer.IsValid()) { - LOG(INFO) << "Unable to send offer. Failed to create our offer locally."; - RemoveConnectionFlow(remote_peer_id); - return; - } - - const webrtc::SessionDescriptionInterface& sdp = offer.GetSdp(); - if (!connection_flow->SetLocalSessionDescription(offer)) { - LOG(INFO) << "Unable to send offer. Failed to register our offer locally."; - RemoveConnectionFlow(remote_peer_id); - return; - } - - // Grab our info from the map. - auto& info = accepting_connections_info_.find(service_id)->second; - - // Pass the offer to the remote side. - if (!info.signaling_messenger->SendMessage( - remote_peer_id.GetId(), - webrtc_frames::EncodeOffer(info.self_peer_id, sdp))) { - LOG(INFO) - << "Unable to send offer. Failed to write the offer to the remote peer " - << remote_peer_id.GetId(); - RemoveConnectionFlow(remote_peer_id); - return; - } - - // Store the ConnectionFlow so that other methods can use it later. - connection_flows_.emplace(remote_peer_id.GetId(), std::move(connection_flow)); - LOG(INFO) << "Sent offer to " << remote_peer_id.GetId(); -} - -void WebRtc::ReceiveOffer(const WebrtcPeerId& remote_peer_id, - SessionDescriptionWrapper offer) { - const auto& entry = connection_flows_.find(remote_peer_id.GetId()); - if (entry == connection_flows_.end()) { - LOG(INFO) << "Unable to receive offer. Failed to create a ConnectionFlow."; - return; - } - - if (!entry->second->OnOfferReceived(offer)) { - LOG(INFO) << "Unable to receive offer. Failed to process the offer."; - RemoveConnectionFlow(remote_peer_id); - } -} - -void WebRtc::SendAnswer(const WebrtcPeerId& remote_peer_id) { - const auto& entry = connection_flows_.find(remote_peer_id.GetId()); - if (entry == connection_flows_.end()) { - LOG(INFO) << "Unable to send answer. Failed to create a ConnectionFlow."; - return; - } - - SessionDescriptionWrapper answer = entry->second->CreateAnswer(); - if (!answer.IsValid()) { - LOG(INFO) << "Unable to send answer. Failed to create our answer locally."; - RemoveConnectionFlow(remote_peer_id); - return; - } - - const webrtc::SessionDescriptionInterface& sdp = answer.GetSdp(); - if (!entry->second->SetLocalSessionDescription(answer)) { - LOG(INFO) - << "Unable to send answer. Failed to register our answer locally."; - RemoveConnectionFlow(remote_peer_id); - return; - } - - // Grab our info from the map. - const auto& connection_request_entry = - requesting_connections_info_.find(remote_peer_id.GetId()); - if (connection_request_entry == requesting_connections_info_.end()) { - LOG(INFO) << "Unable to send answer. Failed to find an outgoing " - "connection request."; - RemoveConnectionFlow(remote_peer_id); - return; - } - - // Pass the answer to the remote side. - if (!connection_request_entry->second.signaling_messenger->SendMessage( - remote_peer_id.GetId(), - webrtc_frames::EncodeAnswer( - connection_request_entry->second.self_peer_id, sdp))) { - LOG(INFO) - << "Unable to send answer. Failed to write the answer to the remote " - "peer " - << remote_peer_id.GetId(); - RemoveConnectionFlow(remote_peer_id); - return; - } - - LOG(INFO) << "Sent answer to " << remote_peer_id.GetId(); -} - -void WebRtc::ReceiveAnswer(const WebrtcPeerId& remote_peer_id, - SessionDescriptionWrapper answer) { - const auto& entry = connection_flows_.find(remote_peer_id.GetId()); - if (entry == connection_flows_.end()) { - LOG(INFO) << "Unable to receive answer. Failed to create a ConnectionFlow."; - return; - } - - if (!entry->second->OnAnswerReceived(answer)) { - LOG(INFO) << "Unable to receive answer. Failed to process the answer."; - RemoveConnectionFlow(remote_peer_id); - } -} - -void WebRtc::ReceiveIceCandidates( - const WebrtcPeerId& remote_peer_id, - std::vector> ice_candidates) { - const auto& entry = connection_flows_.find(remote_peer_id.GetId()); - if (entry == connection_flows_.end()) { - LOG(INFO) << "Unable to receive ice candidates. Failed to create a " - "ConnectionFlow."; - return; - } - - entry->second->OnRemoteIceCandidatesReceived(std::move(ice_candidates)); -} - -void WebRtc::ProcessRestartTachyonReceiveMessages( - const std::string& service_id) { - MutexLock lock(&mutex_); - RestartTachyonReceiveMessages(service_id); -} - -void WebRtc::RestartTachyonReceiveMessages(const std::string& service_id) { - if (!IsAcceptingConnectionsLocked(service_id)) { - LOG(INFO) - << "Skipping restart listening for tachyon inbox messages since we are " - "not accepting connections for service " - << service_id; - return; - } - - // Grab our info from the map. - auto& info = accepting_connections_info_.find(service_id)->second; - - // Ensure we've disconnected from Tachyon. - info.signaling_messenger->StopReceivingMessages(); - - // Attempt to re-register. - if (!info.signaling_messenger->StartReceivingMessages( - absl::bind_front(&WebRtc::OnSignalingMessage, this, service_id), - absl::bind_front(&WebRtc::OnSignalingComplete, this, service_id))) { - LOG(WARNING) - << "Failed to restart listening for tachyon inbox messages for " - "service " - << service_id << " since we failed to reach Tachyon."; - return; - } - - LOG(INFO) << "Successfully restarted listening for tachyon inbox " - "messages on service " - << service_id; -} - -void WebRtc::ProcessDataChannelOpen(const std::string& service_id, - const WebrtcPeerId& remote_peer_id, - WebRtcSocketWrapper socket_wrapper) { - MutexLock lock(&mutex_); - - // Notify the client of the newly formed socket. - const auto& connection_request_entry = - requesting_connections_info_.find(remote_peer_id.GetId()); - if (connection_request_entry != requesting_connections_info_.end()) { - connection_request_entry->second.socket_future.Set(socket_wrapper); - return; - } - - const auto& accepting_connection_entry = - accepting_connections_info_.find(service_id); - if (accepting_connection_entry != accepting_connections_info_.end() && - accepting_connection_entry->second.accepted_connection_callback) { - accepting_connection_entry->second.accepted_connection_callback( - service_id, socket_wrapper); - return; - } - - // No one to handle the newly created DataChannel, so we'll just close it. - socket_wrapper.Close(); - LOG(INFO) << "Ignoring new DataChannel because we are not accepting " - "connections for service " - << service_id; -} - -void WebRtc::ProcessDataChannelClosed(const WebrtcPeerId& remote_peer_id) { - MutexLock lock(&mutex_); - LOG(INFO) << "Data channel has closed, removing connection flow for peer " - << remote_peer_id.GetId(); - - RemoveConnectionFlow(remote_peer_id); -} - -std::unique_ptr WebRtc::CreateConnectionFlow( - const std::string& service_id, const WebrtcPeerId& remote_peer_id) { - RemoveConnectionFlow(remote_peer_id); - - return ConnectionFlow::Create( - {.local_ice_candidate_found_cb = - {[this, service_id, - remote_peer_id](const webrtc::IceCandidate* ice_candidate) { - // Note: We need to encode the ice candidate here, before we jump - // off the thread. Otherwise, it gets destroyed and we can't read - // it later. - location::nearby::mediums::IceCandidate encoded_ice_candidate = - webrtc_frames::EncodeIceCandidate(*ice_candidate); - OffloadFromThread( - "rtc-ice-candidates", - [this, service_id, remote_peer_id, encoded_ice_candidate]() { - ProcessLocalIceCandidate(service_id, remote_peer_id, - encoded_ice_candidate); - }); - }}}, - { - .data_channel_open_cb = {[this, service_id, remote_peer_id]( - WebRtcSocketWrapper socket_wrapper) { - OffloadFromThread( - "rtc-channel-created", - [this, service_id, remote_peer_id, socket_wrapper]() { - ProcessDataChannelOpen(service_id, remote_peer_id, - socket_wrapper); - }); - }}, - .data_channel_closed_cb = {[this, remote_peer_id]() { - OffloadFromThread("rtc-channel-closed", [this, remote_peer_id]() { - ProcessDataChannelClosed(remote_peer_id); - }); - }}, - }, - { - .adapter_type_changed_cb = {[this](webrtc::AdapterType adapter_type) { - OffloadFromThread("rtc-adapter-type-changed", - [this, adapter_type]() { - if (FeatureFlags::GetInstance() - .GetFlags() - .support_web_rtc_non_cellular_medium) { - AdapterTypeChangedHandler(adapter_type); - } - }); - }}, - }, - *medium_); -} - -void WebRtc::AdapterTypeChangedHandler(webrtc::AdapterType adapter_type) { - MutexLock lock(&mutex_); - is_using_cellular_ = adapter_type == webrtc::ADAPTER_TYPE_CELLULAR || - adapter_type == webrtc::ADAPTER_TYPE_CELLULAR_2G || - adapter_type == webrtc::ADAPTER_TYPE_CELLULAR_3G || - adapter_type == webrtc::ADAPTER_TYPE_CELLULAR_4G || - adapter_type == webrtc::ADAPTER_TYPE_CELLULAR_5G; -} - -void WebRtc::RemoveConnectionFlow(const WebrtcPeerId& remote_peer_id) { - if (!connection_flows_.erase(remote_peer_id.GetId())) { - return; - } - - // If we had an outgoing connection request w/ this peer, report the failure - // to the future that's being waited on. - const auto& connection_request_entry = - requesting_connections_info_.find(remote_peer_id.GetId()); - if (connection_request_entry != requesting_connections_info_.end()) { - connection_request_entry->second.socket_future.SetException( - {Exception::kFailed}); - } -} - -void WebRtc::OffloadFromThread(const std::string& name, Runnable runnable) { - single_thread_executor_.Execute(name, std::move(runnable)); -} - -bool WebRtc::IsUsingCellular() { - MutexLock lock(&mutex_); - return is_using_cellular_; -} - -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif diff --git a/connections/implementation/mediums/webrtc.h b/connections/implementation/mediums/webrtc.h index 66b84b45..93203d98 100644 --- a/connections/implementation/mediums/webrtc.h +++ b/connections/implementation/mediums/webrtc.h @@ -15,262 +15,76 @@ #ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_H_ #define CORE_INTERNAL_MEDIUMS_WEBRTC_H_ -#ifndef NO_WEBRTC - -#include #include #include -#include -#include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" #include "absl/functional/any_invocable.h" -#include "connections/implementation/mediums/webrtc/connection_flow.h" -#include "connections/implementation/mediums/webrtc/session_description_wrapper.h" +#include "connections/implementation/bwu_handler.h" #include "connections/implementation/mediums/webrtc_peer_id.h" #include "connections/implementation/mediums/webrtc_socket.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/cancelable_alarm.h" +#include "connections/implementation/proto/offline_wire_formats.pb.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/expected.h" -#include "internal/platform/future.h" -#include "internal/platform/mutex.h" -#include "internal/platform/runnable.h" -#include "internal/platform/scheduled_executor.h" -#include "internal/platform/webrtc.h" -#include "proto/mediums/web_rtc_signaling_frames.pb.h" -#include "webrtc/api/jsep.h" namespace nearby { namespace connections { namespace mediums { -// Entry point for connecting a data channel between two devices via WebRtc. +// A non-working base implementation for connecting a data channel between two +// devices via WebRtc. class WebRtc { public: // Callback that is invoked when a new connection is accepted. using AcceptedConnectionCallback = absl::AnyInvocable; + const std::string& service_id, std::shared_ptr socket)>; - WebRtc(); - ~WebRtc(); - - // Gets the default two-letter country code associated with current locale. - // For example, en_US locale resolves to "US". - std::string GetDefaultCountryCode(); + virtual ~WebRtc() = default; // Returns if WebRtc is available as a medium for nearby to transport data. // Runs on @MainThread. - bool IsAvailable(); + virtual bool IsAvailable() { return false; } // Returns if the device is accepting connection with specific service id. // Runs on @MainThread. - bool IsAcceptingConnections(const std::string& service_id) - ABSL_LOCKS_EXCLUDED(mutex_); + virtual bool IsAcceptingConnections(const std::string& service_id) { + return false; + } // Prepares the device to accept incoming WebRtc connections. Returns a // boolean value indicating if the device has started accepting connections. // Runs on @MainThread. - bool StartAcceptingConnections( + virtual bool StartAcceptingConnections( const std::string& service_id, const WebrtcPeerId& self_peer_id, const location::nearby::connections::LocationHint& location_hint, - AcceptedConnectionCallback callback, bool non_cellular) - ABSL_LOCKS_EXCLUDED(mutex_); + AcceptedConnectionCallback callback, bool non_cellular) { + return false; + } // Try to stop (accepting) the specific connection with provided service id. // Runs on @MainThread - void StopAcceptingConnections(const std::string& service_id) - ABSL_LOCKS_EXCLUDED(mutex_); + virtual void StopAcceptingConnections(const std::string& service_id) {} // Initiates a WebRtc connection with peer device identified by |peer_id| // with internal retry for maximum attempts of kConnectAttemptsLimit. // Runs on @MainThread. - ErrorOr Connect( + virtual ErrorOr> Connect( const std::string& service_id, const WebrtcPeerId& peer_id, const location::nearby::connections::LocationHint& location_hint, - CancellationFlag* cancellation_flag, bool non_cellular) - ABSL_LOCKS_EXCLUDED(mutex_); + CancellationFlag* cancellation_flag, bool non_cellular) { + return {Error(location::nearby::proto::connections::OperationResultCode:: + DETAIL_UNKNOWN)}; + } - bool IsUsingCellular() ABSL_LOCKS_EXCLUDED(mutex_); + virtual bool IsUsingCellular() { return false; } - protected: - // Use for unit tests only to inject a WebRtcMedium. - explicit WebRtc(std::unique_ptr medium); - - // Used in unit tests to determine how many calls to `AttemptToConnect` - // occured during a call to `Connect`, per service id. - std::map service_id_to_connect_attempts_count_map_; - - private: - static constexpr int kConnectAttemptsLimit = 3; - static constexpr int kRestartAcceptConnectionsLimit = 3; - - enum class Role { - kNone = 0, - kOfferer = 1, - kAnswerer = 2, - }; - - struct AcceptingConnectionsInfo { - // The self_peer_id is generated from the BT/WiFi advertisements and allows - // the scanner to message us over Tachyon. - WebrtcPeerId self_peer_id; - - // The registered callback. When there's an incoming connection, this - // callback is notified. - AcceptedConnectionCallback accepted_connection_callback; - - // Allows us to communicate with the Tachyon web server. - std::unique_ptr signaling_messenger; - - // Restarts the tachyon inbox receives messages streaming rpc if the - // streaming rpc times out. The streaming rpc times out after 60s while - // advertising. Non-null when listening for WebRTC connections as an - // offerer. - std::unique_ptr restart_tachyon_receive_messages_alarm; - - // Tracks the number of times we've restarted receiving messages after a - // failure. We limit the number to prevent endless restarts if we are - // repeatedly unable to communicate with Tachyon. - int restart_accept_connections_count = 0; - }; - - struct ConnectionRequestInfo { - // The self_peer_id is randomly generated and allows the advertiser to - // message us over Tachyon. - WebrtcPeerId self_peer_id; - - // Allows us to communicate with the Tachyon web server. - std::unique_ptr signaling_messenger; - - // The pending DataChannel future. Our client will be blocked on this while - // they wait for us to set up the channel over Tachyon. - Future socket_future; - }; - - // Attempt to initiates a WebRtc connection with peer device identified by - // |peer_id|. - // Runs on @MainThread. - ErrorOr AttemptToConnect( - const std::string& service_id, const WebrtcPeerId& peer_id, - const location::nearby::connections::LocationHint& location_hint, - CancellationFlag* cancellation_flag) ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns if the device is accepting connection with specific service id. - // Runs on @MainThread. - bool IsAcceptingConnectionsLocked(const std::string& service_id) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Receives a message from the signaling messenger. - void OnSignalingMessage(const std::string& service_id, - const ByteArray& message); - - // Decides whether to restart receiving messages. - void OnSignalingComplete(const std::string& service_id, bool success); - - // Runs on |single_thread_executor_|. - void ProcessTachyonInboxMessage(const std::string& service_id, - const ByteArray& message) - ABSL_LOCKS_EXCLUDED(mutex_); - - // Runs on |single_thread_executor_|. - void SendOffer(const std::string& service_id, - const WebrtcPeerId& remote_peer_id) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Runs on |single_thread_executor_|. - void ReceiveOffer(const WebrtcPeerId& remote_peer_id, - SessionDescriptionWrapper offer) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Runs on |single_thread_executor_|. - void SendAnswer(const WebrtcPeerId& remote_peer_id) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Runs on |single_thread_executor_|. - void ReceiveAnswer(const WebrtcPeerId& remote_peer_id, - SessionDescriptionWrapper answer) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Runs on |single_thread_executor_|. - void ReceiveIceCandidates( - const WebrtcPeerId& remote_peer_id, - std::vector> ice_candidates) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Runs on |single_thread_executor_|. - std::unique_ptr CreateConnectionFlow( - const std::string& service_id, const WebrtcPeerId& remote_peer_id) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Runs on |single_thread_executor_|. - std::unique_ptr GetConnectionFlow( - const WebrtcPeerId& remote_peer_id) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Runs on |single_thread_executor_|. - void RemoveConnectionFlow(const WebrtcPeerId& remote_peer_id) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Runs on |single_thread_executor_|. - void ProcessDataChannelOpen(const std::string& service_id, - const WebrtcPeerId& remote_peer_id, - WebRtcSocketWrapper socket_wrapper) - ABSL_LOCKS_EXCLUDED(mutex_); - - // Runs on |single_thread_executor_|. - void ProcessDataChannelClosed(const WebrtcPeerId& remote_peer_id) - ABSL_LOCKS_EXCLUDED(mutex_); - - // Runs on |single_thread_executor_|. - void ProcessLocalIceCandidate( - const std::string& service_id, const WebrtcPeerId& remote_peer_id, - const location::nearby::mediums::IceCandidate ice_candidate) - ABSL_LOCKS_EXCLUDED(mutex_); - - // Runs on |single_thread_executor_|. - void ProcessRestartTachyonReceiveMessages(const std::string& service_id) - ABSL_LOCKS_EXCLUDED(mutex_); - - // Runs on |single_thread_executor_|. - void RestartTachyonReceiveMessages(const std::string& service_id) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Runs on |single_thread_executor_|. - void AdapterTypeChangedHandler(webrtc::AdapterType adapter_type) - ABSL_LOCKS_EXCLUDED(mutex_); - - void OffloadFromThread(const std::string& name, Runnable runnable); - - Mutex mutex_; - - std::unique_ptr medium_; - - // The single thread we throw the potentially blocking work on to. - ScheduledExecutor single_thread_executor_; - - // A map of ServiceID -> State for all services that are listening for - // incoming connections. - absl::flat_hash_map - accepting_connections_info_ ABSL_GUARDED_BY(mutex_); - - // A map of a remote PeerId -> State for pending connection requests. As - // messages from Tachyon come in, this lets us look up the connection request - // info to handle the interaction. - absl::flat_hash_map - requesting_connections_info_ ABSL_GUARDED_BY(mutex_); - - // A map of a remote PeerId -> ConnectionFlow. For each connection, we create - // a unique ConnectionFlow. - absl::flat_hash_map> - connection_flows_ ABSL_GUARDED_BY(mutex_); - - bool is_using_cellular_ ABSL_GUARDED_BY(mutex_) = true; + virtual std::unique_ptr CreateBwuHandler( + BwuHandler::IncomingConnectionCallback incoming_connection_callback) { + return nullptr; + } }; } // namespace mediums } // namespace connections } // namespace nearby -#endif - #endif // CORE_INTERNAL_MEDIUMS_WEBRTC_H_ diff --git a/connections/implementation/mediums/webrtc/BUILD b/connections/implementation/mediums/webrtc/BUILD deleted file mode 100644 index 71662a36..00000000 --- a/connections/implementation/mediums/webrtc/BUILD +++ /dev/null @@ -1,108 +0,0 @@ -load("@rules_cc//cc:cc_library.bzl", "cc_library") -load("@rules_cc//cc:cc_test.bzl", "cc_test") - -# 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. -licenses(["notice"]) - -cc_library( - name = "webrtc", - srcs = [ - "connection_flow.cc", - "signaling_frames.cc", - ], - hdrs = [ - "connection_flow.h", - "data_channel_listener.h", - "local_ice_candidate_listener.h", - "session_description_wrapper.h", - "signaling_frames.h", - ], - copts = [ - "-DCORE_ADAPTER_DLL", - "-DNO_WEBRTC", - ], - visibility = [ - "//connections/implementation:__subpackages__", - ], - deps = [ - ":data_types", - "//connections:core_types", - "//connections/implementation/mediums:webrtc_utils", - "//internal/platform:base", - "//internal/platform:comm", - "//internal/platform:logging", - "//internal/platform:types", - "//proto/mediums:web_rtc_signaling_frames_cc_proto", - # TODO: Support WebRTC - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/time", - ], -) - -cc_library( - name = "data_types", - srcs = [ - "webrtc_socket_impl.cc", - ], - hdrs = [ - "webrtc_socket_impl.h", - ], - copts = [ - "-DCORE_ADAPTER_DLL", - "-DNO_WEBRTC", - ], - visibility = [ - "//connections/implementation:__subpackages__", - ], - deps = [ - "//internal/platform:base", - "//internal/platform:logging", - "//internal/platform:types", - "@com_google_absl//absl/strings:string_view", - ], -) - -cc_test( - name = "webrtc_test", - timeout = "short", - srcs = [ - "connection_flow_test.cc", - "signaling_frames_test.cc", - "webrtc_socket_impl_test.cc", - ], - tags = [ - "notsan", # NOTE(b/139734036): known data race in usrsctplib. - "requires-net:external", - ], - deps = [ - ":data_types", - ":webrtc", - "//connections/implementation/mediums:webrtc_utils", - "//internal/platform:base", - "//internal/platform:comm", - "//internal/platform:test_util", - "//internal/platform:types", - "//internal/platform/implementation/g3", # buildcleaner: keep - "//third_party/webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", - "//third_party/webrtc/files/stable/webrtc/api:scoped_refptr", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/strings:string_view", - "@com_google_absl//absl/time", - "@com_google_googletest//:gtest_main", - "@com_google_protobuf//:protobuf", - ], -) diff --git a/connections/implementation/mediums/webrtc/connection_flow.cc b/connections/implementation/mediums/webrtc/connection_flow.cc deleted file mode 100644 index 1d32ae17..00000000 --- a/connections/implementation/mediums/webrtc/connection_flow.cc +++ /dev/null @@ -1,583 +0,0 @@ -// 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. - -#ifndef NO_WEBRTC - -#include "connections/implementation/mediums/webrtc/connection_flow.h" - -#include -#include -#include -#include - -#include "absl/memory/memory.h" -#include "absl/time/time.h" -#include "connections/implementation/mediums/webrtc/data_channel_listener.h" -#include "connections/implementation/mediums/webrtc/local_ice_candidate_listener.h" -#include "connections/implementation/mediums/webrtc/session_description_wrapper.h" -#include "connections/implementation/mediums/webrtc/webrtc_socket_impl.h" -#include "connections/implementation/mediums/webrtc_socket.h" -#include "internal/platform/exception.h" -#include "internal/platform/future.h" -#include "internal/platform/logging.h" -#include "internal/platform/mutex_lock.h" -#include "internal/platform/runnable.h" -#include "internal/platform/webrtc.h" -#include "webrtc/api/data_channel_interface.h" -#include "webrtc/api/jsep.h" - -namespace nearby { -namespace connections { -namespace mediums { - -constexpr absl::Duration ConnectionFlow::kTimeout; -constexpr absl::Duration ConnectionFlow::kPeerConnectionTimeout; - -// This is the same as the nearby data channel name. -constexpr char kDataChannelName[] = "dataChannel"; - -class CreateSessionDescriptionObserverImpl - : public webrtc::CreateSessionDescriptionObserver { - public: - CreateSessionDescriptionObserverImpl( - ConnectionFlow* connection_flow, - Future settable_future, - ConnectionFlow::State expected_entry_state, - ConnectionFlow::State exit_state) - : connection_flow_{connection_flow}, - settable_future_{settable_future}, - expected_entry_state_{expected_entry_state}, - exit_state_{exit_state} {} - - // webrtc::CreateSessionDescriptionObserver - void OnSuccess(webrtc::SessionDescriptionInterface* desc) override { - if (connection_flow_->TransitionState(expected_entry_state_, exit_state_)) { - settable_future_.Set(SessionDescriptionWrapper{desc}); - } else { - settable_future_.SetException({Exception::kFailed}); - } - } - - void OnFailure(webrtc::RTCError error) override { - LOG(ERROR) << "Error when creating session description: " - << error.message(); - settable_future_.SetException({Exception::kFailed}); - } - - private: - ConnectionFlow* connection_flow_; - Future settable_future_; - ConnectionFlow::State expected_entry_state_; - ConnectionFlow::State exit_state_; -}; - -class SetDescriptionObserverBase { - public: - ExceptionOr GetResult(absl::Duration timeout) { - return settable_future_.Get(timeout); - } - - protected: - void OnSetDescriptionComplete(webrtc::RTCError error) { - // On success, |error.ok()| is true. - if (error.ok()) { - settable_future_.Set(true); - return; - } - settable_future_.SetException({Exception::kFailed}); - } - - private: - Future settable_future_; -}; - -class SetLocalDescriptionObserver - : public webrtc::SetLocalDescriptionObserverInterface, - public SetDescriptionObserverBase { - public: - void OnSetLocalDescriptionComplete(webrtc::RTCError error) override { - OnSetDescriptionComplete(error); - } -}; - -class SetRemoteDescriptionObserver - : public webrtc::SetRemoteDescriptionObserverInterface, - public SetDescriptionObserverBase { - public: - void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override { - OnSetDescriptionComplete(error); - } -}; - -using PeerConnectionState = - webrtc::PeerConnectionInterface::PeerConnectionState; - -std::unique_ptr ConnectionFlow::Create( - LocalIceCandidateListener local_ice_candidate_listener, - DataChannelListener data_channel_listener, - AdapterTypeListener adapter_type_listener, WebRtcMedium& webrtc_medium) { - auto connection_flow = absl::WrapUnique(new ConnectionFlow( - std::move(local_ice_candidate_listener), std::move(data_channel_listener), - std::move(adapter_type_listener))); - if (connection_flow->InitPeerConnection(webrtc_medium)) { - return connection_flow; - } - - return nullptr; -} - -ConnectionFlow::ConnectionFlow( - LocalIceCandidateListener local_ice_candidate_listener, - DataChannelListener data_channel_listener, - AdapterTypeListener adapter_type_listener) - : data_channel_listener_(std::move(data_channel_listener)), - local_ice_candidate_listener_(std::move(local_ice_candidate_listener)), - adapter_type_listener_(std::move(adapter_type_listener)) {} - -ConnectionFlow::~ConnectionFlow() { - LOG(INFO) << "~ConnectionFlow"; - RunOnSignalingThread([this] { CloseOnSignalingThread(); }); - shutdown_latch_.Await(); - LOG(INFO) << "~ConnectionFlow done"; -} - -SessionDescriptionWrapper ConnectionFlow::CreateOffer() { - CHECK(!IsRunningOnSignalingThread()); - Future success_future; - if (!RunOnSignalingThread([this, success_future] { - CreateOfferOnSignalingThread(success_future); - })) { - LOG(ERROR) << "Failed to create offer"; - return SessionDescriptionWrapper(); - } - ExceptionOr result = success_future.Get(kTimeout); - if (result.ok()) { - return std::move(result.result()); - } - LOG(ERROR) << "Failed to create offer: " << result.exception(); - return SessionDescriptionWrapper(); -} - -void ConnectionFlow::CreateOfferOnSignalingThread( - Future success_future) { - if (!TransitionState(State::kInitialized, State::kCreatingOffer)) { - success_future.SetException({Exception::kFailed}); - return; - } - webrtc::DataChannelInit data_channel_init; - data_channel_init.reliable = true; - auto pc = GetPeerConnection(); - auto result = - pc->CreateDataChannelOrError(kDataChannelName, &data_channel_init); - if (!result.ok()) { - success_future.SetException({Exception::kFailed}); - return; - } - CreateSocketFromDataChannel(result.MoveValue()); - - webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; - webrtc::scoped_refptr observer( - new webrtc::RefCountedObject( - this, success_future, State::kCreatingOffer, - State::kWaitingForAnswer)); - pc->CreateOffer(observer.get(), options); -} - -SessionDescriptionWrapper ConnectionFlow::CreateAnswer() { - CHECK(!IsRunningOnSignalingThread()); - Future success_future; - if (!RunOnSignalingThread([this, success_future] { - CreateAnswerOnSignalingThread(success_future); - })) { - LOG(ERROR) << "Failed to create answer"; - return SessionDescriptionWrapper(); - } - ExceptionOr result = success_future.Get(kTimeout); - if (result.ok()) { - return std::move(result.result()); - } - LOG(ERROR) << "Failed to create answer: " << result.exception(); - return SessionDescriptionWrapper(); -} - -void ConnectionFlow::CreateAnswerOnSignalingThread( - Future success_future) { - if (!TransitionState(State::kReceivedOffer, State::kCreatingAnswer)) { - success_future.SetException({Exception::kFailed}); - return; - } - webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; - webrtc::scoped_refptr observer( - new webrtc::RefCountedObject( - this, success_future, State::kCreatingAnswer, - State::kWaitingToConnect)); - auto pc = GetPeerConnection(); - pc->CreateAnswer(observer.get(), options); -} - -bool ConnectionFlow::SetLocalSessionDescription(SessionDescriptionWrapper sdp) { - CHECK(!IsRunningOnSignalingThread()); - if (!sdp.IsValid()) return false; - - webrtc::scoped_refptr observer( - new webrtc::RefCountedObject()); - - if (!RunOnSignalingThread([this, observer, sdp = std::move(sdp)]() mutable { - if (state_ == State::kEnded) { - observer->OnSetLocalDescriptionComplete( - webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE)); - return; - } - auto pc = GetPeerConnection(); - - pc->SetLocalDescription( - std::unique_ptr(sdp.Release()), - observer); - })) { - return false; - } - - ExceptionOr result = observer->GetResult(kTimeout); - bool success = result.ok() && result.result(); - if (!success) { - LOG(ERROR) << "Failed to set local session description: " - << result.exception(); - } - return success; -} - -bool ConnectionFlow::SetRemoteSessionDescription(SessionDescriptionWrapper sdp, - State expected_entry_state, - State exit_state) { - if (!sdp.IsValid()) return false; - - webrtc::scoped_refptr observer( - new webrtc::RefCountedObject()); - - if (!RunOnSignalingThread([this, observer, sdp = std::move(sdp), - expected_entry_state, exit_state]() mutable { - if (!TransitionState(expected_entry_state, exit_state)) { - observer->OnSetRemoteDescriptionComplete( - webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE)); - return; - } - auto pc = GetPeerConnection(); - - pc->SetRemoteDescription( - std::unique_ptr(sdp.Release()), - observer); - })) { - return false; - } - - ExceptionOr result = observer->GetResult(kTimeout); - bool success = result.ok() && result.result(); - if (!success) { - LOG(ERROR) << "Failed to set remote description: " << result.exception(); - } - return success; -} - -bool ConnectionFlow::OnOfferReceived(SessionDescriptionWrapper offer) { - CHECK(!IsRunningOnSignalingThread()); - return SetRemoteSessionDescription(std::move(offer), State::kInitialized, - State::kReceivedOffer); -} - -bool ConnectionFlow::OnAnswerReceived(SessionDescriptionWrapper answer) { - CHECK(!IsRunningOnSignalingThread()); - return SetRemoteSessionDescription( - std::move(answer), State::kWaitingForAnswer, State::kWaitingToConnect); -} - -bool ConnectionFlow::OnRemoteIceCandidatesReceived( - std::vector> ice_candidates) { - CHECK(!IsRunningOnSignalingThread()); - // We can't call RunOnSignalingThread because C++ wants to copy ice_candidates - // if we try. unique_ptr is not CopyConstructible and compilation fails. - auto pc = GetPeerConnection(); - - if (!pc) { - return false; - } - pc->signaling_thread()->PostTask( - [this, can_run_tasks = std::weak_ptr(can_run_tasks_), - candidates = std::move(ice_candidates)]() mutable { - // Don't run the task if the weak_ptr is no longer valid. - if (!can_run_tasks.lock()) { - return; - } - AddIceCandidatesOnSignalingThread(std::move(candidates)); - }); - return true; -} - -void ConnectionFlow::AddIceCandidatesOnSignalingThread( - std::vector> ice_candidates) { - CHECK(IsRunningOnSignalingThread()); - if (state_ == State::kEnded) { - LOG(WARNING) << "You cannot add ice candidates to a disconnected session."; - return; - } - if (state_ != State::kWaitingToConnect && state_ != State::kConnected) { - cached_remote_ice_candidates_.insert( - cached_remote_ice_candidates_.end(), - std::make_move_iterator(ice_candidates.begin()), - std::make_move_iterator(ice_candidates.end())); - return; - } - auto pc = GetPeerConnection(); - for (auto&& ice_candidate : ice_candidates) { - if (!pc->AddIceCandidate(ice_candidate.get())) { - LOG(WARNING) << "Unable to add remote ice candidate."; - } - } -} - -bool ConnectionFlow::CloseIfNotConnected() { - CHECK(!IsRunningOnSignalingThread()); - Future closed; - if (RunOnSignalingThread([this, closed]() mutable { - if (state_ == State::kConnected) { - closed.Set(false); - } else { - CloseOnSignalingThread(); - closed.Set(true); - } - })) { - auto result = closed.Get(); - return result.ok() && result.result(); - } - return true; -} - -bool ConnectionFlow::InitPeerConnection(WebRtcMedium& webrtc_medium) { - Future success_future; - // CreatePeerConnection callback may be invoked after ConnectionFlow lifetime - // has ended, in case of a timeout. Future is captured by value, and is safe - // to access, but it is not safe to access ConnectionFlow member variables - // unless the Future::Set() returns true. - webrtc_medium.CreatePeerConnection( - this, [this, success_future]( - webrtc::scoped_refptr - peer_connection) mutable { - if (!peer_connection) { - success_future.Set(false); - return; - } - - // If this fails, means we have already assigned something to - // success_future; it is either: - // 1) this is the 2nd call of this callback (and this is a bug), or - // 2) Get(timeout) has set the future value as exception already. - if (success_future.IsSet()) return; - MutexLock lock(&mutex_); - peer_connection_ = peer_connection; - signaling_thread_for_dcheck_only_ = - peer_connection_->signaling_thread(); - success_future.Set(true); - }); - - ExceptionOr result = success_future.Get(kPeerConnectionTimeout); - bool success = result.ok() && result.result(); - if (!success) { - shutdown_latch_.CountDown(); - LOG(ERROR) << "Failed to create peer connection: " << result.exception(); - } - return success; -} - -void ConnectionFlow::OnSignalingStable() { - if (state_ != State::kWaitingToConnect && state_ != State::kConnected) return; - auto pc = GetPeerConnection(); - for (auto&& ice_candidate : cached_remote_ice_candidates_) { - if (!pc->AddIceCandidate(ice_candidate.get())) { - LOG(WARNING) << "Unable to add remote ice candidate."; - } - } - cached_remote_ice_candidates_.clear(); -} - -void ConnectionFlow::CreateSocketFromDataChannel( - webrtc::scoped_refptr data_channel) { - LOG(INFO) << "Creating data channel socket"; - auto socket = - std::make_unique("WebRtcSocket", std::move(data_channel)); - socket->SetSocketListener({ - .socket_ready_cb = {[this](WebRtcSocket* socket) { - CHECK(IsRunningOnSignalingThread()); - if (!TransitionState(State::kWaitingToConnect, State::kConnected)) { - LOG(ERROR) << "Data channel socket is open but connection " - "flow was not in the required state"; - socket->Close(); - return; - } - // Pass socket wrapper by copy on purpose - data_channel_listener_.data_channel_open_cb(socket_wrapper_); - }}, - .socket_closed_cb = - [this](WebRtcSocket*) { - data_channel_listener_.data_channel_closed_cb(); - }, - }); - socket_wrapper_ = WebRtcSocketWrapper(std::move(socket)); -} - -void ConnectionFlow::OnIceCandidate(const webrtc::IceCandidate* candidate) { - CHECK(IsRunningOnSignalingThread()); - local_ice_candidate_listener_.local_ice_candidate_found_cb(candidate); -} - -void ConnectionFlow::OnSignalingChange( - webrtc::PeerConnectionInterface::SignalingState new_state) { - LOG(INFO) << "OnSignalingChange: " << new_state; - CHECK(IsRunningOnSignalingThread()); - if (new_state == webrtc::PeerConnectionInterface::SignalingState::kStable) { - OnSignalingStable(); - } -} - -void ConnectionFlow::OnDataChannel( - webrtc::scoped_refptr data_channel) { - LOG(INFO) << "OnDataChannel"; - CHECK(IsRunningOnSignalingThread()); - CreateSocketFromDataChannel(std::move(data_channel)); -} - -void ConnectionFlow::OnIceGatheringChange( - webrtc::PeerConnectionInterface::IceGatheringState new_state) { - LOG(INFO) << "OnIceGatheringChange: " << new_state; - CHECK(IsRunningOnSignalingThread()); -} - -void ConnectionFlow::OnConnectionChange( - webrtc::PeerConnectionInterface::PeerConnectionState new_state) { - LOG(INFO) << "OnConnectionChange: " << static_cast(new_state); - CHECK(IsRunningOnSignalingThread()); - if (new_state == PeerConnectionState::kClosed || - new_state == PeerConnectionState::kFailed || - new_state == PeerConnectionState::kDisconnected) { - LOG(INFO) << "Closing due to peer connection state change: " - << static_cast(new_state); - CloseOnSignalingThread(); - } -} - -void ConnectionFlow::OnRenegotiationNeeded() { - LOG(INFO) << "OnRenegotiationNeeded"; - CHECK(IsRunningOnSignalingThread()); -} - -void ConnectionFlow::OnIceSelectedCandidatePairChanged( - const webrtc::CandidatePairChangeEvent& event) { - LOG(INFO) << "OnIceSelectedCandidatePairChanged"; - CHECK(IsRunningOnSignalingThread()); - // TODO(edwinwu) - Implement the unit test for this. We should be able to get - // the adapter type from the PeerConnection. - adapter_type_listener_.adapter_type_changed_cb( - event.selected_candidate_pair.local_candidate().network_type()); -} - -bool ConnectionFlow::TransitionState(State current_state, State new_state) { - CHECK(IsRunningOnSignalingThread()); - if (current_state != state_) { - LOG(WARNING) << "Invalid state transition to " - << static_cast(new_state) << ": current state is " - << static_cast(state_) << " but expected " - << static_cast(current_state); - return false; - } - LOG(INFO) << "Transition: " << static_cast(state_) << "->" - << static_cast(new_state); - state_ = new_state; - return true; -} - -bool ConnectionFlow::CloseOnSignalingThread() { - if (state_ == State::kEnded) { - return false; - } - state_ = State::kEnded; - // Close the socket wrapper before terminating the PeerConnection - // since the teardown process of the PC may close threads that are - // otherwise depended upon by objects kept alive by the socket_wrapper. - if (socket_wrapper_.IsValid()) socket_wrapper_.Close(); - - // This prevents other tasks from queuing on the signaling thread for this - // object. - auto pc = GetAndResetPeerConnection(); - - LOG(INFO) << "Closing WebRTC peer connection."; - // NOTE: Closing the peer connection will close the data channel and thus the - // socket implicitly. - if (pc) pc->Close(); - LOG(INFO) << "Closed WebRTC peer connection."; - // Prevent any already queued tasks from running on the signaling thread - can_run_tasks_.reset(); - // If anyone was waiting for shutdown to be done let them know. - shutdown_latch_.CountDown(); - return true; -} - -bool ConnectionFlow::RunOnSignalingThread(Runnable&& runnable) { - CHECK(!IsRunningOnSignalingThread()); - auto pc = GetPeerConnection(); - if (!pc) { - LOG(WARNING) << "Peer connection not available. Cannot schedule tasks."; - return false; - } - // We are off signaling thread, so we can't use peer connection's methods - // but we can access the signaling thread handle. - pc->signaling_thread()->PostTask( - [can_run_tasks = std::weak_ptr(can_run_tasks_), - task = std::move(runnable)]() mutable { - // Don't run the task if the weak_ptr is no longer valid. - // shared_ptr |can_run_tasks_| is destroyed on the same thread - // (signaling thread). This guarantees that if the weak_ptr is valid - // when this task starts, it will stay valid until the task ends. - if (!can_run_tasks.lock()) { - LOG(INFO) << "Peer connection already closed. Cannot run tasks."; - return; - } - task(); - }); - return true; -} - -bool ConnectionFlow::IsRunningOnSignalingThread() { - return signaling_thread_for_dcheck_only_ != nullptr && - signaling_thread_for_dcheck_only_ == webrtc::Thread::Current(); -} - -webrtc::scoped_refptr -ConnectionFlow::GetPeerConnection() { - // We must use a mutex to ensure that peer connection is - // fully initialized. - // We increase the peer_connection_'s refcount to keep it - // alive while we use it. - MutexLock lock(&mutex_); - return peer_connection_; -} - -webrtc::scoped_refptr -ConnectionFlow::GetAndResetPeerConnection() { - MutexLock lock(&mutex_); - return std::move(peer_connection_); -} - -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif diff --git a/connections/implementation/mediums/webrtc/connection_flow.h b/connections/implementation/mediums/webrtc/connection_flow.h deleted file mode 100644 index e0986786..00000000 --- a/connections/implementation/mediums/webrtc/connection_flow.h +++ /dev/null @@ -1,251 +0,0 @@ -// 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. - -#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_CONNECTION_FLOW_H_ -#define CORE_INTERNAL_MEDIUMS_WEBRTC_CONNECTION_FLOW_H_ - -#ifndef NO_WEBRTC - -#include -#include - -#include "absl/base/thread_annotations.h" -#include "absl/functional/any_invocable.h" -#include "absl/time/time.h" -#include "connections/implementation/mediums/webrtc/data_channel_listener.h" -#include "connections/implementation/mediums/webrtc/local_ice_candidate_listener.h" -#include "connections/implementation/mediums/webrtc/session_description_wrapper.h" -#include "connections/implementation/mediums/webrtc_socket.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/future.h" -#include "internal/platform/listeners.h" -#include "internal/platform/mutex.h" -#include "internal/platform/runnable.h" -#include "internal/platform/webrtc.h" -#include "webrtc/api/data_channel_interface.h" -#include "webrtc/api/peer_connection_interface.h" - -namespace nearby { -namespace connections { -namespace mediums { - -/** - * Flow for an offerer: - * - *

    - *
  • INITIALIZED: After construction. - *
  • CREATING_OFFER: After CreateOffer(). Local ice candidate collection - * begins. - *
  • WAITING_FOR_ANSWER: Until the remote peer sends their answer. - *
  • WAITING_TO_CONNECT: Until the data channel actually connects. Remote - * ice candidates should be added with OnRemoteIceCandidatesReceived as they are - * gathered. - *
  • CONNECTED: We successfully connected to the remote data - * channel. - *
  • ENDED: The final state that can occur from any of the previous - * states if we disconnect at any point in the flow. - *
- * - *

Flow for an answerer: - * - *

    - *
  • INITIALIZED: After construction. - *
  • RECEIVED_OFFER: After onOfferReceived(). - *
  • CREATING_ANSWER: After CreateAnswer(). Local ice candidate collection - * begins. - *
  • WAITING_TO_CONNECT: Until the data channel actually connects. - * Remote ice candidates should be added with OnRemoteIceCandidatesReceived as - * they are gathered. - *
  • CONNECTED: We successfully connected to the remote - * data channel. - *
  • ENDED: The final state that can occur from any of the - * previous states if we disconnect at any point in the flow. - *
- */ -class ConnectionFlow : public webrtc::PeerConnectionObserver { - public: - enum class State { - kInitialized, - kCreatingOffer, - kWaitingForAnswer, - kReceivedOffer, - kCreatingAnswer, - kWaitingToConnect, - kConnected, - kEnded, - }; - - struct AdapterTypeListener { - absl::AnyInvocable - adapter_type_changed_cb = DefaultCallback(); - }; - - // This method blocks on the creation of the peer connection object. - // Can be called on any thread but never called on signaling thread. - static std::unique_ptr Create( - LocalIceCandidateListener local_ice_candidate_listener, - DataChannelListener data_channel_listener, - AdapterTypeListener adapter_type_listener, WebRtcMedium& webrtc_medium); - ~ConnectionFlow() override; - - // Create the offer that will be sent to the remote. Mirrors the behaviour of - // PeerConnectionInterface::CreateOffer. - // Can be called on any thread but never called on signaling thread. - SessionDescriptionWrapper CreateOffer() ABSL_LOCKS_EXCLUDED(mutex_); - // Create the answer that will be sent to the remote. Mirrors the behaviour of - // PeerConnectionInterface::CreateAnswer. - // Can be called on any thread but never called on signaling thread. - SessionDescriptionWrapper CreateAnswer() ABSL_LOCKS_EXCLUDED(mutex_); - // Set the local session description. |sdp| was created via CreateOffer() - // or CreateAnswer(). - // Can be called on any thread but never called on signaling thread. - bool SetLocalSessionDescription(SessionDescriptionWrapper sdp) - ABSL_LOCKS_EXCLUDED(mutex_); - // Invoked when an offer was received from a remote; this will set the remote - // session description on the peer connection. Returns true if the offer was - // successfully set as remote session description. - // Can be called on any thread but never called on signaling thread. - bool OnOfferReceived(SessionDescriptionWrapper offer) - ABSL_LOCKS_EXCLUDED(mutex_); - // Invoked when an answer was received from a remote; this will set the remote - // session description on the peer connection. Returns true if the offer was - // successfully set as remote session description. - // Can be called on any thread but never called on signaling thread. - bool OnAnswerReceived(SessionDescriptionWrapper answer) - ABSL_LOCKS_EXCLUDED(mutex_); - // Invoked when an ice candidate was received from a remote; this will add the - // ice candidate to the peer connection if ready or cache it otherwise. - // Can be called on any thread but never called on signaling thread. - bool OnRemoteIceCandidatesReceived( - std::vector> ice_candidates) - ABSL_LOCKS_EXCLUDED(mutex_); - // Close the peer connection and data channel if not connected. - // Can be called on any thread but never called on signaling thread. - bool CloseIfNotConnected() ABSL_LOCKS_EXCLUDED(mutex_); - - // webrtc::PeerConnectionObserver: - // All methods called only on signaling thread. - void OnIceCandidate(const webrtc::IceCandidate* candidate) override; - void OnSignalingChange( - webrtc::PeerConnectionInterface::SignalingState new_state) override; - void OnDataChannel(webrtc::scoped_refptr - data_channel) override; - void OnIceGatheringChange( - webrtc::PeerConnectionInterface::IceGatheringState new_state) override; - void OnConnectionChange( - webrtc::PeerConnectionInterface::PeerConnectionState new_state) override; - void OnRenegotiationNeeded() override; - void OnIceSelectedCandidatePairChanged( - const webrtc::CandidatePairChangeEvent& event) override; - - // Public because it's used in tests too. - webrtc::scoped_refptr GetPeerConnection(); - - private: - ConnectionFlow(LocalIceCandidateListener local_ice_candidate_listener, - DataChannelListener data_channel_listener, - AdapterTypeListener adapter_type_listener); - - // Resets peer connection reference. Returns old value. - webrtc::scoped_refptr - GetAndResetPeerConnection(); - void CreateOfferOnSignalingThread( - Future success_future); - void CreateAnswerOnSignalingThread( - Future success_future); - void AddIceCandidatesOnSignalingThread( - std::vector> ice_candidates); - // Invoked when the peer connection indicates that signaling is stable. - void OnSignalingStable() ABSL_LOCKS_EXCLUDED(mutex_); - - void CreateSocketFromDataChannel( - webrtc::scoped_refptr data_channel); - - // TODO(bfranz): Consider whether this needs to be configurable per platform - static constexpr absl::Duration kTimeout = absl::Milliseconds(250); - static constexpr absl::Duration kPeerConnectionTimeout = - absl::Milliseconds(2500); - - bool InitPeerConnection(WebRtcMedium& webrtc_medium); - - bool TransitionState(State current_state, State new_state); - - bool SetRemoteSessionDescription(SessionDescriptionWrapper sdp, - State expected_entry_state, - State exit_state); - - bool CloseOnSignalingThread() ABSL_LOCKS_EXCLUDED(mutex_); - - bool RunOnSignalingThread(Runnable&& runnable); - bool IsRunningOnSignalingThread(); - - Mutex mutex_; - // Used to prevent the destructor from returning while the signaling thread is - // still running CloseOnSignalingThread() - CountDownLatch shutdown_latch_{1}; - - // State is used on signaling thread only. - State state_ = State::kInitialized; - // Used to communicate data channel events back to the caller of Create() - DataChannelListener data_channel_listener_; - - LocalIceCandidateListener local_ice_candidate_listener_; - // Peer connection can be used only on signaling thread. The only exception - // is accessing the signaling thread handle. Tasks posted on the - // signaling thread may outlive both |peer_connection_| and |this| objects. - // A mutex is required to access peer connection reference because peer - // connection object and the reference can be initialized on different - // threads - the reference could be initialized before peer connection's - // constructor has finished. - // |peer_connection_| is actually implemented by PeerConnectionProxy, which - // runs the real PeerConnection's methods on the correct thread (signaling or - // worker). If a proxy method is called on the correct thread, then the real - // method is called directly. Otherwise, a task is posted on the correct - // thread and the current thread is blocked until that task finishes. We - // choose to explicitly use |peer_connection_| on the signaling thread, - // because it allows us to do state management on the signaling thread too, - // simplifies locking, and we don't have to block the current thread for every - // peer connection call. - webrtc::scoped_refptr peer_connection_ - ABSL_GUARDED_BY(mutex_); - - // Used to hold a reference to the WebRtcSocket while the data channel is - // connecting. - WebRtcSocketWrapper socket_wrapper_; - - std::vector> - cached_remote_ice_candidates_; - // This pointer is only for DCHECK() assertions. - // It allows us to check if we are running on signaling thread even - // after destroying |peer_connection_|. - const void* signaling_thread_for_dcheck_only_ = nullptr; - // This shared_ptr is reset on the signaling thread when ConnectionFlow is - // closed. This prevents us from running tasks on the signaling thread when - // peer connection is closed. The value stored in |can_run_tasks_| is not - // used. We are using std::shared_ptr instead of webrtc::WeakPtrFactory - // because the former is thread-safe. - std::shared_ptr can_run_tasks_ = std::make_shared(); - - AdapterTypeListener adapter_type_listener_; - - friend class CreateSessionDescriptionObserverImpl; -}; - -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif - -#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_CONNECTION_FLOW_H_ diff --git a/connections/implementation/mediums/webrtc/connection_flow_test.cc b/connections/implementation/mediums/webrtc/connection_flow_test.cc deleted file mode 100644 index 64a7766d..00000000 --- a/connections/implementation/mediums/webrtc/connection_flow_test.cc +++ /dev/null @@ -1,441 +0,0 @@ -// 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 "connections/implementation/mediums/webrtc/connection_flow.h" - -#include -#include -#include - -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "connections/implementation/mediums/webrtc/data_channel_listener.h" -#include "connections/implementation/mediums/webrtc/local_ice_candidate_listener.h" -#include "connections/implementation/mediums/webrtc/session_description_wrapper.h" -#include "connections/implementation/mediums/webrtc_socket.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/exception.h" -#include "internal/platform/future.h" -#include "internal/platform/medium_environment.h" -#include "internal/platform/webrtc.h" -#include "webrtc/api/jsep.h" -#include "webrtc/api/scoped_refptr.h" - -namespace nearby { -namespace connections { -namespace mediums { -namespace { - -class ConnectionFlowTest : public ::testing::Test { - protected: - ConnectionFlowTest() { - MediumEnvironment::Instance().Start({.webrtc_enabled = true}); - } - ~ConnectionFlowTest() override { MediumEnvironment::Instance().Stop(); } -}; - -std::unique_ptr CopyCandidate( - const webrtc::IceCandidate* candidate) { - return webrtc::CreateIceCandidate(candidate->sdp_mid(), - candidate->sdp_mline_index(), - candidate->candidate()); -} - -// TODO(bfranz) - Add test that deterministically sends answerer_ice_candidates -// before answer is sent. -TEST_F(ConnectionFlowTest, SuccessfulOfferAnswerFlow) { - WebRtcMedium webrtc_medium_offerer, webrtc_medium_answerer; - - Future message_received_future; - - Future offerer_socket_future, answerer_socket_future; - - std::unique_ptr offerer, answerer; - - // Send Ice Candidates immediately when you retrieve them - offerer = ConnectionFlow::Create( - {.local_ice_candidate_found_cb = - [&answerer](const webrtc::IceCandidate* candidate) { - std::vector> vec; - vec.push_back(CopyCandidate(candidate)); - // The callback might be alive while the objects in test are - // destroyed. - if (answerer) - answerer->OnRemoteIceCandidatesReceived(std::move(vec)); - }}, - {.data_channel_open_cb = - [&offerer_socket_future](WebRtcSocketWrapper socket) { - offerer_socket_future.Set(std::move(socket)); - }}, - {.adapter_type_changed_cb = - [](webrtc::AdapterType adapter_type) { - // Do nothing - }}, - webrtc_medium_offerer); - ASSERT_NE(offerer, nullptr); - answerer = ConnectionFlow::Create( - {.local_ice_candidate_found_cb = - [&offerer](const webrtc::IceCandidate* candidate) { - std::vector> vec; - vec.push_back(CopyCandidate(candidate)); - // The callback might be alive while the objects in test are - // destroyed. - if (offerer) - offerer->OnRemoteIceCandidatesReceived(std::move(vec)); - }}, - {.data_channel_open_cb = - [&answerer_socket_future](WebRtcSocketWrapper socket) { - answerer_socket_future.Set(std::move(socket)); - }}, - {.adapter_type_changed_cb = - [](webrtc::AdapterType adapter_type) { - // Do nothing - }}, - webrtc_medium_answerer); - ASSERT_NE(answerer, nullptr); - - // Create and send offer - SessionDescriptionWrapper offer = offerer->CreateOffer(); - ASSERT_TRUE(offer.IsValid()); - EXPECT_EQ(offer.GetType(), webrtc::SdpType::kOffer); - EXPECT_TRUE(answerer->OnOfferReceived(offer)); - EXPECT_TRUE(offerer->SetLocalSessionDescription(std::move(offer))); - - // Create and send answer - SessionDescriptionWrapper answer = answerer->CreateAnswer(); - ASSERT_TRUE(answer.IsValid()); - EXPECT_EQ(answer.GetType(), webrtc::SdpType::kAnswer); - EXPECT_TRUE(offerer->OnAnswerReceived(answer)); - EXPECT_TRUE(answerer->SetLocalSessionDescription(std::move(answer))); - - // Retrieve Data Channels - ExceptionOr offerer_socket = - offerer_socket_future.Get(absl::Seconds(1)); - EXPECT_TRUE(offerer_socket.ok()); - ExceptionOr answerer_socket = - answerer_socket_future.Get(absl::Seconds(1)); - EXPECT_TRUE(answerer_socket.ok()); - - // Send message on data channel - absl::string_view message = "Test"; - offerer_socket.result().GetImpl().GetOutputStream().Write(message); - ExceptionOr received_message = - answerer_socket.result().GetImpl().GetInputStream().Read(4); - EXPECT_TRUE(received_message.ok()); - EXPECT_EQ(received_message.result(), ByteArray{message.data()}); -} - -TEST_F(ConnectionFlowTest, CreateAnswerBeforeOfferReceived) { - WebRtcMedium webrtc_medium; - - std::unique_ptr answerer = ConnectionFlow::Create( - LocalIceCandidateListener(), DataChannelListener(), - ConnectionFlow::AdapterTypeListener(), webrtc_medium); - ASSERT_NE(answerer, nullptr); - - SessionDescriptionWrapper answer = answerer->CreateAnswer(); - EXPECT_FALSE(answer.IsValid()); -} - -TEST_F(ConnectionFlowTest, SetAnswerBeforeOffer) { - WebRtcMedium webrtc_medium_offerer, webrtc_medium_answerer; - - std::unique_ptr offerer = ConnectionFlow::Create( - LocalIceCandidateListener(), DataChannelListener(), - ConnectionFlow::AdapterTypeListener(), webrtc_medium_offerer); - ASSERT_NE(offerer, nullptr); - std::unique_ptr answerer = ConnectionFlow::Create( - LocalIceCandidateListener(), DataChannelListener(), - ConnectionFlow::AdapterTypeListener(), webrtc_medium_answerer); - ASSERT_NE(answerer, nullptr); - - SessionDescriptionWrapper offer = offerer->CreateOffer(); - ASSERT_TRUE(offer.IsValid()); - EXPECT_EQ(offer.GetType(), webrtc::SdpType::kOffer); - // Did not set offer as local session description - EXPECT_TRUE(answerer->OnOfferReceived(offer)); - - SessionDescriptionWrapper answer = answerer->CreateAnswer(); - ASSERT_TRUE(answer.IsValid()); - EXPECT_EQ(answer.GetType(), webrtc::SdpType::kAnswer); - EXPECT_FALSE(offerer->OnAnswerReceived(answer)); -} - -TEST_F(ConnectionFlowTest, CannotCreateOfferAfterClose) { - WebRtcMedium webrtc_medium; - - std::unique_ptr offerer = ConnectionFlow::Create( - LocalIceCandidateListener(), DataChannelListener(), - ConnectionFlow::AdapterTypeListener(), webrtc_medium); - ASSERT_NE(offerer, nullptr); - - EXPECT_TRUE(offerer->CloseIfNotConnected()); - - EXPECT_FALSE(offerer->CreateOffer().IsValid()); -} - -TEST_F(ConnectionFlowTest, CannotSetSessionDescriptionAfterClose) { - WebRtcMedium webrtc_medium; - - std::unique_ptr offerer = ConnectionFlow::Create( - LocalIceCandidateListener(), DataChannelListener(), - ConnectionFlow::AdapterTypeListener(), webrtc_medium); - ASSERT_NE(offerer, nullptr); - - SessionDescriptionWrapper offer = offerer->CreateOffer(); - ASSERT_TRUE(offer.IsValid()); - EXPECT_EQ(offer.GetType(), webrtc::SdpType::kOffer); - - EXPECT_TRUE(offerer->CloseIfNotConnected()); - - EXPECT_FALSE(offerer->SetLocalSessionDescription(offer)); -} - -TEST_F(ConnectionFlowTest, CannotReceiveOfferAfterClose) { - WebRtcMedium webrtc_medium_offerer, webrtc_medium_answerer; - - std::unique_ptr offerer = ConnectionFlow::Create( - LocalIceCandidateListener(), DataChannelListener(), - ConnectionFlow::AdapterTypeListener(), webrtc_medium_offerer); - ASSERT_NE(offerer, nullptr); - std::unique_ptr answerer = ConnectionFlow::Create( - LocalIceCandidateListener(), DataChannelListener(), - ConnectionFlow::AdapterTypeListener(), webrtc_medium_answerer); - ASSERT_NE(answerer, nullptr); - - EXPECT_TRUE(answerer->CloseIfNotConnected()); - - SessionDescriptionWrapper offer = offerer->CreateOffer(); - ASSERT_TRUE(offer.IsValid()); - EXPECT_EQ(offer.GetType(), webrtc::SdpType::kOffer); - - EXPECT_FALSE(answerer->OnOfferReceived(offer)); -} - -TEST_F(ConnectionFlowTest, NullPeerConnection) { - MediumEnvironment::Instance().SetUseValidPeerConnection( - /*use_valid_peer_connection=*/false); - - WebRtcMedium medium; - std::unique_ptr answerer = - ConnectionFlow::Create(LocalIceCandidateListener(), DataChannelListener(), - ConnectionFlow::AdapterTypeListener(), medium); - EXPECT_EQ(answerer, nullptr); -} - -TEST_F(ConnectionFlowTest, PeerConnectionTimeout) { - MediumEnvironment::Instance().SetUseValidPeerConnection( - /*use_valid_peer_connection=*/true); - WebRtcMedium medium1; - std::unique_ptr flow1 = - ConnectionFlow::Create(LocalIceCandidateListener(), DataChannelListener(), - ConnectionFlow::AdapterTypeListener(), medium1); - EXPECT_NE(flow1, nullptr); - - // Attempt to trigger the 2.5s peer connection timeout. - MediumEnvironment::Instance().SetPeerConnectionLatency(absl::Seconds(5)); - WebRtcMedium medium2; - std::unique_ptr flow2 = - ConnectionFlow::Create(LocalIceCandidateListener(), DataChannelListener(), - ConnectionFlow::AdapterTypeListener(), medium2); - EXPECT_EQ(flow2, nullptr); -} - -TEST_F(ConnectionFlowTest, TerminateAnswerer) { - WebRtcMedium webrtc_medium_offerer, webrtc_medium_answerer; - - Future message_received_future; - - Future offerer_socket_future, answerer_socket_future; - - std::unique_ptr offerer, answerer; - - // Send Ice Candidates immediately when you retrieve them - offerer = ConnectionFlow::Create( - {.local_ice_candidate_found_cb = - [&answerer](const webrtc::IceCandidate* candidate) { - std::vector> vec; - vec.push_back(CopyCandidate(candidate)); - // The callback might be alive while the objects in test are - // destroyed. - if (answerer) - answerer->OnRemoteIceCandidatesReceived(std::move(vec)); - }}, - {.data_channel_open_cb = - [&offerer_socket_future](WebRtcSocketWrapper socket) { - offerer_socket_future.Set(std::move(socket)); - }}, - {.adapter_type_changed_cb = - [](webrtc::AdapterType adapter_type) { - // Do nothing - }}, - webrtc_medium_offerer); - ASSERT_NE(offerer, nullptr); - answerer = ConnectionFlow::Create( - {.local_ice_candidate_found_cb = - [&offerer](const webrtc::IceCandidate* candidate) { - std::vector> vec; - vec.push_back(CopyCandidate(candidate)); - // The callback might be alive while the objects in test are - // destroyed. - if (offerer) - offerer->OnRemoteIceCandidatesReceived(std::move(vec)); - }}, - {.data_channel_open_cb = - [&answerer_socket_future](WebRtcSocketWrapper wrapper) { - answerer_socket_future.Set(std::move(wrapper)); - }}, - {.adapter_type_changed_cb = - [](webrtc::AdapterType adapter_type) { - EXPECT_GE(adapter_type, webrtc::ADAPTER_TYPE_UNKNOWN); - EXPECT_LE(adapter_type, webrtc::ADAPTER_TYPE_CELLULAR_5G); - }}, - webrtc_medium_answerer); - ASSERT_NE(answerer, nullptr); - - // Create and send offer - SessionDescriptionWrapper offer = offerer->CreateOffer(); - ASSERT_TRUE(offer.IsValid()); - EXPECT_EQ(offer.GetType(), webrtc::SdpType::kOffer); - EXPECT_TRUE(answerer->OnOfferReceived(offer)); - EXPECT_TRUE(offerer->SetLocalSessionDescription(std::move(offer))); - - // Create and send answer - SessionDescriptionWrapper answer = answerer->CreateAnswer(); - ASSERT_TRUE(answer.IsValid()); - EXPECT_EQ(answer.GetType(), webrtc::SdpType::kAnswer); - EXPECT_TRUE(offerer->OnAnswerReceived(answer)); - EXPECT_TRUE(answerer->SetLocalSessionDescription(std::move(answer))); - - // Retrieve Data Channels - ExceptionOr offerer_socket = - offerer_socket_future.Get(absl::Seconds(1)); - EXPECT_TRUE(offerer_socket.ok()); - ExceptionOr answerer_socket = - answerer_socket_future.Get(absl::Seconds(1)); - EXPECT_TRUE(offerer_socket.ok()); - - CountDownLatch latch(1); - auto pc = answerer->GetPeerConnection(); - pc->signaling_thread()->PostTask([pc, latch]() mutable { - pc->Close(); - latch.CountDown(); - }); - latch.Await(); - - // Send message on data channel - absl::string_view message = "Test"; - offerer_socket.result().GetOutputStream().Write(message); - ExceptionOr received_message = - answerer_socket.result().GetInputStream().Read(4); - EXPECT_TRUE(received_message.GetResult().Empty()); -} - -TEST_F(ConnectionFlowTest, TerminateOfferer) { - WebRtcMedium webrtc_medium_offerer, webrtc_medium_answerer; - - Future message_received_future; - - Future offerer_socket_future, answerer_socket_future; - - std::unique_ptr offerer, answerer; - - // Send Ice Candidates immediately when you retrieve them - offerer = ConnectionFlow::Create( - {.local_ice_candidate_found_cb = - [&answerer](const webrtc::IceCandidate* candidate) { - std::vector> vec; - vec.push_back(CopyCandidate(candidate)); - // The callback might be alive while the objects in test are - // destroyed. - if (answerer) - answerer->OnRemoteIceCandidatesReceived(std::move(vec)); - }}, - {.data_channel_open_cb = - [&offerer_socket_future](WebRtcSocketWrapper socket) { - offerer_socket_future.Set(std::move(socket)); - }}, - {.adapter_type_changed_cb = - [](webrtc::AdapterType adapter_type) { - EXPECT_GE(adapter_type, webrtc::ADAPTER_TYPE_UNKNOWN); - EXPECT_LE(adapter_type, webrtc::ADAPTER_TYPE_CELLULAR_5G); - }}, - webrtc_medium_offerer); - ASSERT_NE(offerer, nullptr); - answerer = ConnectionFlow::Create( - {.local_ice_candidate_found_cb = - [&offerer](const webrtc::IceCandidate* candidate) { - std::vector> vec; - vec.push_back(CopyCandidate(candidate)); - // The callback might be alive while the objects in test are - // destroyed. - if (offerer) - offerer->OnRemoteIceCandidatesReceived(std::move(vec)); - }}, - {.data_channel_open_cb = - [&answerer_socket_future](WebRtcSocketWrapper wrapper) { - answerer_socket_future.Set(std::move(wrapper)); - }}, - {.adapter_type_changed_cb = - [](webrtc::AdapterType adapter_type) { - EXPECT_GE(adapter_type, webrtc::ADAPTER_TYPE_UNKNOWN); - EXPECT_LE(adapter_type, webrtc::ADAPTER_TYPE_CELLULAR_5G); - }}, - webrtc_medium_answerer); - ASSERT_NE(answerer, nullptr); - - // Create and send offer - SessionDescriptionWrapper offer = offerer->CreateOffer(); - ASSERT_TRUE(offer.IsValid()); - EXPECT_EQ(offer.GetType(), webrtc::SdpType::kOffer); - EXPECT_TRUE(answerer->OnOfferReceived(offer)); - EXPECT_TRUE(offerer->SetLocalSessionDescription(std::move(offer))); - - // Create and send answer - SessionDescriptionWrapper answer = answerer->CreateAnswer(); - ASSERT_TRUE(answer.IsValid()); - EXPECT_EQ(answer.GetType(), webrtc::SdpType::kAnswer); - EXPECT_TRUE(offerer->OnAnswerReceived(answer)); - EXPECT_TRUE(answerer->SetLocalSessionDescription(std::move(answer))); - - // Retrieve Data Channels - ExceptionOr offerer_socket = - offerer_socket_future.Get(absl::Seconds(1)); - EXPECT_TRUE(offerer_socket.ok()); - ExceptionOr answerer_socket = - answerer_socket_future.Get(absl::Seconds(1)); - EXPECT_TRUE(offerer_socket.ok()); - - CountDownLatch latch(1); - auto pc = offerer->GetPeerConnection(); - pc->signaling_thread()->PostTask([pc, latch]() mutable { - pc->Close(); - latch.CountDown(); - }); - latch.Await(); - - // Send message on data channel - absl::string_view message = "Test"; - offerer_socket.result().GetOutputStream().Write(message); - ExceptionOr received_message = - answerer_socket.result().GetInputStream().Read(4); - EXPECT_TRUE(received_message.GetResult().Empty()); -} - -} // namespace -} // namespace mediums -} // namespace connections -} // namespace nearby diff --git a/connections/implementation/mediums/webrtc/data_channel_listener.h b/connections/implementation/mediums/webrtc/data_channel_listener.h deleted file mode 100644 index 17e6a0dc..00000000 --- a/connections/implementation/mediums/webrtc/data_channel_listener.h +++ /dev/null @@ -1,44 +0,0 @@ -// 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. - -#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_DATA_CHANNEL_LISTENER_H_ -#define CORE_INTERNAL_MEDIUMS_WEBRTC_DATA_CHANNEL_LISTENER_H_ - -#ifndef NO_WEBRTC - -#include "absl/functional/any_invocable.h" -#include "connections/implementation/mediums/webrtc_socket.h" - -namespace nearby { -namespace connections { -namespace mediums { - -// Callbacks from the data channel. -struct DataChannelListener { - // Called when the data channel is open and the socket wrapper is ready to - // read and write. - absl::AnyInvocable data_channel_open_cb = - [](WebRtcSocketWrapper) {}; - - // Called when the data channel is closed. - absl::AnyInvocable data_channel_closed_cb = []() {}; -}; - -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif - -#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_DATA_CHANNEL_LISTENER_H_ diff --git a/connections/implementation/mediums/webrtc/local_ice_candidate_listener.h b/connections/implementation/mediums/webrtc/local_ice_candidate_listener.h deleted file mode 100644 index da71236f..00000000 --- a/connections/implementation/mediums/webrtc/local_ice_candidate_listener.h +++ /dev/null @@ -1,42 +0,0 @@ -// 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. - -#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_LOCAL_ICE_CANDIDATE_LISTENER_H_ -#define CORE_INTERNAL_MEDIUMS_WEBRTC_LOCAL_ICE_CANDIDATE_LISTENER_H_ - -#ifndef NO_WEBRTC - -#include "connections/listeners.h" -#include "webrtc/api/jsep.h" -#include "webrtc/api/peer_connection_interface.h" - -namespace nearby { -namespace connections { -namespace mediums { - -// Callbacks from local ice candidate collection. -struct LocalIceCandidateListener { - // Called when a new local ice candidate has been found. - absl::AnyInvocable - local_ice_candidate_found_cb = - nearby::DefaultCallback(); -}; - -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif - -#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_LOCAL_ICE_CANDIDATE_LISTENER_H_ diff --git a/connections/implementation/mediums/webrtc/session_description_wrapper.h b/connections/implementation/mediums/webrtc/session_description_wrapper.h deleted file mode 100644 index ef468120..00000000 --- a/connections/implementation/mediums/webrtc/session_description_wrapper.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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. - -#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_SESSION_DESCRIPTION_WRAPPER_H_ -#define CORE_INTERNAL_MEDIUMS_WEBRTC_SESSION_DESCRIPTION_WRAPPER_H_ - -#ifndef NO_WEBRTC - -#include "webrtc/api/peer_connection_interface.h" - -// Wrapper object around SessionDescriptionInterface*. -// This object owns the SessionDescriptionInterface* unless Release() has been -// called. -class SessionDescriptionWrapper { - public: - SessionDescriptionWrapper() = default; - explicit SessionDescriptionWrapper(webrtc::SessionDescriptionInterface* sdp) - : impl_(sdp) {} - - // Copy constructor that performs a deep copy, i.e. creates a new - // SessionDescriptionInterface. - SessionDescriptionWrapper(const SessionDescriptionWrapper& sdp) { - if (sdp.IsValid()) { - impl_ = webrtc::CreateSessionDescription(sdp.GetType(), sdp.ToString()); - } - } - - SessionDescriptionWrapper(SessionDescriptionWrapper&&) = default; - SessionDescriptionWrapper& operator=(SessionDescriptionWrapper&&) = default; - - // Release the ownership of the SessionDescriptionInterface*. - webrtc::SessionDescriptionInterface* Release() { return impl_.release(); } - - // Returns a string representation of the sdp. Only call this, if IsValid() is - // true. - std::string ToString() const { - std::string str; - impl_->ToString(&str); - return str; - } - - // Returns the SdpType of the SessionDescriptionInterface. Only call this, if - // IsValid() is true. - webrtc::SdpType GetType() const { return impl_->GetType(); } - - const webrtc::SessionDescriptionInterface& GetSdp() { return *impl_; } - - // Return whether this object currently holds a SessionDescriptionInterface. - bool IsValid() const { return impl_ != nullptr; } - - private: - std::unique_ptr impl_; -}; - -#endif - -#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_SESSION_DESCRIPTION_WRAPPER_H_ diff --git a/connections/implementation/mediums/webrtc/signaling_frames.cc b/connections/implementation/mediums/webrtc/signaling_frames.cc deleted file mode 100644 index 8991c60a..00000000 --- a/connections/implementation/mediums/webrtc/signaling_frames.cc +++ /dev/null @@ -1,135 +0,0 @@ -// 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. - -#ifndef NO_WEBRTC - -#include "connections/implementation/mediums/webrtc/signaling_frames.h" - -namespace nearby { -namespace connections { -namespace mediums { -namespace webrtc_frames { -using WebRtcSignalingFrame = ::location::nearby::mediums::WebRtcSignalingFrame; - -namespace { - -ByteArray FrameToByteArray(const WebRtcSignalingFrame& signaling_frame) { - std::string message; - signaling_frame.SerializeToString(&message); - return ByteArray(message.c_str(), message.size()); -} - -void SetSenderId(const WebrtcPeerId& sender_id, WebRtcSignalingFrame& frame) { - frame.mutable_sender_id()->set_id(sender_id.GetId()); -} - -std::unique_ptr DecodeIceCandidate( - location::nearby::mediums::IceCandidate ice_candidate_proto) { - webrtc::SdpParseError error; - return std::unique_ptr(webrtc::CreateIceCandidate( - ice_candidate_proto.sdp_mid(), ice_candidate_proto.sdp_m_line_index(), - ice_candidate_proto.sdp(), &error)); -} - -} // namespace - -ByteArray EncodeReadyForSignalingPoke(const WebrtcPeerId& sender_id) { - WebRtcSignalingFrame signaling_frame; - signaling_frame.set_type(WebRtcSignalingFrame::READY_FOR_SIGNALING_POKE_TYPE); - SetSenderId(sender_id, signaling_frame); - signaling_frame.set_allocated_ready_for_signaling_poke( - new location::nearby::mediums::ReadyForSignalingPoke()); - return FrameToByteArray(std::move(signaling_frame)); -} - -ByteArray EncodeOffer(const WebrtcPeerId& sender_id, - const webrtc::SessionDescriptionInterface& offer) { - WebRtcSignalingFrame signaling_frame; - signaling_frame.set_type(WebRtcSignalingFrame::OFFER_TYPE); - SetSenderId(sender_id, signaling_frame); - std::string offer_str; - offer.ToString(&offer_str); - signaling_frame.mutable_offer() - ->mutable_session_description() - ->set_description(offer_str); - return FrameToByteArray(std::move(signaling_frame)); -} - -ByteArray EncodeAnswer(const WebrtcPeerId& sender_id, - const webrtc::SessionDescriptionInterface& answer) { - WebRtcSignalingFrame signaling_frame; - signaling_frame.set_type(WebRtcSignalingFrame::ANSWER_TYPE); - SetSenderId(sender_id, signaling_frame); - std::string answer_str; - answer.ToString(&answer_str); - signaling_frame.mutable_answer() - ->mutable_session_description() - ->set_description(answer_str); - return FrameToByteArray(std::move(signaling_frame)); -} - -ByteArray EncodeIceCandidates( - const WebrtcPeerId& sender_id, - const std::vector& - ice_candidates) { - WebRtcSignalingFrame signaling_frame; - signaling_frame.set_type(WebRtcSignalingFrame::ICE_CANDIDATES_TYPE); - SetSenderId(sender_id, signaling_frame); - for (const auto& ice_candidate : ice_candidates) { - *signaling_frame.mutable_ice_candidates()->add_ice_candidates() = - ice_candidate; - } - return FrameToByteArray(std::move(signaling_frame)); -} - -std::unique_ptr DecodeOffer( - const WebRtcSignalingFrame& frame) { - return webrtc::CreateSessionDescription( - webrtc::SdpType::kOffer, - frame.offer().session_description().description()); -} - -std::unique_ptr DecodeAnswer( - const WebRtcSignalingFrame& frame) { - return webrtc::CreateSessionDescription( - webrtc::SdpType::kAnswer, - frame.answer().session_description().description()); -} - -std::vector> DecodeIceCandidates( - const WebRtcSignalingFrame& frame) { - std::vector> ice_candidates; - for (const auto& candidate : frame.ice_candidates().ice_candidates()) { - ice_candidates.push_back(DecodeIceCandidate(candidate)); - } - return ice_candidates; -} - -location::nearby::mediums::IceCandidate EncodeIceCandidate( - const webrtc::IceCandidate& ice_candidate) { - std::string sdp; - ice_candidate.ToString(&sdp); - location::nearby::mediums::IceCandidate ice_candidate_proto; - ice_candidate_proto.set_sdp(sdp); - ice_candidate_proto.set_sdp_mid(ice_candidate.sdp_mid()); - ice_candidate_proto.set_sdp_m_line_index(ice_candidate.sdp_mline_index()); - return ice_candidate_proto; -} - -} // namespace webrtc_frames -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif diff --git a/connections/implementation/mediums/webrtc/signaling_frames.h b/connections/implementation/mediums/webrtc/signaling_frames.h deleted file mode 100644 index 6e13645d..00000000 --- a/connections/implementation/mediums/webrtc/signaling_frames.h +++ /dev/null @@ -1,60 +0,0 @@ -// 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. - -#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_SIGNALING_FRAMES_H_ -#define CORE_INTERNAL_MEDIUMS_WEBRTC_SIGNALING_FRAMES_H_ - -#ifndef NO_WEBRTC - -#include - -#include "connections/implementation/mediums/webrtc_peer_id.h" -#include "internal/platform/byte_array.h" -#include "proto/mediums/web_rtc_signaling_frames.pb.h" -#include "webrtc/api/peer_connection_interface.h" - -namespace nearby { -namespace connections { -namespace mediums { -namespace webrtc_frames { - -ByteArray EncodeReadyForSignalingPoke(const WebrtcPeerId& sender_id); - -ByteArray EncodeOffer(const WebrtcPeerId& sender_id, - const webrtc::SessionDescriptionInterface& offer); -ByteArray EncodeAnswer(const WebrtcPeerId& sender_id, - const webrtc::SessionDescriptionInterface& answer); - -ByteArray EncodeIceCandidates( - const WebrtcPeerId& sender_id, - const std::vector& ice_candidates); -location::nearby::mediums::IceCandidate EncodeIceCandidate( - const webrtc::IceCandidate& ice_candidate); - -std::unique_ptr DecodeOffer( - const location::nearby::mediums::WebRtcSignalingFrame& frame); -std::unique_ptr DecodeAnswer( - const location::nearby::mediums::WebRtcSignalingFrame& frame); - -std::vector> DecodeIceCandidates( - const location::nearby::mediums::WebRtcSignalingFrame& frame); - -} // namespace webrtc_frames -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif - -#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_SIGNALING_FRAMES_H_ diff --git a/connections/implementation/mediums/webrtc/signaling_frames_test.cc b/connections/implementation/mediums/webrtc/signaling_frames_test.cc deleted file mode 100644 index de5f92bb..00000000 --- a/connections/implementation/mediums/webrtc/signaling_frames_test.cc +++ /dev/null @@ -1,198 +0,0 @@ -// 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 "connections/implementation/mediums/webrtc/signaling_frames.h" - -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "connections/implementation/mediums/webrtc_peer_id.h" -#include "google/protobuf/text_format.h" -#include "webrtc/api/jsep.h" - -namespace nearby { -namespace connections { -namespace mediums { -namespace webrtc_frames { - -namespace { - -using ::location::nearby::mediums::IceCandidate; -using ::location::nearby::mediums::WebRtcSignalingFrame; -const char kSampleSdp[] = - "v=0\r\no=- 7859371131 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 " - "0\r\na=msid-semantic: WMS\r\n"; - -const char kIceCandidateSdp1[] = - "a=candidate:1 1 UDP 2130706431 10.0.1.1 8998 typ host"; -const char kIceCandidateSdp2[] = - "a=candidate:2 1 UDP 1694498815 192.0.2.3 45664 typ srflx raddr"; - -const char kIceSdpMid[] = "data"; -const int kIceSdpMLineIndex = 0; - -const char kOfferProto[] = R"( - sender_id { id: "abc" } - type: OFFER_TYPE - offer { - session_description { - description: "v=0\r\no=- 7859371131 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=msid-semantic: WMS\r\n" - } - } - )"; - -const char kAnswerProto[] = R"( - sender_id { id: "abc" } - type: ANSWER_TYPE - answer { - session_description { - description: "v=0\r\no=- 7859371131 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=msid-semantic: WMS\r\n" - } - } - )"; - -const char kIceCandidatesProto[] = R"( - sender_id { id: "abc" } - type: ICE_CANDIDATES_TYPE - ice_candidates { - ice_candidates { - sdp: "candidate:1 1 udp 2130706431 10.0.1.1 8998 typ host generation 0" - sdp_mid: "data" - sdp_m_line_index: 0 - } - ice_candidates { - sdp: "candidate:2 1 udp 1694498815 192.0.2.3 45664 typ srflx generation 0" - sdp_mid: "data" - sdp_m_line_index: 0 - } - } - )"; -} // namespace - -TEST(SignalingFramesTest, SignalingPoke) { - WebrtcPeerId sender_id("abc"); - ByteArray encoded_poke = EncodeReadyForSignalingPoke(sender_id); - - WebRtcSignalingFrame frame; - frame.ParseFromString(std::string(encoded_poke.data(), encoded_poke.size())); - - EXPECT_THAT(frame, protobuf_matchers::EqualsProto(R"pb( - sender_id { id: "abc" } - type: READY_FOR_SIGNALING_POKE_TYPE - ready_for_signaling_poke {} - )pb")); -} - -TEST(SignalingFramesTest, EncodeValidOffer) { - WebrtcPeerId sender_id("abc"); - std::unique_ptr offer = - webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, kSampleSdp); - ByteArray encoded_offer = EncodeOffer(sender_id, *offer); - - WebRtcSignalingFrame frame; - frame.ParseFromString( - std::string(encoded_offer.data(), encoded_offer.size())); - - EXPECT_THAT(frame, protobuf_matchers::EqualsProto(kOfferProto)); -} - -TEST(SignaingFramesTest, DecodeValidOffer) { - WebRtcSignalingFrame frame; - proto2::TextFormat::ParseFromString(kOfferProto, &frame); - std::unique_ptr decoded_offer = - DecodeOffer(frame); - - EXPECT_EQ(webrtc::SdpType::kOffer, decoded_offer->GetType()); - std::string description; - decoded_offer->ToString(&description); - EXPECT_EQ(kSampleSdp, description); -} - -TEST(SignalingFramesTest, EncodeValidAnswer) { - WebrtcPeerId sender_id("abc"); - std::unique_ptr answer( - webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, kSampleSdp)); - ByteArray encoded_answer = EncodeAnswer(sender_id, *answer); - - WebRtcSignalingFrame frame; - frame.ParseFromString( - std::string(encoded_answer.data(), encoded_answer.size())); - - EXPECT_THAT(frame, protobuf_matchers::EqualsProto(kAnswerProto)); -} - -TEST(SignalingFramesTest, DecodeValidAnswer) { - WebRtcSignalingFrame frame; - proto2::TextFormat::ParseFromString(kAnswerProto, &frame); - std::unique_ptr decoded_answer = - DecodeAnswer(frame); - - EXPECT_EQ(webrtc::SdpType::kAnswer, decoded_answer->GetType()); - std::string description; - decoded_answer->ToString(&description); - EXPECT_EQ(kSampleSdp, description); -} - -TEST(SignalingFramesTest, EncodeValidIceCandidates) { - WebrtcPeerId sender_id("abc"); - webrtc::SdpParseError error; - - std::vector> ice_candidates; - ice_candidates.emplace_back(webrtc::CreateIceCandidate( - kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp1, &error)); - ice_candidates.emplace_back(webrtc::CreateIceCandidate( - kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp2, &error)); - std::vector encoded_candidates_vec; - for (const auto& ice_candidate : ice_candidates) { - encoded_candidates_vec.push_back(EncodeIceCandidate(*ice_candidate)); - } - ByteArray encoded_candidates = - EncodeIceCandidates(sender_id, encoded_candidates_vec); - - WebRtcSignalingFrame frame; - frame.ParseFromString( - std::string(encoded_candidates.data(), encoded_candidates.size())); - - EXPECT_THAT(frame, protobuf_matchers::EqualsProto(kIceCandidatesProto)); -} - -TEST(SignalingFramesTest, DecodeValidIceCandidates) { - webrtc::SdpParseError error; - std::vector> ice_candidates; - ice_candidates.emplace_back(webrtc::CreateIceCandidate( - kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp1, &error)); - ice_candidates.emplace_back(webrtc::CreateIceCandidate( - kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp2, &error)); - - WebRtcSignalingFrame frame; - proto2::TextFormat::ParseFromString(kIceCandidatesProto, &frame); - std::vector> decoded_candidates = - DecodeIceCandidates(frame); - - ASSERT_EQ(2u, decoded_candidates.size()); - for (int i = 0; i < static_cast(decoded_candidates.size()); i++) { - EXPECT_TRUE(ice_candidates[i]->candidate().IsEquivalent( - decoded_candidates[i]->candidate())); - EXPECT_EQ(ice_candidates[i]->sdp_mid(), decoded_candidates[i]->sdp_mid()); - EXPECT_EQ(ice_candidates[i]->sdp_mline_index(), - decoded_candidates[i]->sdp_mline_index()); - } -} - -} // namespace webrtc_frames -} // namespace mediums -} // namespace connections -} // namespace nearby diff --git a/connections/implementation/mediums/webrtc/webrtc_socket_impl.cc b/connections/implementation/mediums/webrtc/webrtc_socket_impl.cc deleted file mode 100644 index aca93c67..00000000 --- a/connections/implementation/mediums/webrtc/webrtc_socket_impl.cc +++ /dev/null @@ -1,202 +0,0 @@ -// 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 -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/exception.h" -#include "internal/platform/input_stream.h" -#include "internal/platform/pipe.h" -#ifndef NO_WEBRTC - -#include "connections/implementation/mediums/webrtc/webrtc_socket_impl.h" -#include "internal/platform/logging.h" -#include "internal/platform/mutex_lock.h" - -namespace nearby { -namespace connections { -namespace mediums { - -// OutputStreamImpl -Exception WebRtcSocket::OutputStreamImpl::Write(absl::string_view data) { - if (data.size() > kMaxDataSize) { - LOG(WARNING) << "Sending data larger than 1MB"; - return {Exception::kIo}; - } - - socket_->BlockUntilSufficientSpaceInBuffer(data.size()); - - if (socket_->IsClosed()) { - LOG(WARNING) << "Tried sending message while socket is closed"; - return {Exception::kIo}; - } - - if (!socket_->SendMessage(ByteArray::FromStringView(data))) { - LOG(INFO) << "Unable to write data to socket."; - return {Exception::kIo}; - } - return {Exception::kSuccess}; -} - -Exception WebRtcSocket::OutputStreamImpl::Flush() { - // Java implementation is empty. - return {Exception::kSuccess}; -} - -Exception WebRtcSocket::OutputStreamImpl::Close() { - socket_->Close(); - return {Exception::kSuccess}; -} - -// WebRtcSocket -WebRtcSocket::WebRtcSocket( - const std::string& name, - webrtc::scoped_refptr data_channel) - : name_(name), data_channel_(std::move(data_channel)) { - LOG(INFO) << "WebRtcSocket::WebRtcSocket(" << name_ << ") this: " << this; - std::tie(pipe_input_, pipe_output_) = CreatePipe(); - data_channel_->RegisterObserver(this); -} - -WebRtcSocket::~WebRtcSocket() { - LOG(INFO) << "WebRtcSocket::~WebRtcSocket(" << name_ << ") this: " << this; - - if (!IsClosed()) { - data_channel_->UnregisterObserver(); - Close(); - } - - LOG(INFO) << "WebRtcSocket::~WebRtcSocket(" << name_ << ") this: " << this - << " done"; -} - -InputStream& WebRtcSocket::GetInputStream() { return *pipe_input_; } - -OutputStream& WebRtcSocket::GetOutputStream() { return output_stream_; } - -Exception WebRtcSocket::Close() { - LOG(INFO) << "WebRtcSocket::Close(" << name_ << ") this: " << this; - if (closed_.Set(true)) return {Exception::kSuccess}; - - ClosePipe(); - // NOTE: This call blocks and triggers a state change on the siginaling thread - // to 'closing' but does not block until 'closed' is sent so the data channel - // is not fully closed when this call is done. - data_channel_->Close(); - LOG(INFO) << "WebRtcSocket::Close(" << name_ << ") this: " << this << " done"; - return {Exception::kSuccess}; -} - -void WebRtcSocket::OnStateChange() { - // Running on the signaling thread right now. - LOG(ERROR) << "WebRtcSocket::OnStateChange() webrtc data channel state: " - << webrtc::DataChannelInterface::DataStateString( - data_channel_->state()); - switch (data_channel_->state()) { - case webrtc::DataChannelInterface::DataState::kConnecting: - break; - case webrtc::DataChannelInterface::DataState::kOpen: - // We implicitly depend on the |socket_listener_| to offload from - // the signaling thread so it does not get blocked. - socket_listener_.socket_ready_cb(this); - break; - case webrtc::DataChannelInterface::DataState::kClosing: - break; - case webrtc::DataChannelInterface::DataState::kClosed: - LOG(ERROR) << "WebRtcSocket::OnStateChange() unregistering data " - "channel observer."; - // This will trigger a destruction of the owning connection flow - // We implicitly depend on the |socket_listener_| to offload from - // the signaling thread so it does not get blocked. - socket_listener_.socket_closed_cb(this); - - if (!closed_.Set(true)) { - OffloadFromSignalingThread([this] { ClosePipe(); }); - } - break; - } -} -void WebRtcSocket::OnMessage(const webrtc::DataBuffer& buffer) { - // This is a data channel callback on the signaling thread, lets off load so - // we don't block signaling. - OffloadFromSignalingThread( - [this, buffer = ByteArray(buffer.data.data(), buffer.size())] { - if (!pipe_output_->Write(buffer.AsStringView()).Ok()) { - Close(); - return; - } - - if (!pipe_output_->Flush().Ok()) { - Close(); - } - }); -} - -void WebRtcSocket::OnBufferedAmountChange(uint64_t sent_data_size) { - // This is a data channel callback on the signaling thread, lets off load so - // we don't block signaling. - OffloadFromSignalingThread([this] { WakeUpWriter(); }); -} - -bool WebRtcSocket::SendMessage(const ByteArray& data) { - return data_channel_->Send( - webrtc::DataBuffer(std::string(data.data(), data.size()))); -} - -bool WebRtcSocket::IsClosed() { return closed_.Get(); } - -void WebRtcSocket::ClosePipe() { - LOG(INFO) << "WebRtcSocket::ClosePipe(" << name_ << ") this: " << this; - // This is thread-safe to close these sockets even if a read or write is in - // process on another thread, Close will wait for the exclusive mutex before - // setting state. - pipe_input_->Close(); - pipe_output_->Close(); - WakeUpWriter(); - LOG(INFO) << "WebRtcSocket::ClosePipe(" << name_ << ") this: " << this - << " done"; -} - -// Must not be called on signalling thread. -void WebRtcSocket::WakeUpWriter() { - MutexLock lock(&backpressure_mutex_); - buffer_variable_.Notify(); -} - -void WebRtcSocket::SetSocketListener(SocketListener&& listener) { - socket_listener_ = std::move(listener); -} - -void WebRtcSocket::BlockUntilSufficientSpaceInBuffer(int length) { - MutexLock lock(&backpressure_mutex_); - while (!IsClosed() && - (data_channel_->buffered_amount() + length > kMaxDataSize)) { - // TODO(himanshujaju): Add wait with timeout. - buffer_variable_.Wait(); - } -} - -void WebRtcSocket::OffloadFromSignalingThread(Runnable runnable) { - single_thread_executor_.Execute(std::move(runnable)); -} - -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif diff --git a/connections/implementation/mediums/webrtc/webrtc_socket_impl.h b/connections/implementation/mediums/webrtc/webrtc_socket_impl.h deleted file mode 100644 index 109367b7..00000000 --- a/connections/implementation/mediums/webrtc/webrtc_socket_impl.h +++ /dev/null @@ -1,129 +0,0 @@ -// 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. - -#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_IMPL_H_ -#define CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_IMPL_H_ - -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/exception.h" -#include "internal/platform/listeners.h" -#include "internal/platform/runnable.h" -#ifndef NO_WEBRTC -#include "internal/platform/atomic_boolean.h" -#include "internal/platform/condition_variable.h" -#include "internal/platform/input_stream.h" -#include "internal/platform/mutex.h" -#include "internal/platform/output_stream.h" -#include "internal/platform/single_thread_executor.h" -#include "internal/platform/socket.h" - -namespace nearby { -namespace connections { -namespace mediums { - -// Maximum data size: 1 MB -constexpr int kMaxDataSize = 1 * 1024 * 1024; - -// Defines the Socket implementation specific to WebRTC, which uses the WebRTC -// data channel to send and receive messages. -// -// Messages are buffered here to prevent the data channel from overflowing, -// which could lead to data loss. -class WebRtcSocket : public Socket, public webrtc::DataChannelObserver { - public: - WebRtcSocket( - const std::string& name, - webrtc::scoped_refptr data_channel); - ~WebRtcSocket() override; - - WebRtcSocket(const WebRtcSocket& other) = delete; - WebRtcSocket& operator=(const WebRtcSocket& other) = delete; - - // Overrides for nearby::Socket: - InputStream& GetInputStream() override; - OutputStream& GetOutputStream() override; - Exception Close() override; - - // webrtc::DataChannelObserver: - void OnStateChange() override; - void OnMessage(const webrtc::DataBuffer& buffer) override; - void OnBufferedAmountChange(uint64_t sent_data_size) override; - - // Listener class the gets called when the socket is ready or closed - struct SocketListener { - absl::AnyInvocable socket_ready_cb = - DefaultCallback(); - absl::AnyInvocable socket_closed_cb = - DefaultCallback(); - }; - - void SetSocketListener(SocketListener&& listener); - - private: - class OutputStreamImpl : public OutputStream { - public: - explicit OutputStreamImpl(WebRtcSocket* const socket) : socket_(socket) {} - ~OutputStreamImpl() override = default; - - OutputStreamImpl(const OutputStreamImpl& other) = delete; - OutputStreamImpl& operator=(const OutputStreamImpl& other) = delete; - - // OutputStream: - Exception Write(absl::string_view data) override; - Exception Flush() override; - Exception Close() override; - - private: - // |this| OutputStreamImpl is owned by |socket_|. - WebRtcSocket* const socket_; - }; - - void WakeUpWriter(); - bool IsClosed(); - void ClosePipe(); - bool SendMessage(const ByteArray& data); - void BlockUntilSufficientSpaceInBuffer(int length); - void OffloadFromSignalingThread(Runnable runnable); - - std::string name_; - webrtc::scoped_refptr data_channel_; - - std::unique_ptr pipe_input_; - std::unique_ptr pipe_output_; - OutputStreamImpl output_stream_{this}; - - AtomicBoolean closed_{false}; - - SocketListener socket_listener_; - - mutable Mutex backpressure_mutex_; - ConditionVariable buffer_variable_{&backpressure_mutex_}; - - // This should be destroyed first to ensure any remaining tasks flushed on - // shutdown get run while the other members are still alive. - SingleThreadExecutor single_thread_executor_; -}; - -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif - -#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_IMPL_H_ diff --git a/connections/implementation/mediums/webrtc/webrtc_socket_impl_test.cc b/connections/implementation/mediums/webrtc/webrtc_socket_impl_test.cc deleted file mode 100644 index 78ec1771..00000000 --- a/connections/implementation/mediums/webrtc/webrtc_socket_impl_test.cc +++ /dev/null @@ -1,247 +0,0 @@ -// 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 "connections/implementation/mediums/webrtc/webrtc_socket_impl.h" - -#include -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/exception.h" -#include "webrtc/api/data_channel_interface.h" - -namespace nearby { -namespace connections { -namespace mediums { - -namespace { - -// using TestPlatform = platform::ImplementationPlatform; - -const char kSocketName[] = "TestSocket"; - -class MockDataChannel - : public webrtc::RefCountedObject { - public: - MOCK_METHOD(void, RegisterObserver, (webrtc::DataChannelObserver*)); - MOCK_METHOD(void, UnregisterObserver, ()); - - MOCK_METHOD(std::string, label, (), (const)); - - MOCK_METHOD(bool, reliable, (), (const)); - MOCK_METHOD(int, id, (), (const)); - MOCK_METHOD(DataState, state, (), (const)); - MOCK_METHOD(uint32_t, messages_sent, (), (const)); - MOCK_METHOD(uint64_t, bytes_sent, (), (const)); - MOCK_METHOD(uint32_t, messages_received, (), (const)); - MOCK_METHOD(uint64_t, bytes_received, (), (const)); - - MOCK_METHOD(uint64_t, buffered_amount, (), (const)); - - MOCK_METHOD(void, Close, ()); - - MOCK_METHOD(bool, Send, (const webrtc::DataBuffer&)); -}; - -} // namespace - -TEST(WebRtcSocketTest, ReadFromSocket) { - const char* message = "message"; - webrtc::scoped_refptr mock_data_channel( - new MockDataChannel()); - WebRtcSocket webrtc_socket(kSocketName, mock_data_channel); - - webrtc_socket.OnMessage(webrtc::DataBuffer{message}); - ExceptionOr result = webrtc_socket.GetInputStream().Read(7); - EXPECT_TRUE(result.ok()); - EXPECT_EQ(result.result(), ByteArray{message}); -} - -TEST(WebRtcSocketTest, ReadMultipleMessages) { - webrtc::scoped_refptr mock_data_channel( - new MockDataChannel()); - WebRtcSocket webrtc_socket(kSocketName, mock_data_channel); - - webrtc_socket.OnMessage(webrtc::DataBuffer{"Me"}); - webrtc_socket.OnMessage(webrtc::DataBuffer{"ssa"}); - webrtc_socket.OnMessage(webrtc::DataBuffer{"ge"}); - - ExceptionOr result; - - // This behaviour is different from the Java code - result = webrtc_socket.GetInputStream().Read(7); - EXPECT_TRUE(result.ok()); - EXPECT_EQ(result.result(), ByteArray{"Me"}); - - result = webrtc_socket.GetInputStream().Read(7); - EXPECT_TRUE(result.ok()); - EXPECT_EQ(result.result(), ByteArray{"ssa"}); - - result = webrtc_socket.GetInputStream().Read(7); - EXPECT_TRUE(result.ok()); - EXPECT_EQ(result.result(), ByteArray{"ge"}); -} - -TEST(WebRtcSocketTest, WriteToSocket) { - absl::string_view kMessage{"Message"}; - webrtc::scoped_refptr mock_data_channel( - new MockDataChannel()); - WebRtcSocket webrtc_socket(kSocketName, mock_data_channel); - - EXPECT_CALL(*mock_data_channel, Send(testing::_)) - .WillRepeatedly(testing::Return(true)); - EXPECT_TRUE(webrtc_socket.GetOutputStream().Write(kMessage).Ok()); -} - -TEST(WebRtcSocketTest, SendDataBiggerThanMax) { - std::string kMessage(kMaxDataSize + 1, '0'); - webrtc::scoped_refptr mock_data_channel( - new MockDataChannel()); - WebRtcSocket webrtc_socket(kSocketName, mock_data_channel); - - EXPECT_CALL(*mock_data_channel, Send(testing::_)).Times(0); - EXPECT_EQ(webrtc_socket.GetOutputStream().Write(kMessage), - Exception{Exception::kIo}); -} - -TEST(WebRtcSocketTest, WriteToDataChannelFails) { - absl::string_view kMessage{"Message"}; - webrtc::scoped_refptr mock_data_channel( - new MockDataChannel()); - WebRtcSocket webrtc_socket(kSocketName, mock_data_channel); - - ON_CALL(*mock_data_channel, Send(testing::_)) - .WillByDefault(testing::Return(false)); - EXPECT_EQ(webrtc_socket.GetOutputStream().Write(kMessage), - Exception{Exception::kIo}); -} - -TEST(WebRtcSocketTest, Close) { - webrtc::scoped_refptr mock_data_channel( - new MockDataChannel()); - WebRtcSocket webrtc_socket(kSocketName, mock_data_channel); - - EXPECT_CALL(*mock_data_channel, Close()); - - int socket_closed_cb_called = 0; - - webrtc_socket.SetSocketListener( - {.socket_closed_cb = [&](WebRtcSocket* socket) { - socket_closed_cb_called++; - }}); - webrtc_socket.Close(); - - // We have to fake the close event to get the callback to run. - ON_CALL(*mock_data_channel, state()) - .WillByDefault( - testing::Return(webrtc::DataChannelInterface::DataState::kClosed)); - - webrtc_socket.OnStateChange(); - - EXPECT_EQ(socket_closed_cb_called, 1); -} - -TEST(WebRtcSocketTest, WriteOnClosedChannel) { - absl::string_view kMessage{"Message"}; - webrtc::scoped_refptr mock_data_channel( - new MockDataChannel()); - WebRtcSocket webrtc_socket(kSocketName, mock_data_channel); - webrtc_socket.Close(); - - EXPECT_CALL(*mock_data_channel, Send(testing::_)).Times(0); - EXPECT_EQ(webrtc_socket.GetOutputStream().Write(kMessage), - Exception{Exception::kIo}); -} - -TEST(WebRtcSocketTest, ReadFromClosedChannel) { - absl::string_view kMessage{"Message"}; - webrtc::scoped_refptr mock_data_channel( - new MockDataChannel()); - WebRtcSocket webrtc_socket(kSocketName, mock_data_channel); - ON_CALL(*mock_data_channel, Send(testing::_)) - .WillByDefault(testing::Return(true)); - - webrtc_socket.GetOutputStream().Write(kMessage); - webrtc_socket.Close(); - - EXPECT_TRUE(webrtc_socket.GetInputStream().Read(7).GetResult().Empty()); -} - -TEST(WebRtcSocketTest, DataChannelCloseEventCleansUp) { - webrtc::scoped_refptr mock_data_channel( - new MockDataChannel()); - WebRtcSocket webrtc_socket(kSocketName, mock_data_channel); - - ON_CALL(*mock_data_channel, state()) - .WillByDefault( - testing::Return(webrtc::DataChannelInterface::DataState::kClosed)); - - webrtc_socket.OnStateChange(); - - EXPECT_TRUE(webrtc_socket.GetInputStream().Read(7).GetResult().Empty()); - - // Calling Close again should be safe even if the channel is already shut - // down. - webrtc_socket.Close(); -} - -TEST(WebRtcSocketTest, OpenStateTriggersCallback) { - webrtc::scoped_refptr mock_data_channel( - new MockDataChannel()); - WebRtcSocket webrtc_socket(kSocketName, mock_data_channel); - - int socket_ready_cb_called = 0; - - webrtc_socket.SetSocketListener( - {.socket_ready_cb = [&](WebRtcSocket* socket) { - socket_ready_cb_called++; - }}); - - ON_CALL(*mock_data_channel, state()) - .WillByDefault( - testing::Return(webrtc::DataChannelInterface::DataState::kOpen)); - - webrtc_socket.OnStateChange(); - - EXPECT_EQ(socket_ready_cb_called, 1); -} - -TEST(WebRtcSocketTest, CloseStateTriggersCallback) { - webrtc::scoped_refptr mock_data_channel( - new MockDataChannel()); - WebRtcSocket webrtc_socket(kSocketName, mock_data_channel); - - int socket_closed_cb_called = 0; - - webrtc_socket.SetSocketListener( - {.socket_closed_cb = [&](WebRtcSocket* socket) { - socket_closed_cb_called++; - }}); - - ON_CALL(*mock_data_channel, state()) - .WillByDefault( - testing::Return(webrtc::DataChannelInterface::DataState::kClosed)); - - webrtc_socket.OnStateChange(); - - EXPECT_EQ(socket_closed_cb_called, 1); -} - -} // namespace mediums -} // namespace connections -} // namespace nearby diff --git a/connections/implementation/mediums/webrtc_peer_id.cc b/connections/implementation/mediums/webrtc_peer_id.cc index 801d40c6..5e7f6224 100644 --- a/connections/implementation/mediums/webrtc_peer_id.cc +++ b/connections/implementation/mediums/webrtc_peer_id.cc @@ -12,15 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef NO_WEBRTC - #include "connections/implementation/mediums/webrtc_peer_id.h" -#include +#include #include "absl/strings/ascii.h" #include "absl/strings/escaping.h" #include "connections/implementation/mediums/utils.h" +#include "internal/platform/byte_array.h" namespace nearby { namespace connections { @@ -52,5 +51,3 @@ bool WebrtcPeerId::IsValid() const { return !id_.empty(); } } // namespace mediums } // namespace connections } // namespace nearby - -#endif diff --git a/connections/implementation/mediums/webrtc_peer_id.h b/connections/implementation/mediums/webrtc_peer_id.h index 93013902..e97deee3 100644 --- a/connections/implementation/mediums/webrtc_peer_id.h +++ b/connections/implementation/mediums/webrtc_peer_id.h @@ -15,9 +15,6 @@ #ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_PEER_ID_H_ #define CORE_INTERNAL_MEDIUMS_WEBRTC_PEER_ID_H_ -#ifndef NO_WEBRTC - -#include #include #include "internal/platform/byte_array.h" @@ -49,6 +46,4 @@ class WebrtcPeerId { } // namespace connections } // namespace nearby -#endif - #endif // CORE_INTERNAL_MEDIUMS_WEBRTC_PEER_ID_H_ diff --git a/connections/implementation/mediums/webrtc_peer_id_stub.cc b/connections/implementation/mediums/webrtc_peer_id_stub.cc deleted file mode 100644 index 1b74dad2..00000000 --- a/connections/implementation/mediums/webrtc_peer_id_stub.cc +++ /dev/null @@ -1,39 +0,0 @@ -// 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. - -#ifdef NO_WEBRTC - -#include "connections/implementation/mediums/webrtc_peer_id_stub.h" - -#include - -#include "absl/strings/ascii.h" -#include "absl/strings/escaping.h" -#include "connections/implementation/mediums/utils.h" - -namespace nearby { -namespace connections { -namespace mediums { - -WebrtcPeerId WebrtcPeerId::FromRandom() { return {}; } - -WebrtcPeerId WebrtcPeerId::FromSeed(const ByteArray& seed) { return {}; } - -bool WebrtcPeerId::IsValid() const { return false; } - -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif diff --git a/connections/implementation/mediums/webrtc_peer_id_stub.h b/connections/implementation/mediums/webrtc_peer_id_stub.h deleted file mode 100644 index 8d249a8c..00000000 --- a/connections/implementation/mediums/webrtc_peer_id_stub.h +++ /dev/null @@ -1,54 +0,0 @@ -// 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. - -#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_PEER_ID_STUB_H_ -#define CORE_INTERNAL_MEDIUMS_WEBRTC_PEER_ID_STUB_H_ - -#ifdef NO_WEBRTC - -#include -#include - -#include "internal/platform/byte_array.h" - -namespace nearby { -namespace connections { -namespace mediums { - -// WebrtcPeerId is used as an identifier to exchange SDP messages to establish -// WebRTC p2p connection. An empty WebrtcPeerId is considered to be invalid. -class WebrtcPeerId { - public: - WebrtcPeerId() = default; - explicit WebrtcPeerId(const std::string& id) : id_(id) {} - ~WebrtcPeerId() = default; - - static WebrtcPeerId FromRandom(); - static WebrtcPeerId FromSeed(const ByteArray& seed); - - bool IsValid() const; - - const std::string& GetId() const { return id_; } - - private: - std::string id_; -}; - -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif - -#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_PEER_ID_STUB_H_ diff --git a/connections/implementation/mediums/webrtc_socket.h b/connections/implementation/mediums/webrtc_socket.h index 724e247a..3881c2f0 100644 --- a/connections/implementation/mediums/webrtc_socket.h +++ b/connections/implementation/mediums/webrtc_socket.h @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2026 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,44 +15,57 @@ #ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_SOCKET_H_ #define CORE_INTERNAL_MEDIUMS_WEBRTC_SOCKET_H_ +#include + +#include "absl/strings/string_view.h" +#include "internal/platform/byte_array.h" #include "internal/platform/exception.h" -#ifndef NO_WEBRTC - -#include - -#include "connections/implementation/mediums/webrtc/webrtc_socket_impl.h" +#include "internal/platform/input_stream.h" +#include "internal/platform/output_stream.h" +#include "internal/platform/socket.h" namespace nearby { namespace connections { namespace mediums { -class WebRtcSocketWrapper final { +// A base implementation that creates a non-working WebRtcSocket that can be +// used as a placeholder when WebRTC is disabled. +class WebRtcSocket : public Socket { public: - WebRtcSocketWrapper() = default; - WebRtcSocketWrapper(const WebRtcSocketWrapper&) = default; - WebRtcSocketWrapper& operator=(const WebRtcSocketWrapper&) = default; - explicit WebRtcSocketWrapper(std::unique_ptr socket) - : impl_(socket.release()) {} - ~WebRtcSocketWrapper() = default; + ~WebRtcSocket() override = default; - InputStream& GetInputStream() { return impl_->GetInputStream(); } + InputStream& GetInputStream() override { return fake_input_stream_; } - OutputStream& GetOutputStream() { return impl_->GetOutputStream(); } + OutputStream& GetOutputStream() override { return fake_output_stream_; } - Exception Close() { return impl_->Close(); } + Exception Close() override { return {Exception::kSuccess}; } - bool IsValid() const { return impl_ != nullptr; } - - WebRtcSocket& GetImpl() { return *impl_; } + virtual bool IsValid() const { return false; } private: - std::shared_ptr impl_; + class FakeInputStream : public InputStream { + public: + ExceptionOr Read(std::int64_t size) override { + return {Exception::kSuccess}; + } + Exception Close() override { return {Exception::kSuccess}; } + }; + + class FakeOutputStream : public OutputStream { + public: + Exception Write(absl::string_view data) override { + return {Exception::kSuccess}; + } + Exception Flush() override { return {Exception::kSuccess}; } + Exception Close() override { return {Exception::kSuccess}; } + }; + + FakeInputStream fake_input_stream_; + FakeOutputStream fake_output_stream_; }; } // namespace mediums } // namespace connections } // namespace nearby -#endif - -#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_H_ +#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_SOCKET_H_ diff --git a/connections/implementation/mediums/webrtc_socket_stub.h b/connections/implementation/mediums/webrtc_socket_stub.h deleted file mode 100644 index f3f104ef..00000000 --- a/connections/implementation/mediums/webrtc_socket_stub.h +++ /dev/null @@ -1,71 +0,0 @@ -// 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. - -#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_SOCKET_STUB_H_ -#define CORE_INTERNAL_MEDIUMS_WEBRTC_SOCKET_STUB_H_ - -#ifdef NO_WEBRTC - -#include - -#include "internal/platform/input_stream.h" -#include "internal/platform/output_stream.h" - -namespace nearby { -namespace connections { -namespace mediums { -class FakeInputStream : public InputStream { - public: - ExceptionOr Read(std::int64_t size) { - return {Exception::kSuccess}; - } - Exception Close() { return {Exception::kSuccess}; } -}; - -class FakeOutputStream : public OutputStream { - public: - Exception Write(absl::string_view data) override { - return {Exception::kSuccess}; - } - Exception Flush() override { return {Exception::kSuccess}; } - Exception Close() override { return {Exception::kSuccess}; } -}; - -class WebRtcSocketWrapper final { - public: - WebRtcSocketWrapper() = default; - WebRtcSocketWrapper(const WebRtcSocketWrapper&) = default; - WebRtcSocketWrapper& operator=(const WebRtcSocketWrapper&) = default; - ~WebRtcSocketWrapper() = default; - - InputStream& GetInputStream() { return fake_input_stream_; } - - OutputStream& GetOutputStream() { return fake_output_stream_; } - - void Close() {} - - bool IsValid() const { return false; } - - private: - FakeInputStream fake_input_stream_; - FakeOutputStream fake_output_stream_; -}; - -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif - -#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_SOCKET_STUB_H_ diff --git a/connections/implementation/mediums/webrtc_stub.cc b/connections/implementation/mediums/webrtc_stub.cc deleted file mode 100644 index 385fc882..00000000 --- a/connections/implementation/mediums/webrtc_stub.cc +++ /dev/null @@ -1,67 +0,0 @@ -// 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. - -#ifdef NO_WEBRTC - -#include "connections/implementation/mediums/webrtc_stub.h" - -#include -#include - -#include "connections/implementation/mediums/webrtc_socket_stub.h" -#include "internal/platform/cancelable_alarm.h" -#include "internal/platform/expected.h" -#include "internal/platform/future.h" -#include "internal/platform/listeners.h" - -namespace nearby { -namespace connections { -namespace mediums { -using ::location::nearby::connections::LocationHint; -using ::location::nearby::proto::connections::OperationResultCode; - -WebRtc::WebRtc() = default; - -WebRtc::~WebRtc() {} - -std::string WebRtc::GetDefaultCountryCode() { return "US"; } - -bool WebRtc::IsAvailable() { return false; } - -bool WebRtc::IsAcceptingConnections(const std::string& service_id) { - return false; -} - -bool WebRtc::StartAcceptingConnections(const std::string& service_id, - const WebrtcPeerId& self_peer_id, - const LocationHint& location_hint, - AcceptedConnectionCallback callback) { - return false; -} - -void WebRtc::StopAcceptingConnections(const std::string& service_id) {} - -ErrorOr WebRtc::Connect( - const std::string& service_id, const WebrtcPeerId& remote_peer_id, - const LocationHint& location_hint, CancellationFlag* cancellation_flag) { - return {Error(OperationResultCode::DETAIL_UNKNOWN)}; -} - -bool WebRtc::IsUsingCellular() { return false; } - -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif diff --git a/connections/implementation/mediums/webrtc_stub.h b/connections/implementation/mediums/webrtc_stub.h deleted file mode 100644 index 787acd7a..00000000 --- a/connections/implementation/mediums/webrtc_stub.h +++ /dev/null @@ -1,86 +0,0 @@ -// 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. - -#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_STUB_H_ -#define CORE_INTERNAL_MEDIUMS_WEBRTC_STUB_H_ - -#ifdef NO_WEBRTC - -#include -#include -#include -#include - -#include "connections/implementation/mediums/webrtc_peer_id_stub.h" -#include "connections/implementation/mediums/webrtc_socket_stub.h" -#include "connections/implementation/proto/offline_wire_formats.pb.h" -#include "internal/platform/cancellation_flag.h" -#include "internal/platform/expected.h" -#include "internal/platform/listeners.h" - -namespace nearby { -namespace connections { -namespace mediums { - -// Entry point for connecting a data channel between two devices via WebRtc. -class WebRtc { - public: - // Callback that is invoked when a new connection is accepted. - using AcceptedConnectionCallback = - absl::AnyInvocable; - WebRtc(); - ~WebRtc(); - - // Gets the default two-letter country code associated with current locale. - // For example, en_US locale resolves to "US". - std::string GetDefaultCountryCode(); - - // Returns if WebRtc is available as a medium for nearby to transport data. - // Runs on @MainThread. - bool IsAvailable(); - - // Returns if the device is accepting connection with specific service id. - // Runs on @MainThread. - bool IsAcceptingConnections(const std::string& service_id); - - // Prepares the device to accept incoming WebRtc connections. Returns a - // boolean value indicating if the device has started accepting connections. - // Runs on @MainThread. - bool StartAcceptingConnections( - const std::string& service_id, const WebrtcPeerId& self_peer_id, - const location::nearby::connections::LocationHint& location_hint, - AcceptedConnectionCallback callback); - - // Try to stop (accepting) the specific connection with provided service id. - // Runs on @MainThread - void StopAcceptingConnections(const std::string& service_id); - - // Initiates a WebRtc connection with peer device identified by |peer_id| - // with internal retry for maximum attempts of kConnectAttemptsLimit. - // Runs on @MainThread. - ErrorOr Connect( - const std::string& service_id, const WebrtcPeerId& peer_id, - const location::nearby::connections::LocationHint& location_hint, - CancellationFlag* cancellation_flag); - - bool IsUsingCellular(); -}; - -} // namespace mediums -} // namespace connections -} // namespace nearby - -#endif - -#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_STUB_H_ diff --git a/connections/implementation/mediums/webrtc_test.cc b/connections/implementation/mediums/webrtc_test.cc deleted file mode 100644 index 061dbd4c..00000000 --- a/connections/implementation/mediums/webrtc_test.cc +++ /dev/null @@ -1,641 +0,0 @@ -// 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 "connections/implementation/mediums/webrtc.h" - -#include -#include -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "connections/implementation/mediums/webrtc_peer_id.h" -#include "connections/implementation/mediums/webrtc_socket.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/cancellation_flag.h" -#include "internal/platform/exception.h" -#include "internal/platform/expected.h" -#include "internal/platform/feature_flags.h" -#include "internal/platform/future.h" -#include "internal/platform/medium_environment.h" -#include "internal/platform/webrtc.h" -#include "internal/test/fake_webrtc.h" - -namespace nearby { -namespace connections { -namespace mediums { - -namespace { - -using FeatureFlags = FeatureFlags::Flags; -using ::location::nearby::connections::LocationHint; - -struct WebRtcTestParams { - FeatureFlags feature_flags; - bool non_cellular; -}; - -class TestWebRtc : public WebRtc { - public: - explicit TestWebRtc(std::unique_ptr medium) - : WebRtc(std::move(medium)) {} - - int connect_attempts_count(std::string service_id) { - return service_id_to_connect_attempts_count_map_[service_id]; - } -}; - -class WebRtcTest : public ::testing::TestWithParam { - protected: - using MockAcceptedCallback = testing::MockFunction; - - MediumEnvironment& env_{MediumEnvironment::Instance()}; -}; - -// Tests the flow when the two devices exchange SDP messages and connect to each -// other but the signaling channel is closed before sending the data. -TEST_P(WebRtcTest, ConnectBothDevices_ShutdownSignaling_SendData) { - env_.Start({.webrtc_enabled = true}); - WebRtcTestParams params = GetParam(); - env_.SetFeatureFlags(params.feature_flags); - WebRtc receiver, sender; - WebRtcSocketWrapper receiver_socket; - const WebrtcPeerId self_id("self_id"); - const std::string service_id("NearbySharing"); - LocationHint location_hint; - Future connected; - absl::string_view message("message xyz"); - - receiver.StartAcceptingConnections( - service_id, self_id, location_hint, - [&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { - receiver_socket = wrapper; - connected.Set(receiver_socket.IsValid()); - }, - params.non_cellular); - - CancellationFlag flag; - ErrorOr sender_socket_result = sender.Connect( - service_id, self_id, location_hint, &flag, params.non_cellular); - EXPECT_TRUE(sender_socket_result.has_value()); - EXPECT_TRUE(sender_socket_result.value().IsValid()); - - ExceptionOr devices_connected = connected.Get(); - ASSERT_TRUE(devices_connected.ok()); - EXPECT_TRUE(devices_connected.result()); - - // Only shuts down signaling channel. - receiver.StopAcceptingConnections(service_id); - - sender_socket_result.value().GetOutputStream().Write(message); - ExceptionOr received_msg = - receiver_socket.GetInputStream().Read(/*size=*/32); - ASSERT_TRUE(received_msg.ok()); - EXPECT_EQ(message, received_msg.result().AsStringView()); - env_.Stop(); -} - -TEST_P(WebRtcTest, CanCancelConnect) { - env_.Start({.webrtc_enabled = true}); - WebRtcTestParams params = GetParam(); - env_.SetFeatureFlags(params.feature_flags); - WebRtc receiver, sender; - WebRtcSocketWrapper receiver_socket; - const WebrtcPeerId self_id("self_id"); - const std::string service_id("NearbySharing"); - LocationHint location_hint; - Future connected; - absl::string_view message("message"); - - receiver.StartAcceptingConnections( - service_id, self_id, location_hint, - [&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { - receiver_socket = wrapper; - connected.Set(receiver_socket.IsValid()); - }, - params.non_cellular); - - CancellationFlag flag(true); - ErrorOr sender_socket_result = sender.Connect( - service_id, self_id, location_hint, &flag, params.non_cellular); - // If FeatureFlag is disabled, Cancelled is false as no-op. - if (!params.feature_flags.enable_cancellation_flag) { - EXPECT_TRUE(sender_socket_result.has_value()); - EXPECT_TRUE(sender_socket_result.value().IsValid()); - - ExceptionOr devices_connected = connected.Get(); - ASSERT_TRUE(devices_connected.ok()); - EXPECT_TRUE(devices_connected.result()); - - sender_socket_result.value().GetOutputStream().Write(message); - ExceptionOr received_msg = - receiver_socket.GetInputStream().Read(/*size=*/32); - ASSERT_TRUE(received_msg.ok()); - EXPECT_EQ(message, received_msg.result().AsStringView()); - - receiver_socket.Close(); - } else { - EXPECT_TRUE(sender_socket_result.has_error()); - } - env_.Stop(); -} - -// Basic test to check that device is accepting connections when initialized. -TEST_P(WebRtcTest, NotAcceptingConnections) { - env_.Start({.webrtc_enabled = true}); - WebRtc webrtc; - ASSERT_TRUE(webrtc.IsAvailable()); - EXPECT_FALSE(webrtc.IsAcceptingConnections(std::string{})); - env_.Stop(); -} - -// Tests the flow when the device tries to accept connections twice. In this -// case, only the first call is successful and subsequent calls fail. -TEST_P(WebRtcTest, StartAcceptingConnectionTwice) { - env_.Start({.webrtc_enabled = true}); - WebRtcTestParams params = GetParam(); - testing::StrictMock mock_accepted_callback_; - WebRtc webrtc; - WebrtcPeerId self_id("peer_id"); - const std::string service_id("NearbySharing"); - LocationHint location_hint{}; - - ASSERT_TRUE(webrtc.IsAvailable()); - ASSERT_TRUE(webrtc.StartAcceptingConnections( - service_id, self_id, location_hint, - mock_accepted_callback_.AsStdFunction(), params.non_cellular)); - EXPECT_FALSE(webrtc.StartAcceptingConnections( - service_id, self_id, location_hint, - mock_accepted_callback_.AsStdFunction(), params.non_cellular)); - EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id)); - EXPECT_FALSE(webrtc.IsAcceptingConnections(std::string{})); - env_.Stop(); -} - -// Tests the flow when the device tries to connect but there is no peer -// accepting connections at the given peer ID. -TEST_P(WebRtcTest, Connect_NoPeer) { - env_.Start({.webrtc_enabled = true}); - WebRtcTestParams params = GetParam(); - WebRtc webrtc; - WebrtcPeerId peer_id("peer_id"); - const std::string service_id("NearbySharing"); - LocationHint location_hint; - - ASSERT_TRUE(webrtc.IsAvailable()); - CancellationFlag flag; - ErrorOr wrapper_1_result = webrtc.Connect( - service_id, peer_id, location_hint, &flag, params.non_cellular); - EXPECT_TRUE(wrapper_1_result.has_error()); - - EXPECT_TRUE(webrtc.StartAcceptingConnections( - service_id, peer_id, location_hint, nullptr, params.non_cellular)); - env_.Stop(); -} - -// Tests the flow when the device calls Connect() after calling -// StartAcceptingConnections() without StopAcceptingConnections(). -TEST_P(WebRtcTest, StartAcceptingConnection_ThenConnect) { - env_.Start({.webrtc_enabled = true}); - testing::StrictMock mock_accepted_callback_; - WebRtcTestParams params = GetParam(); - WebRtc webrtc; - WebrtcPeerId self_id("peer_id"); - const std::string service_id("NearbySharing"); - LocationHint location_hint; - - ASSERT_TRUE(webrtc.IsAvailable()); - ASSERT_TRUE(webrtc.StartAcceptingConnections( - service_id, self_id, location_hint, - mock_accepted_callback_.AsStdFunction(), params.non_cellular)); - CancellationFlag flag; - ErrorOr wrapper_result = - webrtc.Connect(service_id, WebrtcPeerId("random_peer_id"), location_hint, - &flag, params.non_cellular); - EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id)); - EXPECT_TRUE(wrapper_result.has_error()); - EXPECT_FALSE(webrtc.StartAcceptingConnections( - service_id, self_id, location_hint, - mock_accepted_callback_.AsStdFunction(), params.non_cellular)); - env_.Stop(); -} - -// Tests the flow when the device calls StartAcceptingConnections but the medium -// is closed before a peer device can connect to it. -TEST_P(WebRtcTest, StartAndStopAcceptingConnections) { - env_.Start({.webrtc_enabled = true}); - testing::StrictMock mock_accepted_callback_; - WebRtcTestParams params = GetParam(); - WebRtc webrtc; - WebrtcPeerId self_id("peer_id"); - const std::string service_id("NearbySharing"); - LocationHint location_hint; - - ASSERT_TRUE(webrtc.IsAvailable()); - ASSERT_TRUE(webrtc.StartAcceptingConnections( - service_id, self_id, location_hint, - mock_accepted_callback_.AsStdFunction(), params.non_cellular)); - EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id)); - webrtc.StopAcceptingConnections(service_id); - EXPECT_FALSE(webrtc.IsAcceptingConnections(service_id)); - env_.Stop(); -} - -// Tests the flow when the device tries to connect to two different peers -// without disconnecting in between. -TEST_P(WebRtcTest, ConnectTwice) { - env_.Start({.webrtc_enabled = true}); - WebRtc receiver, sender, device_c; - WebRtcSocketWrapper receiver_socket; - WebRtcTestParams params = GetParam(); - const WebrtcPeerId self_id("self_id"), other_id("other_id"); - const std::string service_id("NearbySharing"); - LocationHint location_hint; - Future connected; - absl::string_view message("message xyz"); - - receiver.StartAcceptingConnections( - service_id, self_id, location_hint, - [&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { - receiver_socket = wrapper; - connected.Set(receiver_socket.IsValid()); - }, - params.non_cellular); - - device_c.StartAcceptingConnections( - service_id, other_id, location_hint, - [](const std::string& service_id, WebRtcSocketWrapper wrapper) {}, - params.non_cellular); - - CancellationFlag flag; - ErrorOr sender_socket_result = sender.Connect( - service_id, self_id, location_hint, &flag, params.non_cellular); - EXPECT_TRUE(sender_socket_result.has_value()); - EXPECT_TRUE(sender_socket_result.value().IsValid()); - - ExceptionOr devices_connected = connected.Get(); - ASSERT_TRUE(devices_connected.ok()); - EXPECT_TRUE(devices_connected.result()); - - ErrorOr socket_result = sender.Connect( - service_id, other_id, location_hint, &flag, params.non_cellular); - EXPECT_TRUE(socket_result.has_value()); - EXPECT_TRUE(socket_result.value().IsValid()); - socket_result.value().Close(); - - EXPECT_TRUE(receiver_socket.IsValid()); - EXPECT_TRUE(sender_socket_result.has_value()); - EXPECT_TRUE(sender_socket_result.value().IsValid()); - - sender_socket_result.value().GetOutputStream().Write(message); - ExceptionOr received_msg = - receiver_socket.GetInputStream().Read(/*size=*/32); - ASSERT_TRUE(received_msg.ok()); - EXPECT_EQ(message, received_msg.result().AsStringView()); - - receiver_socket.Close(); - env_.Stop(); -} - -// Tests the flow when the two devices exchange SDP messages and connect to each -// other but disconnect before being able to send/receive the actual data. -TEST_P(WebRtcTest, ConnectBothDevicesAndAbort) { - env_.Start({.webrtc_enabled = true}); - WebRtc receiver, sender; - WebRtcSocketWrapper receiver_socket, sender_socket; - WebRtcTestParams params = GetParam(); - const WebrtcPeerId self_id("self_id"); - const std::string service_id("NearbySharing"); - LocationHint location_hint; - Future connected; - - receiver.StartAcceptingConnections( - service_id, self_id, location_hint, - [&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { - receiver_socket = wrapper; - connected.Set(receiver_socket.IsValid()); - }, - params.non_cellular); - - CancellationFlag flag; - ErrorOr sender_socket_result = sender.Connect( - service_id, self_id, location_hint, &flag, params.non_cellular); - EXPECT_TRUE(sender_socket_result.has_value()); - EXPECT_TRUE(sender_socket_result.value().IsValid()); - - ExceptionOr devices_connected = connected.Get(); - ASSERT_TRUE(devices_connected.ok()); - EXPECT_TRUE(devices_connected.result()); - - receiver_socket.Close(); - env_.Stop(); -} - -// Tests the flow when the two devices exchange SDP messages and connect to each -// other and the actual data is exchanged successfully between the devices. -TEST_P(WebRtcTest, ConnectBothDevicesAndSendData) { - env_.Start({.webrtc_enabled = true}); - WebRtc receiver, sender; - WebRtcSocketWrapper receiver_socket; - WebRtcTestParams params = GetParam(); - const WebrtcPeerId self_id("self_id"); - const std::string service_id("NearbySharing"); - LocationHint location_hint; - Future connected; - absl::string_view message("message"); - - receiver.StartAcceptingConnections( - service_id, self_id, location_hint, - [&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { - receiver_socket = wrapper; - connected.Set(receiver_socket.IsValid()); - }, - params.non_cellular); - - CancellationFlag flag; - ErrorOr sender_socket_result = sender.Connect( - service_id, self_id, location_hint, &flag, params.non_cellular); - EXPECT_TRUE(sender_socket_result.has_value()); - EXPECT_TRUE(sender_socket_result.value().IsValid()); - - ExceptionOr devices_connected = connected.Get(); - ASSERT_TRUE(devices_connected.ok()); - EXPECT_TRUE(devices_connected.result()); - - sender_socket_result.value().GetOutputStream().Write(message); - ExceptionOr received_msg = - receiver_socket.GetInputStream().Read(/*size=*/32); - ASSERT_TRUE(received_msg.ok()); - EXPECT_EQ(message, received_msg.result().AsStringView()); - - receiver_socket.Close(); - env_.Stop(); -} - -TEST_P(WebRtcTest, Connect_NullPeerConnection) { - env_.Start({.webrtc_enabled = true}); - WebRtcTestParams params = GetParam(); - testing::StrictMock mock_accepted_callback_; - env_.SetUseValidPeerConnection( - /*use_valid_peer_connection=*/false); - - WebRtc webrtc; - const std::string service_id("NearbySharing"); - WebrtcPeerId self_id("peer_id"); - LocationHint location_hint; - - ASSERT_TRUE(webrtc.IsAvailable()); - CancellationFlag flag; - ErrorOr wrapper_result = - webrtc.Connect(service_id, WebrtcPeerId("random_peer_id"), location_hint, - &flag, params.non_cellular); - EXPECT_TRUE(wrapper_result.has_error()); - env_.Stop(); -} - -// Tests the flow when the device calls StartAcceptingConnections and the -// receive messages stream fails. -TEST_P(WebRtcTest, ContinueAcceptingConnectionsOnComplete) { - env_.Start({.webrtc_enabled = true}); - testing::StrictMock mock_accepted_callback_; - WebRtcTestParams params = GetParam(); - WebRtc webrtc; - WebrtcPeerId self_id("peer_id"); - const std::string service_id("NearbySharing"); - LocationHint location_hint; - - ASSERT_TRUE(webrtc.IsAvailable()); - ASSERT_TRUE(webrtc.StartAcceptingConnections( - service_id, self_id, location_hint, - mock_accepted_callback_.AsStdFunction(), params.non_cellular)); - EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id)); - - // Simulate a failure in receiving messages stream, WebRtc should restart - // accepting connections. - env_.SendWebRtcSignalingComplete(self_id.GetId(), - /*success=*/false); - EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id)); - - // And a "success" message should not cause accepting connections to stop. - env_.SendWebRtcSignalingComplete(self_id.GetId(), - /*success=*/true); - EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id)); - - webrtc.StopAcceptingConnections(service_id); - EXPECT_FALSE(webrtc.IsAcceptingConnections(service_id)); - env_.Stop(); -} - -// Tests when a CancellationFlag is cancelled during an attempt to -// `WebRtc::AttemptToConnect` triggered by `WebRtc::Connect`. -TEST_P(WebRtcTest, CancelDuringConnect) { - env_.Start({.webrtc_enabled = true}); - WebRtcTestParams params = GetParam(); - - // Enable cancellation flags. - env_.SetFeatureFlags(FeatureFlags{ - .enable_cancellation_flag = true, - }); - - WebRtcSocketWrapper receiver_socket, sender_socket; - const WebrtcPeerId self_id("self_id"); - const std::string service_id("NearbySharing"); - LocationHint location_hint; - Future connected; - - CancellationFlag receiver_flag; - std::unique_ptr receiver = std::make_unique( - std::make_unique(&receiver_flag)); - - CancellationFlag sender_flag; - std::unique_ptr sender_medium = - std::make_unique(&sender_flag); - FakeWebRtcMedium* fake_sender_medium = - static_cast(sender_medium.get()); - auto sender = std::make_unique(std::move(sender_medium)); - - // Calls `CancellationFlag::Cancel` during a call to `GetSignalingMessenger` - // to simulate the cancellation occuring during an `AttemptToConnect`. - fake_sender_medium->TriggerCancellationDuringGetSignalingMessenger(); - - receiver->StartAcceptingConnections( - service_id, self_id, location_hint, - [&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { - receiver_socket = wrapper; - connected.Set(receiver_socket.IsValid()); - }, - params.non_cellular); - - ErrorOr sender_socket_result = sender->Connect( - service_id, self_id, location_hint, &sender_flag, params.non_cellular); - - // Since the flag was cancelled during the initial `AttemptToConnect`, except - // only one attempt instead of the usual three, because the cancellation flag - // should short-circuit the lengthy connection attempts during shutdown. - // Because of the way the iteration happens, the check for is cancelled - // happens after the counter has already been incremented, but before the - // attempt actually occurs. - EXPECT_TRUE(sender_socket_result.has_error()); - EXPECT_EQ(2, sender->connect_attempts_count(service_id)); - - env_.Stop(); -} - -// Tests when a CancellationFlag is cancelled before `WebRtc::Connect` is -// called. -TEST_P(WebRtcTest, CancelBeforeConnect) { - env_.Start({.webrtc_enabled = true}); - WebRtcTestParams params = GetParam(); - - // Enable cancellation flags. - env_.SetFeatureFlags(FeatureFlags{ - .enable_cancellation_flag = true, - }); - - WebRtcSocketWrapper receiver_socket; - const WebrtcPeerId self_id("self_id"); - const std::string service_id("NearbySharing"); - LocationHint location_hint; - Future connected; - - CancellationFlag receiver_flag; - std::unique_ptr receiver = std::make_unique( - std::make_unique(&receiver_flag)); - - CancellationFlag sender_flag(true); - auto sender = std::make_unique( - std::make_unique(&sender_flag)); - - receiver->StartAcceptingConnections( - service_id, self_id, location_hint, - [&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { - receiver_socket = wrapper; - connected.Set(receiver_socket.IsValid()); - }, - params.non_cellular); - - ErrorOr sender_socket_result = sender->Connect( - service_id, self_id, location_hint, &sender_flag, params.non_cellular); - - // Expect an invalid socket from stopping during the first attempt to connect, - // because `Connect` returned immediatley when it checked for cancellation. - EXPECT_TRUE(sender_socket_result.has_error()); - EXPECT_EQ(1, sender->connect_attempts_count(service_id)); - - env_.Stop(); -} - -// Tests when a CancellationFlag is cancelled during an attempt to -// `WebRtc::AttemptToConnect` triggered by `WebRtc::Connect` when multiple -// `WebRTC::Connect` calls are in flight for multiple service ids. -TEST_P(WebRtcTest, CancelDuringConnect_MultipleConnect) { - env_.Start({.webrtc_enabled = true}); - WebRtcTestParams params = GetParam(); - - // Enable cancellation flags. - env_.SetFeatureFlags(FeatureFlags{ - .enable_cancellation_flag = true, - }); - - WebRtcSocketWrapper receiver_socket; - const WebrtcPeerId self_id("self_id"); - const std::string ns_service_id("NearbySharing"); - const std::string ph_service_id("PhoneHub"); - LocationHint location_hint; - Future connected; - - CancellationFlag receiver_flag; - std::unique_ptr receiver = std::make_unique( - std::make_unique(&receiver_flag)); - - CancellationFlag flag; - auto sender_medium = std::make_unique(&flag); - FakeWebRtcMedium* fake_sender_medium = sender_medium.get(); - auto sender = std::make_unique(std::move(sender_medium)); - - receiver->StartAcceptingConnections( - ns_service_id, self_id, location_hint, - [&receiver_socket, connected](const std::string& ns_service_id, - WebRtcSocketWrapper wrapper) mutable { - receiver_socket = wrapper; - connected.Set(receiver_socket.IsValid()); - }, - params.non_cellular); - - // Simulate a successful connect for the endpoint of NearbySharing. - ErrorOr sender_socket_result = sender->Connect( - ns_service_id, self_id, location_hint, &flag, params.non_cellular); - EXPECT_TRUE(sender_socket_result.has_value()); - EXPECT_TRUE(sender_socket_result.value().IsValid()); - - // Calls `CancellationFlag::Cancel` during a call to `GetSignalingMessenger` - // to simulate the cancellation occuring during an `AttemptToConnect` for the - // endpoint of Phone Hub. - fake_sender_medium->TriggerCancellationDuringGetSignalingMessenger(); - sender_socket_result = sender->Connect(ph_service_id, self_id, location_hint, - &flag, params.non_cellular); - EXPECT_TRUE(sender_socket_result.has_error()); - - // Since the flag was cancelled during the initial `AttemptToConnect`, except - // only one attempt instead of the usual three, because the cancellation flag - // should short-circuit the lengthy connection attempts during shutdown. - // Because of the way the iteration happens, the check for is cancelled - // happens after the counter has already been incremented, but before the - // attempt actually occurs. For the successful connect, expect only one - // attempt. - EXPECT_EQ(1, sender->connect_attempts_count(ns_service_id)); - EXPECT_EQ(2, sender->connect_attempts_count(ph_service_id)); - - env_.Stop(); -} - -INSTANTIATE_TEST_SUITE_P(ParametrisedWebRtcTest, WebRtcTest, - testing::ValuesIn({ - {.feature_flags = - FeatureFlags{ - .enable_cancellation_flag = true, - }, - .non_cellular = true}, - {.feature_flags = - FeatureFlags{ - .enable_cancellation_flag = true, - }, - .non_cellular = false}, - {.feature_flags = - FeatureFlags{ - .enable_cancellation_flag = false, - }, - .non_cellular = true}, - {.feature_flags = - FeatureFlags{ - .enable_cancellation_flag = false, - }, - .non_cellular = false}, - })); - -} // namespace - -} // namespace mediums -} // namespace connections -} // namespace nearby diff --git a/connections/implementation/mediums/wifi_direct.cc b/connections/implementation/mediums/wifi_direct.cc index 75f7b35d..1d1559a8 100644 --- a/connections/implementation/mediums/wifi_direct.cc +++ b/connections/implementation/mediums/wifi_direct.cc @@ -14,12 +14,15 @@ #include "connections/implementation/mediums/wifi_direct.h" +#include #include #include #include #include #include "absl/strings/string_view.h" +#include "connections/implementation/bwu_handler.h" +#include "connections/implementation/mediums/wifi_direct_bwu_handler.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/expected.h" #include "internal/platform/logging.h" @@ -307,5 +310,12 @@ bool WifiDirect::SetPreferredWifiDirectAuthType(WifiDirectAuthType auth_type) { return true; } +std::unique_ptr WifiDirect::CreateBwuHandler( + BwuHandler::IncomingConnectionCallback incoming_connection_callback) { + MutexLock lock(&mutex_); + return std::make_unique( + this, std::move(incoming_connection_callback)); +} + } // namespace connections } // namespace nearby diff --git a/connections/implementation/mediums/wifi_direct.h b/connections/implementation/mediums/wifi_direct.h index e85d2e64..1ea6211d 100644 --- a/connections/implementation/mediums/wifi_direct.h +++ b/connections/implementation/mediums/wifi_direct.h @@ -19,6 +19,7 @@ #include #include "absl/base/thread_annotations.h" +#include "connections/implementation/bwu_handler.h" #include "absl/container/flat_hash_map.h" #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" @@ -110,6 +111,9 @@ class WifiDirect { // Sets the preferred WifiDirect auth type. bool SetPreferredWifiDirectAuthType(WifiDirectAuthType auth_type); + std::unique_ptr CreateBwuHandler( + BwuHandler::IncomingConnectionCallback incoming_connection_callback); + private: mutable Mutex mutex_; static constexpr int kMaxConcurrentAcceptLoops = 5; diff --git a/connections/implementation/wifi_direct_bwu_handler.cc b/connections/implementation/mediums/wifi_direct_bwu_handler.cc similarity index 88% rename from connections/implementation/wifi_direct_bwu_handler.cc rename to connections/implementation/mediums/wifi_direct_bwu_handler.cc index d637e0e4..55346fc7 100644 --- a/connections/implementation/wifi_direct_bwu_handler.cc +++ b/connections/implementation/mediums/wifi_direct_bwu_handler.cc @@ -12,23 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/wifi_direct_bwu_handler.h" +#include "connections/implementation/mediums/wifi_direct_bwu_handler.h" #include #include #include #include +#include "absl/base/nullability.h" #include "absl/functional/bind_front.h" #include "connections/implementation/base_bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" -#include "connections/implementation/mediums/mediums.h" +#include "connections/implementation/mediums/wifi_direct.h" +#include "connections/implementation/mediums/wifi_direct_endpoint_channel.h" #include "connections/implementation/offline_frames.h" -#include "connections/implementation/wifi_direct_endpoint_channel.h" #include "connections/strategy.h" #include "internal/base/masker.h" -#include "internal/platform/byte_array.h" +#include "internal/platform/cancellation_flag.h" #include "internal/platform/expected.h" #include "internal/platform/logging.h" #include "internal/platform/wifi_credential.h" @@ -42,14 +43,15 @@ using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame; using ::location::nearby::proto::connections::OperationResultCode; } // namespace WifiDirectBwuHandler::WifiDirectBwuHandler( - Mediums& mediums, IncomingConnectionCallback incoming_connection_callback) + WifiDirect* absl_nonnull wifi_direct_medium, + IncomingConnectionCallback incoming_connection_callback) : BaseBwuHandler(std::move(incoming_connection_callback)), - mediums_(mediums) {} + wifi_direct_medium_(*wifi_direct_medium) {} // Called by BWU initiator. Set up WifiDirect upgraded medium for this // endpoint, and returns an upgrade path info (ServiceName, Pin for Wifi WPS, // Gateway used as IPAddress, Port) for remote party to perform connection. -ByteArray WifiDirectBwuHandler::HandleInitializeUpgradedMediumForEndpoint( +std::string WifiDirectBwuHandler::HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, const std::string& endpoint_id) { // Create WifiDirect GO @@ -84,14 +86,14 @@ ByteArray WifiDirectBwuHandler::HandleInitializeUpgradedMediumForEndpoint( wifi_direct_medium_.GetCredentials(upgrade_service_id); std::string ssid = wifi_direct_crendential->GetSSID(); std::string password = wifi_direct_crendential->GetPassword(); - std::string service_name = wifi_direct_crendential->GetServiceName(); + std::string device_name = wifi_direct_crendential->GetDeviceName(); std::string pin = wifi_direct_crendential->GetPin(); std::string gateway = wifi_direct_crendential->GetGateway(); int port = wifi_direct_crendential->GetPort(); int freq = wifi_direct_crendential->GetFrequency(); if (ssid.empty()) { - LOG(INFO) << "Start WifiDirect GO with ServiceName: " << service_name + LOG(INFO) << "Start WifiDirect GO with DeviceName: " << device_name << ", pin: " << masker::Mask(pin) << ", Port: " << port << ", Gateway: " << gateway << ", Frequency: " << freq; } else { @@ -106,7 +108,7 @@ ByteArray WifiDirectBwuHandler::HandleInitializeUpgradedMediumForEndpoint( return parser::ForBwuWifiDirectPathAvailable( ssid, password, port, freq, /* supports_disabling_encryption */ disabling_encryption, gateway, - service_name, pin); + device_name, pin); } void WifiDirectBwuHandler::HandleRevertInitiatorStateForService( @@ -136,8 +138,8 @@ WifiDirectBwuHandler::CreateUpgradedEndpointChannel( const std::string& ssid = upgrade_path_info_credentials.ssid(); const std::string& password = upgrade_path_info_credentials.password(); - const std::string& service_name = - upgrade_path_info_credentials.service_name(); + const std::string& device_name = + upgrade_path_info_credentials.device_name(); const std::string& pin = upgrade_path_info_credentials.pin(); std::int32_t port = upgrade_path_info_credentials.port(); const std::string& gateway = upgrade_path_info_credentials.gateway(); @@ -146,14 +148,14 @@ WifiDirectBwuHandler::CreateUpgradedEndpointChannel( WifiDirectCredentials wifi_direct_credentials; wifi_direct_credentials.SetSSID(ssid); wifi_direct_credentials.SetPassword(password); - wifi_direct_credentials.SetServiceName(service_name); + wifi_direct_credentials.SetDeviceName(device_name); wifi_direct_credentials.SetPin(pin); wifi_direct_credentials.SetPort(port); wifi_direct_credentials.SetGateway(gateway); wifi_direct_credentials.SetFrequency(freq); if (ssid.empty()) { - LOG(INFO) << "Received WifiDirect credential ServiceName: " << service_name + LOG(INFO) << "Received WifiDirect credential DeviceName: " << device_name << ", pin: " << masker::Mask(pin) << ", Port: " << port << ", Gateway: " << gateway << ", Frequency: " << freq; } else { @@ -172,8 +174,10 @@ WifiDirectBwuHandler::CreateUpgradedEndpointChannel( OperationResultCode::CONNECTIVITY_WIFI_DIRECT_INVALID_CREDENTIAL)}; } + std::shared_ptr cancellation_flag = + client->GetCancellationFlag(endpoint_id); ErrorOr socket_result = wifi_direct_medium_.Connect( - service_id, gateway, port, client->GetCancellationFlag(endpoint_id)); + service_id, gateway, port, cancellation_flag.get()); if (socket_result.has_error()) { LOG(ERROR) << "WifiDirectBwuHandler failed to connect to the WifiDirect service(" diff --git a/connections/implementation/wifi_direct_bwu_handler.h b/connections/implementation/mediums/wifi_direct_bwu_handler.h similarity index 88% rename from connections/implementation/wifi_direct_bwu_handler.h rename to connections/implementation/mediums/wifi_direct_bwu_handler.h index 34623d83..6fec11d3 100644 --- a/connections/implementation/wifi_direct_bwu_handler.h +++ b/connections/implementation/mediums/wifi_direct_bwu_handler.h @@ -12,19 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CORE_INTERNAL_WIFI_DIRECT_BWU_HANDLER_H_ -#define CORE_INTERNAL_WIFI_DIRECT_BWU_HANDLER_H_ +#ifndef CORE_INTERNAL_MEDIUMS_WIFI_DIRECT_BWU_HANDLER_H_ +#define CORE_INTERNAL_MEDIUMS_WIFI_DIRECT_BWU_HANDLER_H_ #include #include +#include "absl/base/nullability.h" #include "connections/implementation/base_bwu_handler.h" #include "connections/implementation/bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" -#include "connections/implementation/mediums/mediums.h" #include "connections/implementation/mediums/wifi_direct.h" -#include "internal/platform/byte_array.h" #include "internal/platform/expected.h" #include "internal/platform/wifi_direct.h" @@ -35,8 +34,8 @@ namespace connections { // per-Medium-specific operations needed to upgrade an EndpointChannel. class WifiDirectBwuHandler : public BaseBwuHandler { public: - explicit WifiDirectBwuHandler( - Mediums& mediums, + WifiDirectBwuHandler( + WifiDirect* absl_nonnull wifi_direct_medium, IncomingConnectionCallback incoming_connection_callback); private: @@ -73,7 +72,7 @@ class WifiDirectBwuHandler : public BaseBwuHandler { // Called by BWU initiator. Set up WifiDirect upgraded medium for this // endpoint, and returns a upgrade path info (SSID, Password, Gateway used as // IPAddress, Port) for remote party to perform connection. - ByteArray HandleInitializeUpgradedMediumForEndpoint( + std::string HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, const std::string& endpoint_id) final; @@ -86,11 +85,10 @@ class WifiDirectBwuHandler : public BaseBwuHandler { const std::string& upgrade_service_id, WifiDirectSocket socket); - Mediums& mediums_; - WifiDirect& wifi_direct_medium_ = mediums_.GetWifiDirect(); + WifiDirect& wifi_direct_medium_; }; } // namespace connections } // namespace nearby -#endif // CORE_INTERNAL_WIFI_DIRECT_BWU_HANDLER_H_ +#endif // CORE_INTERNAL_MEDIUMS_WIFI_DIRECT_BWU_HANDLER_H_ diff --git a/connections/implementation/wifi_direct_bwu_test.cc b/connections/implementation/mediums/wifi_direct_bwu_handler_test.cc similarity index 92% rename from connections/implementation/wifi_direct_bwu_test.cc rename to connections/implementation/mediums/wifi_direct_bwu_handler_test.cc index f9eed52f..fd3dccec 100644 --- a/connections/implementation/wifi_direct_bwu_test.cc +++ b/connections/implementation/mediums/wifi_direct_bwu_handler_test.cc @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "connections/implementation/mediums/wifi_direct_bwu_handler.h" + #include #include #include @@ -26,9 +28,7 @@ #include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/implementation/mediums/mediums.h" #include "connections/implementation/offline_frames.h" -#include "connections/implementation/wifi_direct_bwu_handler.h" #include "internal/flags/nearby_flags.h" -#include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/exception.h" #include "internal/platform/expected.h" @@ -68,7 +68,8 @@ TEST_F(WifiDirectTest, CanCreateBwuHandler) { ClientProxy client; Mediums mediums; - auto handler = std::make_unique(mediums, nullptr); + auto handler = + std::make_unique(&mediums.GetWifiDirect(), nullptr); handler->InitializeUpgradedMediumForEndpoint(&client, std::string(kServiceID), std::string(kEndpointID)); @@ -87,7 +88,7 @@ TEST_F(WifiDirectTest, WFDGOBWUInit_GCCreateEndpointChannel) { ExceptionOr upgrade_frame; auto wfd_go_bwu_handler = std::make_unique( - mediums_wfd_go, [&](ClientProxy* client, + &mediums_wfd_go.GetWifiDirect(), [&](ClientProxy* client, std::unique_ptr mutable_connection) { LOG(INFO) << "Server socket connection accept call back, Socket name: " @@ -98,10 +99,10 @@ TEST_F(WifiDirectTest, WFDGOBWUInit_GCCreateEndpointChannel) { SingleThreadExecutor wfd_go_executor; wfd_go_executor.Execute([&]() { - ByteArray upgrade_path_available_frame = + std::string upgrade_path_available_frame = wfd_go_bwu_handler->InitializeUpgradedMediumForEndpoint( &wifi_direct_go, std::string(kServiceID), std::string(kEndpointID)); - EXPECT_FALSE(upgrade_path_available_frame.Empty()); + EXPECT_FALSE(upgrade_path_available_frame.empty()); upgrade_frame = parser::FromBytes(upgrade_path_available_frame); start_latch.CountDown(); @@ -113,7 +114,8 @@ TEST_F(WifiDirectTest, WFDGOBWUInit_GCCreateEndpointChannel) { EXPECT_TRUE(start_latch.Await(kWaitDuration).result()); EXPECT_FALSE(mediums_wfd_gc.GetWifiDirect().IsConnectedToGO()); std::unique_ptr wfd_gc_bwu_handler = - std::make_unique(mediums_wfd_gc, nullptr); + std::make_unique(&mediums_wfd_gc.GetWifiDirect(), + nullptr); wfd_gc_executor.Execute([&]() { UpgradePathInfo upgrade_path_info; diff --git a/connections/implementation/wifi_direct_endpoint_channel.cc b/connections/implementation/mediums/wifi_direct_endpoint_channel.cc similarity index 95% rename from connections/implementation/wifi_direct_endpoint_channel.cc rename to connections/implementation/mediums/wifi_direct_endpoint_channel.cc index 379112b8..b271d475 100644 --- a/connections/implementation/wifi_direct_endpoint_channel.cc +++ b/connections/implementation/mediums/wifi_direct_endpoint_channel.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/wifi_direct_endpoint_channel.h" +#include "connections/implementation/mediums/wifi_direct_endpoint_channel.h" #include #include diff --git a/connections/implementation/wifi_direct_endpoint_channel.h b/connections/implementation/mediums/wifi_direct_endpoint_channel.h similarity index 89% rename from connections/implementation/wifi_direct_endpoint_channel.h rename to connections/implementation/mediums/wifi_direct_endpoint_channel.h index 9cf735b6..45d4d6f9 100644 --- a/connections/implementation/wifi_direct_endpoint_channel.h +++ b/connections/implementation/mediums/wifi_direct_endpoint_channel.h @@ -12,9 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. - -#ifndef CORE_INTERNAL_WIFI_DIRECT_ENDPOINT_CHANNEL_H_ -#define CORE_INTERNAL_WIFI_DIRECT_ENDPOINT_CHANNEL_H_ +#ifndef CORE_INTERNAL_MEDIUMS_WIFI_DIRECT_ENDPOINT_CHANNEL_H_ +#define CORE_INTERNAL_MEDIUMS_WIFI_DIRECT_ENDPOINT_CHANNEL_H_ #include @@ -48,4 +47,4 @@ class WifiDirectEndpointChannel final : public BaseEndpointChannel { } // namespace connections } // namespace nearby -#endif // CORE_INTERNAL_WIFI_DIRECT_ENDPOINT_CHANNEL_H_ +#endif // CORE_INTERNAL_MEDIUMS_WIFI_DIRECT_ENDPOINT_CHANNEL_H_ diff --git a/connections/implementation/mediums/wifi_direct_test.cc b/connections/implementation/mediums/wifi_direct_test.cc index c3056442..b121930b 100644 --- a/connections/implementation/mediums/wifi_direct_test.cc +++ b/connections/implementation/mediums/wifi_direct_test.cc @@ -45,7 +45,7 @@ constexpr FeatureFlags kTestCases[] = { }; constexpr absl::string_view kServiceID{"com.google.location.nearby.apps.test"}; -constexpr absl::string_view kServiceName{"NC-WifiDirectTest"}; +constexpr absl::string_view kDeviceName{"NC-WifiDirectTest"}; constexpr absl::string_view kPin{"12345678"}; constexpr absl::string_view kIp = "123.234.23.1"; constexpr const size_t kPort = 20; @@ -93,9 +93,9 @@ TEST_F(WifiDirectTest, CanStartStopGO) { TEST_F(WifiDirectTest, GCCanConnectDisconnectGO) { WifiDirectCredentials wifi_direct_credentials; - std::string service_name(kServiceName); + std::string device_name(kDeviceName); std::string pin(kPin); - wifi_direct_credentials.SetServiceName(service_name); + wifi_direct_credentials.SetDeviceName(device_name); wifi_direct_credentials.SetPin(pin); WifiDirect wifi_direct_a; @@ -187,9 +187,9 @@ TEST_F(WifiDirectTest, CanStartGOTheOtherFailConnect) { EXPECT_TRUE(wifi_direct_a.StartWifiDirect()); WifiDirectCredentials wifi_direct_credentials; - std::string service_name(kServiceName); + std::string device_name(kDeviceName); std::string pin(kPin); - wifi_direct_credentials.SetServiceName(service_name); + wifi_direct_credentials.SetDeviceName(device_name); wifi_direct_credentials.SetPin(pin); EXPECT_FALSE(wifi_direct_b.ConnectWifiDirect(wifi_direct_credentials)); EXPECT_TRUE(wifi_direct_b.DisconnectWifiDirect()); @@ -201,23 +201,24 @@ TEST_F(WifiDirectTest, GetSupportedWifiDirectAuthTypes) { auto supported_types = wifi_direct.GetSupportedWifiDirectAuthTypes(); EXPECT_EQ(supported_types.size(), 1); EXPECT_EQ(supported_types[0], - WifiDirect::WifiDirectAuthType::WIFI_DIRECT_WITH_PIN); + WifiDirect::WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME); } TEST_F(WifiDirectTest, GetPreferredWifiDirectAuthType_Default) { WifiDirect wifi_direct; - // Default should be the first supported type, which is WIFI_DIRECT_WITH_PIN + // Default should be the first supported type, which is + // WIFI_DIRECT_WITH_DEVICE_NAME EXPECT_EQ(wifi_direct.GetPreferredWifiDirectAuthType(), - WifiDirect::WifiDirectAuthType::WIFI_DIRECT_WITH_PIN); + WifiDirect::WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME); } TEST_F(WifiDirectTest, SetPreferredWifiDirectAuthType_Supported) { WifiDirect wifi_direct; // Attempt to set the preferred type to the already default/supported type. EXPECT_TRUE(wifi_direct.SetPreferredWifiDirectAuthType( - WifiDirect::WifiDirectAuthType::WIFI_DIRECT_WITH_PIN)); + WifiDirect::WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME)); EXPECT_EQ(wifi_direct.GetPreferredWifiDirectAuthType(), - WifiDirect::WifiDirectAuthType::WIFI_DIRECT_WITH_PIN); + WifiDirect::WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME); } TEST_F(WifiDirectTest, SetPreferredWifiDirectAuthType_Unsupported) { @@ -227,7 +228,7 @@ TEST_F(WifiDirectTest, SetPreferredWifiDirectAuthType_Unsupported) { WifiDirect::WifiDirectAuthType::WIFI_DIRECT_WITH_PASSWORD)); // Preferred type should remain the default. EXPECT_EQ(wifi_direct.GetPreferredWifiDirectAuthType(), - WifiDirect::WifiDirectAuthType::WIFI_DIRECT_WITH_PIN); + WifiDirect::WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME); } } // namespace diff --git a/connections/implementation/mediums/wifi_hotspot.cc b/connections/implementation/mediums/wifi_hotspot.cc index 6d91e27e..4701a176 100644 --- a/connections/implementation/mediums/wifi_hotspot.cc +++ b/connections/implementation/mediums/wifi_hotspot.cc @@ -15,6 +15,7 @@ #include "connections/implementation/mediums/wifi_hotspot.h" #include +#include #include #include #include @@ -22,12 +23,15 @@ #include "absl/strings/string_view.h" #include "absl/time/clock.h" #include "absl/time/time.h" +#include "connections/implementation/bwu_handler.h" +#include "connections/implementation/mediums/wifi_hotspot_bwu_handler.h" #include "internal/flags/nearby_flags.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/expected.h" #include "internal/platform/flags/nearby_platform_feature_flags.h" #include "internal/platform/logging.h" #include "internal/platform/mutex_lock.h" +#include "internal/platform/service_address.h" #include "internal/platform/wifi_credential.h" #include "internal/platform/wifi_hotspot.h" @@ -325,5 +329,12 @@ ErrorOr WifiHotspot::Connect( return socket; } +std::unique_ptr WifiHotspot::CreateBwuHandler( + BwuHandler::IncomingConnectionCallback incoming_connection_callback) { + MutexLock lock(&mutex_); + return std::make_unique( + this, std::move(incoming_connection_callback)); +} + } // namespace connections } // namespace nearby diff --git a/connections/implementation/mediums/wifi_hotspot.h b/connections/implementation/mediums/wifi_hotspot.h index 11b0bd3e..06e622c9 100644 --- a/connections/implementation/mediums/wifi_hotspot.h +++ b/connections/implementation/mediums/wifi_hotspot.h @@ -21,6 +21,7 @@ #include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" +#include "connections/implementation/bwu_handler.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/expected.h" #include "internal/platform/multi_thread_executor.h" @@ -86,6 +87,9 @@ class WifiHotspot { HotspotCredentials* GetCredentials(absl::string_view service_id) ABSL_LOCKS_EXCLUDED(mutex_); + std::unique_ptr CreateBwuHandler( + BwuHandler::IncomingConnectionCallback incoming_connection_callback); + private: mutable Mutex mutex_; static constexpr int kMaxConcurrentAcceptLoops = 5; diff --git a/connections/implementation/wifi_hotspot_bwu_handler.cc b/connections/implementation/mediums/wifi_hotspot_bwu_handler.cc similarity index 93% rename from connections/implementation/wifi_hotspot_bwu_handler.cc rename to connections/implementation/mediums/wifi_hotspot_bwu_handler.cc index f369a3aa..d31b8a15 100644 --- a/connections/implementation/wifi_hotspot_bwu_handler.cc +++ b/connections/implementation/mediums/wifi_hotspot_bwu_handler.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/wifi_hotspot_bwu_handler.h" +#include "connections/implementation/mediums/wifi_hotspot_bwu_handler.h" #if !defined(_WIN32) #include @@ -27,17 +27,18 @@ #include #include +#include "absl/base/nullability.h" #include "absl/functional/bind_front.h" #include "connections/implementation/base_bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" -#include "connections/implementation/mediums/mediums.h" +#include "connections/implementation/mediums/wifi_hotspot.h" +#include "connections/implementation/mediums/wifi_hotspot_endpoint_channel.h" #include "connections/implementation/offline_frames.h" #include "connections/implementation/proto/offline_wire_formats.pb.h" -#include "connections/implementation/wifi_hotspot_endpoint_channel.h" #include "connections/strategy.h" #include "internal/base/masker.h" -#include "internal/platform/byte_array.h" +#include "internal/platform/cancellation_flag.h" #include "internal/platform/expected.h" #include "internal/platform/implementation/wifi_utils.h" #include "internal/platform/logging.h" @@ -67,14 +68,15 @@ std::vector GatewayToAddressBytes(const std::string& gateway) { } // namespace WifiHotspotBwuHandler::WifiHotspotBwuHandler( - Mediums& mediums, IncomingConnectionCallback incoming_connection_callback) + WifiHotspot* absl_nonnull wifi_hotspot_medium, + IncomingConnectionCallback incoming_connection_callback) : BaseBwuHandler(std::move(incoming_connection_callback)), - mediums_(mediums) {} + wifi_hotspot_medium_(*wifi_hotspot_medium) {} // Called by BWU initiator. Set up WifiHotspot upgraded medium for this // endpoint, and returns a upgrade path info (SSID, Password, Gateway used as // IPAddress, Port) for remote party to perform connection. -ByteArray WifiHotspotBwuHandler::HandleInitializeUpgradedMediumForEndpoint( +std::string WifiHotspotBwuHandler::HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, const std::string& endpoint_id) { // Create SoftAP @@ -219,9 +221,11 @@ WifiHotspotBwuHandler::CreateUpgradedEndpointChannel( CONNECTIVITY_WIFI_HOTSPOT_LEGACY_STA_CONNECTION_FAILURE)}; } + std::shared_ptr cancellation_flag = + client->GetCancellationFlag(endpoint_id); ErrorOr socket_result = wifi_hotspot_medium_.Connect( service_id, hotspot_credentials.GetAddressCandidates(), - client->GetCancellationFlag(endpoint_id)); + cancellation_flag.get()); if (socket_result.has_error()) { LOG(ERROR) << "WifiHotspotBwuHandler failed to connect to the WifiHotspot " "service for endpoint " diff --git a/connections/implementation/wifi_hotspot_bwu_handler.h b/connections/implementation/mediums/wifi_hotspot_bwu_handler.h similarity index 86% rename from connections/implementation/wifi_hotspot_bwu_handler.h rename to connections/implementation/mediums/wifi_hotspot_bwu_handler.h index a21705b9..1c3c8f83 100644 --- a/connections/implementation/wifi_hotspot_bwu_handler.h +++ b/connections/implementation/mediums/wifi_hotspot_bwu_handler.h @@ -12,19 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CORE_INTERNAL_WIFI_HOTSPOT_BWU_HANDLER_H_ -#define CORE_INTERNAL_WIFI_HOTSPOT_BWU_HANDLER_H_ +#ifndef CORE_INTERNAL_MEDIUMS_WIFI_HOTSPOT_BWU_HANDLER_H_ +#define CORE_INTERNAL_MEDIUMS_WIFI_HOTSPOT_BWU_HANDLER_H_ #include #include +#include "absl/base/nullability.h" #include "connections/implementation/base_bwu_handler.h" #include "connections/implementation/bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" -#include "connections/implementation/mediums/mediums.h" #include "connections/implementation/mediums/wifi_hotspot.h" -#include "internal/platform/byte_array.h" #include "internal/platform/expected.h" #include "internal/platform/wifi_hotspot.h" @@ -35,8 +34,8 @@ namespace connections { // per-Medium-specific operations needed to upgrade an EndpointChannel. class WifiHotspotBwuHandler : public BaseBwuHandler { public: - explicit WifiHotspotBwuHandler( - Mediums& mediums, + WifiHotspotBwuHandler( + WifiHotspot* absl_nonnull wifi_hotspot_medium, IncomingConnectionCallback incoming_connection_callback); // BwuHandler implementation: @@ -68,7 +67,7 @@ class WifiHotspotBwuHandler : public BaseBwuHandler { }; // BaseBwuHandler implementation: - ByteArray HandleInitializeUpgradedMediumForEndpoint( + std::string HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, const std::string& endpoint_id) final; void HandleRevertInitiatorStateForService( @@ -78,11 +77,10 @@ class WifiHotspotBwuHandler : public BaseBwuHandler { const std::string& upgrade_service_id, WifiHotspotSocket socket); - Mediums& mediums_; - WifiHotspot& wifi_hotspot_medium_{mediums_.GetWifiHotspot()}; + WifiHotspot& wifi_hotspot_medium_; }; } // namespace connections } // namespace nearby -#endif // CORE_INTERNAL_WIFI_HOTSPOT_BWU_HANDLER_H_ +#endif // CORE_INTERNAL_MEDIUMS_WIFI_HOTSPOT_BWU_HANDLER_H_ diff --git a/connections/implementation/wifi_hotspot_bwu_test.cc b/connections/implementation/mediums/wifi_hotspot_bwu_handler_test.cc similarity index 93% rename from connections/implementation/wifi_hotspot_bwu_test.cc rename to connections/implementation/mediums/wifi_hotspot_bwu_handler_test.cc index 125e4028..fcd42428 100644 --- a/connections/implementation/wifi_hotspot_bwu_test.cc +++ b/connections/implementation/mediums/wifi_hotspot_bwu_handler_test.cc @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "connections/implementation/mediums/wifi_hotspot_bwu_handler.h" + #include #include #include @@ -25,9 +27,7 @@ #include "connections/implementation/endpoint_channel.h" #include "connections/implementation/mediums/mediums.h" #include "connections/implementation/offline_frames.h" -#include "connections/implementation/wifi_hotspot_bwu_handler.h" #include "internal/flags/nearby_flags.h" -#include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/exception.h" #include "internal/platform/expected.h" @@ -69,7 +69,8 @@ TEST_F(WifiHotspotTest, CanCreateBwuHandler) { ClientProxy client; Mediums mediums; - auto handler = std::make_unique(mediums, nullptr); + auto handler = std::make_unique( + &mediums.GetWifiHotspot(), nullptr); handler->InitializeUpgradedMediumForEndpoint(&client, std::string(kServiceID), std::string(kEndpointID)); @@ -88,7 +89,7 @@ TEST_F(WifiHotspotTest, SoftAPBWUInit_STACreateEndpointChannel) { ExceptionOr upgrade_frame; auto handler_1 = std::make_unique( - mediums_HS_ap, [&](ClientProxy* client, + &mediums_HS_ap.GetWifiHotspot(), [&](ClientProxy* client, std::unique_ptr mutable_connection) { LOG(INFO) << "Server socket connection accept call back, Socket name: " @@ -100,11 +101,11 @@ TEST_F(WifiHotspotTest, SoftAPBWUInit_STACreateEndpointChannel) { // client_hotspot_ap works as Hotspot SoftAP SingleThreadExecutor server_executor; server_executor.Execute([&]() { - ByteArray upgrade_path_available_frame = + std::string upgrade_path_available_frame = handler_1->InitializeUpgradedMediumForEndpoint( &client_hotspot_ap, std::string(kServiceID), std::string(kEndpointID)); - EXPECT_FALSE(upgrade_path_available_frame.Empty()); + EXPECT_FALSE(upgrade_path_available_frame.empty()); upgrade_frame = parser::FromBytes(upgrade_path_available_frame); start_latch.CountDown(); @@ -117,7 +118,8 @@ TEST_F(WifiHotspotTest, SoftAPBWUInit_STACreateEndpointChannel) { // Wait till client_hotspot_ap started as hotspot and then connect to it EXPECT_TRUE(start_latch.Await(kWaitDuration).result()); std::unique_ptr handler_2 = - std::make_unique(mediums_HS_sta, nullptr); + std::make_unique(&mediums_HS_sta.GetWifiHotspot(), + nullptr); client_executor.Execute([&]() { UpgradePathInfo upgrade_path_info; diff --git a/connections/implementation/wifi_hotspot_endpoint_channel.cc b/connections/implementation/mediums/wifi_hotspot_endpoint_channel.cc similarity index 95% rename from connections/implementation/wifi_hotspot_endpoint_channel.cc rename to connections/implementation/mediums/wifi_hotspot_endpoint_channel.cc index 53e99fbe..0b355d36 100644 --- a/connections/implementation/wifi_hotspot_endpoint_channel.cc +++ b/connections/implementation/mediums/wifi_hotspot_endpoint_channel.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/wifi_hotspot_endpoint_channel.h" +#include "connections/implementation/mediums/wifi_hotspot_endpoint_channel.h" #include #include diff --git a/connections/implementation/wifi_hotspot_endpoint_channel.h b/connections/implementation/mediums/wifi_hotspot_endpoint_channel.h similarity index 89% rename from connections/implementation/wifi_hotspot_endpoint_channel.h rename to connections/implementation/mediums/wifi_hotspot_endpoint_channel.h index b44da4de..2cb2b67f 100644 --- a/connections/implementation/wifi_hotspot_endpoint_channel.h +++ b/connections/implementation/mediums/wifi_hotspot_endpoint_channel.h @@ -12,9 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. - -#ifndef CORE_INTERNAL_WIFI_HOTSPOT_ENDPOINT_CHANNEL_H_ -#define CORE_INTERNAL_WIFI_HOTSPOT_ENDPOINT_CHANNEL_H_ +#ifndef CORE_INTERNAL_MEDIUMS_WIFI_HOTSPOT_ENDPOINT_CHANNEL_H_ +#define CORE_INTERNAL_MEDIUMS_WIFI_HOTSPOT_ENDPOINT_CHANNEL_H_ #include @@ -48,4 +47,4 @@ class WifiHotspotEndpointChannel final : public BaseEndpointChannel { } // namespace connections } // namespace nearby -#endif // CORE_INTERNAL_WIFI_HOTSPOT_ENDPOINT_CHANNEL_H_ +#endif // CORE_INTERNAL_MEDIUMS_WIFI_HOTSPOT_ENDPOINT_CHANNEL_H_ diff --git a/connections/implementation/mediums/wifi_lan.cc b/connections/implementation/mediums/wifi_lan.cc index ff3c58ad..d789590b 100644 --- a/connections/implementation/mediums/wifi_lan.cc +++ b/connections/implementation/mediums/wifi_lan.cc @@ -22,29 +22,24 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" -#include "connections/implementation/mediums/multiplex/multiplex_socket.h" +#include "connections/implementation/bwu_handler.h" #include "connections/implementation/mediums/utils.h" -#include "connections/medium_selector.h" -#include "internal/platform/base64_utils.h" +#include "connections/implementation/mediums/wifi_lan_bwu_handler.h" #include "internal/platform/byte_array.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/exception.h" #include "internal/platform/expected.h" #include "internal/platform/implementation/upgrade_address_info.h" -#include "internal/platform/implementation/wifi_utils.h" #include "internal/platform/logging.h" #include "internal/platform/mutex_lock.h" #include "internal/platform/nsd_service_info.h" #include "internal/platform/service_address.h" -#include "internal/platform/socket.h" -#include "internal/platform/types.h" #include "internal/platform/wifi_lan.h" namespace nearby { namespace connections { namespace { -using MultiplexSocket = mediums::multiplex::MultiplexSocket; using location::nearby::proto::connections::OperationResultCode; } // namespace @@ -59,18 +54,6 @@ WifiLan::~WifiLan() { while (!advertising_info_.nsd_service_infos.empty()) { StopAdvertising(advertising_info_.nsd_service_infos.begin()->first); } - { - MutexLock lock(&mutex_); - if (is_multiplex_enabled_) { - LOG(INFO) << "Closing multiplex sockets for " << multiplex_sockets_.size() - << " IPs"; - for (auto& [ip_addr, multiplex_socket] : multiplex_sockets_) { - LOG(INFO) << "Closing multiplex sockets for: " << ip_addr; - multiplex_socket->~MultiplexSocket(); - } - multiplex_sockets_.clear(); - } - } // All the AcceptLoopRunnable objects in here should already have gotten an // opportunity to shut themselves down cleanly in the calls to // StopAcceptingConnections() above. @@ -265,18 +248,6 @@ ErrorOr WifiLan::StartAcceptingConnectionsLocked( server_sockets_.insert({service_id, std::move(server_socket)}) .first->second; - // Register the callback to listen for incoming multiplex virtual socket. - if (is_multiplex_enabled_) { - MultiplexSocket::ListenForIncomingConnection( - service_id, Medium::WIFI_LAN, - [&callback](const std::string& listening_service_id, - MediumSocket* virtual_socket) mutable { - if (callback) { - callback(listening_service_id, - *(down_cast(virtual_socket))); - } - }); - } port = owned_server_socket.GetPort(); // Start the accept loop on a dedicated thread - this stays alive and // listening for new incoming connections until StopAcceptingConnections() is @@ -284,7 +255,7 @@ ErrorOr WifiLan::StartAcceptingConnectionsLocked( accept_loops_runner_.Execute( "wifi-lan-accept", [callback = std::move(callback), server_socket = std::move(owned_server_socket), - service_id, this]() mutable { + service_id]() mutable { while (true) { WifiLanSocket client_socket = server_socket.Accept(); if (!client_socket.IsValid()) { @@ -293,53 +264,6 @@ ErrorOr WifiLan::StartAcceptingConnectionsLocked( } LOG(INFO) << "Accepted connection for " << service_id; bool callback_called = false; - { - MutexLock lock(&mutex_); - if (is_multiplex_enabled_) { - // Observed from the log that when the sender tries to connect to - // the receiver's server socket, the server side will somehow - // receive 3 connection request events(don’t know what’s happening - // in Windows’s lower layer code). The 2nd normally is the real - // one. The other two will result in a failed data receiving in - // Windows platform layer. To avoid creating multiplex - // IncomingSocket, we will check if the first read is successful - // or not. If not, discard it. If yes, save that packet - // content(the first frame length), then create the multiplex - // socket, then feed that content to that multiplex socket. - ExceptionOr read_int = - Base64Utils::ReadInt(&client_socket.GetInputStream()); - if (!read_int.ok()) { - LOG(WARNING) - << __func__ - << "Failed to read. Exception:" << read_int.exception() - << "Discard the connection."; - continue; - } - WifiLanSocket client_socket_bak = client_socket; - auto physical_socket_ptr = - std::make_shared(client_socket_bak); - - MultiplexSocket* multiplex_socket = - MultiplexSocket::CreateIncomingSocket( - physical_socket_ptr, service_id, read_int.result()); - if (multiplex_socket != nullptr && - multiplex_socket->GetVirtualSocket(service_id)) { - multiplex_sockets_.emplace(server_socket.GetIPAddress(), - multiplex_socket); - MultiplexSocket::StopListeningForIncomingConnection( - service_id, Medium::WIFI_LAN); - LOG(INFO) << "Multiplex virtaul socket created for " - << server_socket.GetIPAddress(); - if (callback) { - callback( - service_id, - *(down_cast( - multiplex_socket->GetVirtualSocket(service_id)))); - callback_called = true; - } - } - } - } if (callback && !callback_called) { LOG(INFO) << "Call back triggered for physical socket."; callback(service_id, std::move(client_socket)); @@ -402,10 +326,6 @@ bool WifiLan::StopAcceptingConnectionsLocked(const std::string& service_id) { << " because it was never started."; return false; } - if (is_multiplex_enabled_) { - MultiplexSocket::StopListeningForIncomingConnection(service_id, - Medium::WIFI_LAN); - } // Closing the WifiLanServerSocket will kick off the suicide of the thread // in accept_loops_thread_pool_ that blocks on WifiLanServerSocket.accept(). @@ -555,60 +475,12 @@ ErrorOr WifiLan::Connect(const std::string& service_id, ExceptionOr WifiLan::ConnectWithMultiplexSocketLocked( const std::string& service_id, const std::string& ip_address) { - if (is_multiplex_enabled_) { - LOG(INFO) << "multiplex_sockets_ size:" << multiplex_sockets_.size(); - auto it = multiplex_sockets_.find(ip_address); - if (it != multiplex_sockets_.end()) { - MultiplexSocket* multiplex_socket = it->second; - if (multiplex_socket->IsShutdown()) { - LOG(INFO) << "Erase multiplex_socket(already shutdown) for ip_address: " - << WifiUtils::GetHumanReadableIpAddress(ip_address); - multiplex_socket->~MultiplexSocket(); - multiplex_sockets_.erase(it); - return ExceptionOr(Exception::kFailed); - } - if (multiplex_socket->IsEnabled()) { - auto* virtual_socket = - multiplex_socket->EstablishVirtualSocket(service_id); - // Should not happen. - auto* wlan_socket = down_cast(virtual_socket); - if (wlan_socket == nullptr) { - LOG(INFO) << "Failed to cast to WifiLanSocket for " << service_id - << " with ip_address: " - << WifiUtils::GetHumanReadableIpAddress(ip_address); - return ExceptionOr(Exception::kFailed); - } - return ExceptionOr(*wlan_socket); - } - } - } return ExceptionOr(Exception::kFailed); } ExceptionOr WifiLan::CreateOutgoingMultiplexSocketLocked( WifiLanSocket& socket, const std::string& service_id, const std::string& ip_address) { - if (is_multiplex_enabled_) { - // Create MultiplexSocket, but set it to be disabled as default. It will be - // enabled if both side support multiplex for WIFI_LAN - auto physical_socket_ptr = std::make_shared(socket); - MultiplexSocket* multiplex_socket = - MultiplexSocket::CreateOutgoingSocket(physical_socket_ptr, service_id); - - auto* virtual_socket = multiplex_socket->GetVirtualSocket(service_id); - // Should not happen. - auto* wlan_socket = down_cast(virtual_socket); - if (wlan_socket == nullptr) { - LOG(INFO) << "Failed to cast to WifiLanSocket for " << service_id - << " with ip_address: " - << WifiUtils::GetHumanReadableIpAddress(ip_address); - return ExceptionOr(Exception::kFailed); - } - LOG(INFO) << "Multiplex socket created for ip_address: " - << WifiUtils::GetHumanReadableIpAddress(ip_address); - multiplex_sockets_.emplace(ip_address, multiplex_socket); - return ExceptionOr(*wlan_socket); - } return ExceptionOr(Exception::kFailed); } @@ -648,5 +520,12 @@ int WifiLan::GeneratePort(const std::string& service_id, (uint_of_service_id_hash % (port_range.second - port_range.first)); } +std::unique_ptr WifiLan::CreateBwuHandler( + BwuHandler::IncomingConnectionCallback incoming_connection_callback) { + MutexLock lock(&mutex_); + return std::make_unique( + this, std::move(incoming_connection_callback)); +} + } // namespace connections } // namespace nearby diff --git a/connections/implementation/mediums/wifi_lan.h b/connections/implementation/mediums/wifi_lan.h index 990439ce..7d6e6cb1 100644 --- a/connections/implementation/mediums/wifi_lan.h +++ b/connections/implementation/mediums/wifi_lan.h @@ -16,17 +16,15 @@ #define CORE_INTERNAL_MEDIUMS_WIFI_LAN_H_ #include +#include #include #include -#include #include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/functional/any_invocable.h" -#include "connections/implementation/flags/nearby_connections_feature_flags.h" -#include "connections/implementation/mediums/multiplex/multiplex_socket.h" -#include "internal/flags/nearby_flags.h" +#include "connections/implementation/bwu_handler.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/exception.h" #include "internal/platform/expected.h" @@ -118,6 +116,9 @@ class WifiLan { api::UpgradeAddressInfo GetUpgradeAddressCandidates( const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_); + std::unique_ptr CreateBwuHandler( + BwuHandler::IncomingConnectionCallback incoming_connection_callback); + private: struct AdvertisingInfo { bool Empty() const { return nsd_service_infos.empty(); } @@ -218,17 +219,6 @@ class WifiLan { absl::flat_hash_map server_sockets_ ABSL_GUARDED_BY(mutex_); - // Whether the multiplex feature is enabled. - bool is_multiplex_enabled_ = NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature::kEnableMultiplex) && - NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexWifiLan); - - // A map of IpAddress -> MultiplexSocket. - absl::flat_hash_map - multiplex_sockets_ ABSL_GUARDED_BY(mutex_); - std::string last_mdns_service_name_ ABSL_GUARDED_BY(mutex_); int last_server_port_ ABSL_GUARDED_BY(mutex_) = 0; }; diff --git a/connections/implementation/wifi_lan_bwu_handler.cc b/connections/implementation/mediums/wifi_lan_bwu_handler.cc similarity index 88% rename from connections/implementation/wifi_lan_bwu_handler.cc rename to connections/implementation/mediums/wifi_lan_bwu_handler.cc index 1a31c931..a25cd81c 100644 --- a/connections/implementation/wifi_lan_bwu_handler.cc +++ b/connections/implementation/mediums/wifi_lan_bwu_handler.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/wifi_lan_bwu_handler.h" +#include "connections/implementation/mediums/wifi_lan_bwu_handler.h" #include #include @@ -20,14 +20,15 @@ #include #include +#include "absl/base/nullability.h" #include "absl/functional/bind_front.h" #include "connections/implementation/base_bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" -#include "connections/implementation/mediums/mediums.h" +#include "connections/implementation/mediums/wifi_lan.h" +#include "connections/implementation/mediums/wifi_lan_endpoint_channel.h" #include "connections/implementation/offline_frames.h" -#include "connections/implementation/wifi_lan_endpoint_channel.h" -#include "internal/platform/byte_array.h" +#include "internal/platform/cancellation_flag.h" #include "internal/platform/expected.h" #include "internal/platform/implementation/upgrade_address_info.h" #include "internal/platform/logging.h" @@ -43,9 +44,10 @@ using ::location::nearby::proto::connections::OperationResultCode; } // namespace WifiLanBwuHandler::WifiLanBwuHandler( - Mediums& mediums, IncomingConnectionCallback incoming_connection_callback) + WifiLan* absl_nonnull wifi_lan_medium, + IncomingConnectionCallback incoming_connection_callback) : BaseBwuHandler(std::move(incoming_connection_callback)), - mediums_(mediums) {} + wifi_lan_medium_(*wifi_lan_medium) {} // Called by BWU target. Retrieves a new medium info from incoming message, // and establishes connection over WifiLan using this info. @@ -92,9 +94,10 @@ WifiLanBwuHandler::CreateUpgradedEndpointChannel( VLOG(1) << "WifiLanBwuHandler is attempting to connect to available " "WifiLan service (" << address_candidate << ") for endpoint " << endpoint_id; - ErrorOr socket_result = - wifi_lan_medium_.Connect(service_id, address_candidate, - client->GetCancellationFlag(endpoint_id)); + std::shared_ptr cancellation_flag = + client->GetCancellationFlag(endpoint_id); + ErrorOr socket_result = wifi_lan_medium_.Connect( + service_id, address_candidate, cancellation_flag.get()); if (socket_result.has_error()) { LOG(ERROR) << "WifiLanBwuHandler failed to connect to the WifiLan service (" @@ -118,9 +121,10 @@ WifiLanBwuHandler::CreateUpgradedEndpointChannel( // Called by BWU initiator. Set up WifiLan upgraded medium for this endpoint, // and returns a upgrade path info (ip address, port) for remote party to // perform discovery. -ByteArray WifiLanBwuHandler::HandleInitializeUpgradedMediumForEndpoint( +std::string WifiLanBwuHandler::HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, const std::string& endpoint_id) { + bool started_accepting = false; if (!wifi_lan_medium_.IsAcceptingConnections(upgrade_service_id)) { if (!wifi_lan_medium_.StartAcceptingConnections( upgrade_service_id, @@ -137,6 +141,7 @@ ByteArray WifiLanBwuHandler::HandleInitializeUpgradedMediumForEndpoint( << "WifiLanBwuHandler successfully started listening for incoming " "WifiLan connections while upgrading endpoint " << endpoint_id; + started_accepting = true; } // Address candidates are not populated until StartAcceptingConnections() is @@ -148,6 +153,9 @@ ByteArray WifiLanBwuHandler::HandleInitializeUpgradedMediumForEndpoint( LOG(INFO) << "WifiLanBwuHandler couldn't initiate the wifi_lan upgrade for " << "service " << upgrade_service_id << " and endpoint " << endpoint_id << " because there are no available ip addresses."; + if (started_accepting) { + wifi_lan_medium_.StopAcceptingConnections(upgrade_service_id); + } return {}; } client->GetAnalyticsRecorder().UpdateBwUpgradeNetworkInfo( diff --git a/connections/implementation/wifi_lan_bwu_handler.h b/connections/implementation/mediums/wifi_lan_bwu_handler.h similarity index 87% rename from connections/implementation/wifi_lan_bwu_handler.h rename to connections/implementation/mediums/wifi_lan_bwu_handler.h index 2e00bf79..46c4c0a0 100644 --- a/connections/implementation/wifi_lan_bwu_handler.h +++ b/connections/implementation/mediums/wifi_lan_bwu_handler.h @@ -12,19 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CORE_INTERNAL_WIFI_LAN_BWU_HANDLER_H_ -#define CORE_INTERNAL_WIFI_LAN_BWU_HANDLER_H_ +#ifndef CORE_INTERNAL_MEDIUMS_WIFI_LAN_BWU_HANDLER_H_ +#define CORE_INTERNAL_MEDIUMS_WIFI_LAN_BWU_HANDLER_H_ #include #include +#include "absl/base/nullability.h" #include "connections/implementation/base_bwu_handler.h" #include "connections/implementation/bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" -#include "connections/implementation/mediums/mediums.h" #include "connections/implementation/mediums/wifi_lan.h" -#include "internal/platform/byte_array.h" #include "internal/platform/expected.h" #include "internal/platform/wifi_lan.h" @@ -35,8 +34,8 @@ namespace connections { // per-Medium-specific operations needed to upgrade an EndpointChannel. class WifiLanBwuHandler : public BaseBwuHandler { public: - explicit WifiLanBwuHandler( - Mediums& mediums, + WifiLanBwuHandler( + WifiLan* absl_nonnull wifi_lan_medium, IncomingConnectionCallback incoming_connection_callback); // BwuHandler implementation: @@ -68,7 +67,7 @@ class WifiLanBwuHandler : public BaseBwuHandler { }; // BaseBwuHandler implementation: - ByteArray HandleInitializeUpgradedMediumForEndpoint( + std::string HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, const std::string& endpoint_id) final; void HandleRevertInitiatorStateForService( @@ -78,11 +77,10 @@ class WifiLanBwuHandler : public BaseBwuHandler { const std::string& upgrade_service_id, WifiLanSocket socket); - Mediums& mediums_; - WifiLan& wifi_lan_medium_{mediums_.GetWifiLan()}; + WifiLan& wifi_lan_medium_; }; } // namespace connections } // namespace nearby -#endif // CORE_INTERNAL_WIFI_LAN_BWU_HANDLER_H_ +#endif // CORE_INTERNAL_MEDIUMS_WIFI_LAN_BWU_HANDLER_H_ diff --git a/connections/implementation/wifi_lan_bwu_handler_test.cc b/connections/implementation/mediums/wifi_lan_bwu_handler_test.cc similarity index 74% rename from connections/implementation/wifi_lan_bwu_handler_test.cc rename to connections/implementation/mediums/wifi_lan_bwu_handler_test.cc index f6623db9..1cf09752 100644 --- a/connections/implementation/wifi_lan_bwu_handler_test.cc +++ b/connections/implementation/mediums/wifi_lan_bwu_handler_test.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/wifi_lan_bwu_handler.h" +#include "connections/implementation/mediums/wifi_lan_bwu_handler.h" #include #include @@ -26,10 +26,6 @@ #include "connections/implementation/bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/mediums/mediums.h" -#include "connections/strategy.h" -#include "internal/analytics/mock_event_logger.h" -#include "internal/analytics/sharing_log_matchers.h" -#include "internal/platform/byte_array.h" #include "internal/platform/implementation/platform.h" #include "internal/platform/implementation/upgrade_address_info.h" #include "internal/platform/implementation/wifi_lan.h" @@ -40,7 +36,7 @@ #include "internal/platform/mock_wifi_lan_server_socket.h" #include "internal/platform/mock_wifi_lan_socket.h" #include "internal/platform/service_address.h" -#include "internal/proto/analytics/connections_log.pb.h" +#include "internal/platform/wifi_lan.h" namespace nearby { @@ -48,18 +44,14 @@ MockWifiLanMedium* wifi_lan_medium = nullptr; namespace connections { namespace { -using ::location::nearby::analytics::proto::ConnectionsLog; using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame; using ::location::nearby::connections::OfflineFrame; using ::location::nearby::connections::V1Frame; -using ::location::nearby::proto::connections::EventType; using ::location::nearby::proto::connections::OperationResultCode; -using ::nearby::analytics::HasEventType; using ::testing::_; using ::testing::ByMove; using ::protobuf_matchers::EqualsProto; using ::testing::InSequence; -using ::testing::Matcher; using ::testing::MockFunction; using ::testing::Return; using ::testing::ReturnRef; @@ -73,19 +65,19 @@ constexpr absl::string_view kEndpointId{"endpoint_id"}; class WifiLanBwuHandlerTest : public ::testing::Test { protected: WifiLanBwuHandlerTest() - : handler_(mediums_, incoming_connection_callback_.AsStdFunction()) {} + : handler_(&mediums_.GetWifiLan(), + incoming_connection_callback_.AsStdFunction()) {} Mediums mediums_; MockFunction)> incoming_connection_callback_; WifiLanBwuHandler handler_; - nearby::analytics::MockEventLogger mock_event_logger_; }; TEST_F(WifiLanBwuHandlerTest, CreateUpgradedEndpointChannel_EmptyPathInfo_Fails) { - ClientProxy client(&mock_event_logger_); + ClientProxy client; BandwidthUpgradeNegotiationFrame::UpgradePathInfo path_info; // Create an empty wifi_lan_socket. path_info.mutable_wifi_lan_socket(); @@ -99,7 +91,7 @@ TEST_F(WifiLanBwuHandlerTest, }; TEST_F(WifiLanBwuHandlerTest, CreateUpgradedEndpointChannel_IpAddress_Success) { - ClientProxy client(&mock_event_logger_); + ClientProxy client; client.AddCancellationFlag(std::string(kEndpointId)); MockInputStream input_stream; MockOutputStream output_stream; @@ -132,7 +124,7 @@ TEST_F(WifiLanBwuHandlerTest, CreateUpgradedEndpointChannel_IpAddress_Success) { TEST_F(WifiLanBwuHandlerTest, CreateUpgradedEndpointChannel_AddressCandidates_FirstCandidate_Success) { - ClientProxy client(&mock_event_logger_); + ClientProxy client; client.AddCancellationFlag(std::string(kEndpointId)); MockInputStream input_stream; MockOutputStream output_stream; @@ -171,7 +163,7 @@ TEST_F(WifiLanBwuHandlerTest, TEST_F(WifiLanBwuHandlerTest, CreateUpgradedEndpointChannel_AddressCandidates_FirstCandidate_Fails) { - ClientProxy client(&mock_event_logger_); + ClientProxy client; client.AddCancellationFlag(std::string(kEndpointId)); MockInputStream input_stream; MockOutputStream output_stream; @@ -219,18 +211,7 @@ TEST_F(WifiLanBwuHandlerTest, TEST_F(WifiLanBwuHandlerTest, InitializeUpgradedMediumForEndpoint_Success) { MediumEnvironment::Instance().Start({.use_simulated_clock = true}); - ClientProxy client(&mock_event_logger_); - client.GetAnalyticsRecorder().OnStartAdvertising( - Strategy::kP2pPointToPoint, - {location::nearby::proto::connections::Medium::BLUETOOTH}, - /*advertising_metadata_params=*/nullptr); - client.GetAnalyticsRecorder().OnBandwidthUpgradeStarted( - std::string(kEndpointId), - location::nearby::proto::connections::Medium::BLUETOOTH, - location::nearby::proto::connections::Medium::WIFI_LAN, - location::nearby::proto::connections::ConnectionAttemptDirection:: - OUTGOING, - /*connection_token=*/""); + ClientProxy client; client.AddCancellationFlag(std::string(kEndpointId)); auto wifi_lan_server_socket = std::make_unique(); EXPECT_CALL(*wifi_lan_server_socket, GetPort()).WillRepeatedly(Return(8080)); @@ -271,77 +252,78 @@ TEST_F(WifiLanBwuHandlerTest, InitializeUpgradedMediumForEndpoint_Success) { address_candidate->set_port(8888); upgrade_path_info->set_supports_client_introduction_ack(true); - ByteArray result = handler_.InitializeUpgradedMediumForEndpoint( + std::string result = handler_.InitializeUpgradedMediumForEndpoint( &client, std::string(kServiceId), std::string(kEndpointId)); - EXPECT_FALSE(result.Empty()); + EXPECT_FALSE(result.empty()); OfflineFrame result_frame; - EXPECT_TRUE(result_frame.ParseFromString(std::string(result))); + EXPECT_TRUE(result_frame.ParseFromString(result)); EXPECT_THAT(result_frame, EqualsProto(expected_frame)); +} - constexpr absl::string_view kClientSessionLog = R"pb( - event_type: CLIENT_SESSION - client_session { duration_millis: 0 } - version: "v1.5.0" - )pb"; - constexpr absl::string_view kExpectedUpgradeLog = R"pb( - event_type: CLIENT_SESSION - client_session { - duration_millis: 0 - strategy_session { - duration_millis: 0 - strategy: P2P_POINT_TO_POINT - role: ADVERTISER - advertising_phase { - duration_millis: 0 - medium: BLUETOOTH - advertising_metadata { - supports_extended_ble_advertisements: false - connected_ap_frequency: 0 - supports_nfc_technology: false - } - stop_reason: FINISH_SESSION_STOP_ADVERTISING - } - upgrade_attempt { - direction: OUTGOING - duration_millis: 0 - from_medium: BLUETOOTH - to_medium: WIFI_LAN - upgrade_result: UNFINISHED_ERROR - error_stage: UPGRADE_UNFINISHED - connection_token: "" - operation_result { - result_category: CATEGORY_DEVICE_STATE_ERROR - result_code: DEVICE_STATE_ERROR_UNFINISHED_UPGRADE_ATTEMPTS - } - num_interfaces: 1 - num_ipv6_only_interfaces: 1 - } - } - } - version: "v1.5.0" - )pb"; - EXPECT_CALL(mock_event_logger_, - Log(Matcher( - HasEventType(EventType::STOP_STRATEGY_SESSION)))) - .Times(1); - EXPECT_CALL(mock_event_logger_, - Log(Matcher( - HasEventType(EventType::STOP_CLIENT_SESSION)))) - .Times(3); - EXPECT_CALL(mock_event_logger_, - Log(Matcher( - HasEventType(EventType::START_CLIENT_SESSION)))) - .Times(3); - EXPECT_CALL( - mock_event_logger_, - Log(Matcher(EqualsProto(kClientSessionLog)))) - .Times(2); - EXPECT_CALL( - mock_event_logger_, - Log(Matcher(EqualsProto(kExpectedUpgradeLog)))); - // Flush pending logs. - client.GetAnalyticsRecorder().LogSession(); +TEST_F(WifiLanBwuHandlerTest, + InitializeUpgradedMediumForEndpoint_EmptyCandidates_StopsAccepting) { + MediumEnvironment::Instance().Start({.use_simulated_clock = true}); + ClientProxy client; + client.AddCancellationFlag(std::string(kEndpointId)); + + auto mock_server_socket = std::make_unique(); + MockWifiLanServerSocket* raw_server_socket = mock_server_socket.get(); + + EXPECT_CALL(*raw_server_socket, GetPort()).WillRepeatedly(Return(8080)); + EXPECT_CALL(*wifi_lan_medium, IsNetworkConnected()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*wifi_lan_medium, ListenForService(_)) + .WillOnce(Return(ByMove(std::move(mock_server_socket)))); + + EXPECT_CALL(*wifi_lan_medium, GetUpgradeAddressCandidates(_)) + .WillOnce(Return(api::UpgradeAddressInfo{.num_interfaces = 0, + .num_ipv6_only_interfaces = 0, + .address_candidates = {}})); + + std::string result = handler_.InitializeUpgradedMediumForEndpoint( + &client, std::string(kServiceId), std::string(kEndpointId)); + + EXPECT_TRUE(result.empty()); + EXPECT_FALSE( + mediums_.GetWifiLan().IsAcceptingConnections("service_id_UPGRADE")); +} + +TEST_F( + WifiLanBwuHandlerTest, + InitializeUpgradedMediumForEndpoint_AlreadyAccepting_KeepAccepting) { + MediumEnvironment::Instance().Start({.use_simulated_clock = true}); + ClientProxy client; + client.AddCancellationFlag(std::string(kEndpointId)); + + auto mock_server_socket = std::make_unique(); + MockWifiLanServerSocket* raw_server_socket = mock_server_socket.get(); + + EXPECT_CALL(*raw_server_socket, GetPort()).WillRepeatedly(Return(8080)); + EXPECT_CALL(*wifi_lan_medium, IsNetworkConnected()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*wifi_lan_medium, ListenForService(_)) + .WillOnce(Return(ByMove(std::move(mock_server_socket)))); + + EXPECT_TRUE( + mediums_.GetWifiLan() + .StartAcceptingConnections("service_id_UPGRADE", + [](const std::string&, WifiLanSocket) {}) + .has_value()); + EXPECT_TRUE( + mediums_.GetWifiLan().IsAcceptingConnections("service_id_UPGRADE")); + + EXPECT_CALL(*wifi_lan_medium, GetUpgradeAddressCandidates(_)) + .WillOnce(Return(api::UpgradeAddressInfo{.num_interfaces = 0, + .num_ipv6_only_interfaces = 0, + .address_candidates = {}})); + + std::string result = handler_.InitializeUpgradedMediumForEndpoint( + &client, std::string(kServiceId), std::string(kEndpointId)); + + EXPECT_TRUE(result.empty()); + EXPECT_TRUE( + mediums_.GetWifiLan().IsAcceptingConnections("service_id_UPGRADE")); } } // namespace diff --git a/connections/implementation/wifi_lan_endpoint_channel.cc b/connections/implementation/mediums/wifi_lan_endpoint_channel.cc similarity index 96% rename from connections/implementation/wifi_lan_endpoint_channel.cc rename to connections/implementation/mediums/wifi_lan_endpoint_channel.cc index ec3e02b1..94fb69e4 100644 --- a/connections/implementation/wifi_lan_endpoint_channel.cc +++ b/connections/implementation/mediums/wifi_lan_endpoint_channel.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/wifi_lan_endpoint_channel.h" +#include "connections/implementation/mediums/wifi_lan_endpoint_channel.h" #include diff --git a/connections/implementation/wifi_lan_endpoint_channel.h b/connections/implementation/mediums/wifi_lan_endpoint_channel.h similarity index 87% rename from connections/implementation/wifi_lan_endpoint_channel.h rename to connections/implementation/mediums/wifi_lan_endpoint_channel.h index 587f5002..59d5d9e3 100644 --- a/connections/implementation/wifi_lan_endpoint_channel.h +++ b/connections/implementation/mediums/wifi_lan_endpoint_channel.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CORE_INTERNAL_WIFI_LAN_ENDPOINT_CHANNEL_H_ -#define CORE_INTERNAL_WIFI_LAN_ENDPOINT_CHANNEL_H_ +#ifndef CORE_INTERNAL_MEDIUMS_WIFI_LAN_ENDPOINT_CHANNEL_H_ +#define CORE_INTERNAL_MEDIUMS_WIFI_LAN_ENDPOINT_CHANNEL_H_ #include @@ -41,4 +41,4 @@ class WifiLanEndpointChannel final : public BaseEndpointChannel { } // namespace connections } // namespace nearby -#endif // CORE_INTERNAL_WIFI_LAN_ENDPOINT_CHANNEL_H_ +#endif // CORE_INTERNAL_MEDIUMS_WIFI_LAN_ENDPOINT_CHANNEL_H_ diff --git a/connections/implementation/mediums/wifi_lan_test.cc b/connections/implementation/mediums/wifi_lan_test.cc index 95bbfa5c..6b80bd8e 100644 --- a/connections/implementation/mediums/wifi_lan_test.cc +++ b/connections/implementation/mediums/wifi_lan_test.cc @@ -167,83 +167,6 @@ TEST_P(WifiLanTest, CanConnect) { env_.Stop(); } -TEST_P(WifiLanTest, CanConnectWithMultiplex) { - bool is_multiplex_enabled = NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature::kEnableMultiplex); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature::kEnableMultiplex, - true); - bool is_multiplex_enabled_wifi_lan = NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexWifiLan); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexWifiLan, - true); - FeatureFlags feature_flags = GetParam(); - env_.SetFeatureFlags(feature_flags); - env_.Start(); - WifiLan wifi_lan_client; - WifiLan wifi_lan_server; - std::string service_id(kServiceID); - std::string service_info_name(kServiceInfoName); - std::string endpoint_info_name(kEndpointName); - CountDownLatch discovered_latch(1); - CountDownLatch accept_latch(1); - - WifiLanSocket socket_for_server; - NsdServiceInfo nsd_service_info; - nsd_service_info.SetServiceName(service_info_name); - nsd_service_info.SetTxtRecord(std::string(kEndpointInfoKey), - endpoint_info_name); - wifi_lan_server.StartAdvertising( - service_id, nsd_service_info, - [&](const std::string& service_id, WifiLanSocket socket) { - socket_for_server = std::move(socket); - accept_latch.CountDown(); - }); - - WifiLanSocket socket_for_client; - SingleThreadExecutor client_executor; - client_executor.Execute([&]() { - NsdServiceInfo discovered_service_info; - wifi_lan_client.StartDiscovery( - service_id, { - .service_discovered_cb = - [&discovered_latch, &discovered_service_info]( - NsdServiceInfo service_info, - const std::string& service_id) { - LOG(INFO) << "Discovered service_info=" - << &service_info; - discovered_service_info = service_info; - discovered_latch.CountDown(); - }, - }); - discovered_latch.Await(kWaitDuration).result(); - ASSERT_TRUE(discovered_service_info.IsValid()); - - CancellationFlag flag; - ErrorOr socket_for_client_result = - wifi_lan_client.Connect(service_id, discovered_service_info, &flag); - socket_for_client = std::move(socket_for_client_result.value()); - Base64Utils::WriteInt(&socket_for_client_result.value().GetOutputStream(), - 4); - }); - EXPECT_TRUE(accept_latch.Await(kWaitDuration).result()); - EXPECT_TRUE(wifi_lan_server.StopAcceptingConnections(service_id)); - EXPECT_TRUE(wifi_lan_server.StopAdvertising(service_id)); - EXPECT_TRUE(socket_for_server.IsValid()); - EXPECT_TRUE(socket_for_client.IsValid()); - env_.Stop(); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature::kEnableMultiplex, - is_multiplex_enabled); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature:: - kEnableMultiplexWifiLan, - is_multiplex_enabled_wifi_lan); -} - TEST_P(WifiLanTest, CanCancelConnect) { FeatureFlags feature_flags = GetParam(); env_.SetFeatureFlags(feature_flags); diff --git a/connections/implementation/mock_endpoint_channel.h b/connections/implementation/mock_endpoint_channel.h new file mode 100644 index 00000000..14ea499e --- /dev/null +++ b/connections/implementation/mock_endpoint_channel.h @@ -0,0 +1,76 @@ +// Copyright 2026 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 THIRD_PARTY_NEARBY_CONNECTIONS_IMPLEMENTATION_MOCK_ENDPOINT_CHANNEL_H_ +#define THIRD_PARTY_NEARBY_CONNECTIONS_IMPLEMENTATION_MOCK_ENDPOINT_CHANNEL_H_ + +#include +#include +#include +#include "gmock/gmock.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "connections/implementation/analytics/analytics_recorder.h" +#include "connections/implementation/endpoint_channel.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/exception.h" + +namespace nearby::connections { + +class MockEndpointChannel : public EndpointChannel { + public: + MOCK_METHOD(ExceptionOr, Read, (), (override)); + MOCK_METHOD(Exception, Write, (absl::string_view data), + (override)); + MOCK_METHOD(void, Close, (), (override)); + MOCK_METHOD( + void, Close, + (location::nearby::proto::connections::DisconnectionReason reason), + (override)); + MOCK_METHOD(void, Close, + (location::nearby::proto::connections::DisconnectionReason reason, + nearby::analytics::SafeDisconnectionResult result), + (override)); + MOCK_METHOD(bool, IsClosed, (), (const, override)); + MOCK_METHOD(std::string, GetType, (), (const, override)); + MOCK_METHOD(std::string, GetServiceId, (), (const, override)); + MOCK_METHOD(std::string, GetName, (), (const, override)); + MOCK_METHOD(location::nearby::proto::connections::Medium, GetMedium, (), + (const, override)); + MOCK_METHOD(location::nearby::proto::connections::ConnectionTechnology, + GetTechnology, (), (const, override)); + MOCK_METHOD(location::nearby::proto::connections::ConnectionBand, GetBand, (), + (const, override)); + MOCK_METHOD(int, GetFrequency, (), (const, override)); + MOCK_METHOD(int, GetTryCount, (), (const, override)); + MOCK_METHOD(int, GetMaxTransmitPacketSize, (), (const, override)); + MOCK_METHOD(void, EnableEncryption, (std::shared_ptr), + (override)); + MOCK_METHOD(void, DisableEncryption, (), (override)); + MOCK_METHOD(bool, IsEncrypted, (), (override)); + MOCK_METHOD(ExceptionOr, TryDecrypt, (const ByteArray& data), + (override)); + MOCK_METHOD(bool, IsPaused, (), (const, override)); + MOCK_METHOD(void, Pause, (), (override)); + MOCK_METHOD(void, Resume, (), (override)); + MOCK_METHOD(absl::Time, GetLastReadTimestamp, (), (const, override)); + MOCK_METHOD(absl::Time, GetLastWriteTimestamp, (), (const, override)); + MOCK_METHOD(uint32_t, GetNextKeepAliveSeqNo, (), (const, override)); + MOCK_METHOD(void, SetAnalyticsRecorder, + (analytics::AnalyticsRecorder*, const std::string&), (override)); +}; + +} // namespace nearby::connections + +#endif // THIRD_PARTY_NEARBY_CONNECTIONS_IMPLEMENTATION_MOCK_ENDPOINT_CHANNEL_H_ diff --git a/connections/implementation/offline_frames.cc b/connections/implementation/offline_frames.cc index ce7ff30e..14f2d983 100644 --- a/connections/implementation/offline_frames.cc +++ b/connections/implementation/offline_frames.cc @@ -19,6 +19,7 @@ #include #include +#include "absl/strings/string_view.h" #include "connections/connection_options.h" #include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/implementation/internal_payload.h" @@ -27,7 +28,6 @@ #include "connections/medium_selector.h" #include "connections/status.h" #include "internal/flags/nearby_flags.h" -#include "internal/platform/byte_array.h" #include "internal/platform/exception.h" #include "internal/platform/logging.h" #include "internal/platform/mac_address.h" @@ -38,7 +38,6 @@ namespace { using ExceptionOrOfflineFrame = ExceptionOr<::location::nearby::connections::OfflineFrame>; -using ::location::nearby::connections::AutoReconnectFrame; using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame; using ::location::nearby::connections::ConnectionRequestFrame; using ::location::nearby::connections::ConnectionResponseFrame; @@ -50,19 +49,12 @@ using ::location::nearby::connections::OsInfo; using ::location::nearby::connections::PayloadTransferFrame; using ::location::nearby::connections::V1Frame; -ByteArray ToBytes(OfflineFrame&& frame) { - ByteArray bytes(frame.ByteSizeLong()); - frame.set_version(OfflineFrame::V1); - frame.SerializeToArray(bytes.data(), bytes.size()); - return bytes; -} - } // namespace -ExceptionOrOfflineFrame FromBytes(const ByteArray& bytes) { +ExceptionOrOfflineFrame FromBytes(absl::string_view bytes) { OfflineFrame frame; - if (frame.ParseFromString(std::string(bytes))) { + if (frame.ParseFromString(bytes)) { Exception validation_exception = EnsureValidOfflineFrame(frame); if (validation_exception.Raised()) { return ExceptionOrOfflineFrame(validation_exception); @@ -81,7 +73,7 @@ V1Frame::FrameType GetFrameType(const OfflineFrame& frame) { return V1Frame::UNKNOWN_FRAME_TYPE; } -ByteArray ForConnectionRequestConnections( +std::string ForConnectionRequestConnections( const location::nearby::connections::ConnectionsDevice& proto_connections_device, const ConnectionInfo& connection_info) { @@ -140,10 +132,10 @@ ByteArray ForConnectionRequestConnections( connection_info.keep_alive_timeout_millis); } - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForConnectionRequestPresence( +std::string ForConnectionRequestPresence( const location::nearby::connections::PresenceDevice& proto_presence_device, const ConnectionInfo& connection_info) { OfflineFrame frame; @@ -185,11 +177,10 @@ ByteArray ForConnectionRequestPresence( connection_info.keep_alive_timeout_millis); } - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForConnectionResponse(std::int32_t status, const OsInfo& os_info, - std::int32_t multiplex_socket_bitmask) { +std::string ForConnectionResponse(std::int32_t status, const OsInfo& os_info) { OfflineFrame frame; frame.set_version(OfflineFrame::V1); @@ -205,16 +196,16 @@ ByteArray ForConnectionResponse(std::int32_t status, const OsInfo& os_info, ? ConnectionResponseFrame::ACCEPT : ConnectionResponseFrame::REJECT); *sub_frame->mutable_os_info() = os_info; - sub_frame->set_multiplex_socket_bitmask(multiplex_socket_bitmask); + sub_frame->set_multiplex_socket_bitmask(0); sub_frame->set_safe_to_disconnect_version( NearbyFlags::GetInstance().GetInt64Flag( config_package_nearby::nearby_connections_feature:: kSafeToDisconnectVersion)); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForDataPayloadTransfer( +std::string ForDataPayloadTransfer( const PayloadTransferFrame::PayloadHeader& header, const PayloadTransferFrame::PayloadChunk& chunk) { OfflineFrame frame; @@ -227,10 +218,10 @@ ByteArray ForDataPayloadTransfer( *sub_frame->mutable_payload_header() = header; *sub_frame->mutable_payload_chunk() = chunk; - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForControlPayloadTransfer( +std::string ForControlPayloadTransfer( const PayloadTransferFrame::PayloadHeader& header, const PayloadTransferFrame::ControlMessage& control) { OfflineFrame frame; @@ -243,10 +234,10 @@ ByteArray ForControlPayloadTransfer( *sub_frame->mutable_payload_header() = header; *sub_frame->mutable_control_message() = control; - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForPayloadAckPayloadTransfer(std::int64_t payload_id) { +std::string ForPayloadAckPayloadTransfer(std::int64_t payload_id) { OfflineFrame frame; frame.set_version(OfflineFrame::V1); @@ -260,10 +251,10 @@ ByteArray ForPayloadAckPayloadTransfer(std::int64_t payload_id) { header.set_total_size(InternalPayload::kIndeterminateSize); *sub_frame->mutable_payload_header() = header; - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForBwuWifiHotspotPathAvailable( +std::string ForBwuWifiHotspotPathAvailable( BandwidthUpgradeNegotiationFrame::UpgradePathInfo::WifiHotspotCredentials credentials, bool supports_disabling_encryption) { @@ -283,10 +274,10 @@ ByteArray ForBwuWifiHotspotPathAvailable( auto* wifi_hotspot_credentials = upgrade_path_info->mutable_wifi_hotspot_credentials(); *wifi_hotspot_credentials = std::move(credentials); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForBwuWifiLanPathAvailable( +std::string ForBwuWifiLanPathAvailable( const std::vector& addresses) { OfflineFrame frame; @@ -315,10 +306,10 @@ ByteArray ForBwuWifiLanPathAvailable( VLOG(1) << "ForBwuWifiLanPathAvailable: " << address; } } - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForBwuAwdlPathAvailable(const std::string& service_name, +std::string ForBwuAwdlPathAvailable(const std::string& service_name, const std::string& service_type, const std::string& password, bool supports_disabling_encryption) { @@ -340,10 +331,10 @@ ByteArray ForBwuAwdlPathAvailable(const std::string& service_name, awdl_socket->set_service_type(service_type); awdl_socket->set_password(password); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForBwuWifiAwarePathAvailable(const std::string& service_id, +std::string ForBwuWifiAwarePathAvailable(const std::string& service_id, const std::string& service_info, const std::string& password, bool supports_disabling_encryption) { @@ -366,13 +357,13 @@ ByteArray ForBwuWifiAwarePathAvailable(const std::string& service_id, wifi_aware_credentials->set_service_info(service_info); if (!password.empty()) wifi_aware_credentials->set_password(password); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForBwuWifiDirectPathAvailable( +std::string ForBwuWifiDirectPathAvailable( const std::string& ssid, const std::string& password, std::int32_t port, std::int32_t frequency, bool supports_disabling_encryption, - const std::string& gateway, const std::string& service_name, + const std::string& gateway, const std::string& device_name, const std::string& pin) { OfflineFrame frame; @@ -394,13 +385,13 @@ ByteArray ForBwuWifiDirectPathAvailable( wifi_direct_credentials->set_port(port); wifi_direct_credentials->set_frequency(frequency); wifi_direct_credentials->set_gateway(gateway); - wifi_direct_credentials->set_service_name(service_name); + wifi_direct_credentials->set_device_name(device_name); wifi_direct_credentials->set_pin(pin); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForBwuBluetoothPathAvailable(const std::string& service_id, +std::string ForBwuBluetoothPathAvailable(const std::string& service_id, MacAddress mac_address) { OfflineFrame frame; @@ -418,10 +409,10 @@ ByteArray ForBwuBluetoothPathAvailable(const std::string& service_id, bluetooth_credentials->set_mac_address(mac_address.ToString()); bluetooth_credentials->set_service_name(service_id); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForBwuWebrtcPathAvailable(const std::string& peer_id, +std::string ForBwuWebrtcPathAvailable(const std::string& peer_id, const LocationHint& location_hint) { OfflineFrame frame; @@ -439,10 +430,10 @@ ByteArray ForBwuWebrtcPathAvailable(const std::string& peer_id, auto* local_location_hint = webrtc_credentials->mutable_location_hint(); *local_location_hint = location_hint; - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForBwuLastWrite() { +std::string ForBwuLastWrite() { OfflineFrame frame; frame.set_version(OfflineFrame::V1); @@ -452,10 +443,10 @@ ByteArray ForBwuLastWrite() { sub_frame->set_event_type( BandwidthUpgradeNegotiationFrame::LAST_WRITE_TO_PRIOR_CHANNEL); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForBwuSafeToClose() { +std::string ForBwuSafeToClose() { OfflineFrame frame; frame.set_version(OfflineFrame::V1); @@ -465,10 +456,10 @@ ByteArray ForBwuSafeToClose() { sub_frame->set_event_type( BandwidthUpgradeNegotiationFrame::SAFE_TO_CLOSE_PRIOR_CHANNEL); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForBwuIntroduction(const std::string& endpoint_id, +std::string ForBwuIntroduction(const std::string& endpoint_id, bool supports_disabling_encryption) { OfflineFrame frame; @@ -483,10 +474,10 @@ ByteArray ForBwuIntroduction(const std::string& endpoint_id, client_introduction->set_supports_disabling_encryption( supports_disabling_encryption); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForBwuIntroductionAck() { +std::string ForBwuIntroductionAck() { OfflineFrame frame; frame.set_version(OfflineFrame::V1); @@ -496,10 +487,10 @@ ByteArray ForBwuIntroductionAck() { sub_frame->set_event_type( BandwidthUpgradeNegotiationFrame::CLIENT_INTRODUCTION_ACK); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForBwuFailure(const UpgradePathInfo& info) { +std::string ForBwuFailure(const UpgradePathInfo& info) { OfflineFrame frame; frame.set_version(OfflineFrame::V1); @@ -512,10 +503,10 @@ ByteArray ForBwuFailure(const UpgradePathInfo& info) { *sub_frame->mutable_upgrade_path_info() = info; - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForBwuPathRequest(const std::vector& mediums, +std::string ForBwuPathRequest(const std::vector& mediums, const MediumRole& medium_role) { OfflineFrame frame; @@ -534,10 +525,10 @@ ByteArray ForBwuPathRequest(const std::vector& mediums, upgrade_path_request->mutable_medium_meta_data()->mutable_medium_role(); role->MergeFrom(medium_role); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForKeepAlive() { +std::string ForKeepAlive() { OfflineFrame frame; frame.set_version(OfflineFrame::V1); @@ -545,10 +536,10 @@ ByteArray ForKeepAlive() { v1_frame->set_type(V1Frame::KEEP_ALIVE); v1_frame->mutable_keep_alive(); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForKeepAlive(bool ack, uint32_t seq_num) { +std::string ForKeepAlive(bool ack, uint32_t seq_num) { OfflineFrame frame; frame.set_version(OfflineFrame::V1); @@ -557,10 +548,10 @@ ByteArray ForKeepAlive(bool ack, uint32_t seq_num) { KeepAliveFrame* keep_alive = v1_frame->mutable_keep_alive(); keep_alive->set_ack(ack); keep_alive->set_seq_num(seq_num); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForDisconnection(bool request_safe_to_disconnect, +std::string ForDisconnection(bool request_safe_to_disconnect, bool ack_safe_to_disconnect) { OfflineFrame frame; @@ -571,33 +562,9 @@ ByteArray ForDisconnection(bool request_safe_to_disconnect, disconnection->set_request_safe_to_disconnect(request_safe_to_disconnect); disconnection->set_ack_safe_to_disconnect(ack_safe_to_disconnect); - return ToBytes(std::move(frame)); + return frame.SerializeAsString(); } -ByteArray ForAutoReconnectIntroduction(const std::string& endpoint_id) { - OfflineFrame frame; - - frame.set_version(OfflineFrame::V1); - auto* v1_frame = frame.mutable_v1(); - v1_frame->set_type(V1Frame::AUTO_RECONNECT); - auto* auto_reconnect = v1_frame->mutable_auto_reconnect(); - auto_reconnect->set_endpoint_id(endpoint_id); - auto_reconnect->set_event_type(AutoReconnectFrame::CLIENT_INTRODUCTION); - - return ToBytes(std::move(frame)); -} - -ByteArray ForAutoReconnectIntroductionAck() { - OfflineFrame frame; - - frame.set_version(OfflineFrame::V1); - auto* v1_frame = frame.mutable_v1(); - v1_frame->set_type(V1Frame::AUTO_RECONNECT); - auto* auto_reconnect = v1_frame->mutable_auto_reconnect(); - auto_reconnect->set_event_type(AutoReconnectFrame::CLIENT_INTRODUCTION_ACK); - - return ToBytes(std::move(frame)); -} UpgradePathInfo::Medium MediumToUpgradePathInfoMedium(Medium medium) { switch (medium) { @@ -742,8 +709,8 @@ MediumMetadata::WifiDirectAuthType WFDAuthTypeToMediumMetadataWFDAuthType( switch (wifi_direct_auth_type) { case WifiDirectAuthType::WIFI_DIRECT_WITH_PASSWORD: return MediumMetadata::WIFI_DIRECT_WITH_PASSWORD; - case WifiDirectAuthType::WIFI_DIRECT_WITH_PIN: - return MediumMetadata::WIFI_DIRECT_WITH_PIN; + case WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME: + return MediumMetadata::WIFI_DIRECT_WITH_DEVICE_NAME; default: return MediumMetadata::WIFI_DIRECT_TYPE_UNKNOWN; } @@ -754,8 +721,8 @@ WifiDirectAuthType MediumMetadataWFDAuthTypeToWFDAuthType( switch (wifi_direct_auth_type) { case MediumMetadata::WIFI_DIRECT_WITH_PASSWORD: return WifiDirectAuthType::WIFI_DIRECT_WITH_PASSWORD; - case MediumMetadata::WIFI_DIRECT_WITH_PIN: - return WifiDirectAuthType::WIFI_DIRECT_WITH_PIN; + case MediumMetadata::WIFI_DIRECT_WITH_DEVICE_NAME: + return WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME; default: return WifiDirectAuthType::WIFI_DIRECT_TYPE_UNKNOWN; } diff --git a/connections/implementation/offline_frames.h b/connections/implementation/offline_frames.h index 36567eba..b2fce56f 100644 --- a/connections/implementation/offline_frames.h +++ b/connections/implementation/offline_frames.h @@ -19,10 +19,10 @@ #include #include +#include "absl/strings/string_view.h" #include "connections/connection_options.h" #include "connections/implementation/proto/offline_wire_formats.pb.h" #include "connections/medium_selector.h" -#include "internal/platform/byte_array.h" #include "internal/platform/exception.h" #include "internal/platform/mac_address.h" #include "internal/platform/service_address.h" @@ -43,7 +43,7 @@ using WifiDirectAuthType = // Returns OfflineFrame if parser was able to understand it, or // Exception::kInvalidProtocolBuffer, if parser failed. ExceptionOr FromBytes( - const ByteArray& offline_frame_bytes); + absl::string_view offline_frame_bytes); // Returns FrameType of a parsed message, or // V1Frame::UNKNOWN_FRAME_TYPE, if frame contents is not recognized. @@ -51,74 +51,71 @@ location::nearby::connections::V1Frame::FrameType GetFrameType( const location::nearby::connections::OfflineFrame& offline_frame); // Builds Connection Request / Response messages. -ByteArray ForConnectionRequestConnections( +std::string ForConnectionRequestConnections( const location::nearby::connections::ConnectionsDevice& proto_connections_device, const ConnectionInfo& connection_info); -ByteArray ForConnectionRequestPresence( +std::string ForConnectionRequestPresence( const location::nearby::connections::PresenceDevice& proto_presence_device, const ConnectionInfo& connection_info); -ByteArray ForConnectionResponse( - std::int32_t status, const location::nearby::connections::OsInfo& os_info, - std::int32_t multiplex_socket_bitmask); +std::string ForConnectionResponse( + std::int32_t status, const location::nearby::connections::OsInfo& os_info); // Builds Payload transfer messages. -ByteArray ForDataPayloadTransfer( +std::string ForDataPayloadTransfer( const location::nearby::connections::PayloadTransferFrame::PayloadHeader& header, const location::nearby::connections::PayloadTransferFrame::PayloadChunk& chunk); -ByteArray ForControlPayloadTransfer( +std::string ForControlPayloadTransfer( const location::nearby::connections::PayloadTransferFrame::PayloadHeader& header, const location::nearby::connections::PayloadTransferFrame::ControlMessage& control); -ByteArray ForPayloadAckPayloadTransfer(std::int64_t payload_id); +std::string ForPayloadAckPayloadTransfer(std::int64_t payload_id); // Builds Bandwidth Upgrade [BWU] messages. -ByteArray ForBwuIntroduction(const std::string& endpoint_id, +std::string ForBwuIntroduction(const std::string& endpoint_id, bool supports_disabling_encryption); -ByteArray ForBwuIntroductionAck(); -ByteArray ForBwuWifiHotspotPathAvailable( +std::string ForBwuIntroductionAck(); +std::string ForBwuWifiHotspotPathAvailable( location::nearby::connections::BandwidthUpgradeNegotiationFrame:: UpgradePathInfo::WifiHotspotCredentials credentials, bool supports_disabling_encryption); -ByteArray ForBwuWifiLanPathAvailable( +std::string ForBwuWifiLanPathAvailable( const std::vector& addresses); -ByteArray ForBwuAwdlPathAvailable(const std::string& service_name, +std::string ForBwuAwdlPathAvailable(const std::string& service_name, const std::string& service_type, const std::string& password, bool supports_disabling_encryption); -ByteArray ForBwuWifiAwarePathAvailable(const std::string& service_id, +std::string ForBwuWifiAwarePathAvailable(const std::string& service_id, const std::string& service_info, const std::string& password, bool supports_disabling_encryption); -ByteArray ForBwuWifiDirectPathAvailable(const std::string& ssid, +std::string ForBwuWifiDirectPathAvailable(const std::string& ssid, const std::string& password, std::int32_t port, std::int32_t frequency, bool supports_disabling_encryption, const std::string& gateway, - const std::string& service_name, + const std::string& device_name, const std::string& pin); -ByteArray ForBwuBluetoothPathAvailable(const std::string& service_id, +std::string ForBwuBluetoothPathAvailable(const std::string& service_id, MacAddress mac_address); -ByteArray ForBwuWebrtcPathAvailable( +std::string ForBwuWebrtcPathAvailable( const std::string& peer_id, const location::nearby::connections::LocationHint& location_hint_a); -ByteArray ForBwuFailure(const UpgradePathInfo& info); -ByteArray ForBwuPathRequest( +std::string ForBwuFailure(const UpgradePathInfo& info); +std::string ForBwuPathRequest( const std::vector& mediums, const location::nearby::connections::MediumRole& medium_role); -ByteArray ForBwuLastWrite(); -ByteArray ForBwuSafeToClose(); +std::string ForBwuLastWrite(); +std::string ForBwuSafeToClose(); -ByteArray ForKeepAlive(); -ByteArray ForKeepAlive(bool ack, uint32_t seq_num); -ByteArray ForDisconnection(bool request_safe_to_disconnect, +std::string ForKeepAlive(); +std::string ForKeepAlive(bool ack, uint32_t seq_num); +std::string ForDisconnection(bool request_safe_to_disconnect, bool ack_safe_to_disconnect); -ByteArray ForAutoReconnectIntroduction(const std::string& endpoint_id); -ByteArray ForAutoReconnectIntroductionAck(); UpgradePathInfo::Medium MediumToUpgradePathInfoMedium(Medium medium); Medium UpgradePathInfoMediumToMedium(UpgradePathInfo::Medium medium); diff --git a/connections/implementation/offline_frames_test.cc b/connections/implementation/offline_frames_test.cc index 2f3f7965..1bb982f2 100644 --- a/connections/implementation/offline_frames_test.cc +++ b/connections/implementation/offline_frames_test.cc @@ -88,8 +88,7 @@ TEST(OfflineFramesTest, CanParseMessageFromBytes) { sub_frame->add_mediums(MediumToConnectionRequestMedium(medium)); } } - auto serialized_bytes = ByteArray(tx_message.SerializeAsString()); - auto ret_value = FromBytes(serialized_bytes); + auto ret_value = FromBytes(tx_message.SerializeAsString()); ASSERT_TRUE(ret_value.ok()); const auto& rx_message = ret_value.result(); EXPECT_THAT(rx_message, EqualsProto(tx_message)); @@ -141,8 +140,8 @@ TEST(OfflineFramesTest, CanGenerateLegacyConnectionRequest) { kMediums.begin(), kMediums.end()), kKeepAliveIntervalMillis, kKeepAliveTimeoutMillis}; - ByteArray bytes = ForConnectionRequestConnections({}, connection_info); - auto response = FromBytes(bytes); + auto response = + FromBytes(ForConnectionRequestConnections({}, connection_info)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -208,9 +207,8 @@ TEST(OfflineFramesTest, CanGenerateConnectionsConnectionRequest) { kKeepAliveIntervalMillis, kKeepAliveTimeoutMillis, medium_role}; - ByteArray bytes = - ForConnectionRequestConnections(connections_device, connection_info); - auto response = FromBytes(bytes); + auto response = FromBytes( + ForConnectionRequestConnections(connections_device, connection_info)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -268,9 +266,8 @@ TEST(OfflineFramesTest, CanGeneratePresenceConnectionRequest) { presence_device.set_endpoint_type( location::nearby::connections::PRESENCE_ENDPOINT); presence_device.set_device_name("TEST DEVICE"); - ByteArray bytes = - ForConnectionRequestPresence(presence_device, connection_info); - auto response = FromBytes(bytes); + auto response = + FromBytes(ForConnectionRequestPresence(presence_device, connection_info)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -292,7 +289,7 @@ TEST(OfflineFramesTest, supports_5_ghz: true bssid: "FF:FF:FF:FF:FF:FF" ap_frequency: 2412 - supported_wifi_direct_auth_types: WIFI_DIRECT_WITH_PIN + supported_wifi_direct_auth_types: WIFI_DIRECT_WITH_DEVICE_NAME supported_wifi_direct_auth_types: WIFI_DIRECT_WITH_PASSWORD > mediums: MDNS @@ -327,7 +324,7 @@ TEST(OfflineFramesTest, kKeepAliveIntervalMillis, kKeepAliveTimeoutMillis}; connection_info.supported_wifi_direct_auth_types = { - WifiDirectAuthType::WIFI_DIRECT_WITH_PIN, + WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME, WifiDirectAuthType::WIFI_DIRECT_WITH_PASSWORD}; location::nearby::connections::ConnectionsDevice connections_device; @@ -336,9 +333,8 @@ TEST(OfflineFramesTest, location::nearby::connections::CONNECTIONS_ENDPOINT); connections_device.set_endpoint_info("XYZ"); - ByteArray bytes = - ForConnectionRequestConnections(connections_device, connection_info); - auto response = FromBytes(bytes); + auto response = FromBytes( + ForConnectionRequestConnections(connections_device, connection_info)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -354,7 +350,7 @@ TEST(OfflineFramesTest, CanGenerateConnectionResponse) { status: 1 response: REJECT os_info { type: LINUX } - multiplex_socket_bitmask: 0x01 + multiplex_socket_bitmask: 0 safe_to_disconnect_version: 5 > >)pb"; @@ -365,9 +361,7 @@ TEST(OfflineFramesTest, CanGenerateConnectionResponse) { config_package_nearby::nearby_connections_feature:: kSafeToDisconnectVersion, 5); - ByteArray bytes = - ForConnectionResponse(1, os_info, /*multiplex_socket_bitmask=*/0x01); - auto response = FromBytes(bytes); + auto response = FromBytes(ForConnectionResponse(1, os_info)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -393,8 +387,7 @@ TEST(OfflineFramesTest, CanGenerateControlPayloadTransfer) { control_message: < event: PAYLOAD_CANCELED offset: 150 > > >)pb"; - ByteArray bytes = ForControlPayloadTransfer(header, control); - auto response = FromBytes(bytes); + auto response = FromBytes(ForControlPayloadTransfer(header, control)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -421,8 +414,7 @@ TEST(OfflineFramesTest, CanGenerateDataPayloadTransfer) { payload_chunk: < flags: 1 offset: 150 body: "payload data" > > >)pb"; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - auto response = FromBytes(bytes); + auto response = FromBytes(ForDataPayloadTransfer(header, chunk)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -439,8 +431,7 @@ TEST(OfflineFramesTest, CanGeneratePayloadAckPayloadTransfer) { payload_header: < id: 12345 total_size: -1 > > >)pb"; - ByteArray bytes = ForPayloadAckPayloadTransfer(12345); - auto response = FromBytes(bytes); + auto response = FromBytes(ForPayloadAckPayloadTransfer(12345)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -487,9 +478,8 @@ TEST(OfflineFramesTest, CanGenerateBwuWifiHotspotPathAvailable) { address_candidate = credentials.add_address_candidates(); address_candidate->set_ip_address(std::string("\xc0\xa8\x00\x01", 4)); address_candidate->set_port(5678); - ByteArray bytes = - ForBwuWifiHotspotPathAvailable(std::move(credentials), false); - auto response = FromBytes(bytes); + auto response = + FromBytes(ForBwuWifiHotspotPathAvailable(std::move(credentials), false)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -521,7 +511,7 @@ TEST(OfflineFramesTest, CanGenerateBwuWifiLanPathAvailable) { > > >)pb"; - ByteArray bytes = ForBwuWifiLanPathAvailable( + std::string bytes = ForBwuWifiLanPathAvailable( {ServiceAddress{ .address = {'\x2a', '\x00', '\x79', '\xe0', '\x2e', '\x87', '\x00', '\x06', '\xb7', '\x28', '\x67', '\x45', '\x7a', '\xdd', @@ -555,9 +545,8 @@ TEST(OfflineFramesTest, CanGenerateBwuAwdlPathAvailable) { > > >)pb"; - ByteArray bytes = ForBwuAwdlPathAvailable("service_name", "nearby_upgrade", - "password", true); - auto response = FromBytes(bytes); + auto response = FromBytes(ForBwuAwdlPathAvailable( + "service_name", "nearby_upgrade", "password", true)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -583,9 +572,9 @@ TEST(OfflineFramesTest, CanGenerateBwuWifiAwarePathAvailable) { > > >)pb"; - ByteArray bytes = ForBwuWifiAwarePathAvailable("service_id", "service_info", - "password", false); - auto response = FromBytes(bytes); + auto response = FromBytes( + ForBwuWifiAwarePathAvailable("service_id", "service_info", "password", + /*supports_disabling_encryption=*/false)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -607,7 +596,7 @@ TEST(OfflineFramesTest, CanGenerateBwuWifiDirectPathAvailable) { port: 1000 frequency: 2412 gateway: "192.168.1.1" - service_name: "NC-WifiDirectTest" + device_name: "NC-WifiDirectTest" pin: "b592f7d3" > supports_disabling_encryption: false @@ -615,10 +604,10 @@ TEST(OfflineFramesTest, CanGenerateBwuWifiDirectPathAvailable) { > > >)pb"; - ByteArray bytes = ForBwuWifiDirectPathAvailable( - "", "", 1000, 2412, false, "192.168.1.1", - "NC-WifiDirectTest", "b592f7d3"); - auto response = FromBytes(bytes); + auto response = FromBytes(ForBwuWifiDirectPathAvailable( + /*ssid=*/"", /*password=*/"", /*port=*/1000, /*frequency=*/2412, + /*supports_disabling_encryption=*/false, "192.168.1.1", + "NC-WifiDirectTest", "b592f7d3")); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -644,8 +633,8 @@ TEST(OfflineFramesTest, CanGenerateBwuBluetoothPathAvailable) { >)pb"; MacAddress mac_address; MacAddress::FromString("11:22:33:44:55:66", mac_address); - ByteArray bytes = ForBwuBluetoothPathAvailable("service", mac_address); - auto response = FromBytes(bytes); + auto response = + FromBytes(ForBwuBluetoothPathAvailable("service", mac_address)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -659,8 +648,7 @@ TEST(OfflineFramesTest, CanGenerateBwuLastWrite) { type: BANDWIDTH_UPGRADE_NEGOTIATION bandwidth_upgrade_negotiation: < event_type: LAST_WRITE_TO_PRIOR_CHANNEL > >)pb"; - ByteArray bytes = ForBwuLastWrite(); - auto response = FromBytes(bytes); + auto response = FromBytes(ForBwuLastWrite()); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -674,8 +662,7 @@ TEST(OfflineFramesTest, CanGenerateBwuSafeToClose) { type: BANDWIDTH_UPGRADE_NEGOTIATION bandwidth_upgrade_negotiation: < event_type: SAFE_TO_CLOSE_PRIOR_CHANNEL > >)pb"; - ByteArray bytes = ForBwuSafeToClose(); - auto response = FromBytes(bytes); + auto response = FromBytes(ForBwuSafeToClose()); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -695,9 +682,8 @@ TEST(OfflineFramesTest, CanGenerateBwuIntroduction) { > > >)pb"; - ByteArray bytes = ForBwuIntroduction( - std::string(kEndpointId), false /* supports_disabling_encryption */); - auto response = FromBytes(bytes); + auto response = FromBytes(ForBwuIntroduction( + std::string(kEndpointId), false /* supports_disabling_encryption */)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -711,8 +697,7 @@ TEST(OfflineFramesTest, CanGenerateKeepAlive) { type: KEEP_ALIVE keep_alive: <> >)pb"; - ByteArray bytes = ForKeepAlive(); - auto response = FromBytes(bytes); + auto response = FromBytes(ForKeepAlive()); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -729,43 +714,14 @@ TEST(OfflineFramesTest, CanGenerateDisconnection) { ack_safe_to_disconnect: true > >)pb"; - ByteArray bytes = ForDisconnection(/* request_safe_to_disconnect */ true, - /* ack_safe_to_disconnect */ true); - auto response = FromBytes(bytes); + auto response = + FromBytes(ForDisconnection(/* request_safe_to_disconnect */ true, + /* ack_safe_to_disconnect */ true)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); } -TEST(OfflineFramesTest, CanGenerateAutoReconnectIntroduction) { - constexpr absl::string_view kExpected = - R"pb( - version: V1 - v1: < - type: AUTO_RECONNECT - auto_reconnect: < event_type: CLIENT_INTRODUCTION endpoint_id: "ABC" > - >)pb"; - ByteArray bytes = ForAutoReconnectIntroduction(std::string(kEndpointId)); - auto response = FromBytes(bytes); - ASSERT_TRUE(response.ok()); - OfflineFrame message = response.result(); - EXPECT_THAT(message, EqualsProto(kExpected)); -} - -TEST(OfflineFramesTest, CanGenerateAutoReconnectIntroductionAck) { - constexpr absl::string_view kExpected = - R"pb( - version: V1 - v1: < - type: AUTO_RECONNECT - auto_reconnect: < event_type: CLIENT_INTRODUCTION_ACK > - >)pb"; - ByteArray bytes = ForAutoReconnectIntroductionAck(); - auto response = FromBytes(bytes); - ASSERT_TRUE(response.ok()); - OfflineFrame message = response.result(); - EXPECT_THAT(message, EqualsProto(kExpected)); -} TEST(OfflineFramesTest, CanGenerateBwuPathRequest) { constexpr absl::string_view kExpected = @@ -789,8 +745,7 @@ TEST(OfflineFramesTest, CanGenerateBwuPathRequest) { mediums.push_back(Medium::WIFI_HOTSPOT); MediumRole medium_role; medium_role.set_support_wifi_hotspot_client(true); - ByteArray bytes = ForBwuPathRequest(mediums, medium_role); - auto response = FromBytes(bytes); + auto response = FromBytes(ForBwuPathRequest(mediums, medium_role)); ASSERT_TRUE(response.ok()); OfflineFrame message = response.result(); EXPECT_THAT(message, EqualsProto(kExpected)); @@ -801,8 +756,8 @@ TEST(OfflineFramesTest, WFDAuthTypeToMediumMetadataWFDAuthType) { WifiDirectAuthType::WIFI_DIRECT_WITH_PASSWORD), MediumMetadata::WIFI_DIRECT_WITH_PASSWORD); EXPECT_EQ(WFDAuthTypeToMediumMetadataWFDAuthType( - WifiDirectAuthType::WIFI_DIRECT_WITH_PIN), - MediumMetadata::WIFI_DIRECT_WITH_PIN); + WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME), + MediumMetadata::WIFI_DIRECT_WITH_DEVICE_NAME); EXPECT_EQ(WFDAuthTypeToMediumMetadataWFDAuthType( WifiDirectAuthType::WIFI_DIRECT_TYPE_UNKNOWN), MediumMetadata::WIFI_DIRECT_TYPE_UNKNOWN); @@ -813,8 +768,8 @@ TEST(OfflineFramesTest, MediumMetadataWFDAuthTypeToWFDAuthType) { MediumMetadata::WIFI_DIRECT_WITH_PASSWORD), WifiDirectAuthType::WIFI_DIRECT_WITH_PASSWORD); EXPECT_EQ(MediumMetadataWFDAuthTypeToWFDAuthType( - MediumMetadata::WIFI_DIRECT_WITH_PIN), - WifiDirectAuthType::WIFI_DIRECT_WITH_PIN); + MediumMetadata::WIFI_DIRECT_WITH_DEVICE_NAME), + WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME); EXPECT_EQ(MediumMetadataWFDAuthTypeToWFDAuthType( MediumMetadata::WIFI_DIRECT_TYPE_UNKNOWN), WifiDirectAuthType::WIFI_DIRECT_TYPE_UNKNOWN); @@ -825,11 +780,11 @@ TEST(OfflineFramesTest, MediumMetadataWFDAuthTypesToWFDAuthTypes) { medium_metadata.add_supported_wifi_direct_auth_types( MediumMetadata::WIFI_DIRECT_WITH_PASSWORD); medium_metadata.add_supported_wifi_direct_auth_types( - MediumMetadata::WIFI_DIRECT_WITH_PIN); + MediumMetadata::WIFI_DIRECT_WITH_DEVICE_NAME); std::vector expected = { WifiDirectAuthType::WIFI_DIRECT_WITH_PASSWORD, - WifiDirectAuthType::WIFI_DIRECT_WITH_PIN}; + WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME}; EXPECT_THAT(MediumMetadataWFDAuthTypesToWFDAuthTypes(medium_metadata), Pointwise(testing::Eq(), expected)); diff --git a/connections/implementation/offline_frames_validator.cc b/connections/implementation/offline_frames_validator.cc index 813d0b87..c42cd4a3 100644 --- a/connections/implementation/offline_frames_validator.cc +++ b/connections/implementation/offline_frames_validator.cc @@ -56,19 +56,19 @@ constexpr absl::string_view kIpv4PatternString{ "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"}; -constexpr absl::string_view kIpv6PatternString{ - "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." - "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." - "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." - "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." - "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." - "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"}; constexpr absl::string_view kWifiDirectSsidPatternString{ "^DIRECT-[a-zA-Z0-9]{2}.*$"}; constexpr int kWifiDirectSsidMaxLength = 32; constexpr int kWifiPasswordSsidMinLength = 8; constexpr int kWifiPasswordSsidMaxLength = 64; -constexpr int kWifiDirectPinMinLength = 4; +// For Windows Wifi Direct based on WinRT Windows.Devices.WiFiDirect, user can't +// choose pin when pairing with the other device. Instead, When GO is created, a +// pin is created by OS. But at this stage, BWU has already sent device name as +// credential to GC for connection. Current BWU design has no way to send second +// ForBwuWifiDirectPathAvailable frame with pin as crdential to GC. To avoid +// major change in BWU structure, we decided to use ConfirmOnly(Push Button) for +// WPS, so no pin is required, the min length should be 0. +constexpr int kWifiDirectPinMinLength = 0; constexpr int kWifiDirectPinMaxLength = 16; inline bool WithinRange(int value, int min, int max) { @@ -298,18 +298,17 @@ Exception EnsureValidBandwidthUpgradeWifiDirectPathAvailableFrame( wifi_direct_credentials.has_password() && WithinRange(wifi_direct_credentials.password().length(), kWifiPasswordSsidMinLength, kWifiPasswordSsidMaxLength); - bool service_name_valid = - wifi_direct_credentials.has_service_name() && - wifi_direct_credentials.service_name().length() < + bool device_name_valid = + wifi_direct_credentials.has_device_name() && + wifi_direct_credentials.device_name().length() < kWifiDirectSsidMaxLength; bool pin_valid = wifi_direct_credentials.has_pin() && WithinRange(wifi_direct_credentials.pin().length(), kWifiDirectPinMinLength, kWifiDirectPinMaxLength); - if ((ssid_valid && password_valid) || (service_name_valid && pin_valid)) + if ((ssid_valid && password_valid) || (device_name_valid && pin_valid)) return {Exception::kSuccess}; - return {Exception::kInvalidProtocolBuffer}; // For backwards compatibility reasons, no other fields should be null-checked diff --git a/connections/implementation/offline_frames_validator_test.cc b/connections/implementation/offline_frames_validator_test.cc index 7695b64c..37d92941 100644 --- a/connections/implementation/offline_frames_validator_test.cc +++ b/connections/implementation/offline_frames_validator_test.cc @@ -49,7 +49,7 @@ constexpr absl::string_view kPassword = "password"; constexpr absl::string_view kWifiHotspotGateway = "0.0.0.0"; constexpr absl::string_view kWifiDirectSsid = "DIRECT-A0-0123456789AB"; constexpr absl::string_view kWifiDirectPassword = "WIFIDIRECT123456"; -constexpr absl::string_view kWifiDirectServiceName = "NC-WifiDirectTest"; +constexpr absl::string_view kWifiDirectDeviceName = "NC-WifiDirectTest"; constexpr absl::string_view kWifiDirectPin = "b592f7d3"; constexpr absl::string_view kGateway = "192.168.1.1"; constexpr int kWifiDirectFrequency = 2412; @@ -66,24 +66,24 @@ constexpr int kKeepAliveTimeoutMillis = 5000; class OfflineFramesConnectionRequestTest : public testing::Test { protected: - ConnectionInfo connection_info_{std::string(kEndpointId), - ByteArray{std::string(kEndpointName)}, - kNonce, - kSupports5ghz, - std::string(kBssid), - kApFrequency, - std::vector>( - kMediums.begin(), kMediums.end()), - kKeepAliveIntervalMillis, - kKeepAliveTimeoutMillis}; + ConnectionInfo connection_info_{ + std::string(kEndpointId), + ByteArray{std::string(kEndpointName)}, + kNonce, + kSupports5ghz, + std::string(kBssid), + kApFrequency, + std::vector(kMediums.begin(), kMediums.end()), + kKeepAliveIntervalMillis, + kKeepAliveTimeoutMillis}; }; TEST_F(OfflineFramesConnectionRequestTest, ValidatesAsOkWithValidConnectionRequestFrame) { OfflineFrame offline_frame; - ByteArray bytes = ForConnectionRequestConnections({}, connection_info_); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForConnectionRequestConnections({}, connection_info_); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -94,8 +94,8 @@ TEST_F(OfflineFramesConnectionRequestTest, ValidatesAsFailWithNullConnectionRequestFrame) { OfflineFrame offline_frame; - ByteArray bytes = ForConnectionRequestConnections({}, connection_info_); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForConnectionRequestConnections({}, connection_info_); + offline_frame.ParseFromString(bytes); auto* v1_frame = offline_frame.mutable_v1(); v1_frame->clear_connection_request(); @@ -110,8 +110,8 @@ TEST_F(OfflineFramesConnectionRequestTest, OfflineFrame offline_frame; connection_info_.local_endpoint_id = ""; - ByteArray bytes = ForConnectionRequestConnections({}, connection_info_); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForConnectionRequestConnections({}, connection_info_); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -121,9 +121,9 @@ TEST_F(OfflineFramesConnectionRequestTest, TEST_F(OfflineFramesConnectionRequestTest, ValidatesAsFailWithEmptyEndpointIdInConnectionRequestFrame) { connection_info_.local_endpoint_id = ""; - ByteArray bytes = ForConnectionRequestConnections({}, connection_info_); + std::string bytes = ForConnectionRequestConnections({}, connection_info_); location::nearby::connections::OfflineFrame frame; - frame.ParseFromString(bytes.AsStringView()); + frame.ParseFromString(bytes); frame.mutable_v1()->mutable_connection_request()->set_endpoint_id(""); ASSERT_TRUE(frame.v1().connection_request().has_endpoint_id()); @@ -140,8 +140,8 @@ TEST_F(OfflineFramesConnectionRequestTest, OfflineFrame offline_frame; connection_info_.local_endpoint_info = ByteArray{""}; - ByteArray bytes = ForConnectionRequestConnections({}, connection_info_); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForConnectionRequestConnections({}, connection_info_); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -153,8 +153,8 @@ TEST_F(OfflineFramesConnectionRequestTest, OfflineFrame offline_frame; connection_info_.bssid = ""; - ByteArray bytes = ForConnectionRequestConnections({}, connection_info_); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForConnectionRequestConnections({}, connection_info_); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -166,8 +166,8 @@ TEST_F(OfflineFramesConnectionRequestTest, OfflineFrame offline_frame; connection_info_.supported_mediums = {}; - ByteArray bytes = ForConnectionRequestConnections({}, connection_info_); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForConnectionRequestConnections({}, connection_info_); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -179,9 +179,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; OsInfo os_info; - ByteArray bytes = ForConnectionResponse(kStatusAccepted, os_info, - /*multiplex_socket_bitmask=*/0); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForConnectionResponse(kStatusAccepted, os_info); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -193,9 +192,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; OsInfo os_info; - ByteArray bytes = ForConnectionResponse(kStatusAccepted, os_info, - /*multiplex_socket_bitmask=*/0); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForConnectionResponse(kStatusAccepted, os_info); + offline_frame.ParseFromString(bytes); auto* v1_frame = offline_frame.mutable_v1(); v1_frame->clear_connection_response(); @@ -210,9 +208,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; OsInfo os_info; - ByteArray bytes = - ForConnectionResponse(-1, os_info, /*multiplex_socket_bitmask=*/0); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForConnectionResponse(-1, os_info); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -234,8 +231,8 @@ TEST(OfflineFramesValidatorTest, ValidatesAsOkWithValidPayloadTransferFrame) { OfflineFrame offline_frame; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -259,8 +256,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -284,8 +281,8 @@ TEST(OfflineFramesValidatorTest, ValidatesAsOkTypeFileWithLegalFilePath) { OfflineFrame offline_frame; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -309,8 +306,8 @@ TEST(OfflineFramesValidatorTest, ValidatesAsFailedTypeFileWithIllegalFilePath) { OfflineFrame offline_frame; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -334,8 +331,8 @@ TEST(OfflineFramesValidatorTest, ValidatesAsOkTypeFileWithLegalParentFolder) { OfflineFrame offline_frame; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -360,8 +357,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -377,8 +374,8 @@ TEST(OfflineFramesValidatorTest, ValidatesAsFailWithNullPayloadTransferFrame) { OfflineFrame offline_frame; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); auto* v1_frame = offline_frame.mutable_v1(); v1_frame->clear_payload_transfer(); @@ -401,8 +398,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); auto* v1_frame = offline_frame.mutable_v1(); auto* payload_transfer = v1_frame->mutable_payload_transfer(); @@ -426,8 +423,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -447,8 +444,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); auto* v1_frame = offline_frame.mutable_v1(); auto* payload_transfer = v1_frame->mutable_payload_transfer(); @@ -472,8 +469,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -493,8 +490,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -514,8 +511,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; - ByteArray bytes = ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); auto* v1_frame = offline_frame.mutable_v1(); auto* payload_transfer = v1_frame->mutable_payload_transfer(); auto* payload_chunk = payload_transfer->mutable_payload_chunk(); @@ -539,8 +536,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; - ByteArray bytes = ForControlPayloadTransfer(header, control); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForControlPayloadTransfer(header, control); + offline_frame.ParseFromString(bytes); auto* v1_frame = offline_frame.mutable_v1(); auto* payload_transfer = v1_frame->mutable_payload_transfer(); @@ -564,8 +561,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; - ByteArray bytes = ForControlPayloadTransfer(header, control); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForControlPayloadTransfer(header, control); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -584,8 +581,8 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame; - ByteArray bytes = ForControlPayloadTransfer(header, control); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForControlPayloadTransfer(header, control); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -603,9 +600,9 @@ TEST(OfflineFramesValidatorTest, credentials.set_port(kPort); credentials.set_frequency(kHotspotFrequency); credentials.set_gateway(kWifiHotspotGateway); - ByteArray bytes = ForBwuWifiHotspotPathAvailable( + std::string bytes = ForBwuWifiHotspotPathAvailable( std::move(credentials), kSupportsDisablingEncryption); - offline_frame.ParseFromString(std::string(bytes)); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -628,9 +625,9 @@ TEST(OfflineFramesValidatorTest, candidate = credentials.mutable_address_candidates()->Add(); candidate->set_ip_address(std::string("\xc0\xa8\x00\x01", 4)); candidate->set_port(kPort); - ByteArray bytes = ForBwuWifiHotspotPathAvailable( + std::string bytes = ForBwuWifiHotspotPathAvailable( std::move(credentials), kSupportsDisablingEncryption); - offline_frame.ParseFromString(std::string(bytes)); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -650,9 +647,9 @@ TEST(OfflineFramesValidatorTest, candidate->set_ip_address(std::string( "\xfe\x80\x00\x00\x00\x00\x00\x00\x4d\xb2\xb3\x5c\x22\x03\x98\xa1", 12)); candidate->set_port(kPort); - ByteArray bytes = ForBwuWifiHotspotPathAvailable( + std::string bytes = ForBwuWifiHotspotPathAvailable( std::move(credentials), kSupportsDisablingEncryption); - offline_frame.ParseFromString(std::string(bytes)); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -671,9 +668,9 @@ TEST(OfflineFramesValidatorTest, auto* candidate = credentials.mutable_address_candidates()->Add(); candidate->set_ip_address(std::string( "\xfe\x80\x00\x00\x00\x00\x00\x00\x4d\xb2\xb3\x5c\x22\x03\x98\xa1", 16)); - ByteArray bytes = ForBwuWifiHotspotPathAvailable( + std::string bytes = ForBwuWifiHotspotPathAvailable( std::move(credentials), kSupportsDisablingEncryption); - offline_frame.ParseFromString(std::string(bytes)); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -689,8 +686,8 @@ TEST(OfflineFramesValidatorTest, kPort}, {{'\xc0', '\xa8', '\x00', '\x01'}, kPort}, }; - ByteArray bytes = ForBwuWifiLanPathAvailable(address_candidates); - offline_frame.ParseFromString(std::string(bytes)); + std::string bytes = ForBwuWifiLanPathAvailable(address_candidates); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -708,9 +705,9 @@ TEST(OfflineFramesValidatorTest, credentials.set_port(kPort); credentials.set_frequency(kHotspotFrequency); credentials.set_gateway(kWifiHotspotGateway); - ByteArray bytes = ForBwuWifiHotspotPathAvailable( + std::string bytes = ForBwuWifiHotspotPathAvailable( std::move(credentials), kSupportsDisablingEncryption); - offline_frame.ParseFromString(std::string(bytes)); + offline_frame.ParseFromString(bytes); auto* v1_frame = offline_frame.mutable_v1(); v1_frame->clear_bandwidth_upgrade_negotiation(); @@ -723,11 +720,11 @@ TEST(OfflineFramesValidatorTest, TEST(OfflineFramesValidatorTest, ValidatesAsOkBandwidthUpgradeWifiDirect) { OfflineFrame offline_frame; - ByteArray bytes = ForBwuWifiDirectPathAvailable( + std::string bytes = ForBwuWifiDirectPathAvailable( std::string(kWifiDirectSsid), std::string(kWifiDirectPassword), kPort, kWifiDirectFrequency, kSupportsDisablingEncryption, std::string(kGateway), - std::string(kWifiDirectServiceName), std::string(kWifiDirectPin)); - offline_frame.ParseFromString(std::string(bytes)); + std::string(kWifiDirectDeviceName), std::string(kWifiDirectPin)); + offline_frame.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame); @@ -740,11 +737,11 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame_2; // Anything less than -1 is invalid - ByteArray bytes = ForBwuWifiDirectPathAvailable( + std::string bytes = ForBwuWifiDirectPathAvailable( std::string(kWifiDirectSsid), std::string(kWifiDirectPassword), kPort, -2, kSupportsDisablingEncryption, std::string(kGateway), - std::string(kWifiDirectServiceName), std::string(kWifiDirectPin)); - offline_frame_1.ParseFromString(std::string(bytes)); + std::string(kWifiDirectDeviceName), std::string(kWifiDirectPin)); + offline_frame_1.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame_1); @@ -754,8 +751,8 @@ TEST(OfflineFramesValidatorTest, bytes = ForBwuWifiDirectPathAvailable( std::string(kWifiDirectSsid), std::string(kWifiDirectPassword), kPort, -1, kSupportsDisablingEncryption, std::string(kGateway), - std::string(kWifiDirectServiceName), std::string(kWifiDirectPin)); - offline_frame_2.ParseFromString(std::string(bytes)); + std::string(kWifiDirectDeviceName), std::string(kWifiDirectPin)); + offline_frame_2.ParseFromString(bytes); ret_value = EnsureValidOfflineFrame(offline_frame_2); @@ -768,13 +765,13 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame_2; std::string wifi_direct_ssid{"DIRECT-A*-0123456789AB"}; - std::string wifi_direct_pin_wrong_length = "abc"; - ByteArray bytes = ForBwuWifiDirectPathAvailable( + std::string wifi_direct_pin_wrong_length = "abcefghijklmnopqrstuvwxyz"; + std::string bytes = ForBwuWifiDirectPathAvailable( wifi_direct_ssid, std::string(kWifiDirectPassword), kPort, kWifiDirectFrequency, kSupportsDisablingEncryption, - std::string(kGateway), std::string(kWifiDirectServiceName), + std::string(kGateway), std::string(kWifiDirectDeviceName), wifi_direct_pin_wrong_length); - offline_frame_1.ParseFromString(std::string(bytes)); + offline_frame_1.ParseFromString(bytes); auto ret_value = EnsureValidOfflineFrame(offline_frame_1); @@ -782,15 +779,15 @@ TEST(OfflineFramesValidatorTest, std::string wifi_direct_ssid_wrong_length = std::string{kWifiDirectSsid} + "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"; - std::string wifi_direct_service_name_wrong_length = - std::string{kWifiDirectServiceName} + + std::string wifi_direct_device_name_wrong_length = + std::string{kWifiDirectDeviceName} + "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"; bytes = ForBwuWifiDirectPathAvailable( wifi_direct_ssid_wrong_length, std::string(kWifiDirectPassword), kPort, kWifiDirectFrequency, kSupportsDisablingEncryption, - std::string(kGateway), wifi_direct_service_name_wrong_length, + std::string(kGateway), wifi_direct_device_name_wrong_length, std::string(kWifiDirectPin)); - offline_frame_2.ParseFromString(std::string(bytes)); + offline_frame_2.ParseFromString(bytes); ret_value = EnsureValidOfflineFrame(offline_frame_2); @@ -802,33 +799,20 @@ TEST(OfflineFramesValidatorTest, OfflineFrame offline_frame_1; OfflineFrame offline_frame_2; - std::string short_wifi_direct_password{"Test"}; - std::string short_wifi_direct_pin{"abc"}; - ByteArray bytes = ForBwuWifiDirectPathAvailable( - std::string(kWifiDirectSsid), short_wifi_direct_password, kPort, - kWifiDirectFrequency, kSupportsDisablingEncryption, - std::string(kGateway), std::string(kWifiDirectServiceName), - short_wifi_direct_pin); - offline_frame_1.ParseFromString(std::string(bytes)); - - auto ret_value = EnsureValidOfflineFrame(offline_frame_1); - - ASSERT_FALSE(ret_value.Ok()); - std::string long_wifi_direct_password = std::string{kWifiDirectSsid} + "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789"; std::string long_wifi_direct_pin = std::string{kWifiDirectPin} + "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789"; - bytes = ForBwuWifiDirectPathAvailable( + std::string bytes = ForBwuWifiDirectPathAvailable( std::string(kWifiDirectSsid), long_wifi_direct_password, kPort, kWifiDirectFrequency, kSupportsDisablingEncryption, - std::string(kGateway), std::string(kWifiDirectServiceName), + std::string(kGateway), std::string(kWifiDirectDeviceName), long_wifi_direct_pin); - offline_frame_2.ParseFromString(std::string(bytes)); + offline_frame_2.ParseFromString(bytes); - ret_value = EnsureValidOfflineFrame(offline_frame_2); + auto ret_value = EnsureValidOfflineFrame(offline_frame_2); EXPECT_FALSE(ret_value.Ok()); } diff --git a/connections/implementation/offline_service_controller.cc b/connections/implementation/offline_service_controller.cc index 4e667773..af10471e 100644 --- a/connections/implementation/offline_service_controller.cc +++ b/connections/implementation/offline_service_controller.cc @@ -90,6 +90,7 @@ OfflineServiceController::StartListeningForIncomingConnections( ClientProxy* client, absl::string_view service_id, v3::ConnectionListener listener, const v3::ConnectionListeningOptions& options) { + if (stop_) return {{Status::kOutOfOrderApiCall}, {}}; LOG(INFO) << "Client " << client->GetClientId() << " requested to start listening for service_id " << service_id; return pcp_manager_.StartListeningForIncomingConnections( @@ -98,6 +99,7 @@ OfflineServiceController::StartListeningForIncomingConnections( void OfflineServiceController::StopListeningForIncomingConnections( ClientProxy* client) { + if (stop_) return; LOG(INFO) << "Client " << client->GetClientId() << " requested to stop listening for service_id " << client->GetListeningForIncomingConnectionsServiceId(); diff --git a/connections/implementation/offline_service_controller_test.cc b/connections/implementation/offline_service_controller_test.cc index d82d1893..59fb79cb 100644 --- a/connections/implementation/offline_service_controller_test.cc +++ b/connections/implementation/offline_service_controller_test.cc @@ -567,6 +567,33 @@ TEST_P(OfflineServiceControllerTest, ShutdownBwuManagerExecutors) { env_.Stop(); } +TEST_P(OfflineServiceControllerTest, TestNoStartListeningAfterStop) { + env_.Start(); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + v3::ConnectionListener listener; + v3::ConnectionListeningOptions options; + + user_a.Stop(); + + auto result = user_a.StartListeningForIncomingConnections( + std::string(kServiceId), listener, options); + EXPECT_EQ(result.first.value, Status::kOutOfOrderApiCall); + + env_.Stop(); +} + +TEST_P(OfflineServiceControllerTest, TestNoStopListeningAfterStop) { + env_.Start(); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + + user_a.Stop(); + + // Verify that calling StopListening after Stop does not crash. + user_a.StopListeningForIncomingConnections(); + + env_.Stop(); +} + INSTANTIATE_TEST_SUITE_P(ParametrisedOfflineServiceControllerTest, OfflineServiceControllerTest, ::testing::ValuesIn(kTestCases)); diff --git a/connections/implementation/p2p_cluster_pcp_handler.cc b/connections/implementation/p2p_cluster_pcp_handler.cc index 73dc47f4..c207e91b 100644 --- a/connections/implementation/p2p_cluster_pcp_handler.cc +++ b/connections/implementation/p2p_cluster_pcp_handler.cc @@ -29,13 +29,10 @@ #include "absl/strings/string_view.h" #include "connections/advertising_options.h" #include "connections/discovery_options.h" -#include "connections/implementation/awdl_endpoint_channel.h" +#include "connections/implementation/analytics/operation_result_with_medium.h" #include "connections/implementation/base_pcp_handler.h" #include "connections/implementation/ble_advertisement.h" -#include "connections/implementation/ble_endpoint_channel.h" -#include "connections/implementation/ble_l2cap_endpoint_channel.h" #include "connections/implementation/bluetooth_device_name.h" -#include "connections/implementation/bluetooth_endpoint_channel.h" #include "connections/implementation/bwu_manager.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel_manager.h" @@ -44,16 +41,20 @@ #include "connections/implementation/injected_bluetooth_device_store.h" #include "connections/implementation/mediums/advertisements/advertisement_util.h" #include "connections/implementation/mediums/advertisements/dct_advertisement.h" +#include "connections/implementation/mediums/awdl_endpoint_channel.h" #include "connections/implementation/mediums/ble.h" #include "connections/implementation/mediums/ble/ble_advertisement_header.h" #include "connections/implementation/mediums/ble/ble_socket.h" +#include "connections/implementation/mediums/ble_endpoint_channel.h" +#include "connections/implementation/mediums/ble_l2cap_endpoint_channel.h" #include "connections/implementation/mediums/bluetooth_classic.h" +#include "connections/implementation/mediums/bluetooth_endpoint_channel.h" #include "connections/implementation/mediums/mediums.h" #include "connections/implementation/mediums/utils.h" +#include "connections/implementation/mediums/wifi_lan_endpoint_channel.h" #include "connections/implementation/pcp.h" #include "connections/implementation/pcp_handler.h" #include "connections/implementation/webrtc_state.h" -#include "connections/implementation/wifi_lan_endpoint_channel.h" #include "connections/implementation/wifi_lan_service_info.h" #include "connections/medium_selector.h" #include "connections/out_of_band_connection_metadata.h" @@ -67,6 +68,7 @@ #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/bluetooth_classic.h" #include "internal/platform/byte_array.h" +#include "internal/platform/cancellation_flag.h" #include "internal/platform/expected.h" #include "internal/platform/implementation/platform.h" #include "internal/platform/logging.h" @@ -81,7 +83,6 @@ namespace nearby { namespace connections { namespace { -using ::location::nearby::analytics::proto::ConnectionsLog; using ::location::nearby::proto::connections::OperationResultCode; using ::location::nearby::proto::connections::Medium::AWDL; using ::location::nearby::proto::connections::Medium::BLE; @@ -89,6 +90,7 @@ using ::location::nearby::proto::connections::Medium::BLUETOOTH; using ::location::nearby::proto::connections::Medium::UNKNOWN_MEDIUM; using ::location::nearby::proto::connections::Medium::WEB_RTC; using ::location::nearby::proto::connections::Medium::WIFI_LAN; +using ::nearby::analytics::OperationResultWithMedium; } // namespace @@ -156,8 +158,7 @@ BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartAdvertisingImpl( const std::string& local_endpoint_id, const ByteArray& local_endpoint_info, const AdvertisingOptions& advertising_options) { std::vector mediums_started_successfully; - std::vector - operation_result_with_mediums; + std::vector operation_result_with_mediums; WebRtcState web_rtc_state{WebRtcState::kUnconnectable}; @@ -177,13 +178,12 @@ BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartAdvertisingImpl( VLOG(1) << "P2pClusterPcpHandler::StartAdvertisingImpl: Awdl added"; mediums_started_successfully.push_back(awdl_medium); } - std::unique_ptr - operation_result_with_medium = GetOperationResultWithMediumByResultCode( + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( client, AWDL, /*update_index=*/0, awdl_result.has_error() ? awdl_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + : OperationResultCode::DETAIL_SUCCESS)); } // WifiLan @@ -199,13 +199,12 @@ BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartAdvertisingImpl( VLOG(1) << "P2pClusterPcpHandler::StartAdvertisingImpl: WifiLan added"; mediums_started_successfully.push_back(wifi_lan_medium); } - std::unique_ptr - operation_result_with_medium = GetOperationResultWithMediumByResultCode( + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( client, WIFI_LAN, /*update_index=*/0, wifi_lan_result.has_error() ? wifi_lan_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + : OperationResultCode::DETAIL_SUCCESS)); } if (advertising_options.allowed.bluetooth) { @@ -243,14 +242,13 @@ BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartAdvertisingImpl( bluetooth_classic_advertiser_client_id_ = client->GetClientId(); } } - std::unique_ptr - operation_result_with_medium = GetOperationResultWithMediumByResultCode( + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( client, BLUETOOTH, /*update_index=*/0, bluetooth_result.has_error() ? bluetooth_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + : OperationResultCode::DETAIL_SUCCESS)); } if (advertising_options.allowed.ble) { @@ -263,12 +261,12 @@ BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartAdvertisingImpl( mediums_started_successfully.push_back(ble_result.value()); } - std::unique_ptr - operation_result_with_medium = GetOperationResultWithMediumByResultCode( + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( client, BLE, /*update_index=*/0, ble_result.has_error() ? ble_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); + : OperationResultCode::DETAIL_SUCCESS)); } if (mediums_started_successfully.empty()) { @@ -1042,8 +1040,7 @@ BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartDiscoveryImpl( } std::vector mediums_started_successfully; - std::vector - operation_result_with_mediums; + std::vector operation_result_with_mediums; // Due to singleton, apple only allow start discovery once. So need to keep // the start discovery order of awdl before the wifi_lan. @@ -1059,14 +1056,13 @@ BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartDiscoveryImpl( LOG(INFO) << "P2pClusterPcpHandler::StartDiscoveryImpl: AWDL added"; mediums_started_successfully.push_back(awdl_medium); } - std::unique_ptr - operation_result_with_medium = GetOperationResultWithMediumByResultCode( + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( client, AWDL, /*update_index=*/0, awdl_result.has_error() ? awdl_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + : OperationResultCode::DETAIL_SUCCESS)); } // WifiLan @@ -1080,14 +1076,13 @@ BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartDiscoveryImpl( LOG(INFO) << "P2pClusterPcpHandler::StartDiscoveryImpl: WifiLan added"; mediums_started_successfully.push_back(wifi_lan_medium); } - std::unique_ptr - operation_result_with_medium = GetOperationResultWithMediumByResultCode( + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( client, WIFI_LAN, /*update_index=*/0, wifi_lan_result.has_error() ? wifi_lan_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + : OperationResultCode::DETAIL_SUCCESS)); } if (discovery_options.allowed.ble) { @@ -1102,14 +1097,13 @@ BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartDiscoveryImpl( mediums_started_successfully.push_back(ble_medium); } - std::unique_ptr - operation_result_with_medium = GetOperationResultWithMediumByResultCode( + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( client, BLE, /*update_index=*/0, ble_result.has_error() ? ble_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + : OperationResultCode::DETAIL_SUCCESS)); } if (discovery_options.allowed.bluetooth) { @@ -1158,8 +1152,7 @@ Status P2pClusterPcpHandler::StopDiscoveryImpl(ClientProxy* client) { ble_medium_.StopScanning(client->GetDiscoveryServiceId()); - paused_bluetooth_clients_discoveries_.erase( - client->GetDiscoveryServiceId()); + paused_bluetooth_clients_discoveries_.erase(client->GetDiscoveryServiceId()); return {Status::kSuccess}; } @@ -1235,8 +1228,7 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl( bool refactor_ble_l2cap = NearbyFlags::GetInstance().GetBoolFlag( config_package_nearby::nearby_connections_feature::kRefactorBleL2cap); std::vector started_mediums; - std::vector - operation_result_with_mediums; + std::vector operation_result_with_mediums; int update_index = client_proxy->GetAnalyticsRecorder().GetNextAdvertisingUpdateIndex(); if (options.enable_bluetooth_listening && @@ -1254,13 +1246,12 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl( } else { started_mediums.push_back(BLUETOOTH); } - std::unique_ptr - operation_result_with_medium = GetOperationResultWithMediumByResultCode( + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( client_proxy, Medium::BLUETOOTH, update_index, bluetooth_result.has_error() ? bluetooth_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + : OperationResultCode::DETAIL_SUCCESS)); } // ble @@ -1344,13 +1335,12 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl( } else { started_mediums.push_back(WIFI_LAN); } - std::unique_ptr - operation_result_with_medium = GetOperationResultWithMediumByResultCode( + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( client_proxy, Medium::BLUETOOTH, update_index, wifi_lan_result.has_error() ? wifi_lan_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + : OperationResultCode::DETAIL_SUCCESS)); } if (started_mediums.empty()) { LOG(WARNING) << absl::StrFormat( @@ -1443,8 +1433,7 @@ P2pClusterPcpHandler::UpdateAdvertisingOptionsImpl( // restart std::vector restarted_mediums; - std::vector - operation_result_with_mediums; + std::vector operation_result_with_mediums; int update_index = client->GetAnalyticsRecorder().GetNextAdvertisingUpdateIndex(); Status status = {Status::kSuccess}; @@ -1457,12 +1446,9 @@ P2pClusterPcpHandler::UpdateAdvertisingOptionsImpl( if (new_mediums.ble) { if (old_mediums.ble && !needs_restart) { restarted_mediums.push_back(BLE); - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, BLE, update_index, - OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, BLE, update_index, OperationResultCode::DETAIL_SUCCESS)); } else { ErrorOr ble_result = {Error(OperationResultCode::DETAIL_UNKNOWN)}; ble_result = StartBleAdvertising( @@ -1475,14 +1461,12 @@ P2pClusterPcpHandler::UpdateAdvertisingOptionsImpl( status = {Status::kBleError}; } - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, BLE, update_index, - ble_result.has_error() - ? ble_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, BLE, update_index, + ble_result.has_error() + ? ble_result.error().operation_result_code().value() + : OperationResultCode::DETAIL_SUCCESS)); } } // awdl @@ -1491,12 +1475,9 @@ P2pClusterPcpHandler::UpdateAdvertisingOptionsImpl( new_mediums.awdl && !advertising_options.low_power) { if (old_mediums.awdl && !needs_restart) { restarted_mediums.push_back(AWDL); - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, AWDL, update_index, - OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, AWDL, update_index, OperationResultCode::DETAIL_SUCCESS)); } else { ErrorOr awdl_result = StartAwdlAdvertising( client, std::string(service_id), std::string(local_endpoint_id), @@ -1506,26 +1487,22 @@ P2pClusterPcpHandler::UpdateAdvertisingOptionsImpl( } else { status = {Status::kWifiLanError}; } - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, AWDL, update_index, - awdl_result.has_error() - ? awdl_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, AWDL, update_index, + awdl_result.has_error() + ? awdl_result.error().operation_result_code().value() + : OperationResultCode::DETAIL_SUCCESS)); } } // wifi lan if (new_mediums.wifi_lan && !advertising_options.low_power) { if (old_mediums.wifi_lan && !needs_restart) { restarted_mediums.push_back(WIFI_LAN); - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, WIFI_LAN, update_index, - OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, WIFI_LAN, update_index, + OperationResultCode::DETAIL_SUCCESS)); } else { ErrorOr wifi_lan_result = StartWifiLanAdvertising( client, std::string(service_id), std::string(local_endpoint_id), @@ -1536,26 +1513,22 @@ P2pClusterPcpHandler::UpdateAdvertisingOptionsImpl( } else { status = {Status::kWifiLanError}; } - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, WIFI_LAN, update_index, - wifi_lan_result.has_error() - ? wifi_lan_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, WIFI_LAN, update_index, + wifi_lan_result.has_error() + ? wifi_lan_result.error().operation_result_code().value() + : OperationResultCode::DETAIL_SUCCESS)); } } // bluetooth classic if (new_mediums.bluetooth && !advertising_options.low_power) { if (old_mediums.bluetooth && !needs_restart) { restarted_mediums.push_back(BLUETOOTH); - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, BLUETOOTH, update_index, - OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, BLUETOOTH, update_index, + OperationResultCode::DETAIL_SUCCESS)); } else { const ByteArray bluetooth_hash = GenerateHash( std::string(service_id), BluetoothDeviceName::kServiceIdHashLength); @@ -1589,27 +1562,19 @@ P2pClusterPcpHandler::UpdateAdvertisingOptionsImpl( restarted_mediums.push_back(BLUETOOTH); } - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, BLUETOOTH, update_index, - bluetooth_result.has_error() - ? bluetooth_result.error() - .operation_result_code() - .value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, BLUETOOTH, update_index, + bluetooth_result.has_error() + ? bluetooth_result.error().operation_result_code().value() + : OperationResultCode::DETAIL_SUCCESS)); } else { - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, BLUETOOTH, update_index, - bluetooth_result.has_error() - ? bluetooth_result.error() - .operation_result_code() - .value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, BLUETOOTH, update_index, + bluetooth_result.has_error() + ? bluetooth_result.error().operation_result_code().value() + : OperationResultCode::DETAIL_SUCCESS)); return StartOperationResult{.status = {Status::kBluetoothError}, .mediums = restarted_mediums, .operation_result_with_mediums = std::move( @@ -1661,8 +1626,7 @@ P2pClusterPcpHandler::UpdateDiscoveryOptionsImpl( bool should_start_discovery = false; auto new_mediums = discovery_options.allowed; auto old_mediums = old_options.allowed; - std::vector - operation_result_with_mediums; + std::vector operation_result_with_mediums; int update_index = client->GetAnalyticsRecorder().GetNextDiscoveryUpdateIndex(); // ble @@ -1670,12 +1634,9 @@ P2pClusterPcpHandler::UpdateDiscoveryOptionsImpl( should_start_discovery = true; if (old_mediums.ble) { restarted_mediums.push_back(BLE); - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, BLE, update_index, - OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, BLE, update_index, OperationResultCode::DETAIL_SUCCESS)); } else { ErrorOr ble_result = {Error(OperationResultCode::DETAIL_UNKNOWN)}; ble_result = @@ -1687,14 +1648,12 @@ P2pClusterPcpHandler::UpdateDiscoveryOptionsImpl( "restart ble scanning"; } - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, BLE, update_index, - ble_result.has_error() - ? ble_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, BLE, update_index, + ble_result.has_error() + ? ble_result.error().operation_result_code().value() + : OperationResultCode::DETAIL_SUCCESS)); } } // bt classic @@ -1702,12 +1661,10 @@ P2pClusterPcpHandler::UpdateDiscoveryOptionsImpl( should_start_discovery = true; if (!needs_restart && old_mediums.bluetooth) { restarted_mediums.push_back(BLUETOOTH); - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, BLUETOOTH, update_index, - OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, BLUETOOTH, update_index, + OperationResultCode::DETAIL_SUCCESS)); } else { StartBluetoothDiscoveryWithPause( client, std::string(service_id), discovery_options, restarted_mediums, @@ -1721,12 +1678,10 @@ P2pClusterPcpHandler::UpdateDiscoveryOptionsImpl( should_start_discovery = true; if (!needs_restart && old_mediums.awdl) { restarted_mediums.push_back(AWDL); - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, AWDL, update_index, - OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, AWDL, update_index, + OperationResultCode::DETAIL_SUCCESS)); } else { ErrorOr awdl_result = StartAwdlDiscovery(client, std::string(service_id)); @@ -1736,14 +1691,12 @@ P2pClusterPcpHandler::UpdateDiscoveryOptionsImpl( LOG(WARNING) << "UpdateDiscoveryOptionsImpl: unable to restart " "awdl scanning"; } - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, AWDL, update_index, - awdl_result.has_error() - ? awdl_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, AWDL, update_index, + awdl_result.has_error() + ? awdl_result.error().operation_result_code().value() + : OperationResultCode::DETAIL_SUCCESS)); } } // wifi lan @@ -1751,12 +1704,10 @@ P2pClusterPcpHandler::UpdateDiscoveryOptionsImpl( should_start_discovery = true; if (!needs_restart && old_mediums.wifi_lan) { restarted_mediums.push_back(WIFI_LAN); - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, WIFI_LAN, update_index, - OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, WIFI_LAN, update_index, + OperationResultCode::DETAIL_SUCCESS)); } else { ErrorOr wifi_lan_result = StartWifiLanDiscovery(client, std::string(service_id)); @@ -1766,14 +1717,12 @@ P2pClusterPcpHandler::UpdateDiscoveryOptionsImpl( LOG(WARNING) << "UpdateDiscoveryOptionsImpl: unable to restart " "wifi lan scanning"; } - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, WIFI_LAN, update_index, - wifi_lan_result.has_error() - ? wifi_lan_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, WIFI_LAN, update_index, + wifi_lan_result.has_error() + ? wifi_lan_result.error().operation_result_code().value() + : OperationResultCode::DETAIL_SUCCESS)); } } if (restarted_mediums.empty() && should_start_discovery) { @@ -1946,8 +1895,7 @@ void P2pClusterPcpHandler::StartBluetoothDiscoveryWithPause( ClientProxy* client, const std::string& service_id, const DiscoveryOptions& discovery_options, std::vector& mediums_started_successfully, - std::vector& - operation_result_with_mediums, + std::vector& operation_result_with_mediums, int update_index) { if (bluetooth_radio_.IsEnabled()) { if (ble_medium_.IsExtendedAdvertisementsAvailable() && @@ -1965,16 +1913,12 @@ void P2pClusterPcpHandler::StartBluetoothDiscoveryWithPause( bluetooth_classic_client_id_to_service_id_map_.insert( {client->GetClientId(), service_id}); } - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, BLUETOOTH, update_index, - bluetooth_result.has_error() - ? bluetooth_result.error() - .operation_result_code() - .value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, BLUETOOTH, update_index, + bluetooth_result.has_error() + ? bluetooth_result.error().operation_result_code().value() + : OperationResultCode::DETAIL_SUCCESS)); } else { LOG(INFO) << "Pause bluetooth discovery for service id : " << service_id; @@ -1992,14 +1936,12 @@ void P2pClusterPcpHandler::StartBluetoothDiscoveryWithPause( bluetooth_classic_client_id_to_service_id_map_.insert( {client->GetClientId(), service_id}); } - std::unique_ptr - operation_result_with_medium = - GetOperationResultWithMediumByResultCode( - client, BLUETOOTH, update_index, - bluetooth_result.has_error() - ? bluetooth_result.error().operation_result_code().value() - : OperationResultCode::DETAIL_SUCCESS); - operation_result_with_mediums.push_back(*operation_result_with_medium); + operation_result_with_mediums.push_back( + GetOperationResultWithMediumByResultCode( + client, BLUETOOTH, update_index, + bluetooth_result.has_error() + ? bluetooth_result.error().operation_result_code().value() + : OperationResultCode::DETAIL_SUCCESS)); } } else { LOG(WARNING) << "Ignore to discover on bluetooth for service id: " @@ -2015,9 +1957,10 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::BluetoothConnectImpl( << endpoint->endpoint_id << ") over Bluetooth Classic."; BluetoothDevice& device = endpoint->bluetooth_device; + std::shared_ptr cancellation_flag = + client->GetCancellationFlag(endpoint->endpoint_id); ErrorOr bluetooth_socket_result = bluetooth_medium_.Connect( - device, endpoint->service_id, - client->GetCancellationFlag(endpoint->endpoint_id)); + device, endpoint->service_id, cancellation_flag.get()); if (bluetooth_socket_result.has_error()) { LOG(ERROR) << "In BluetoothConnectImpl(), failed to connect to Bluetooth device " @@ -2386,15 +2329,17 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::BleConnectImpl( << " is attempting to connect to (" << peripheral.ToReadableString() << ") over BLE."; + std::shared_ptr cancellation_flag = + client->GetCancellationFlag(endpoint->endpoint_id); + if (NearbyFlags::GetInstance().GetBoolFlag( config_package_nearby::nearby_connections_feature::kEnableBleL2cap) && peripheral.GetPsm() != mediums::BleAdvertisementHeader::kDefaultPsmValue) { if (refactor_ble_l2cap) { ErrorOr> ble_l2cap_socket_result = - ble_medium_.ConnectOverL2cap2( - endpoint->service_id, peripheral, - client->GetCancellationFlag(endpoint->endpoint_id)); + ble_medium_.ConnectOverL2cap2(endpoint->service_id, peripheral, + cancellation_flag.get()); if (!ble_l2cap_socket_result.has_error()) { LOG(INFO) << "In BleV2ConnectImpl(), connected to Ble L2CAP device " << absl::BytesToHexString(peripheral.GetId().data()) @@ -2416,9 +2361,8 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::BleConnectImpl( } } else { ErrorOr ble_l2cap_socket_result = - ble_medium_.ConnectOverL2cap( - endpoint->service_id, peripheral, - client->GetCancellationFlag(endpoint->endpoint_id)); + ble_medium_.ConnectOverL2cap(endpoint->service_id, peripheral, + cancellation_flag.get()); if (!ble_l2cap_socket_result.has_error()) { LOG(INFO) << "In BleConnectImpl(), connected to Ble L2CAP device " << absl::BytesToHexString(peripheral.GetId().data()) @@ -2444,9 +2388,8 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::BleConnectImpl( std::unique_ptr channel = nullptr; if (refactor_ble_l2cap) { ErrorOr> ble_socket_result = - ble_medium_.Connect2( - endpoint->service_id, peripheral, - client->GetCancellationFlag(endpoint->endpoint_id)); + ble_medium_.Connect2(endpoint->service_id, peripheral, + cancellation_flag.get()); if (ble_socket_result.has_error()) { LOG(ERROR) << "In BleConnectImpl(), failed to connect to BLE device " << absl::BytesToHexString(peripheral.GetId().data()) @@ -2461,9 +2404,8 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::BleConnectImpl( endpoint->service_id, /*channel_name=*/endpoint->endpoint_id, std::move(ble_socket_result.value())); } else { - ErrorOr ble_socket_result = - ble_medium_.Connect(endpoint->service_id, peripheral, - client->GetCancellationFlag(endpoint->endpoint_id)); + ErrorOr ble_socket_result = ble_medium_.Connect( + endpoint->service_id, peripheral, cancellation_flag.get()); if (ble_socket_result.has_error()) { LOG(ERROR) << "In BleConnectImpl(), failed to connect to BLE device " << absl::BytesToHexString(peripheral.GetId().data()) @@ -2737,9 +2679,10 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::AwdlConnectImpl( LOG(INFO) << "Client " << client->GetClientId() << " is attempting to connect to endpoint(id=" << endpoint->endpoint_id << ") over Awdl."; - ErrorOr socket_result = - awdl_medium_.Connect(endpoint->service_id, endpoint->service_info, - client->GetCancellationFlag(endpoint->endpoint_id)); + std::shared_ptr cancellation_flag = + client->GetCancellationFlag(endpoint->endpoint_id); + ErrorOr socket_result = awdl_medium_.Connect( + endpoint->service_id, endpoint->service_info, cancellation_flag.get()); if (socket_result.has_error()) { LOG(ERROR) << "In AwdlConnectImpl(), failed to connect to service " << endpoint->service_info.GetServiceName() @@ -2773,9 +2716,10 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::WifiLanConnectImpl( LOG(INFO) << "Client " << client->GetClientId() << " is attempting to connect to endpoint(id=" << endpoint->endpoint_id << ") over WifiLan."; + std::shared_ptr cancellation_flag = + client->GetCancellationFlag(endpoint->endpoint_id); ErrorOr socket_result = wifi_lan_medium_.Connect( - endpoint->service_id, endpoint->service_info, - client->GetCancellationFlag(endpoint->endpoint_id)); + endpoint->service_id, endpoint->service_info, cancellation_flag.get()); if (socket_result.has_error()) { LOG(ERROR) << "In WifiLanConnectImpl(), failed to connect to service " << endpoint->service_info.GetServiceName() diff --git a/connections/implementation/p2p_cluster_pcp_handler.h b/connections/implementation/p2p_cluster_pcp_handler.h index a3ac0891..d5d2c345 100644 --- a/connections/implementation/p2p_cluster_pcp_handler.h +++ b/connections/implementation/p2p_cluster_pcp_handler.h @@ -25,6 +25,7 @@ #include "absl/strings/string_view.h" #include "connections/advertising_options.h" #include "connections/discovery_options.h" +#include "connections/implementation/analytics/operation_result_with_medium.h" #include "connections/implementation/base_pcp_handler.h" #include "connections/implementation/ble_advertisement.h" #include "connections/implementation/bluetooth_device_name.h" @@ -55,12 +56,7 @@ #include "internal/platform/bluetooth_classic.h" #include "internal/platform/nsd_service_info.h" #include "internal/platform/wifi_lan.h" -#ifdef NO_WEBRTC -#include "connections/implementation/mediums/webrtc_socket_stub.h" -#include "connections/implementation/mediums/webrtc_stub.h" -#else #include "connections/implementation/mediums/webrtc.h" -#endif #include "connections/implementation/pcp.h" #include "connections/implementation/wifi_lan_service_info.h" #include "internal/platform/byte_array.h" @@ -212,8 +208,8 @@ class P2pClusterPcpHandler : public BasePcpHandler { ClientProxy* client, const std::string& service_id, const DiscoveryOptions& discovery_options, std::vector& mediums_started_successfully, - std::vector& operation_result_with_mediums, + std::vector& + operation_result_with_mediums, int update_index); BasePcpHandler::ConnectImplResult BluetoothConnectImpl( ClientProxy* client, BluetoothEndpoint* endpoint); diff --git a/connections/implementation/p2p_cluster_pcp_handler_test.cc b/connections/implementation/p2p_cluster_pcp_handler_test.cc index 956856a7..5f6c813e 100644 --- a/connections/implementation/p2p_cluster_pcp_handler_test.cc +++ b/connections/implementation/p2p_cluster_pcp_handler_test.cc @@ -88,11 +88,15 @@ class P2pClusterPcpHandlerTest : public testing::Test { LOG(INFO) << "SetUp: begin"; NearbyFlags::GetInstance().OverrideBoolFlagValue( config_package_nearby::nearby_connections_feature::kEnableAwdl, true); - SetBleExtendedAdvertisementsAvailable(true); + SetBleExtendedAdvertisementsAvailable(false); + } + + void TearDown() override { + NearbyFlags::GetInstance().ResetOverridedValues(); } void SetBleExtendedAdvertisementsAvailable(bool available) { - env_.SetBleExtendedAdvertisementsAvailable(false); + env_.SetBleExtendedAdvertisementsAvailable(available); } AdvertisingOptions GetBluetoothOnlyAdvertisingOptions() { @@ -141,6 +145,8 @@ class P2pClusterPcpHandlerTest : public testing::Test { return ByteArray(reinterpret_cast(bytes), 6); } + void RunCanConnectHelper(BooleanMediumSelector selector); + ClientProxy client_a_; ClientProxy client_b_; ClientProxy client_c_; @@ -148,6 +154,130 @@ class P2pClusterPcpHandlerTest : public testing::Test { MediumEnvironment& env_{MediumEnvironment::Instance()}; }; +void P2pClusterPcpHandlerTest::RunCanConnectHelper( + BooleanMediumSelector selector) { + env_.Start(); + std::string endpoint_name_a{"endpoint_name"}; + Mediums mediums_a; + Mediums mediums_b; + BluetoothRadio& radio_a = mediums_a.GetBluetoothRadio(); + BluetoothRadio& radio_b = mediums_b.GetBluetoothRadio(); + radio_a.GetBluetoothAdapter().SetName("BT Device A"); + radio_b.GetBluetoothAdapter().SetName("BT Device B"); + EndpointChannelManager ecm_a; + EndpointChannelManager ecm_b; + EndpointManager em_a(&ecm_a); + EndpointManager em_b(&ecm_b); + BwuManager bwu_a(mediums_a, em_a, ecm_a, {}, + {.allow_upgrade_to = {.bluetooth = true}}); + BwuManager bwu_b(mediums_b, em_b, ecm_b, {}, + {.allow_upgrade_to = {.bluetooth = true}}); + InjectedBluetoothDeviceStore ibds_a; + InjectedBluetoothDeviceStore ibds_b; + P2pClusterPcpHandler handler_a(&mediums_a, &em_a, &ecm_a, &bwu_a, ibds_a); + P2pClusterPcpHandler handler_b(&mediums_b, &em_b, &ecm_b, &bwu_b, ibds_b); + CountDownLatch discover_latch(1); + CountDownLatch connect_latch(2); + struct DiscoveredInfo { + std::string endpoint_id; + ByteArray endpoint_info; + std::string service_id; + } discovered; + + // Build options locally using passed selector! + AdvertisingOptions advertising_options = {{Strategy::kP2pCluster, selector}}; + DiscoveryOptions discovery_options = {{Strategy::kP2pCluster, selector}}; + ConnectionOptions connection_options = {{Strategy::kP2pCluster, selector}}; + + EXPECT_EQ( + handler_a.StartAdvertising( + &client_a_, service_id_, advertising_options, + { + .endpoint_info = ByteArray{endpoint_name_a}, + .listener = + { + .initiated_cb = + [&connect_latch](const std::string& endpoint_id, + const ConnectionResponseInfo& info) { + LOG(INFO) + << "StartAdvertising: initiated_cb called"; + connect_latch.CountDown(); + }, + }, + }), + Status{Status::kSuccess}); + EXPECT_EQ(handler_b.StartDiscovery( + &client_b_, service_id_, discovery_options, + { + .endpoint_found_cb = + [&discover_latch, &discovered]( + const std::string& endpoint_id, + const ByteArray& endpoint_info, + const std::string& service_id) { + LOG(INFO) << "Device discovered: id=" << endpoint_id + << ", endpoint_info=" + << std::string{endpoint_info}; + discovered = { + .endpoint_id = endpoint_id, + .endpoint_info = endpoint_info, + .service_id = service_id, + }; + discover_latch.CountDown(); + }, + }), + Status{Status::kSuccess}); + + EXPECT_TRUE(discover_latch.Await(absl::Milliseconds(1000)).result()); + EXPECT_EQ(endpoint_name_a, std::string{discovered.endpoint_info}); + + const std::string kBssid = "34:36:3B:C7:8C:71"; + const std::int32_t kFreq = 5200; + + connection_options.connection_info.supports_5_ghz = true; + connection_options.connection_info.bssid = kBssid; + connection_options.connection_info.ap_frequency = kFreq; + + client_b_.AddCancellationFlag(discovered.endpoint_id); + handler_b.RequestConnection( + &client_b_, discovered.endpoint_id, + {.endpoint_info = discovered.endpoint_info, + .listener = + { + .initiated_cb = + [&connect_latch](const std::string& endpoint_id, + const ConnectionResponseInfo& info) { + LOG(INFO) << "RequestConnection: initiated_cb called"; + connect_latch.CountDown(); + }, + }}, + connection_options); + std::string client_b_local_endpoint = client_b_.GetLocalEndpointId(); + + EXPECT_TRUE(connect_latch.Await(absl::Milliseconds(1000)).result()); + EXPECT_TRUE(client_b_.Is5GHzSupported(discovered.endpoint_id)); + EXPECT_EQ(client_b_.GetBssid(discovered.endpoint_id), kBssid); + EXPECT_EQ(client_b_.GetApFrequency(discovered.endpoint_id), kFreq); + // When connection is established, EndpointManager will setup KeepAliveManager + // loop. When it fails, the connection will be dismantled. Since this a unit + // test, KeepAliveManager won't be really up. The disconnection may happen + // before the following check, which cause the check fail. So we check the + // connection status first. + if (client_b_.IsConnectedToEndpoint(discovered.endpoint_id)) { + EXPECT_EQ(client_a_.Is5GHzSupported(client_b_local_endpoint), + mediums_b.GetWifi().GetCapability().supports_5_ghz); + EXPECT_EQ(client_a_.GetBssid(client_b_local_endpoint), + mediums_b.GetWifi().GetInformation().bssid); + EXPECT_EQ(client_a_.GetApFrequency(client_b_local_endpoint), + mediums_b.GetWifi().GetInformation().ap_frequency); + } + + handler_a.StopAdvertising(&client_a_); + handler_b.StopDiscovery(&client_b_); + bwu_a.Shutdown(); + bwu_b.Shutdown(); + env_.Stop(); +} + TEST_F(P2pClusterPcpHandlerTest, NoBluetoothDiscoveryWhenRadioIsOff) { env_.Start(); Mediums mediums; @@ -231,7 +361,8 @@ TEST_F(P2pClusterPcpHandlerTest, } class P2pClusterPcpHandlerTestWithParam - : public testing::TestWithParam { + : public P2pClusterPcpHandlerTest, + public ::testing::WithParamInterface { protected: void SetUp() override { LOG(INFO) << "SetUp: begin"; @@ -264,9 +395,6 @@ class P2pClusterPcpHandlerTestWithParam LOG(INFO) << "SetUp: end"; } - ClientProxy client_a_; - ClientProxy client_b_; - std::string service_id_{"service"}; ConnectionOptions connection_options_{ { Strategy::kP2pCluster, @@ -285,7 +413,6 @@ class P2pClusterPcpHandlerTestWithParam GetParam(), }, }; - MediumEnvironment& env_{MediumEnvironment::Instance()}; }; TEST_P(P2pClusterPcpHandlerTestWithParam, CanConstructOne) { @@ -900,128 +1027,21 @@ TEST_P(P2pClusterPcpHandlerTestWithParam, } TEST_P(P2pClusterPcpHandlerTestWithParam, CanConnect) { - env_.Start(); - std::string endpoint_name_a{"endpoint_name"}; - Mediums mediums_a; - Mediums mediums_b; - BluetoothRadio& radio_a = mediums_a.GetBluetoothRadio(); - BluetoothRadio& radio_b = mediums_b.GetBluetoothRadio(); - radio_a.GetBluetoothAdapter().SetName("BT Device A"); - radio_b.GetBluetoothAdapter().SetName("BT Device B"); - EndpointChannelManager ecm_a; - EndpointChannelManager ecm_b; - EndpointManager em_a(&ecm_a); - EndpointManager em_b(&ecm_b); - BwuManager bwu_a(mediums_a, em_a, ecm_a, {}, - {.allow_upgrade_to = {.bluetooth = true}}); - BwuManager bwu_b(mediums_b, em_b, ecm_b, {}, - {.allow_upgrade_to = {.bluetooth = true}}); - InjectedBluetoothDeviceStore ibds_a; - InjectedBluetoothDeviceStore ibds_b; - P2pClusterPcpHandler handler_a(&mediums_a, &em_a, &ecm_a, &bwu_a, ibds_a); - P2pClusterPcpHandler handler_b(&mediums_b, &em_b, &ecm_b, &bwu_b, ibds_b); - CountDownLatch discover_latch(1); - CountDownLatch connect_latch(2); - struct DiscoveredInfo { - std::string endpoint_id; - ByteArray endpoint_info; - std::string service_id; - } discovered; - EXPECT_EQ( - handler_a.StartAdvertising( - &client_a_, service_id_, advertising_options_, - { - .endpoint_info = ByteArray{endpoint_name_a}, - .listener = - { - .initiated_cb = - [&connect_latch](const std::string& endpoint_id, - const ConnectionResponseInfo& info) { - LOG(INFO) - << "StartAdvertising: initiated_cb called"; - connect_latch.CountDown(); - }, - }, - }), - Status{Status::kSuccess}); - EXPECT_EQ(handler_b.StartDiscovery( - &client_b_, service_id_, discovery_options_, - { - .endpoint_found_cb = - [&discover_latch, &discovered]( - const std::string& endpoint_id, - const ByteArray& endpoint_info, - const std::string& service_id) { - LOG(INFO) << "Device discovered: id=" << endpoint_id - << ", endpoint_info=" - << std::string{endpoint_info}; - discovered = { - .endpoint_id = endpoint_id, - .endpoint_info = endpoint_info, - .service_id = service_id, - }; - discover_latch.CountDown(); - }, - }), - Status{Status::kSuccess}); - - EXPECT_TRUE(discover_latch.Await(absl::Milliseconds(1000)).result()); - EXPECT_EQ(endpoint_name_a, std::string{discovered.endpoint_info}); - - const std::string kBssid = "34:36:3B:C7:8C:71"; - const std::int32_t kFreq = 5200; - - connection_options_.connection_info.supports_5_ghz = true; - connection_options_.connection_info.bssid = kBssid; - connection_options_.connection_info.ap_frequency = kFreq; - - client_b_.AddCancellationFlag(discovered.endpoint_id); - handler_b.RequestConnection( - &client_b_, discovered.endpoint_id, - {.endpoint_info = discovered.endpoint_info, - .listener = - { - .initiated_cb = - [&connect_latch](const std::string& endpoint_id, - const ConnectionResponseInfo& info) { - LOG(INFO) << "RequestConnection: initiated_cb called"; - connect_latch.CountDown(); - }, - }}, - connection_options_); - std::string client_b_local_endpoint = client_b_.GetLocalEndpointId(); - - EXPECT_TRUE(connect_latch.Await(absl::Milliseconds(1000)).result()); - EXPECT_TRUE(client_b_.Is5GHzSupported(discovered.endpoint_id)); - EXPECT_EQ(client_b_.GetBssid(discovered.endpoint_id), kBssid); - EXPECT_EQ(client_b_.GetApFrequency(discovered.endpoint_id), kFreq); - // When connection is established, EndpointManager will setup KeepAliveManager - // loop. When it fails, the connection will be dismantled. Since this a unit - // test, KeepAliveManager won't be really up. The disconnection may happen - // before the following check, which cause the check fail. So we check the - // connection status first. - if (client_b_.IsConnectedToEndpoint(discovered.endpoint_id)) { - EXPECT_EQ(client_a_.Is5GHzSupported(client_b_local_endpoint), - mediums_b.GetWifi().GetCapability().supports_5_ghz); - EXPECT_EQ(client_a_.GetBssid(client_b_local_endpoint), - mediums_b.GetWifi().GetInformation().bssid); - EXPECT_EQ(client_a_.GetApFrequency(client_b_local_endpoint), - mediums_b.GetWifi().GetInformation().ap_frequency); - } - - handler_a.StopAdvertising(&client_a_); - handler_b.StopDiscovery(&client_b_); - bwu_a.Shutdown(); - bwu_b.Shutdown(); - env_.Stop(); + RunCanConnectHelper(GetParam()); } TEST_P(P2pClusterPcpHandlerTestWithParam, CanConnectWithDctEnabled) { env_.Start(); + // DCT advertisement truncates the device name to 7 bytes. + // "Test device" (11 bytes) -> "Test de" (7 bytes). + // The endpoint info is constructed by advertisements::BuildEndpointInfo which + // adds some overhead. + // For DCT, it seems to be 18 bytes prefix + truncated device name. + // 18 + 7 = 25 bytes. ByteArray endpoint_info_a{ - "\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b" - "\x54\x65\x73\x74\x20\x64\x65\x76\x69\x63\x65", - 29}; + "\x22\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x07" + "Test de", + 25}; ClientProxy client_a; ClientProxy client_b; @@ -1783,5 +1803,47 @@ INSTANTIATE_TEST_SUITE_P(ParametrisedPcpHandlerTest, P2pClusterPcpHandlerTestWithParam, ::testing::ValuesIn(kTestCases)); +TEST_F(P2pClusterPcpHandlerTest, BleConnect_L2cap_Refactor) { + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_connections_feature::kEnableBleL2cap, true); + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_connections_feature::kRefactorBleL2cap, + true); + + RunCanConnectHelper({.ble = true}); +} + +TEST_F(P2pClusterPcpHandlerTest, BleConnect_NoL2cap_Refactor) { + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_connections_feature::kEnableBleL2cap, + false); + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_connections_feature::kRefactorBleL2cap, + true); + + RunCanConnectHelper({.ble = true}); +} + +TEST_F(P2pClusterPcpHandlerTest, BleConnect_NoL2cap_NoRefactor) { + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_connections_feature::kEnableBleL2cap, + false); + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_connections_feature::kRefactorBleL2cap, + false); + + RunCanConnectHelper({.ble = true}); +} + +TEST_F(P2pClusterPcpHandlerTest, BleConnect_L2cap_NoRefactor) { + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_connections_feature::kEnableBleL2cap, true); + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_connections_feature::kRefactorBleL2cap, + false); + + RunCanConnectHelper({.ble = true}); +} + } // namespace } // namespace nearby::connections diff --git a/connections/implementation/payload_manager.cc b/connections/implementation/payload_manager.cc index 6864f312..de2ed310 100644 --- a/connections/implementation/payload_manager.cc +++ b/connections/implementation/payload_manager.cc @@ -27,17 +27,15 @@ #include "absl/functional/any_invocable.h" #include "absl/functional/bind_front.h" #include "absl/strings/str_cat.h" -#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "absl/time/time.h" -#include "connections/implementation/analytics/packet_meta_data.h" -#include "connections/implementation/analytics/throughput_recorder.h" +#include "connections/implementation/analytics/analytics_recorder.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel_manager.h" #include "connections/implementation/endpoint_manager.h" #include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/implementation/internal_payload.h" #include "connections/implementation/internal_payload_factory.h" -#include "connections/implementation/proto/offline_wire_formats.pb.h" #include "connections/listeners.h" #include "connections/medium_selector.h" #include "connections/payload.h" @@ -53,10 +51,8 @@ #include "internal/platform/logging.h" #include "internal/platform/mutex_lock.h" #include "internal/platform/single_thread_executor.h" -#include "proto/connections_enums.pb.h" -namespace nearby { -namespace connections { +namespace nearby::connections { namespace { using ::location::nearby::connections::OfflineFrame; @@ -65,23 +61,62 @@ using ::location::nearby::connections::V1Frame; using ::location::nearby::proto::connections::Medium; using ::location::nearby::proto::connections::OperationResultCode; using ::location::nearby::proto::connections::PayloadStatus; -using PacketMetaData = ::nearby::analytics::PacketMetaData; -using ::nearby::analytics::ThroughputRecorderContainer; -using PayloadDirection = ::nearby::connections::PayloadDirection; +using ::nearby::analytics::AnalyticsRecorder; constexpr absl::Duration kMinTransferUpdateInterval = absl::Milliseconds(50); + +std::string EndpointIdsToString(const std::vector& endpoint_ids) { + return absl::StrCat(endpoint_ids.size(), ":", + absl::StrJoin(endpoint_ids, ",")); +} + +PayloadStatus ControlMessageEventToPayloadStatus( + PayloadTransferFrame::ControlMessage::EventType event) { + switch (event) { + case PayloadTransferFrame::ControlMessage::PAYLOAD_ERROR: + return PayloadStatus::REMOTE_ERROR; + case PayloadTransferFrame::ControlMessage::PAYLOAD_CANCELED: + return PayloadStatus::REMOTE_CANCELLATION; + default: + VLOG(1) << "PayloadManager: unknown event=" << event; + return PayloadStatus::UNKNOWN_PAYLOAD_STATUS; + } +} + +OperationResultCode ControlMessageEventToOperationResultCode( + PayloadTransferFrame::ControlMessage::EventType event) { + switch (event) { + case PayloadTransferFrame::ControlMessage::PAYLOAD_ERROR: + return OperationResultCode::NEARBY_GENERIC_REMOTE_REPORT_PAYLOADS_ERROR; + case PayloadTransferFrame::ControlMessage::PAYLOAD_CANCELED: + return OperationResultCode::CLIENT_CANCELLATION_REMOTE_CANCEL_PAYLOAD; + default: + VLOG(1) << "PayloadManager: unknown event=" << event; + return OperationResultCode::DETAIL_UNKNOWN; + } +} + +PayloadProgressInfo::Status PayloadStatusToTransferUpdateStatus( + PayloadStatus status) { + switch (status) { + case PayloadStatus::LOCAL_CANCELLATION: + case PayloadStatus::REMOTE_CANCELLATION: + return PayloadProgressInfo::Status::kCanceled; + case PayloadStatus::SUCCESS: + return PayloadProgressInfo::Status::kSuccess; + default: + return PayloadProgressInfo::Status::kFailure; + } +} + } // namespace -bool PayloadManager::SendPayloadLoop( +int PayloadManager::SendPayloadLoop( ClientProxy* client, PendingPayload& pending_payload, PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t& next_chunk_offset, size_t resume_offset, int index) { - // in lieu of structured binding: - auto pair = GetAvailableAndUnavailableEndpoints(pending_payload); - const EndpointIds& available_endpoint_ids = - EndpointsToEndpointIds(pair.first); - const Endpoints& unavailable_endpoints = pair.second; - PacketMetaData packet_meta_data; + int64_t next_chunk_offset, size_t resume_offset, int index) { + auto [available_endpoint_ids, unavailable_endpoints] = + GetAvailableAndUnavailableEndpoints(pending_payload); // First, handle any non-available endpoints. for (const auto& endpoint : unavailable_endpoints) { @@ -97,7 +132,7 @@ bool PayloadManager::SendPayloadLoop( << pending_payload.GetInternalPayload()->GetId() << " after sending " << next_chunk_offset << " bytes because none of the endpoints are available anymore."; - return false; + return -1; } // Check if the payload has been cancelled by the client and, if so, @@ -110,13 +145,9 @@ bool PayloadManager::SendPayloadLoop( client, available_endpoint_ids, payload_header, next_chunk_offset, OperationResultCode::CLIENT_CANCELLATION_LOCAL_CANCEL_PAYLOAD, PayloadStatus::LOCAL_CANCELLATION); - return false; + return -1; } - // Update the current offsets for all endpoints still active for this - // payload. For the sake of accuracy, we update the pending payload here - // because it's after all payload terminating events are handled, but - // right before we actually start detaching the next chunk. if (next_chunk_offset == 0 && resume_offset > 0) { ExceptionOr real_offset = pending_payload.GetInternalPayload()->SkipToOffset(resume_offset); @@ -129,13 +160,17 @@ bool PayloadManager::SendPayloadLoop( payload_header, next_chunk_offset, OperationResultCode::IO_FILE_READING_ERROR, PayloadStatus::LOCAL_ERROR); - return false; + return -1; } VLOG(1) << "PayloadManager successfully skipped " << real_offset.GetResult() << " bytes on payload_id " << pending_payload.GetInternalPayload()->GetId(); next_chunk_offset = real_offset.GetResult(); } + // Update the current offsets for all endpoints still active for this + // payload. For the sake of accuracy, we update the pending payload here + // because it's after all payload terminating events are handled, but + // right before we actually start detaching the next chunk. for (const auto& endpoint_id : available_endpoint_ids) { pending_payload.SetOffsetForEndpoint(endpoint_id, next_chunk_offset); } @@ -143,23 +178,22 @@ bool PayloadManager::SendPayloadLoop( // This will block if there is no data to transfer. // It will resume when new data arrives, or if Close() is called. int chunk_size = GetOptimalChunkSize(available_endpoint_ids); - packet_meta_data.StartFileIo(); ByteArray next_chunk = pending_payload.GetInternalPayload()->DetachNextChunk(chunk_size); - packet_meta_data.StopFileIo(); - if (shutdown_.Get()) return false; + if (shutdown_.Get()) return -1; // Save chunk size. We'll need it after we move next_chunk. - auto next_chunk_size = next_chunk.size(); - if (!next_chunk_size && + size_t next_chunk_size = next_chunk.size(); + // If there are no more chunks, check if there should be more data to send. + if (next_chunk_size == 0 && pending_payload.GetInternalPayload()->GetTotalSize() > 0 && - pending_payload.GetInternalPayload()->GetTotalSize() < + pending_payload.GetInternalPayload()->GetTotalSize() > next_chunk_offset) { VLOG(1) << "Payload xfer failed: payload_id=" << pending_payload.GetInternalPayload()->GetId(); HandleFinishedOutgoingPayload( client, available_endpoint_ids, payload_header, next_chunk_offset, OperationResultCode::IO_FILE_READING_ERROR, PayloadStatus::LOCAL_ERROR); - return false; + return -1; } // Only need to handle outgoing data chunk offset, because the offset will be @@ -168,13 +202,14 @@ bool PayloadManager::SendPayloadLoop( // happened. PayloadTransferFrame::PayloadChunk payload_chunk(CreatePayloadChunk( next_chunk_offset - resume_offset, std::move(next_chunk), index)); - const EndpointIds& failed_endpoint_ids = endpoint_manager_->SendPayloadChunk( - payload_header, payload_chunk, available_endpoint_ids, packet_meta_data); + const std::vector& failed_endpoint_ids = + endpoint_manager_->SendPayloadChunk(payload_header, payload_chunk, + available_endpoint_ids); // Check whether at least one endpoint failed. if (!failed_endpoint_ids.empty()) { VLOG(1) << "Payload xfer: endpoints failed: payload_id=" << payload_header.id() << "; endpoint_ids={" - << ToString(failed_endpoint_ids) << "}", + << EndpointIdsToString(failed_endpoint_ids) << "}", HandleFinishedOutgoingPayload( client, failed_endpoint_ids, payload_header, next_chunk_offset, OperationResultCode::CONNECTIVITY_GENERIC_WRITING_CHANNEL_IO_ERROR, @@ -203,94 +238,36 @@ bool PayloadManager::SendPayloadLoop( payload_chunk.offset(), payload_chunk.body().size()); } } - VLOG(1) << "PayloadManager done sending chunk at offset " - << next_chunk_offset << " of payload_id=" - << pending_payload.GetInternalPayload()->GetId(); - next_chunk_offset += next_chunk_size; - if (!next_chunk_size) { + if (next_chunk_size == 0) { // That was the last chunk, we're outta here. VLOG(1) << "Payload xfer done: payload_id=" << pending_payload.GetInternalPayload()->GetId() << "; size=" << next_chunk_offset; - ThroughputRecorderContainer::GetInstance() - .GetTPRecorder(pending_payload.GetInternalPayload()->GetId(), - PayloadDirection::OUTGOING_PAYLOAD) - ->MarkAsSuccess(); - return false; + return -1; + } else { + VLOG(1) << "PayloadManager done sending chunk at offset " + << next_chunk_offset << " of payload_id=" + << pending_payload.GetInternalPayload()->GetId(); } } - return true; + return next_chunk_size; } -std::pair +std::pair, PayloadManager::Endpoints> PayloadManager::GetAvailableAndUnavailableEndpoints( const PendingPayload& pending_payload) { - Endpoints available; - Endpoints unavailable; + auto results = std::make_pair(std::vector(), + std::vector()); for (auto* endpoint_info : pending_payload.GetEndpoints()) { - if (endpoint_info->status.Get() == - PayloadManager::EndpointInfo::Status::kAvailable) { - available.push_back(endpoint_info); + if (endpoint_info->status.Get() == EndpointInfo::Status::kAvailable) { + results.first.push_back(endpoint_info->id); } else { - unavailable.push_back(endpoint_info); + results.second.push_back(endpoint_info); } } - return std::make_pair(std::move(available), std::move(unavailable)); -} - -PayloadManager::EndpointIds PayloadManager::EndpointsToEndpointIds( - const Endpoints& endpoints) { - EndpointIds endpoint_ids; - endpoint_ids.reserve(endpoints.size()); - for (const auto& item : endpoints) { - if (item) { - endpoint_ids.emplace_back(item->id); - } - } - return endpoint_ids; -} - -std::string PayloadManager::ToString(const Endpoints& endpoints) { - std::string endpoints_string = absl::StrCat(endpoints.size(), ": "); - bool first = true; - for (const auto& item : endpoints) { - if (first) { - absl::StrAppend(&endpoints_string, item->id); - first = false; - } else { - absl::StrAppend(&endpoints_string, ", ", item->id); - } - } - return endpoints_string; -} - -std::string PayloadManager::ToString(const EndpointIds& endpoint_ids) { - std::string endpoints_string = absl::StrCat(endpoint_ids.size(), ": "); - bool first = true; - for (const auto& id : endpoint_ids) { - if (first) { - absl::StrAppend(&endpoints_string, id); - first = false; - } else { - absl::StrAppend(&endpoints_string, ", ", id); - } - } - return endpoints_string; -} - -std::string PayloadManager::ToString(PayloadType type) { - switch (type) { - case PayloadType::kBytes: - return std::string("Bytes"); - case PayloadType::kStream: - return std::string("Stream"); - case PayloadType::kFile: - return std::string("File"); - case PayloadType::kUnknown: - return std::string("Unknown"); - } + return results; } std::string PayloadManager::ToString(EndpointInfo::Status status) { @@ -308,7 +285,7 @@ std::string PayloadManager::ToString(EndpointInfo::Status status) { // Creates and starts tracking a PendingPayload for this Payload. Payload::Id PayloadManager::CreateOutgoingPayload( - Payload payload, const EndpointIds& endpoint_ids) { + Payload payload, const std::vector& endpoint_ids) { ErrorOr> result = CreateOutgoingInternalPayload(std::move(payload)); if (result.has_error()) { @@ -368,7 +345,6 @@ void PayloadManager::DisconnectFromEndpointManager() { PayloadManager::~PayloadManager() { VLOG(1) << "PayloadManager: going down; self=" << this; - ThroughputRecorderContainer::GetInstance().Shutdown(); DisconnectFromEndpointManager(); CancelAllPayloads(); VLOG(1) << "PayloadManager: turn down payload executors; self=" << this; @@ -406,13 +382,14 @@ bool PayloadManager::NotifyShutdown() { } void PayloadManager::SendPayload(ClientProxy* client, - const EndpointIds& endpoint_ids, + const std::vector& endpoint_ids, Payload payload) { if (shutdown_.Get()) return; - VLOG(1) << "SendPayload: endpoint_ids={" << ToString(endpoint_ids) << "}"; + VLOG(1) << "SendPayload: endpoint_ids={" << EndpointIdsToString(endpoint_ids) + << "}"; // Before transfer to internal payload, retrieves the Payload size for // analytics. - std::int64_t payload_total_size; + int64_t payload_total_size; switch (payload.GetType()) { case connections::PayloadType::kBytes: payload_total_size = payload.AsBytes().size(); @@ -437,8 +414,7 @@ void PayloadManager::SendPayload(ClientProxy* client, OperationResultCode::NEARBY_GENERIC_OUTGOING_PAYLOAD_CREATION_FAILURE); VLOG(1) << "PayloadManager failed to determine the right executor for " "outgoing payload_id=" - << payload.GetId() - << ", payload_type=" << ToString(payload.GetType()); + << payload.GetId() << ", payload_type=" << payload.GetType(); return; } @@ -467,7 +443,7 @@ void PayloadManager::SendPayload(ClientProxy* client, NEARBY_GENERIC_OUTGOING_PAYLOAD_CREATION_FAILURE); VLOG(1) << "PayloadManager failed to create InternalPayload for outgoing " "payload_id=" - << payload_id << ", payload_type=" << ToString(payload_type) + << payload_id << ", payload_type=" << payload_type << ", aborting sendPayload()."; return; } @@ -482,16 +458,19 @@ void PayloadManager::SendPayload(ClientProxy* client, CreatePayloadHeader(*internal_payload, resume_offset)}; bool should_continue = true; - std::int64_t next_chunk_offset = 0; + int64_t next_chunk_offset = 0; int index = 0; - ThroughputRecorderContainer::GetInstance() - .GetTPRecorder(payload_id, PayloadDirection::OUTGOING_PAYLOAD) - ->Start(payload_type, PayloadDirection::OUTGOING_PAYLOAD); while (should_continue && !shutdown_.Get()) { - should_continue = - SendPayloadLoop(client, *pending_payload, payload_header, - next_chunk_offset, resume_offset, index); + int bytes_sent = SendPayloadLoop(client, *pending_payload, payload_header, + next_chunk_offset, resume_offset, index); + should_continue = (bytes_sent >= 0); + if (should_continue) { + if (next_chunk_offset == 0 && resume_offset > 0) { + next_chunk_offset = resume_offset; + } + next_chunk_offset += bytes_sent; + } index++; } @@ -502,8 +481,7 @@ void PayloadManager::SendPayload(ClientProxy* client, }); }); VLOG(1) << "PayloadManager: xfer scheduled: self=" << this - << "; payload_id=" << payload_id - << ", payload_type=" << ToString(payload_type); + << "; payload_id=" << payload_id << ", payload_type=" << payload_type; } PayloadManager::PendingPayloadHandle PayloadManager::GetPayload( @@ -535,8 +513,7 @@ Status PayloadManager::CancelPayload(ClientProxy* client, void PayloadManager::OnIncomingFrame(OfflineFrame& offline_frame, const std::string& from_endpoint_id, ClientProxy* to_client, - Medium current_medium, - PacketMetaData& packet_meta_data) { + Medium current_medium) { PayloadTransferFrame& frame = *offline_frame.mutable_v1()->mutable_payload_transfer(); @@ -567,8 +544,7 @@ void PayloadManager::OnIncomingFrame(OfflineFrame& offline_frame, ProcessControlPacket(to_client, from_endpoint_id, frame); break; case PayloadTransferFrame::DATA: - ProcessDataPacket(to_client, from_endpoint_id, frame, current_medium, - packet_meta_data); + ProcessDataPacket(to_client, from_endpoint_id, frame, current_medium); break; case PayloadTransferFrame::PAYLOAD_ACK: VLOG(1) << "[safe-to-disconnect][PAYLOAD_RECEIVED_ACK] sender " @@ -602,14 +578,14 @@ void PayloadManager::OnEndpointDisconnect(ClientProxy* client, pending_payloads_.ForEachPayload([&](PendingPayload* pending_payload) { auto endpoint_info = pending_payload->GetEndpoint(endpoint_id); if (!endpoint_info) return; - std::int64_t endpoint_offset = endpoint_info->offset; + int64_t endpoint_offset = endpoint_info->offset; // Stop tracking the endpoint for this payload. pending_payload->RemoveEndpoints({endpoint_id}); // |endpoint_info| is longer valid after calling // RemoveEndpoints. endpoint_info = nullptr; - std::int64_t payload_total_size = + int64_t payload_total_size = pending_payload->GetInternalPayload()->GetTotalSize(); // If no endpoints are left for this payload, close it. @@ -641,9 +617,8 @@ void PayloadManager::OnEndpointDisconnect(ClientProxy* client, default: payload_status = PayloadStatus::ENDPOINT_IO_ERROR; operation_result_code = - client->GetAnalyticsRecorder() - .GetChannelIoErrorResultCodeFromMedium( - client->GetConnectedMedium(endpoint_id)); + AnalyticsRecorder::GetChannelIoErrorResultCodeFromMedium( + client->GetConnectedMedium(endpoint_id)); break; } @@ -692,45 +667,6 @@ OperationResultCode PayloadManager::EndpointInfoStatusToOperationResultCode( } } -PayloadStatus PayloadManager::ControlMessageEventToPayloadStatus( - PayloadTransferFrame::ControlMessage::EventType event) { - switch (event) { - case PayloadTransferFrame::ControlMessage::PAYLOAD_ERROR: - return PayloadStatus::REMOTE_ERROR; - case PayloadTransferFrame::ControlMessage::PAYLOAD_CANCELED: - return PayloadStatus::REMOTE_CANCELLATION; - default: - VLOG(1) << "PayloadManager: unknown event=" << event; - return PayloadStatus::UNKNOWN_PAYLOAD_STATUS; - } -} - -OperationResultCode PayloadManager::ControlMessageEventToOperationResultCode( - PayloadTransferFrame::ControlMessage::EventType event) { - switch (event) { - case PayloadTransferFrame::ControlMessage::PAYLOAD_ERROR: - return OperationResultCode::NEARBY_GENERIC_REMOTE_REPORT_PAYLOADS_ERROR; - case PayloadTransferFrame::ControlMessage::PAYLOAD_CANCELED: - return OperationResultCode::CLIENT_CANCELLATION_REMOTE_CANCEL_PAYLOAD; - default: - VLOG(1) << "PayloadManager: unknown event=" << event; - return OperationResultCode::DETAIL_UNKNOWN; - } -} - -PayloadProgressInfo::Status PayloadManager::PayloadStatusToTransferUpdateStatus( - PayloadStatus status) { - switch (status) { - case PayloadStatus::LOCAL_CANCELLATION: - case PayloadStatus::REMOTE_CANCELLATION: - return PayloadProgressInfo::Status::kCanceled; - case PayloadStatus::SUCCESS: - return PayloadProgressInfo::Status::kSuccess; - default: - return PayloadProgressInfo::Status::kFailure; - } -} - SingleThreadExecutor* PayloadManager::GetOutgoingPayloadExecutor( PayloadType payload_type) { switch (payload_type) { @@ -745,7 +681,8 @@ SingleThreadExecutor* PayloadManager::GetOutgoingPayloadExecutor( } } -int PayloadManager::GetOptimalChunkSize(EndpointIds endpoint_ids) { +int PayloadManager::GetOptimalChunkSize( + const std::vector& endpoint_ids) { int minChunkSize = std::numeric_limits::max(); for (const auto& endpoint_id : endpoint_ids) { minChunkSize = std::min( @@ -777,8 +714,7 @@ PayloadTransferFrame::PayloadHeader PayloadManager::CreatePayloadHeader( } PayloadTransferFrame::PayloadChunk PayloadManager::CreatePayloadChunk( - std::int64_t payload_chunk_offset, ByteArray payload_chunk_body, - int index) { + int64_t payload_chunk_offset, ByteArray payload_chunk_body, int index) { PayloadTransferFrame::PayloadChunk payload_chunk; payload_chunk.set_offset(payload_chunk_offset); @@ -799,9 +735,8 @@ PayloadManager::CreateIncomingPayload(const PayloadTransferFrame& frame, const std::string& endpoint_id, const std::string& save_path) { ErrorOr> result = - CreateIncomingInternalPayload(frame, save_path.empty() - ? custom_save_path_ - : save_path); + CreateIncomingInternalPayload( + frame, save_path.empty() ? custom_save_path_ : save_path); if (result.has_error()) { return {result.error()}; } @@ -811,7 +746,8 @@ PayloadManager::CreateIncomingPayload(const PayloadTransferFrame& frame, pending_payloads_.StartTrackingPayload( payload_id, std::make_unique( - std::move(internal_payload), EndpointIds{endpoint_id}, true, + std::move(internal_payload), std::vector{endpoint_id}, + true, absl::bind_front(&PayloadManager::OnPendingPayloadDestroy, this))); return {pending_payloads_.GetPayload(payload_id)}; } @@ -819,10 +755,6 @@ PayloadManager::CreateIncomingPayload(const PayloadTransferFrame& frame, void PayloadManager::OnPendingPayloadDestroy(const PendingPayload* payload) { VLOG(1) << "PayloadManager: destroying " << payload->ToString() << " self=" << this; - ThroughputRecorderContainer::GetInstance().StopTPRecorder( - payload->GetId(), payload->IsIncoming() - ? PayloadDirection::INCOMING_PAYLOAD - : PayloadDirection::OUTGOING_PAYLOAD); if (payload->IsIncoming()) return; RunOnStatusUpdateThread( "~PendingPayload", @@ -830,9 +762,9 @@ void PayloadManager::OnPendingPayloadDestroy(const PendingPayload* payload) { } void PayloadManager::SendClientCallbacksForFinishedOutgoingPayload( - ClientProxy* client, const EndpointIds& finished_endpoint_ids, + ClientProxy* client, const std::vector& finished_endpoint_ids, const PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t num_bytes_successfully_transferred, PayloadStatus status, + int64_t num_bytes_successfully_transferred, PayloadStatus status, OperationResultCode operation_result_code) { RunOnStatusUpdateThread( "outgoing-payload-callbacks", @@ -846,8 +778,7 @@ void PayloadManager::SendClientCallbacksForFinishedOutgoingPayload( } PayloadProgressInfo update{ - payload_header.id(), - PayloadManager::PayloadStatusToTransferUpdateStatus(status), + payload_header.id(), PayloadStatusToTransferUpdateStatus(status), payload_header.total_size(), num_bytes_successfully_transferred}; for (const auto& endpoint_id : finished_endpoint_ids) { // Skip sending notifications if we have stopped tracking this @@ -864,9 +795,8 @@ void PayloadManager::SendClientCallbacksForFinishedOutgoingPayload( endpoint_id, payload_header.id(), status, (operation_result_code == OperationResultCode::DETAIL_UNKNOWN && status == PayloadStatus::ENDPOINT_IO_ERROR) - ? client->GetAnalyticsRecorder() - .GetChannelIoErrorResultCodeFromMedium( - client->GetConnectedMedium(endpoint_id)) + ? AnalyticsRecorder::GetChannelIoErrorResultCodeFromMedium( + client->GetConnectedMedium(endpoint_id)) : operation_result_code); } @@ -883,7 +813,7 @@ void PayloadManager::SendClientCallbacksForFinishedOutgoingPayload( void PayloadManager::SendClientCallbacksForFinishedIncomingPayload( ClientProxy* client, const std::string& endpoint_id, const PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t offset_bytes, PayloadStatus status, + int64_t offset_bytes, PayloadStatus status, OperationResultCode operation_result_code) { RunOnStatusUpdateThread( "incoming-payload-callbacks", @@ -898,10 +828,9 @@ void PayloadManager::SendClientCallbacksForFinishedIncomingPayload( // Unless we never started tracking this payload (meaning we // failed to even create the InternalPayload), notify the client // (and close it). - PayloadProgressInfo update{ - payload_header.id(), - PayloadManager::PayloadStatusToTransferUpdateStatus(status), - payload_header.total_size(), offset_bytes}; + PayloadProgressInfo update{payload_header.id(), + PayloadStatusToTransferUpdateStatus(status), + payload_header.total_size(), offset_bytes}; NotifyClientOfIncomingPayloadProgressInfo(client, endpoint_id, update); DestroyPendingPayload(payload_header.id()); @@ -912,9 +841,9 @@ void PayloadManager::SendClientCallbacksForFinishedIncomingPayload( } void PayloadManager::SendControlMessage( - const EndpointIds& endpoint_ids, + const std::vector& endpoint_ids, const PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t num_bytes_successfully_transferred, + int64_t num_bytes_successfully_transferred, PayloadTransferFrame::ControlMessage::EventType event_type) { PayloadTransferFrame::ControlMessage control_message; control_message.set_event(event_type); @@ -950,7 +879,7 @@ bool PayloadManager::WaitForReceivedAck( ClientProxy* client, const std::string& endpoint_id, PendingPayload& pending_payload, const PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t payload_chunk_offset, bool is_last_chunk) { + int64_t payload_chunk_offset, bool is_last_chunk) { if (!is_last_chunk || !IsPayloadReceivedAckEnabled(client, endpoint_id, pending_payload)) { return true; @@ -1053,9 +982,9 @@ bool PayloadManager::IsPayloadReceivedAckEnabled( } void PayloadManager::HandleFinishedOutgoingPayload( - ClientProxy* client, const EndpointIds& finished_endpoint_ids, + ClientProxy* client, const std::vector& finished_endpoint_ids, const PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t num_bytes_successfully_transferred, + int64_t num_bytes_successfully_transferred, OperationResultCode operation_result_code, PayloadStatus status) { // This call will destroy a pending payload. SendClientCallbacksForFinishedOutgoingPayload( @@ -1099,7 +1028,7 @@ void PayloadManager::HandleFinishedOutgoingPayload( void PayloadManager::HandleFinishedIncomingPayload( ClientProxy* client, const std::string& endpoint_id, const PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t offset_bytes, PayloadStatus status, + int64_t offset_bytes, PayloadStatus status, OperationResultCode operation_result_code) { SendClientCallbacksForFinishedIncomingPayload(client, endpoint_id, payload_header, offset_bytes, @@ -1125,11 +1054,9 @@ void PayloadManager::HandleFinishedIncomingPayload( void PayloadManager::HandleSuccessfulOutgoingChunk( ClientProxy* client, const std::string& endpoint_id, const PayloadTransferFrame::PayloadHeader& payload_header, - std::int32_t payload_chunk_flags, std::int64_t payload_chunk_offset, - std::int64_t payload_chunk_body_size) { - if (NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kEnablePayloadManagerToSkipChunkUpdate)) { + int32_t payload_chunk_flags, int64_t payload_chunk_offset, + int64_t payload_chunk_body_size) { + { MutexLock lock(&chunk_update_mutex_); ++outgoing_chunk_update_count_; } @@ -1144,9 +1071,7 @@ void PayloadManager::HandleSuccessfulOutgoingChunk( (payload_chunk_flags & PayloadTransferFrame::PayloadChunk::LAST_CHUNK) != 0; - if (NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kEnablePayloadManagerToSkipChunkUpdate)) { + { MutexLock lock(&chunk_update_mutex_); --outgoing_chunk_update_count_; if (payload_header.has_type() && @@ -1224,11 +1149,9 @@ void PayloadManager::DestroyPendingPayload(Payload::Id payload_id) { void PayloadManager::HandleSuccessfulIncomingChunk( ClientProxy* client, const std::string& endpoint_id, const PayloadTransferFrame::PayloadHeader& payload_header, - std::int32_t payload_chunk_flags, std::int64_t payload_chunk_offset, - std::int64_t payload_chunk_body_size) { - if (NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kEnablePayloadManagerToSkipChunkUpdate)) { + int32_t payload_chunk_flags, int64_t payload_chunk_offset, + int64_t payload_chunk_body_size) { + { MutexLock lock(&chunk_update_mutex_); ++incoming_chunk_update_count_; } @@ -1242,9 +1165,7 @@ void PayloadManager::HandleSuccessfulIncomingChunk( (payload_chunk_flags & PayloadTransferFrame::PayloadChunk::LAST_CHUNK) != 0; - if (NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_connections_feature:: - kEnablePayloadManagerToSkipChunkUpdate)) { + { MutexLock lock(&chunk_update_mutex_); --incoming_chunk_update_count_; if (payload_header.has_type() && @@ -1308,8 +1229,7 @@ void PayloadManager::HandleSuccessfulIncomingChunk( // @EndpointManagerDataPool void PayloadManager::ProcessDataPacket( ClientProxy* to_client, const std::string& from_endpoint_id, - PayloadTransferFrame& payload_transfer_frame, Medium medium, - PacketMetaData& packet_meta_data) { + PayloadTransferFrame& payload_transfer_frame, Medium medium) { PayloadTransferFrame::PayloadHeader& payload_header = *payload_transfer_frame.mutable_payload_header(); PayloadTransferFrame::PayloadChunk& payload_chunk = @@ -1330,11 +1250,6 @@ void PayloadManager::ProcessDataPacket( Payload::Id payload_id = payload_header.id(); PendingPayloadHandle pending_payload; if (payload_chunk.offset() == 0) { - ThroughputRecorderContainer::GetInstance() - .GetTPRecorder(payload_id, PayloadDirection::INCOMING_PAYLOAD) - ->Start((PayloadType)payload_header.type(), - PayloadDirection::INCOMING_PAYLOAD); - packet_meta_data.Reset(); RunOnStatusUpdateThread( "process-data-packet", [to_client, from_endpoint_id, payload_header, this]() RUN_ON_PAYLOAD_STATUS_UPDATE_THREAD() { @@ -1419,9 +1334,8 @@ void PayloadManager::ProcessDataPacket( payload_chunk.offset()); // Save size of packet before we move it. - std::int64_t payload_body_size = payload_chunk.body().size(); + int64_t payload_body_size = payload_chunk.body().size(); - packet_meta_data.StartFileIo(); if (pending_payload->GetInternalPayload() ->AttachNextChunk(payload_chunk.body()) .Raised()) { @@ -1433,7 +1347,6 @@ void PayloadManager::ProcessDataPacket( PayloadStatus::LOCAL_ERROR, OperationResultCode::IO_FILE_WRITING_ERROR); return; } - packet_meta_data.StopFileIo(); bool is_last_chunk = (payload_chunk.flags() & PayloadTransferFrame::PayloadChunk::LAST_CHUNK) != 0; SendPayloadReceivedAck(to_client, *pending_payload, from_endpoint_id, @@ -1442,15 +1355,6 @@ void PayloadManager::ProcessDataPacket( HandleSuccessfulIncomingChunk(to_client, from_endpoint_id, payload_header, payload_chunk.flags(), payload_chunk.offset(), payload_body_size); - - ThroughputRecorderContainer::GetInstance() - .GetTPRecorder(payload_header.id(), PayloadDirection::INCOMING_PAYLOAD) - ->OnFrameReceived(medium, packet_meta_data); - if (is_last_chunk) { - ThroughputRecorderContainer::GetInstance() - .GetTPRecorder(payload_header.id(), PayloadDirection::INCOMING_PAYLOAD) - ->MarkAsSuccess(); - } } // @EndpointManagerDataPool @@ -1546,18 +1450,18 @@ void PayloadManager::NotifyClientOfIncomingPayloadProgressInfo( } void PayloadManager::RecordPayloadStartedAnalytics( - ClientProxy* client, const EndpointIds& endpoint_ids, - std::int64_t payload_id, PayloadType payload_type, std::int64_t offset, - std::int64_t total_size) { + ClientProxy* client, const std::vector& endpoint_ids, + int64_t payload_id, PayloadType payload_type, int64_t offset, + int64_t total_size) { client->GetAnalyticsRecorder().OnOutgoingPayloadStarted( endpoint_ids, payload_id, payload_type, total_size == -1 ? -1 : total_size - offset); } void PayloadManager::RecordInvalidPayloadAnalytics( - ClientProxy* client, const EndpointIds& endpoint_ids, - std::int64_t payload_id, PayloadType payload_type, std::int64_t offset, - std::int64_t total_size, OperationResultCode operation_result_code) { + ClientProxy* client, const std::vector& endpoint_ids, + int64_t payload_id, PayloadType payload_type, int64_t offset, + int64_t total_size, OperationResultCode operation_result_code) { RecordPayloadStartedAnalytics(client, endpoint_ids, payload_id, payload_type, offset, total_size); @@ -1635,8 +1539,8 @@ bool PayloadManager::EndpointInfo::IsEndpointAvailable( PayloadManager::PendingPayload::PendingPayload( std::unique_ptr internal_payload, - const EndpointIds& endpoint_ids, bool is_incoming, - DestroyCallback destroy_callback) + const std::vector& endpoint_ids, bool is_incoming, + absl::AnyInvocable destroy_callback) : is_incoming_(is_incoming), internal_payload_(std::move(internal_payload)), destroy_callback_(std::move(destroy_callback)) { @@ -1702,7 +1606,7 @@ PayloadManager::EndpointInfo* PayloadManager::PendingPayload::GetEndpoint( } void PayloadManager::PendingPayload::RemoveEndpoints( - const EndpointIds& endpoint_ids) { + const std::vector& endpoint_ids) { MutexLock lock(&mutex_); for (const auto& id : endpoint_ids) { @@ -1722,7 +1626,7 @@ void PayloadManager::PendingPayload::SetEndpointStatusFromControlMessage( } void PayloadManager::PendingPayload::SetOffsetForEndpoint( - const std::string& endpoint_id, std::int64_t offset) { + const std::string& endpoint_id, int64_t offset) { MutexLock lock(&mutex_); auto item = endpoints_.find(endpoint_id); @@ -1836,7 +1740,8 @@ void PayloadManager::PendingPayloads::Release(PendingPayload* payload) { } PayloadManager::PendingPayloadHandle::PendingPayloadHandle( - PendingPayload* payload, DestroyCallback destroy_callback) + PendingPayload* payload, + absl::AnyInvocable destroy_callback) : payload_(payload), destroy_callback_(std::move(destroy_callback)) {} PayloadManager::PendingPayloadHandle::~PendingPayloadHandle() { @@ -1846,9 +1751,8 @@ PayloadManager::PendingPayloadHandle::~PendingPayloadHandle() { } std::string PayloadManager::PendingPayload::ToString() const { - return absl::StrFormat("Payload(%s, %d)", - IsIncoming() ? "incoming" : "outgoing", GetId()); + return absl::StrCat("Payload(", IsIncoming() ? "incoming" : "outgoing", + GetId(), ")"); } -} // namespace connections -} // namespace nearby +} // namespace nearby::connections diff --git a/connections/implementation/payload_manager.h b/connections/implementation/payload_manager.h index 4ba438c5..cb4b031d 100644 --- a/connections/implementation/payload_manager.h +++ b/connections/implementation/payload_manager.h @@ -26,7 +26,6 @@ #include "absl/container/flat_hash_map.h" #include "absl/functional/any_invocable.h" #include "absl/time/time.h" -#include "connections/implementation/analytics/packet_meta_data.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_manager.h" #include "connections/implementation/internal_payload.h" @@ -43,8 +42,7 @@ #include "internal/platform/mutex.h" #include "internal/platform/single_thread_executor.h" -namespace nearby { -namespace connections { +namespace nearby::connections { // Annotations for methods that need to run on PayloadStatusUpdateThread. // Use only in PayloadManager @@ -53,13 +51,13 @@ namespace connections { class PayloadManager : public EndpointManager::FrameProcessor { public: - using EndpointIds = std::vector; static constexpr absl::Duration kWaitCloseTimeout = absl::Milliseconds(5000); explicit PayloadManager(EndpointManager& endpoint_manager); ~PayloadManager() override; - void SendPayload(ClientProxy* client, const EndpointIds& endpoint_ids, + void SendPayload(ClientProxy* client, + const std::vector& endpoint_ids, Payload payload); Status CancelPayload(ClientProxy* client, Payload::Id payload_id); @@ -67,8 +65,7 @@ class PayloadManager : public EndpointManager::FrameProcessor { void OnIncomingFrame( location::nearby::connections::OfflineFrame& offline_frame, const std::string& from_endpoint_id, ClientProxy* to_client, - location::nearby::proto::connections::Medium current_medium, - analytics::PacketMetaData& packet_meta_data) override; + location::nearby::proto::connections::Medium current_medium) override; // @EndpointManagerThread void OnEndpointDisconnect( @@ -105,7 +102,7 @@ class PayloadManager : public EndpointManager::FrameProcessor { std::string id; AtomicReference status{Status::kUnknown}; - std::int64_t offset = 0; + int64_t offset = 0; mutable Mutex payload_received_ack_mutex; ConditionVariable payload_received_ack_cond{&payload_received_ack_mutex}; bool is_payload_received_ack ABSL_GUARDED_BY(payload_received_ack_mutex) = @@ -115,10 +112,10 @@ class PayloadManager : public EndpointManager::FrameProcessor { // Tracks state for an InternalPayload and the endpoints associated with it. class PendingPayload { public: - using DestroyCallback = absl::AnyInvocable; - PendingPayload(std::unique_ptr internal_payload, - const EndpointIds& endpoint_ids, bool is_incoming, - DestroyCallback destroy_callback); + PendingPayload( + std::unique_ptr internal_payload, + const std::vector& endpoint_ids, bool is_incoming, + absl::AnyInvocable destroy_callback); PendingPayload(PendingPayload&&) = default; PendingPayload& operator=(PendingPayload&&) = default; @@ -148,7 +145,7 @@ class PayloadManager : public EndpointManager::FrameProcessor { ABSL_LOCKS_EXCLUDED(mutex_); // Removes the given endpoints, e.g. on error. - void RemoveEndpoints(const EndpointIds& endpoint_ids_to_remove) + void RemoveEndpoints(const std::vector& endpoint_ids_to_remove) ABSL_LOCKS_EXCLUDED(mutex_); // Sets the status for a particular endpoint. @@ -158,8 +155,8 @@ class PayloadManager : public EndpointManager::FrameProcessor { ControlMessage& control_message) ABSL_LOCKS_EXCLUDED(mutex_); // Sets the offset for a particular endpoint. - void SetOffsetForEndpoint(const std::string& endpoint_id, - std::int64_t offset) ABSL_LOCKS_EXCLUDED(mutex_); + void SetOffsetForEndpoint(const std::string& endpoint_id, int64_t offset) + ABSL_LOCKS_EXCLUDED(mutex_); // Closes internal_payload_. // Close is called when a pending peyload does not have associated @@ -175,11 +172,11 @@ class PayloadManager : public EndpointManager::FrameProcessor { private: mutable Mutex mutex_; - bool is_incoming_; + const bool is_incoming_; AtomicBoolean is_locally_canceled_{false}; AtomicBoolean is_closed_; - std::unique_ptr internal_payload_; - DestroyCallback destroy_callback_; + const std::unique_ptr internal_payload_; + absl::AnyInvocable destroy_callback_; absl::flat_hash_map endpoints_ ABSL_GUARDED_BY(mutex_); int refcount_ = 0; @@ -190,10 +187,10 @@ class PayloadManager : public EndpointManager::FrameProcessor { // Create instances with `GetPayload(Payload::Id)`. class PendingPayloadHandle { public: - using DestroyCallback = absl::AnyInvocable; PendingPayloadHandle() = default; - PendingPayloadHandle(PendingPayload* payload, - DestroyCallback destroy_callback); + PendingPayloadHandle( + PendingPayload* payload, + absl::AnyInvocable destroy_callback); PendingPayloadHandle(const PendingPayloadHandle&) = delete; PendingPayloadHandle(PendingPayloadHandle&& other) { payload_ = other.payload_; @@ -219,7 +216,7 @@ class PayloadManager : public EndpointManager::FrameProcessor { private: PendingPayload* payload_ = nullptr; - DestroyCallback destroy_callback_; + absl::AnyInvocable destroy_callback_; }; // Tracks and manages PendingPayload objects in a synchronized manner. @@ -258,31 +255,26 @@ class PayloadManager : public EndpointManager::FrameProcessor { }; using Endpoints = std::vector; - static std::string ToString(const EndpointIds& endpoint_ids); - static std::string ToString(const Endpoints& endpoints); - static std::string ToString(PayloadType type); static std::string ToString(EndpointInfo::Status status); // Splits the endpoints for this payload by availability. - // Returns a pair of lists of EndpointInfo*, with the first being the list - // of still-available endpoints, and the second for unavailable endpoints. - static std::pair GetAvailableAndUnavailableEndpoints( - const PendingPayload& pending_payload); + // Returns a pair of lists, with the first being the list of still-available + // endpoint ids, and the second for unavailable endpoints. + static std::pair, Endpoints> + GetAvailableAndUnavailableEndpoints(const PendingPayload& pending_payload); - // Converts list of EndpointInfo to list of Endpoint ids. - // Returns list of endpoint ids. - static EndpointIds EndpointsToEndpointIds(const Endpoints& endpoints); - - bool SendPayloadLoop( + // Returns the number of bytes sent. 0 bytes sent indicates end of payload. + // Returns -1 on error. + int SendPayloadLoop( ClientProxy* client, PendingPayload& pending_payload, location::nearby::connections::PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t& next_chunk_offset, size_t resume_offset, int index); + int64_t next_chunk_offset, size_t resume_offset, int index); void SendClientCallbacksForFinishedIncomingPayloadRunnable( ClientProxy* client, const std::string& endpoint_id, const location::nearby::connections::PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t offset_bytes, + int64_t offset_bytes, location::nearby::proto::connections::PayloadStatus status, location::nearby::proto::connections::OperationResultCode operation_result_code); @@ -294,27 +286,14 @@ class PayloadManager : public EndpointManager::FrameProcessor { EndpointInfoStatusToPayloadStatus(EndpointInfo::Status status); static location::nearby::proto::connections::OperationResultCode EndpointInfoStatusToOperationResultCode(EndpointInfo::Status status); - // Converts a ControlMessage::EventType for a particular payload to a - // PayloadStatus. Called when we've received a ControlMessage with this - // event from a remote endpoint; thus the PayloadStatuses are REMOTE_*. - static location::nearby::proto::connections::PayloadStatus - ControlMessageEventToPayloadStatus( - location::nearby::connections::PayloadTransferFrame::ControlMessage:: - EventType event); - static location::nearby::proto::connections::OperationResultCode - ControlMessageEventToOperationResultCode( - location::nearby::connections::PayloadTransferFrame::ControlMessage:: - EventType event); - static PayloadProgressInfo::Status PayloadStatusToTransferUpdateStatus( - location::nearby::proto::connections::PayloadStatus status); - int GetOptimalChunkSize(EndpointIds endpoint_ids); + int GetOptimalChunkSize(const std::vector& endpoint_ids); location::nearby::connections::PayloadTransferFrame::PayloadHeader CreatePayloadHeader(const InternalPayload& internal_payload, size_t offset); location::nearby::connections::PayloadTransferFrame::PayloadChunk - CreatePayloadChunk(std::int64_t offset, ByteArray body, int index); + CreatePayloadChunk(int64_t offset, ByteArray body, int index); bool IsLastChunk( location::nearby::connections::PayloadTransferFrame::PayloadChunk payload_chunk) { @@ -328,18 +307,19 @@ class PayloadManager : public EndpointManager::FrameProcessor { // path set in `SetCustomSavePath()`. ErrorOr CreateIncomingPayload( const location::nearby::connections::PayloadTransferFrame& frame, - const std::string& endpoint_id, - const std::string& save_path) ABSL_LOCKS_EXCLUDED(mutex_); + const std::string& endpoint_id, const std::string& save_path) + ABSL_LOCKS_EXCLUDED(mutex_); - Payload::Id CreateOutgoingPayload(Payload payload, - const EndpointIds& endpoint_ids) + Payload::Id CreateOutgoingPayload( + Payload payload, const std::vector& endpoint_ids) ABSL_LOCKS_EXCLUDED(mutex_); void SendClientCallbacksForFinishedOutgoingPayload( - ClientProxy* client, const EndpointIds& finished_endpoint_ids, + ClientProxy* client, + const std::vector& finished_endpoint_ids, const location::nearby::connections::PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t num_bytes_successfully_transferred, + int64_t num_bytes_successfully_transferred, location::nearby::proto::connections::PayloadStatus status, location::nearby::proto::connections::OperationResultCode operation_result_code); @@ -347,16 +327,16 @@ class PayloadManager : public EndpointManager::FrameProcessor { ClientProxy* client, const std::string& endpoint_id, const location::nearby::connections::PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t offset_bytes, + int64_t offset_bytes, location::nearby::proto::connections::PayloadStatus status, location::nearby::proto::connections::OperationResultCode operation_result_code); void SendControlMessage( - const EndpointIds& endpoint_ids, + const std::vector& endpoint_ids, const location::nearby::connections::PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t num_bytes_successfully_transferred, + int64_t num_bytes_successfully_transferred, location::nearby::connections::PayloadTransferFrame::ControlMessage:: EventType event_type); @@ -370,7 +350,7 @@ class PayloadManager : public EndpointManager::FrameProcessor { PendingPayload& pending_payload, const location::nearby::connections::PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t payload_chunk_offset, bool is_last_chunk); + int64_t payload_chunk_offset, bool is_last_chunk); bool IsPayloadReceivedAckEnabled(ClientProxy* client, const std::string& endpoint_id, PendingPayload& pending_payload); @@ -378,10 +358,11 @@ class PayloadManager : public EndpointManager::FrameProcessor { // Handles a finished outgoing payload for the given endpointIds. All // statuses except for SUCCESS are handled here. void HandleFinishedOutgoingPayload( - ClientProxy* client, const EndpointIds& finished_endpoint_ids, + ClientProxy* client, + const std::vector& finished_endpoint_ids, const location::nearby::connections::PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t num_bytes_successfully_transferred, + int64_t num_bytes_successfully_transferred, location::nearby::proto::connections::OperationResultCode operation_result_code, location::nearby::proto::connections::PayloadStatus status = location:: @@ -390,7 +371,7 @@ class PayloadManager : public EndpointManager::FrameProcessor { ClientProxy* client, const std::string& endpoint_id, const location::nearby::connections::PayloadTransferFrame::PayloadHeader& payload_header, - std::int64_t offset_bytes, + int64_t offset_bytes, location::nearby::proto::connections::PayloadStatus status, location::nearby::proto::connections::OperationResultCode operation_result_code); @@ -399,21 +380,20 @@ class PayloadManager : public EndpointManager::FrameProcessor { ClientProxy* client, const std::string& endpoint_id, const location::nearby::connections::PayloadTransferFrame::PayloadHeader& payload_header, - std::int32_t payload_chunk_flags, std::int64_t payload_chunk_offset, - std::int64_t payload_chunk_body_size); + int32_t payload_chunk_flags, int64_t payload_chunk_offset, + int64_t payload_chunk_body_size); void HandleSuccessfulIncomingChunk( ClientProxy* client, const std::string& endpoint_id, const location::nearby::connections::PayloadTransferFrame::PayloadHeader& payload_header, - std::int32_t payload_chunk_flags, std::int64_t payload_chunk_offset, - std::int64_t payload_chunk_body_size); + int32_t payload_chunk_flags, int64_t payload_chunk_offset, + int64_t payload_chunk_body_size); void ProcessDataPacket(ClientProxy* to_client, const std::string& from_endpoint_id, location::nearby::connections::PayloadTransferFrame& payload_transfer_frame, - location::nearby::proto::connections::Medium medium, - analytics::PacketMetaData& packet_meta_data); + location::nearby::proto::connections::Medium medium); void ProcessControlPacket(ClientProxy* to_client, const std::string& from_endpoint_id, location::nearby::connections::PayloadTransferFrame& @@ -439,16 +419,14 @@ class PayloadManager : public EndpointManager::FrameProcessor { ABSL_LOCKS_EXCLUDED(mutex_); void CancelAllPayloads() ABSL_LOCKS_EXCLUDED(mutex_); - void RecordPayloadStartedAnalytics(ClientProxy* client, - const EndpointIds& endpoint_ids, - std::int64_t payload_id, - PayloadType payload_type, - std::int64_t offset, - std::int64_t total_size); + void RecordPayloadStartedAnalytics( + ClientProxy* client, const std::vector& endpoint_ids, + int64_t payload_id, PayloadType payload_type, int64_t offset, + int64_t total_size); void RecordInvalidPayloadAnalytics( - ClientProxy* client, const EndpointIds& endpoint_ids, - std::int64_t payload_id, PayloadType payload_type, std::int64_t offset, - std::int64_t total_size, + ClientProxy* client, const std::vector& endpoint_ids, + int64_t payload_id, PayloadType payload_type, int64_t offset, + int64_t total_size, location::nearby::proto::connections::OperationResultCode operation_result_code); @@ -483,7 +461,6 @@ class PayloadManager : public EndpointManager::FrameProcessor { ABSL_GUARDED_BY(chunk_update_mutex_) = absl::InfinitePast(); }; -} // namespace connections -} // namespace nearby +} // namespace nearby::connections #endif // CORE_INTERNAL_PAYLOAD_MANAGER_H_ diff --git a/connections/implementation/payload_manager_test.cc b/connections/implementation/payload_manager_test.cc index 6605112c..bcffb773 100644 --- a/connections/implementation/payload_manager_test.cc +++ b/connections/implementation/payload_manager_test.cc @@ -21,7 +21,6 @@ #include "gtest/gtest.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" -#include "connections/implementation/analytics/packet_meta_data.h" #include "connections/implementation/offline_frames.h" #include "connections/implementation/simulation_user.h" #include "connections/listeners.h" @@ -43,7 +42,6 @@ namespace { using ::location::nearby::connections::OfflineFrame; using ::location::nearby::connections::PayloadTransferFrame; using ::location::nearby::proto::connections::Medium; -using ::nearby::analytics::PacketMetaData; constexpr size_t kChunkSize = 64 * 1024; constexpr absl::string_view kServiceId = "service-id"; @@ -113,13 +111,11 @@ class PayloadSimulationUser : public SimulationUser { OfflineFrame offline_frame; - ByteArray bytes = parser::ForDataPayloadTransfer(header, chunk); - offline_frame.ParseFromString(std::string(bytes)); - - PacketMetaData packet_meta_data; + std::string bytes = parser::ForDataPayloadTransfer(header, chunk); + offline_frame.ParseFromString(bytes); pm_.OnIncomingFrame(offline_frame, from_payload_id, &client_, - Medium::WIFI_HOTSPOT, packet_meta_data); + Medium::WIFI_HOTSPOT); } Status CancelPayload() { diff --git a/connections/implementation/pcp_manager.cc b/connections/implementation/pcp_manager.cc index eb35652a..b99dec20 100644 --- a/connections/implementation/pcp_manager.cc +++ b/connections/implementation/pcp_manager.cc @@ -122,6 +122,7 @@ PcpManager::StartListeningForIncomingConnections( ClientProxy* client, absl::string_view service_id, v3::ConnectionListener listener, const v3::ConnectionListeningOptions& options) { + if (shutdown_) return {{Status::kOutOfOrderApiCall}, {}}; if (!SetCurrentPcpHandler(options.strategy)) { return {{Status::kError}, {}}; } diff --git a/connections/implementation/proto/offline_wire_formats.proto b/connections/implementation/proto/offline_wire_formats.proto index 71ce3ba3..f54287ec 100644 --- a/connections/implementation/proto/offline_wire_formats.proto +++ b/connections/implementation/proto/offline_wire_formats.proto @@ -326,9 +326,10 @@ message BandwidthUpgradeNegotiationFrame { // The GO should listen on both IPv4 and IPv6 addresses. // https://en.wikipedia.org/wiki/Link-local_address#IPv6 optional bytes ip_v6_address = 6; - // Windows only supports WifiDirect with Service Discovey. Its - // credentials is the service_name/pin. - optional string service_name = 7; + // Windows and Android will use Wi-Fi P2P device discovery for WifiDirect. + // Its credentials is the device_name. + optional string service_name = 7 [deprecated = true]; + optional string device_name = 9; // WifiDirect spec requires that pin is exactly 8 digits. The first 7 // digits are the PIN. The last 1 digit is a checksum calculated using a // specific algorithm (CRC-8). However, the Windows WinRT @@ -340,6 +341,8 @@ message BandwidthUpgradeNegotiationFrame { // pin is exchanged in the connection handshake stage, but we create and // save it before starting GO, so we can send it to GC side for // authentication. + // Note: pin is not used for WIFI_DIRECT_WITH_DEVICE_NAME, reserve for + // future expansion. optional string pin = 8; } @@ -512,9 +515,10 @@ message MediumMetadata { // WifiDirect type that uses ssid/password for authentication. Android // supports this type, but Windows does not. WIFI_DIRECT_WITH_PASSWORD = 1; - // WifiDirect type that uses service_name/pin for authentication. Android - // and Windows both support this type. - WIFI_DIRECT_WITH_PIN = 2; + // WifiDirect type that uses device_name for discovery and connect. + // Android and Windows both support this type. + WIFI_DIRECT_WITH_PIN = 2 [deprecated = true]; + WIFI_DIRECT_WITH_DEVICE_NAME = 3; } // LINT.ThenChange(//depot/google3/third_party/nearby/proto/connections_enums.proto) diff --git a/connections/implementation/service_controller_router.cc b/connections/implementation/service_controller_router.cc index 250296cd..c79ec237 100644 --- a/connections/implementation/service_controller_router.cc +++ b/connections/implementation/service_controller_router.cc @@ -15,30 +15,43 @@ #include "connections/implementation/service_controller_router.h" #include +#include #include #include #include #include +#include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" +#include "absl/types/span.h" #include "connections/advertising_options.h" +#include "connections/connection_options.h" #include "connections/discovery_options.h" #include "connections/implementation/bwu_manager.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/implementation/offline_service_controller.h" +#include "connections/implementation/service_controller.h" #include "connections/listeners.h" #include "connections/medium_selector.h" +#include "connections/out_of_band_connection_metadata.h" #include "connections/params.h" #include "connections/payload.h" +#include "connections/status.h" #include "connections/v3/bandwidth_info.h" +#include "connections/v3/connection_listening_options.h" #include "connections/v3/connection_result.h" #include "connections/v3/connections_device.h" +#include "connections/v3/listeners.h" #include "connections/v3/listening_result.h" +#include "connections/v3/params.h" #include "internal/flags/nearby_flags.h" +#include "internal/interop/device.h" +#include "internal/platform/byte_array.h" #include "internal/platform/feature_flags.h" #include "internal/platform/logging.h" +#include "internal/platform/runnable.h" namespace nearby { namespace connections { @@ -413,30 +426,35 @@ void ServiceControllerRouter::RequestConnectionV3( // CancellationListener as soon as possible. client->AddCancellationFlag(remote_device.GetEndpointId()); + // v3_info must outlive the serializer task: the v1 ConnectionListener we + // build below is COPIED into ClientProxy::connections_ and its + // disconnected_cb / bandwidth_changed_cb fire long after this task returns. + auto v3_shared = + std::make_shared(std::move(info.listener)); + std::string remote_endpoint_id = remote_device.GetEndpointId(); + RouteToServiceController( "scr-request-connection-v3", - [this, client, &remote_device, v3_info = std::move(info), + [this, client, remote_endpoint_id, v3_shared, + local_endpoint_info = + (info.local_device.GetType() == + NearbyDevice::Type::kConnectionsDevice) + ? reinterpret_cast(info.local_device) + .GetEndpointInfo() + : "", connection_options, callback = std::move(callback)]() mutable { - std::string endpoint_id = remote_device.GetEndpointId(); + const std::string& endpoint_id = remote_endpoint_id; if (client->HasPendingConnectionToEndpoint(endpoint_id) || client->IsConnectedToEndpoint(endpoint_id)) { callback({Status::kAlreadyConnectedToEndpoint}); return; } - std::string endpoint_info; - if (v3_info.local_device.GetType() == - NearbyDevice::Type::kConnectionsDevice) { - endpoint_info = - reinterpret_cast(v3_info.local_device) - .GetEndpointInfo(); - } - ConnectionListener listener = { .initiated_cb = - [&v3_info, &remote_device]( - const std::string& endpoint_id, - const ConnectionResponseInfo& response_info) mutable { + [v3_shared, endpoint_id]( + const std::string& /*endpoint_id*/, + const ConnectionResponseInfo& response_info) { v3::InitialConnectionInfo new_info = { .authentication_digits = response_info.authentication_token, @@ -447,18 +465,19 @@ void ServiceControllerRouter::RequestConnectionV3( .authentication_status = response_info.authentication_status, }; - v3_info.listener.initiated_cb(remote_device, new_info); + v3_shared->initiated_cb( + v3::ConnectionsDevice(endpoint_id, "", {}), new_info); }, .accepted_cb = - [result_cb = v3_info.listener.result_cb]( - const std::string& endpoint_id) { + [result_cb = + v3_shared->result_cb](const std::string& endpoint_id) { v3::ConnectionResult result = { .status = {Status::kSuccess}, }; result_cb(v3::ConnectionsDevice(endpoint_id, "", {}), result); }, .rejected_cb = - [result_cb = v3_info.listener.result_cb]( + [result_cb = v3_shared->result_cb]( const std::string& endpoint_id, Status status) { v3::ConnectionResult result = { .status = status, @@ -466,28 +485,29 @@ void ServiceControllerRouter::RequestConnectionV3( result_cb(v3::ConnectionsDevice(endpoint_id, "", {}), result); }, .disconnected_cb = - [&v3_info](const std::string& endpoint_id) mutable { + [v3_shared](const std::string& endpoint_id) { auto device = v3::ConnectionsDevice(endpoint_id, "", {}); - v3_info.listener.disconnected_cb(device); + v3_shared->disconnected_cb(device); }, .bandwidth_changed_cb = - [this, &v3_info](const std::string& endpoint_id, - Medium medium) mutable { + [this, v3_shared](const std::string& endpoint_id, + Medium medium) mutable { v3::BandwidthInfo bandwidth_info = { .quality = GetMediumQuality(medium), .medium = medium, }; - v3_info.listener.bandwidth_changed_cb( + v3_shared->bandwidth_changed_cb( v3::ConnectionsDevice(endpoint_id, "", {}), bandwidth_info); }, }; ConnectionRequestInfo old_info = { - .endpoint_info = ByteArray(endpoint_info), + .endpoint_info = ByteArray(local_endpoint_info), .listener = std::move(listener), }; Status status = GetServiceController()->RequestConnectionV3( - client, remote_device, std::move(old_info), connection_options); + client, v3::ConnectionsDevice(endpoint_id, "", {}), + std::move(old_info), connection_options); if (!status.Ok()) { LOG(WARNING) << "Unable to request connection to endpoint " << endpoint_id << ": " << status.ToString(); diff --git a/connections/implementation/simulation_user.h b/connections/implementation/simulation_user.h index 417ab2f8..b257dc0b 100644 --- a/connections/implementation/simulation_user.h +++ b/connections/implementation/simulation_user.h @@ -46,16 +46,13 @@ namespace connections { class SetSafeToDisconnect { public: - explicit SetSafeToDisconnect(bool safe_to_disconnect, bool auto_reconnect, + explicit SetSafeToDisconnect(bool safe_to_disconnect, bool payload_received_ack, std::int32_t safe_to_disconnect_version) { NearbyFlags::GetInstance().OverrideBoolFlagValue( config_package_nearby::nearby_connections_feature:: kEnableSafeToDisconnect, safe_to_disconnect); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - config_package_nearby::nearby_connections_feature::kEnableAutoReconnect, - auto_reconnect); NearbyFlags::GetInstance().OverrideBoolFlagValue( config_package_nearby::nearby_connections_feature:: kEnablePayloadReceivedAck, @@ -81,7 +78,7 @@ class SimulationUser { SimulationUser(const std::string& device_name, BooleanMediumSelector allowed = BooleanMediumSelector(), SetSafeToDisconnect set_safe_to_disconnect = - SetSafeToDisconnect(true, false, true, 5)) + SetSafeToDisconnect(true, true, 5)) : info_{ByteArray{device_name}}, advertising_options_{ { diff --git a/connections/implementation/webrtc_bwu_handler.cc b/connections/implementation/webrtc_bwu_handler.cc deleted file mode 100644 index 6f1d07ce..00000000 --- a/connections/implementation/webrtc_bwu_handler.cc +++ /dev/null @@ -1,183 +0,0 @@ -// 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. - -#ifndef NO_WEBRTC - -#include "connections/implementation/webrtc_bwu_handler.h" - -#include -#include -#include - -#include "absl/functional/bind_front.h" -#include "connections/implementation/base_bwu_handler.h" -#include "connections/implementation/client_proxy.h" -#include "connections/implementation/endpoint_channel.h" -#include "connections/implementation/mediums/mediums.h" -#include "connections/implementation/mediums/webrtc_peer_id.h" -#include "connections/implementation/mediums/webrtc_socket.h" -#include "connections/implementation/offline_frames.h" -#include "connections/implementation/proto/offline_wire_formats.pb.h" -#include "connections/implementation/webrtc_endpoint_channel.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/expected.h" -#include "internal/platform/logging.h" - -namespace nearby { -namespace connections { - -namespace { -using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame; -using ::location::nearby::connections::LocationHint; -using ::location::nearby::connections::LocationStandard; -using ::location::nearby::proto::connections::OperationResultCode; - -LocationHint BuildLocationHint(const std::string& location) { - LocationHint location_hint; - location_hint.set_format(LocationStandard::UNKNOWN); - - if (!location.empty()) { - location_hint.set_location(location); - if (location.at(0) == '+') { - location_hint.set_format(LocationStandard::E164_CALLING); - } else { - location_hint.set_format(LocationStandard::ISO_3166_1_ALPHA_2); - } - } - return location_hint; -} - -} // namespace - -WebrtcBwuHandler::WebrtcIncomingSocket::WebrtcIncomingSocket( - const std::string& name, mediums::WebRtcSocketWrapper socket) - : name_(name), socket_(socket) {} - -void WebrtcBwuHandler::WebrtcIncomingSocket::Close() { socket_.Close(); } - -std::string WebrtcBwuHandler::WebrtcIncomingSocket::ToString() { return name_; } - -WebrtcBwuHandler::WebrtcBwuHandler( - Mediums& mediums, IncomingConnectionCallback incoming_connection_callback) - : BaseBwuHandler(std::move(incoming_connection_callback)), - mediums_(mediums) {} - -// Called by BWU target. Retrieves a new medium info from incoming message, -// and establishes connection over WebRTC using this info. -ErrorOr> -WebrtcBwuHandler::CreateUpgradedEndpointChannel( - ClientProxy* client, const std::string& service_id, - const std::string& endpoint_id, - const BandwidthUpgradeNegotiationFrame::UpgradePathInfo& - upgrade_path_info) { - const BandwidthUpgradeNegotiationFrame::UpgradePathInfo::WebRtcCredentials& - web_rtc_credentials = upgrade_path_info.web_rtc_credentials(); - mediums::WebrtcPeerId peer_id(web_rtc_credentials.peer_id()); - - LocationHint location_hint; - location_hint.set_format(LocationStandard::UNKNOWN); - if (web_rtc_credentials.has_location_hint()) { - location_hint = web_rtc_credentials.location_hint(); - } - LOG(INFO) << "WebRtcBwuHandler is attempting to connect to remote peer " - << peer_id.GetId() << ", location hint " - << location_hint.location(); - - ErrorOr socket_result = webrtc_.Connect( - service_id, peer_id, location_hint, - client->GetCancellationFlag(endpoint_id), client->GetWebRtcNonCellular()); - if (socket_result.has_error()) { - LOG(ERROR) << "WebRtcBwuHandler failed to connect to remote peer (" - << peer_id.GetId() << ") on endpoint " << endpoint_id - << ", aborting upgrade."; - return {Error(socket_result.error().operation_result_code().value())}; - } - - LOG(INFO) << "WebRtcBwuHandler successfully connected to remote " - "peer (" - << peer_id.GetId() << ") while upgrading endpoint " << endpoint_id; - - // Create a new WebRtcEndpointChannel. - auto channel = std::make_unique( - service_id, /*channel_name=*/service_id, socket_result.value()); - if (channel == nullptr) { - socket_result.value().Close(); - LOG(ERROR) << "WebRtcBwuHandler failed to create new EndpointChannel for " - "outgoing socket, aborting upgrade."; - return {Error( - OperationResultCode::NEARBY_WEB_RTC_ENDPOINT_CHANNEL_CREATION_FAILURE)}; - } - - return {std::move(channel)}; -} - -void WebrtcBwuHandler::HandleRevertInitiatorStateForService( - const std::string& upgrade_service_id) { - webrtc_.StopAcceptingConnections(upgrade_service_id); - LOG(INFO) << "WebrtcBwuHandler successfully reverted state for service " - << upgrade_service_id; -} - -// Called by BWU initiator. Set up WebRTC upgraded medium for this endpoint, -// and returns a upgrade path info (PeerId, LocationHint) for remote party to -// perform discovery. -ByteArray WebrtcBwuHandler::HandleInitializeUpgradedMediumForEndpoint( - ClientProxy* client, const std::string& upgrade_service_id, - const std::string& endpoint_id) { - LocationHint location_hint = - BuildLocationHint(webrtc_.GetDefaultCountryCode()); - - mediums::WebrtcPeerId self_id{mediums::WebrtcPeerId::FromRandom()}; - if (!webrtc_.IsAcceptingConnections(upgrade_service_id)) { - if (!webrtc_.StartAcceptingConnections( - upgrade_service_id, self_id, location_hint, - absl::bind_front(&WebrtcBwuHandler::OnIncomingWebrtcConnection, - this, client), - client->GetWebRtcNonCellular())) { - LOG(ERROR) << "WebRtcBwuHandler couldn't initiate the WEB_RTC " - "upgrade for endpoint " - << endpoint_id - << " because it failed to start listening for " - "incoming WebRTC connections."; - return {}; - } - LOG(INFO) << "WebRtcBwuHandler successfully started listening for " - "incoming WebRTC connections while upgrading endpoint " - << endpoint_id; - } - - return parser::ForBwuWebrtcPathAvailable(self_id.GetId(), location_hint); -} - -// Accept Connection Callback. -// Notifies that the remote party called WebRtc::Connect() -// for this socket. -void WebrtcBwuHandler::OnIncomingWebrtcConnection( - ClientProxy* client, const std::string& upgrade_service_id, - mediums::WebRtcSocketWrapper socket) { - auto channel = std::make_unique( - upgrade_service_id, /*channel_name=*/upgrade_service_id, socket); - auto webrtc_socket = - std::make_unique(upgrade_service_id, socket); - std::unique_ptr connection( - new IncomingSocketConnection{std::move(webrtc_socket), - std::move(channel)}); - - NotifyOnIncomingConnection(client, std::move(connection)); -} - -} // namespace connections -} // namespace nearby - -#endif diff --git a/connections/implementation/webrtc_bwu_handler.h b/connections/implementation/webrtc_bwu_handler.h deleted file mode 100644 index c865420e..00000000 --- a/connections/implementation/webrtc_bwu_handler.h +++ /dev/null @@ -1,91 +0,0 @@ -// 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. - -#ifndef CORE_INTERNAL_WEBRTC_BWU_HANDLER_H_ -#define CORE_INTERNAL_WEBRTC_BWU_HANDLER_H_ - -#ifndef NO_WEBRTC - -#include -#include - -#include "connections/implementation/base_bwu_handler.h" -#include "connections/implementation/bwu_handler.h" -#include "connections/implementation/client_proxy.h" -#include "connections/implementation/endpoint_channel.h" -#include "connections/implementation/mediums/mediums.h" -#include "connections/implementation/mediums/webrtc.h" -#include "connections/implementation/mediums/webrtc_socket.h" -#include "connections/medium_selector.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/expected.h" - -namespace nearby { -namespace connections { - -// Defines the set of methods that need to be implemented to handle the -// per-Medium-specific operations needed to upgrade an EndpointChannel. -class WebrtcBwuHandler : public BaseBwuHandler { - public: - explicit WebrtcBwuHandler( - Mediums& mediums, - IncomingConnectionCallback incoming_connection_callback); - - private: - class WebrtcIncomingSocket : public BwuHandler::IncomingSocket { - public: - explicit WebrtcIncomingSocket(const std::string& name, - mediums::WebRtcSocketWrapper socket); - - std::string ToString() override; - void Close() override; - - private: - std::string name_; - mediums::WebRtcSocketWrapper socket_; - }; - - // BwuHandler implementation: - ErrorOr> CreateUpgradedEndpointChannel( - ClientProxy* client, const std::string& service_id, - const std::string& endpoint_id, - const location::nearby::connections::BandwidthUpgradeNegotiationFrame:: - UpgradePathInfo& upgrade_path_info) final; - location::nearby::proto::connections::Medium GetUpgradeMedium() const final { - return Medium::WEB_RTC; - } - void OnEndpointDisconnect(ClientProxy* client, - const std::string& endpoint_id) final {} - - // BaseBwuHandler implementation: - ByteArray HandleInitializeUpgradedMediumForEndpoint( - ClientProxy* client, const std::string& upgrade_service_id, - const std::string& endpoint_id) final; - void HandleRevertInitiatorStateForService( - const std::string& upgrade_service_id) final; - - void OnIncomingWebrtcConnection(ClientProxy* client, - const std::string& upgrade_service_id, - mediums::WebRtcSocketWrapper socket); - - Mediums& mediums_; - mediums::WebRtc& webrtc_{mediums_.GetWebRtc()}; -}; - -} // namespace connections -} // namespace nearby - -#endif - -#endif // CORE_INTERNAL_WEBRTC_BWU_HANDLER_H_ diff --git a/connections/implementation/webrtc_bwu_handler_stub.cc b/connections/implementation/webrtc_bwu_handler_stub.cc deleted file mode 100644 index 1b37f9e3..00000000 --- a/connections/implementation/webrtc_bwu_handler_stub.cc +++ /dev/null @@ -1,84 +0,0 @@ -// 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. - -#ifdef NO_WEBRTC - -#include "connections/implementation/webrtc_bwu_handler_stub.h" - -#include -#include - -#include "absl/functional/bind_front.h" -#include "connections/implementation/client_proxy.h" -#include "connections/implementation/mediums/utils.h" -#include "connections/implementation/mediums/webrtc_peer_id_stub.h" -#include "connections/implementation/offline_frames.h" -#include "connections/implementation/webrtc_endpoint_channel.h" -#include "internal/platform/expected.h" - -namespace nearby { -namespace connections { - -namespace { -using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame; -using ::location::nearby::proto::connections::OperationResultCode; -} // namespace - -WebrtcBwuHandler::WebrtcIncomingSocket::WebrtcIncomingSocket( - const std::string& name, mediums::WebRtcSocketWrapper socket) - : name_(name), socket_(socket) {} - -void WebrtcBwuHandler::WebrtcIncomingSocket::Close() {} - -std::string WebrtcBwuHandler::WebrtcIncomingSocket::ToString() { return ""; } - -WebrtcBwuHandler::WebrtcBwuHandler( - Mediums& mediums, IncomingConnectionCallback incoming_connection_callback) - : BaseBwuHandler(std::move(incoming_connection_callback)), - mediums_(mediums) {} - -// Called by BWU target. Retrieves a new medium info from incoming message, -// and establishes connection over WebRTC using this info. -ErrorOr> -WebrtcBwuHandler::CreateUpgradedEndpointChannel( - ClientProxy* client, const std::string& service_id, - const std::string& endpoint_id, - const BandwidthUpgradeNegotiationFrame::UpgradePathInfo& - upgrade_path_info) { - return {Error(OperationResultCode::DETAIL_UNKNOWN)}; -} - -void WebrtcBwuHandler::HandleRevertInitiatorStateForService( - const std::string& upgrade_service_id) {} - -// Called by BWU initiator. Set up WebRTC upgraded medium for this endpoint, -// and returns a upgrade path info (PeerId, LocationHint) for remote party to -// perform discovery. -ByteArray WebrtcBwuHandler::HandleInitializeUpgradedMediumForEndpoint( - ClientProxy* client, const std::string& upgrade_service_id, - const std::string& endpoint_id) { - return {}; -} - -// Accept Connection Callback. -// Notifies that the remote party called WebRtc::Connect() -// for this socket. -void WebrtcBwuHandler::OnIncomingWebrtcConnection( - ClientProxy* client, const std::string& upgrade_service_id, - mediums::WebRtcSocketWrapper socket) {} - -} // namespace connections -} // namespace nearby - -#endif diff --git a/connections/implementation/webrtc_bwu_handler_stub.h b/connections/implementation/webrtc_bwu_handler_stub.h deleted file mode 100644 index f20b4898..00000000 --- a/connections/implementation/webrtc_bwu_handler_stub.h +++ /dev/null @@ -1,90 +0,0 @@ -// 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. - -#ifndef CORE_INTERNAL_WEBRTC_BWU_HANDLER_STUB_H_ -#define CORE_INTERNAL_WEBRTC_BWU_HANDLER_STUB_H_ - -#ifdef NO_WEBRTC - -#include - -#include "connections/implementation/base_bwu_handler.h" -#include "connections/implementation/client_proxy.h" -#include "connections/implementation/endpoint_channel_manager.h" -#include "connections/implementation/mediums/mediums.h" -#ifdef NO_WEBRTC -#include "connections/implementation/mediums/webrtc_socket_stub.h" -#else -#include "connections/implementation/mediums/webrtc_socket.h" -#endif -#include "internal/platform/expected.h" - -namespace nearby { -namespace connections { - -// Defines the set of methods that need to be implemented to handle the -// per-Medium-specific operations needed to upgrade an EndpointChannel. -class WebrtcBwuHandler : public BaseBwuHandler { - public: - explicit WebrtcBwuHandler( - Mediums& mediums, - IncomingConnectionCallback incoming_connection_callback); - - private: - class WebrtcIncomingSocket : public BwuHandler::IncomingSocket { - public: - explicit WebrtcIncomingSocket(const std::string& name, - mediums::WebRtcSocketWrapper socket); - - std::string ToString() override; - void Close() override; - - private: - std::string name_; - mediums::WebRtcSocketWrapper socket_; - }; - - // BwuHandler implementation: - ErrorOr> CreateUpgradedEndpointChannel( - ClientProxy* client, const std::string& service_id, - const std::string& endpoint_id, - const location::nearby::connections::BandwidthUpgradeNegotiationFrame:: - UpgradePathInfo& upgrade_path_info) final; - location::nearby::proto::connections::Medium GetUpgradeMedium() const final { - return Medium::WEB_RTC; - } - void OnEndpointDisconnect(ClientProxy* client, - const std::string& endpoint_id) final {} - - // BaseBwuHandler implementation: - ByteArray HandleInitializeUpgradedMediumForEndpoint( - ClientProxy* client, const std::string& upgrade_service_id, - const std::string& endpoint_id) final; - void HandleRevertInitiatorStateForService( - const std::string& upgrade_service_id) final; - - void OnIncomingWebrtcConnection(ClientProxy* client, - const std::string& upgrade_service_id, - mediums::WebRtcSocketWrapper socket); - - Mediums& mediums_; - mediums::WebRtc& webrtc_{mediums_.GetWebRtc()}; -}; - -} // namespace connections -} // namespace nearby - -#endif - -#endif // CORE_INTERNAL_WEBRTC_BWU_HANDLER_STUB_H_ diff --git a/connections/implementation/webrtc_endpoint_channel.cc b/connections/implementation/webrtc_endpoint_channel.cc deleted file mode 100644 index b22501bf..00000000 --- a/connections/implementation/webrtc_endpoint_channel.cc +++ /dev/null @@ -1,37 +0,0 @@ -// 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 "connections/implementation/webrtc_endpoint_channel.h" - -#include - -namespace nearby { -namespace connections { - -WebRtcEndpointChannel::WebRtcEndpointChannel( - const std::string& service_id, const std::string& channel_name, - mediums::WebRtcSocketWrapper socket) - : BaseEndpointChannel(service_id, channel_name, &socket.GetInputStream(), - &socket.GetOutputStream()), - webrtc_socket_(std::move(socket)) {} - -location::nearby::proto::connections::Medium WebRtcEndpointChannel::GetMedium() - const { - return location::nearby::proto::connections::Medium::WEB_RTC; -} - -void WebRtcEndpointChannel::CloseImpl() { webrtc_socket_.Close(); } - -} // namespace connections -} // namespace nearby diff --git a/connections/implementation/webrtc_endpoint_channel.h b/connections/implementation/webrtc_endpoint_channel.h deleted file mode 100644 index ae0eb551..00000000 --- a/connections/implementation/webrtc_endpoint_channel.h +++ /dev/null @@ -1,47 +0,0 @@ -// 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. - -#ifndef CORE_INTERNAL_WEBRTC_ENDPOINT_CHANNEL_H_ -#define CORE_INTERNAL_WEBRTC_ENDPOINT_CHANNEL_H_ - -#include - -#include "connections/implementation/base_endpoint_channel.h" -#ifdef NO_WEBRTC -#include "connections/implementation/mediums/webrtc_socket_stub.h" -#else -#include "connections/implementation/mediums/webrtc_socket.h" -#endif - -namespace nearby { -namespace connections { - -class WebRtcEndpointChannel final : public BaseEndpointChannel { - public: - WebRtcEndpointChannel(const std::string& service_id, - const std::string& channel_name, - mediums::WebRtcSocketWrapper webrtc_socket); - - location::nearby::proto::connections::Medium GetMedium() const override; - - private: - void CloseImpl() override; - - mediums::WebRtcSocketWrapper webrtc_socket_; -}; - -} // namespace connections -} // namespace nearby - -#endif // CORE_INTERNAL_WEBRTC_ENDPOINT_CHANNEL_H_ diff --git a/connections/payload.cc b/connections/payload.cc index baae0663..88188f27 100644 --- a/connections/payload.cc +++ b/connections/payload.cc @@ -28,9 +28,9 @@ #include "internal/platform/byte_array.h" #include "internal/platform/file.h" #include "internal/platform/input_stream.h" +#include "internal/platform/logging.h" -namespace nearby { -namespace connections { +namespace nearby::connections { namespace { @@ -151,5 +151,4 @@ const std::string& Payload::GetParentFolder() const { return parent_folder_; } const std::string& Payload::GetFileName() const { return file_name_; } -} // namespace connections -} // namespace nearby +} // namespace nearby::connections diff --git a/connections/payload.h b/connections/payload.h index d40b1ba4..17b448d0 100644 --- a/connections/payload.h +++ b/connections/payload.h @@ -15,26 +15,20 @@ #ifndef CORE_PAYLOAD_H_ #define CORE_PAYLOAD_H_ -#include -#include +#include #include #include -#include #include #include "absl/time/clock.h" #include "absl/time/time.h" -#include "absl/types/variant.h" #include "connections/payload_type.h" #include "internal/platform/byte_array.h" #include "internal/platform/file.h" #include "internal/platform/input_stream.h" -#include "internal/platform/logging.h" #include "internal/platform/payload_id.h" -#include "internal/platform/prng.h" -namespace nearby { -namespace connections { +namespace nearby::connections { // Payload is default-constructible, and moveable, but not copyable container // that holds at most one instance of one of: @@ -124,7 +118,6 @@ class Payload { Content content_; }; -} // namespace connections -} // namespace nearby +} // namespace nearby::connections #endif // CORE_PAYLOAD_H_ diff --git a/connections/payload_test.cc b/connections/payload_test.cc index 3c31f762..7b0666c7 100644 --- a/connections/payload_test.cc +++ b/connections/payload_test.cc @@ -21,6 +21,7 @@ #include #include "gtest/gtest.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "connections/payload_type.h" #include "internal/platform/byte_array.h" @@ -28,8 +29,7 @@ #include "internal/platform/input_stream.h" #include "internal/platform/pipe.h" -namespace nearby { -namespace connections { +namespace nearby::connections { TEST(PayloadTest, DefaultPayloadHasUnknownType) { Payload payload; @@ -132,5 +132,20 @@ TEST(PayloadTest, PayloadIsNotCopyable) { EXPECT_FALSE(std::is_copy_assignable_v); } -} // namespace connections -} // namespace nearby +TEST(PayloadTypeTest, Stringify) { + EXPECT_EQ(absl::StrCat(PayloadType::kUnknown), "Unknown"); + EXPECT_EQ(absl::StrCat(PayloadType::kBytes), "Bytes"); + EXPECT_EQ(absl::StrCat(PayloadType::kFile), "File"); + EXPECT_EQ(absl::StrCat(PayloadType::kStream), "Stream"); +} + +TEST(PayloadDirectionTest, Stringify) { + EXPECT_EQ(absl::StrCat(PayloadDirection::UNKNOWN_DIRECTION_PAYLOAD), + "UNKNOWN_DIRECTION_PAYLOAD"); + EXPECT_EQ(absl::StrCat(PayloadDirection::INCOMING_PAYLOAD), + "INCOMING_PAYLOAD"); + EXPECT_EQ(absl::StrCat(PayloadDirection::OUTGOING_PAYLOAD), + "OUTGOING_PAYLOAD"); +} + +} // namespace nearby::connections diff --git a/connections/payload_type.h b/connections/payload_type.h index 5efcbb7f..914fc4bb 100644 --- a/connections/payload_type.h +++ b/connections/payload_type.h @@ -16,59 +16,62 @@ #define CORE_PAYLOAD_TYPE_H_ #include +#include "absl/strings/str_cat.h" -namespace nearby { -namespace connections { +namespace nearby::connections { enum class PayloadType { kUnknown = 0, kBytes = 1, kFile = 2, kStream = 3 }; +// Support logging of PayloadType. +template +void AbslStringify(Sink& sink, PayloadType payload_type) { + switch (payload_type) { + case PayloadType::kBytes: + sink.Append("Bytes"); + break; + case PayloadType::kStream: + sink.Append("Stream"); + break; + case PayloadType::kFile: + sink.Append("File"); + break; + case PayloadType::kUnknown: + sink.Append("Unknown"); + break; + } +} + +inline std::ostream& operator<<(std::ostream& os, PayloadType payload_type) { + return os << absl::StrCat(payload_type); +} + enum class PayloadDirection { UNKNOWN_DIRECTION_PAYLOAD = 0, INCOMING_PAYLOAD = 1, OUTGOING_PAYLOAD = 2, }; -inline std::ostream& operator<<(std::ostream& os, PayloadType payload_type) { - switch (payload_type) { - case PayloadType::kUnknown: - os << "kUnknown"; +// Support logging of PayloadDirection. +template +void AbslStringify(Sink& sink, PayloadDirection payload_direction) { + switch (payload_direction) { + case PayloadDirection::UNKNOWN_DIRECTION_PAYLOAD: + sink.Append("UNKNOWN_DIRECTION_PAYLOAD"); break; - case PayloadType::kBytes: - os << "kBytes"; + case PayloadDirection::INCOMING_PAYLOAD: + sink.Append("INCOMING_PAYLOAD"); break; - case PayloadType::kFile: - os << "kFile"; - break; - case PayloadType::kStream: - os << "kStream"; - break; - default: - os << "Invalid PayloadType"; + case PayloadDirection::OUTGOING_PAYLOAD: + sink.Append("OUTGOING_PAYLOAD"); break; } - return os; } inline std::ostream& operator<<(std::ostream& os, - PayloadDirection payload_direction) { - switch (payload_direction) { - case PayloadDirection::UNKNOWN_DIRECTION_PAYLOAD: - os << "UNKNOWN_DIRECTION_PAYLOAD"; - break; - case PayloadDirection::INCOMING_PAYLOAD: - os << "INCOMING_PAYLOAD"; - break; - case PayloadDirection::OUTGOING_PAYLOAD: - os << "OUTGOING_PAYLOAD"; - break; - default: - os << "Invalid PayloadDirection"; - break; - } - return os; + PayloadDirection payload_direction) { + return os << absl::StrCat(payload_direction); } -} // namespace connections -} // namespace nearby +} // namespace nearby::connections #endif // CORE_PAYLOAD_TYPE_H_ diff --git a/connections/v3/connections_device.cc b/connections/v3/connections_device.cc index 402f5a0a..e32202e6 100644 --- a/connections/v3/connections_device.cc +++ b/connections/v3/connections_device.cc @@ -31,7 +31,7 @@ std::string ConnectionsDevice::ToProtoBytes() const { // Bytes holding the connection info data elements. std::string connection_info_string; for (const auto& connection_info : connection_infos_) { - if (absl::holds_alternative(connection_info)) { + if (absl::holds_alternative(connection_info)) { continue; } if (absl::holds_alternative(connection_info)) { diff --git a/google/nearby/identity/v1/BUILD b/google/nearby/identity/v1/BUILD new file mode 100644 index 00000000..5baf6b00 --- /dev/null +++ b/google/nearby/identity/v1/BUILD @@ -0,0 +1,18 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +licenses(["notice"]) + +cc_library( + name = "resources_cc_proto", + hdrs = ["resources.pb.h"], + visibility = ["//visibility:public"], + deps = ["//location/nearby/sharing/lib/rpc:sharing_rpc_client"], +) + +cc_library( + name = "rpcs_cc_proto", + hdrs = ["rpcs.pb.h"], + visibility = ["//visibility:public"], + deps = [":resources_cc_proto"], +) + diff --git a/presence/fpp/fpp/src/fspl_converter_test.rs b/google/nearby/identity/v1/resources.pb.h similarity index 52% rename from presence/fpp/fpp/src/fspl_converter_test.rs rename to google/nearby/identity/v1/resources.pb.h index efa273c8..a356b034 100644 --- a/presence/fpp/fpp/src/fspl_converter_test.rs +++ b/google/nearby/identity/v1/resources.pb.h @@ -1,10 +1,10 @@ -// Copyright 2023 Google LLC +// Copyright 2026 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 // -// http://www.apache.org/licenses/LICENSE-2.0 +// 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, @@ -12,19 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::fspl_converter::compute_distance_meters_at_high_tx_power; +#ifndef GOOGLE_NEARBY_IDENTITY_V1_RESOURCES_PB_H_ +#define GOOGLE_NEARBY_IDENTITY_V1_RESOURCES_PB_H_ -#[test] -fn test_short_distance() { - assert_eq!(compute_distance_meters_at_high_tx_power(-40), 0.1); -} +#include "location/nearby/sharing/lib/rpc/identity_rpc_types.h" -#[test] -fn test_medium_distance() { - assert_eq!(compute_distance_meters_at_high_tx_power(-60), 1.0); -} +#endif // GOOGLE_NEARBY_IDENTITY_V1_RESOURCES_PB_H_ -#[test] -fn test_large_distance() { - assert_eq!(compute_distance_meters_at_high_tx_power(-80), 10.0); -} diff --git a/google/nearby/identity/v1/rpcs.pb.h b/google/nearby/identity/v1/rpcs.pb.h new file mode 100644 index 00000000..ae0d8f22 --- /dev/null +++ b/google/nearby/identity/v1/rpcs.pb.h @@ -0,0 +1,21 @@ +// Copyright 2026 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 GOOGLE_NEARBY_IDENTITY_V1_RPCS_PB_H_ +#define GOOGLE_NEARBY_IDENTITY_V1_RPCS_PB_H_ + +#include "google/nearby/identity/v1/resources.pb.h" + +#endif // GOOGLE_NEARBY_IDENTITY_V1_RPCS_PB_H_ + diff --git a/google/protobuf/BUILD b/google/protobuf/BUILD new file mode 100644 index 00000000..c92897a9 --- /dev/null +++ b/google/protobuf/BUILD @@ -0,0 +1,10 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +licenses(["notice"]) + +cc_library( + name = "timestamp_cc_proto", + visibility = ["//visibility:public"], + deps = ["@com_google_protobuf//:protobuf"], +) + diff --git a/internal/analytics/BUILD b/internal/analytics/BUILD deleted file mode 100644 index 39427317..00000000 --- a/internal/analytics/BUILD +++ /dev/null @@ -1,49 +0,0 @@ -load("@rules_cc//cc:cc_library.bzl", "cc_library") - -licenses(["notice"]) -# 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. - -cc_library( - name = "event_logger", - hdrs = [ - "event_logger.h", - ], - visibility = [ - "//connections:__subpackages__", - "//location/nearby/analytics/cpp:__subpackages__", - "//location/nearby/cpp/experiments:__subpackages__", - "//sharing:__subpackages__", - ], - deps = [ - "//internal/proto/analytics:connections_log_cc_proto", - "//sharing/proto/analytics:sharing_log_cc_proto", - ], -) - -cc_library( - name = "mock_event_logger", - testonly = True, - hdrs = [ - "mock_event_logger.h", - "sharing_log_matchers.h", - ], - compatible_with = ["//buildenv/target:non_prod"], - visibility = ["//visibility:public"], - deps = [ - ":event_logger", - "@com_google_googletest//:gtest_for_library_testonly", - "@com_google_protobuf//:protobuf_lite", - ], -) diff --git a/internal/analytics/mock_event_logger.h b/internal/analytics/mock_event_logger.h deleted file mode 100644 index 2a6b5774..00000000 --- a/internal/analytics/mock_event_logger.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2024 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 THIRD_PARTY_NEARBY_INTERNAL_ANALYTICS_MOCK_EVENT_LOGGER_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_ANALYTICS_MOCK_EVENT_LOGGER_H_ - -#include "gmock/gmock.h" -#include "internal/analytics/event_logger.h" - -namespace nearby::analytics { - -class MockEventLogger : public ::nearby::analytics::EventLogger { - public: - MockEventLogger() = default; - ~MockEventLogger() override = default; - - MOCK_METHOD( - void, Log, - (const location::nearby::analytics::proto::ConnectionsLog& message), - (override)); - MOCK_METHOD(void, Log, (const sharing::analytics::proto::SharingLog& message), - (override)); -}; - -} // namespace nearby::analytics - -#endif // THIRD_PARTY_NEARBY_INTERNAL_ANALYTICS_MOCK_EVENT_LOGGER_H_ diff --git a/internal/analytics/sharing_log_matchers.h b/internal/analytics/sharing_log_matchers.h deleted file mode 100644 index ce01eb84..00000000 --- a/internal/analytics/sharing_log_matchers.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2024 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 THIRD_PARTY_NEARBY_INTERNAL_ANALYTICS_SHARING_LOG_MATCHERS_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_ANALYTICS_SHARING_LOG_MATCHERS_H_ - -#include "gmock/gmock.h" - -namespace nearby::analytics { - -MATCHER_P(HasCategory, category, "has category") { - return arg.event_category() == category; -} - -MATCHER_P(HasEventType, event_type, "has event type") { - return arg.event_type() == event_type; -} - -MATCHER_P(HasAction, action, "has action") { - return arg.action() == action; -} - -MATCHER_P(HasSessionId, session_id, "has session id") { - return arg.session_id() == session_id; -} - -MATCHER_P(HasDurationMillis, duration_millis, "has duration millis") { - return arg.duration_millis() == duration_millis; -} - -MATCHER_P(SharingLogHasStatus, status, "has status") { - return arg.status() == status; -} - -MATCHER_P(HasRpcName, rpc_name, "has rpc_name") { - return arg.rpc_name() == rpc_name; -} - -MATCHER_P(HasDirection, direction, "has direction") { - return arg.direction() == direction; -} - -MATCHER_P(HasErrorCode, error_code, "has error_code") { - return arg.error_code() == error_code; -} - -MATCHER_P(HasLatencyMillis, latency_millis, "has latency_millis") { - return arg.latency_millis() == latency_millis; -} - -} // namespace nearby::analytics - -#endif // THIRD_PARTY_NEARBY_INTERNAL_ANALYTICS_SHARING_LOG_MATCHERS_H_ diff --git a/internal/base/BUILD b/internal/base/BUILD index 7f8ae71f..771eeecc 100644 --- a/internal/base/BUILD +++ b/internal/base/BUILD @@ -19,8 +19,6 @@ licenses(["notice"]) cc_library( name = "base", - srcs = [ - ], hdrs = [ "observer_list.h", ], @@ -28,6 +26,7 @@ cc_library( "//internal/account:__subpackages__", "//internal/platform:__subpackages__", "//internal/test:__pkg__", + "//location/nearby/sharing/lib:__subpackages__", "//sharing:__subpackages__", ], deps = [ @@ -51,8 +50,6 @@ cc_library( ], deps = [ "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", ], ) diff --git a/internal/base/bluetooth_address.cc b/internal/base/bluetooth_address.cc index ac1de37d..913a863d 100644 --- a/internal/base/bluetooth_address.cc +++ b/internal/base/bluetooth_address.cc @@ -15,18 +15,19 @@ #include "internal/base/bluetooth_address.h" #include +#include +#include +#include #include "absl/strings/string_view.h" -#include "absl/types/optional.h" +#include "absl/types/span.h" namespace nearby { namespace device { namespace { template -// Note that some of the methods return absl::optional instead -// of std::optional, because iOS platform is still in C++14. -absl::optional CharToDigit(CHAR c) { +std::optional CharToDigit(CHAR c) { static_assert(1 <= BASE && BASE <= 36, "BASE needs to be in [1, 36]"); if (c >= '0' && c < '0' + std::min(BASE, 10)) return c - '0'; @@ -34,7 +35,7 @@ absl::optional CharToDigit(CHAR c) { if (c >= 'A' && c < 'A' + BASE - 10) return c - 'A' + 10; - return absl::nullopt; + return std::nullopt; } template @@ -43,9 +44,9 @@ static bool HexStringToByteContainer(absl::string_view input, OutIter output) { if (count == 0 || (count % 2) != 0) return false; for (uintptr_t i = 0; i < count / 2; ++i) { // most significant 4 bits - absl::optional msb = CharToDigit<16>(input[i * 2]); + std::optional msb = CharToDigit<16>(input[i * 2]); // least significant 4 bits - absl::optional lsb = CharToDigit<16>(input[i * 2 + 1]); + std::optional lsb = CharToDigit<16>(input[i * 2 + 1]); if (!msb.has_value() || !lsb.has_value()) { return false; } diff --git a/internal/crypto/BUILD b/internal/crypto/BUILD index 68558128..2f96db1a 100644 --- a/internal/crypto/BUILD +++ b/internal/crypto/BUILD @@ -44,8 +44,6 @@ cc_test( srcs = ["ed25519_unittest.cc"], copts = [ "-DUNIT_TEST", - "-Wno-inconsistent-missing-override", - "-Wno-non-virtual-dtor", "-Ithird_party", ], deps = [ diff --git a/internal/crypto_cros/BUILD b/internal/crypto_cros/BUILD index 3f61a913..d1a06ea1 100644 --- a/internal/crypto_cros/BUILD +++ b/internal/crypto_cros/BUILD @@ -99,8 +99,6 @@ cc_test( ], copts = [ "-DUNIT_TEST", - "-Wno-inconsistent-missing-override", - "-Wno-non-virtual-dtor", "-Ithird_party", ], deps = [ diff --git a/internal/crypto_cros/aead.cc b/internal/crypto_cros/aead.cc index f25699ee..451d9d5f 100644 --- a/internal/crypto_cros/aead.cc +++ b/internal/crypto_cros/aead.cc @@ -104,7 +104,7 @@ bool Aead::Seal(absl::string_view plaintext, absl::string_view nonce, return true; } -absl::optional> Aead::Open( +std::optional> Aead::Open( absl::Span ciphertext, absl::Span nonce, absl::Span additional_data) const { const size_t max_output_length = ciphertext.size(); diff --git a/internal/crypto_cros/aead_unittest.cc b/internal/crypto_cros/aead_unittest.cc index df1c4750..6699c47f 100644 --- a/internal/crypto_cros/aead_unittest.cc +++ b/internal/crypto_cros/aead_unittest.cc @@ -64,7 +64,7 @@ TEST_P(AeadTest, SealOpenSpan) { aead.Seal(kPlaintext, nonce, kAdditionalData); EXPECT_LT(sizeof(kPlaintext), ciphertext.size()); - absl::optional> decrypted = + std::optional> decrypted = aead.Open(ciphertext, nonce, kAdditionalData); ASSERT_TRUE(decrypted); ASSERT_EQ(decrypted->size(), sizeof(kPlaintext)); diff --git a/internal/crypto_cros/encryptor.cc b/internal/crypto_cros/encryptor.cc index 2eaede2c..2081091e 100644 --- a/internal/crypto_cros/encryptor.cc +++ b/internal/crypto_cros/encryptor.cc @@ -127,7 +127,7 @@ bool Encryptor::CryptString(bool do_encrypt, absl::string_view input, uint8_t* out_ptr = reinterpret_cast(nearbybase::WriteInto(&result, out_size + 1)); - absl::optional len = + std::optional len = (mode_ == CTR) ? CryptCTR(do_encrypt, nearbybase::as_bytes(absl::MakeSpan(input)), absl::MakeSpan(out_ptr, out_size)) @@ -143,7 +143,7 @@ bool Encryptor::CryptString(bool do_encrypt, absl::string_view input, bool Encryptor::CryptBytes(bool do_encrypt, absl::Span input, std::vector* output) { std::vector result(MaxOutput(do_encrypt, input.size())); - absl::optional len = + std::optional len = (mode_ == CTR) ? CryptCTR(do_encrypt, input, absl::MakeSpan(result)) : Crypt(do_encrypt, input, absl::MakeSpan(result)); if (!len) return false; @@ -159,9 +159,9 @@ size_t Encryptor::MaxOutput(bool do_encrypt, size_t length) { return result; } -absl::optional Encryptor::Crypt(bool do_encrypt, - absl::Span input, - absl::Span output) { +std::optional Encryptor::Crypt(bool do_encrypt, + absl::Span input, + absl::Span output) { DCHECK(key_); // Must call Init() before En/De-crypt. const EVP_CIPHER* cipher = GetCipherForKey(key_); @@ -197,9 +197,9 @@ absl::optional Encryptor::Crypt(bool do_encrypt, return out_len; } -absl::optional Encryptor::CryptCTR(bool do_encrypt, - absl::Span input, - absl::Span output) { +std::optional Encryptor::CryptCTR(bool do_encrypt, + absl::Span input, + absl::Span output) { if (iv_.size() != AES_BLOCK_SIZE) { LOG(ERROR) << "Counter value not set in CTR mode."; return absl::nullopt; diff --git a/internal/interop/BUILD b/internal/interop/BUILD index ee6deda7..f90f8936 100644 --- a/internal/interop/BUILD +++ b/internal/interop/BUILD @@ -35,7 +35,7 @@ cc_library( ], visibility = [ "//connections:__subpackages__", - "//presence:__subpackages__", + "//third_party/nearby/presence:__subpackages__", ], deps = [ ":authentication_status", @@ -54,8 +54,8 @@ cc_library( ], visibility = [ "//connections:__subpackages__", - "//presence:__subpackages__", "//sharing:__subpackages__", + "//third_party/nearby/presence:__subpackages__", ], ) @@ -70,7 +70,7 @@ cc_library( ], compatible_with = ["//buildenv/target:non_prod"], visibility = [ - "//presence:__subpackages__", + "//third_party/nearby/presence:__subpackages__", ], deps = [ ":authentication_status", diff --git a/internal/platform/BUILD b/internal/platform/BUILD index 67f362f5..e9efd49e 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -164,7 +164,7 @@ cc_library( visibility = [ "//connections/implementation:__subpackages__", "//internal/platform/implementation:__subpackages__", - "//presence:__subpackages__", + "//third_party/nearby/presence:__subpackages__", ], deps = [ ":base", @@ -191,7 +191,7 @@ cc_library( "//connections/implementation:__pkg__", "//connections/v3:__pkg__", "//internal/interop:__pkg__", - "//presence:__subpackages__", + "//third_party/nearby/presence:__subpackages__", ], deps = [ ":logging", @@ -210,7 +210,6 @@ cc_library( srcs = [ "blocking_queue_stream.cc", "clock_impl.cc", - "device_info_impl.cc", "monitored_runnable.cc", "pending_job_registry.cc", "pipe.cc", @@ -231,10 +230,7 @@ cc_library( "condition_variable.h", "count_down_latch.h", "crypto.h", - "device_info.h", - "device_info_impl.h", "direct_executor.h", - "file.h", "future.h", "lockable.h", "monitored_runnable.h", @@ -306,21 +302,18 @@ cc_library( "bluetooth_adapter.h", "bluetooth_classic.h", "credential_storage_impl.h", - "webrtc.h", + "file.h", "wifi.h", "wifi_direct.h", "wifi_hotspot.h", "wifi_lan.h", ], - copts = [ - "-DCORE_ADAPTER_DLL", - "-DNO_WEBRTC", - ], + copts = ["-DCORE_ADAPTER_DLL"], visibility = [ "//connections:__subpackages__", "//internal/platform/implementation:__subpackages__", "//internal/test:__subpackages__", - "//presence:__subpackages__", + "//sharing:__subpackages__", ], deps = [ ":base", @@ -332,11 +325,10 @@ cc_library( "//connections/implementation/flags:connections_flags", "//internal/base", "//internal/flags:nearby_flags", - "//internal/platform/implementation:account_manager", "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", + "//internal/platform/implementation:types", "//internal/platform/implementation:wifi_utils", - # TODO: Support WebRTC "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", @@ -344,9 +336,7 @@ cc_library( "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", - "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", - "@com_google_absl//absl/types:optional", ], ) @@ -376,7 +366,7 @@ cc_library( visibility = [ "//connections:__subpackages__", "//internal/platform/implementation:__subpackages__", - "//presence:__subpackages__", + "//third_party/nearby/presence:__subpackages__", ], deps = [ ":base", @@ -386,6 +376,7 @@ cc_library( ":uuid", "//internal/base", "//internal/platform/implementation:comm", + "//internal/platform/implementation:webrtc_platform", "//internal/platform/implementation:wifi_utils", "//internal/test", "@com_google_absl//absl/base:core_headers", @@ -581,6 +572,7 @@ cc_test( shard_count = 16, deps = [ ":base", + ":comm", ":connection_info", ":logging", ":mac_address", @@ -592,7 +584,6 @@ cc_test( "//internal/crypto_cros", "//internal/platform/implementation:platform_impl", "//internal/platform/implementation:types", - "//internal/test", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/status", diff --git a/internal/platform/awdl.h b/internal/platform/awdl.h index 66dc63f4..7ff1d36a 100644 --- a/internal/platform/awdl.h +++ b/internal/platform/awdl.h @@ -16,6 +16,7 @@ #define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_AWDL_H_ #include #include +#include #include #include @@ -23,7 +24,6 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/functional/any_invocable.h" -#include "absl/types/optional.h" #include "internal/platform/blocking_queue_stream.h" #include "internal/platform/byte_array.h" #include "internal/platform/cancellation_flag.h" @@ -261,7 +261,7 @@ class AwdlMedium { } // Returns the port range as a pair of min and max port. - absl::optional> GetDynamicPortRange() { + std::optional> GetDynamicPortRange() { return impl_->GetDynamicPortRange(); } diff --git a/internal/platform/ble.h b/internal/platform/ble.h index 7aafbce6..4117ea9c 100644 --- a/internal/platform/ble.h +++ b/internal/platform/ble.h @@ -27,7 +27,6 @@ #include "absl/strings/escaping.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/byte_array.h" #include "internal/platform/cancellation_flag.h" @@ -223,7 +222,7 @@ class GattServer final { ~GattServer() { Stop(); } // NOLINTNEXTLINE(google3-legacy-absl-backports) - absl::optional CreateCharacteristic( + std::optional CreateCharacteristic( const Uuid& service_uuid, const Uuid& characteristic_uuid, const api::ble::GattCharacteristic::Permission permission, const api::ble::GattCharacteristic::Property property) { @@ -277,13 +276,13 @@ class GattClient final { } // NOLINTNEXTLINE(google3-legacy-absl-backports) - absl::optional GetCharacteristic( + std::optional GetCharacteristic( const Uuid& service_uuid, const Uuid& characteristic_uuid) { return impl_->GetCharacteristic(service_uuid, characteristic_uuid); } // NOLINTNEXTLINE(google3-legacy-absl-backports) - absl::optional ReadCharacteristic( + std::optional ReadCharacteristic( const api::ble::GattCharacteristic& characteristic) { return impl_->ReadCharacteristic(characteristic); } @@ -295,17 +294,6 @@ class GattClient final { return impl_->WriteCharacteristic(characteristic, value, write_type); } - // TODO(qinwangz): We should not need `on_characteristic_changed_cb` when - // unsubscribing. - // NOLINTNEXTLINE(google3-legacy-absl-backports) - bool SetCharacteristicSubscription( - const api::ble::GattCharacteristic& characteristic, bool enable, - absl::AnyInvocable - on_characteristic_changed_cb) { - return impl_->SetCharacteristicSubscription( - characteristic, enable, std::move(on_characteristic_changed_cb)); - } - void Disconnect() { impl_->Disconnect(); } // Returns true if a client_gatt_connection is usable. If this method diff --git a/internal/platform/ble_test.cc b/internal/platform/ble_test.cc index 9b43629d..6c7ee843 100644 --- a/internal/platform/ble_test.cc +++ b/internal/platform/ble_test.cc @@ -55,7 +55,6 @@ using ::nearby::api::ble::BleAdvertisementData; using ::nearby::api::ble::GattCharacteristic; using ::nearby::api::ble::TxPowerLevel; using ::testing::Optional; -using ::testing::status::StatusIs; constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000); constexpr absl::string_view kAdvertisementString = "\x0a\x0b\x0c\x0d"; @@ -771,83 +770,5 @@ TEST_F(BleMediumTest, GattClientOperatiosOnCharacteristic) { env_.Stop(); } -TEST_F(BleMediumTest, GattClientSubscribeNotificationGattServerCanNotify) { - 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); - GattCharacteristic::Permission permissions = - GattCharacteristic::Permission::kRead; - GattCharacteristic::Property properties = - GattCharacteristic::Property::kRead | - GattCharacteristic::Property::kNotify; - - // Start GattServer - std::unique_ptr gatt_server = - ble_a.StartGattServer(/*ServerGattConnectionCallback=*/{}); - - ASSERT_NE(gatt_server, nullptr); - // Add characteristic and its value. - // NOLINTNEXTLINE(google3-legacy-absl-backports) - std::optional server_characteristic = - gatt_server->CreateCharacteristic(service_uuid, characteristic_uuid, - permissions, properties); - EXPECT_TRUE(gatt_server->UpdateCharacteristic(server_characteristic.value(), - ByteArray("any"))); - - // 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); - - EXPECT_TRUE(gatt_client->DiscoverServiceAndCharacteristics( - service_uuid, {characteristic_uuid})); - - // Subscribes notification - EXPECT_TRUE(gatt_client->SetCharacteristicSubscription( - server_characteristic.value(), true, - [](absl::string_view value) { EXPECT_EQ(value, "hello"); })); - - // Sends notification - EXPECT_EQ(gatt_server->NotifyCharacteristicChanged( - server_characteristic.value(), false, ByteArray("hello")), - absl::OkStatus()); - - std::string notified_value; - CountDownLatch latch(1); - // Subscribes notification - EXPECT_TRUE(gatt_client->SetCharacteristicSubscription( - server_characteristic.value(), true, [&](absl::string_view value) { - notified_value = value; - latch.CountDown(); - })); - // Sends indication - EXPECT_EQ(gatt_server->NotifyCharacteristicChanged( - server_characteristic.value(), true, ByteArray("any")), - absl::OkStatus()); - latch.Await(); - EXPECT_EQ(notified_value, "any"); - - // Unsubscribes notification - EXPECT_TRUE(gatt_client->SetCharacteristicSubscription( - server_characteristic.value(), false, - [&](absl::string_view value) { GTEST_FAIL(); })); - EXPECT_THAT(gatt_server->NotifyCharacteristicChanged( - server_characteristic.value(), true, ByteArray("any")), - StatusIs(absl::StatusCode::kNotFound)); - - gatt_client->Disconnect(); - // Failed to subscribe characteristic notification as gatt is disconnected. - EXPECT_FALSE(gatt_client->SetCharacteristicSubscription( - server_characteristic.value(), true, [](absl::string_view value) {})); - gatt_server->Stop(); - env_.Stop(); -} - } // namespace } // namespace nearby diff --git a/internal/platform/blocking_queue_stream.h b/internal/platform/blocking_queue_stream.h index 79030bc9..f898ce04 100644 --- a/internal/platform/blocking_queue_stream.h +++ b/internal/platform/blocking_queue_stream.h @@ -41,9 +41,7 @@ class BlockingQueueStream : public InputStream { private: mutable Mutex mutex_; - bool is_multiplex_enabled_ = NearbyFlags::GetInstance().GetBoolFlag( - connections::config_package_nearby::nearby_connections_feature:: - kEnableMultiplex); + bool is_multiplex_enabled_ = false; ArrayBlockingQueue blocking_queue_{ FeatureFlags::GetInstance() .GetFlags() diff --git a/internal/platform/blocking_queue_stream_test.cc b/internal/platform/blocking_queue_stream_test.cc index bf1d73ef..3064c002 100644 --- a/internal/platform/blocking_queue_stream_test.cc +++ b/internal/platform/blocking_queue_stream_test.cc @@ -24,47 +24,21 @@ namespace nearby { namespace { TEST(BlockingQueueStreamTest, ReadSuccess) { - bool is_multiplex_enabled = NearbyFlags::GetInstance().GetBoolFlag( - connections::config_package_nearby::nearby_connections_feature:: - kEnableMultiplex); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - connections::config_package_nearby::nearby_connections_feature:: - kEnableMultiplex, true); - - BlockingQueueStream stream; - ByteArray bytes = ByteArray("test1test2test3"); - stream.Write(bytes); - ExceptionOr result = stream.Read(5); - EXPECT_EQ(result.result(), ByteArray("test1")); - result = stream.Read(5); - EXPECT_EQ(result.result(), ByteArray("test2")); - result = stream.Read(5); - EXPECT_EQ(result.result(), ByteArray("test3")); - stream.Close(); - - NearbyFlags::GetInstance().OverrideBoolFlagValue( - connections::config_package_nearby::nearby_connections_feature:: - kEnableMultiplex, is_multiplex_enabled); -} - -TEST(BlockingQueueStreamTest, MultiplexDisabled) { - bool is_multiplex_enabled = NearbyFlags::GetInstance().GetBoolFlag( - connections::config_package_nearby::nearby_connections_feature:: - kEnableMultiplex); - NearbyFlags::GetInstance().OverrideBoolFlagValue( - connections::config_package_nearby::nearby_connections_feature:: - kEnableMultiplex, false); - BlockingQueueStream stream; ByteArray bytes = ByteArray("test1test2test3"); stream.Write(bytes); ExceptionOr result = stream.Read(5); EXPECT_EQ(result, ExceptionOr(Exception::kExecution)); stream.Close(); +} - NearbyFlags::GetInstance().OverrideBoolFlagValue( - connections::config_package_nearby::nearby_connections_feature:: - kEnableMultiplex, is_multiplex_enabled); +TEST(BlockingQueueStreamTest, MultiplexDisabled) { + BlockingQueueStream stream; + ByteArray bytes = ByteArray("test1test2test3"); + stream.Write(bytes); + ExceptionOr result = stream.Read(5); + EXPECT_EQ(result, ExceptionOr(Exception::kExecution)); + stream.Close(); } } // namespace diff --git a/internal/platform/connection_info.cc b/internal/platform/connection_info.cc index eaa745cc..24d4c082 100644 --- a/internal/platform/connection_info.cc +++ b/internal/platform/connection_info.cc @@ -42,6 +42,6 @@ ConnectionInfoVariant ConnectionInfo::FromDataElementBytes( return result.value(); } } - return absl::monostate(); + return std::monostate(); } } // namespace nearby diff --git a/internal/platform/connection_info_test.cc b/internal/platform/connection_info_test.cc index 972456c7..2272f15a 100644 --- a/internal/platform/connection_info_test.cc +++ b/internal/platform/connection_info_test.cc @@ -104,7 +104,7 @@ TEST(ConnectionInfoTest, TestMonostate) { auto serialized = info->ToDataElementBytes(); auto connection_info = ConnectionInfo::FromDataElementBytes(serialized.substr(0, 10)); - EXPECT_TRUE(absl::holds_alternative(connection_info)); + EXPECT_TRUE(absl::holds_alternative(connection_info)); } } diff --git a/internal/platform/device_info.h b/internal/platform/device_info.h deleted file mode 100644 index 159d2d74..00000000 --- a/internal/platform/device_info.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2021 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_DEVICE_INFO_H_ -#define PLATFORM_PUBLIC_DEVICE_INFO_H_ - -#include -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "internal/base/file_path.h" -#include "internal/platform/implementation/device_info.h" - -namespace nearby { - -class DeviceInfo { - public: - virtual ~DeviceInfo() = default; - - // All strings are UTF-8 encoded. - virtual std::string GetOsDeviceName() const = 0; - virtual api::DeviceInfo::DeviceType GetDeviceType() const = 0; - virtual api::DeviceInfo::OsType GetOsType() const = 0; - - virtual FilePath GetDownloadPath() const = 0; - virtual FilePath GetAppDataPath() const = 0; - virtual FilePath GetTemporaryPath() const = 0; - virtual FilePath GetLogPath() const = 0; - - virtual std::optional GetAvailableDiskSpaceInBytes( - const FilePath& path) const = 0; - - virtual bool IsScreenLocked() const = 0; - virtual void RegisterScreenLockedListener( - absl::string_view listener_name, - std::function callback) = 0; - virtual void UnregisterScreenLockedListener( - absl::string_view listener_name) = 0; - - virtual bool PreventSleep() = 0; - virtual bool AllowSleep() = 0; - - // Returns UTF-8 encoded localized device name depending on device type. - std::string GetDeviceTypeName() const { - // TODO(b/230132370): return localized device name. - switch (GetDeviceType()) { - case api::DeviceInfo::DeviceType::kPhone: - return "Phone"; - case api::DeviceInfo::DeviceType::kTablet: - return "Tablet"; - case api::DeviceInfo::DeviceType::kLaptop: - return "PC"; - default: - return "Unknown"; - } - } -}; - -} // namespace nearby - -#endif // PLATFORM_PUBLIC_DEVICE_INFO_H_ diff --git a/internal/platform/device_info_impl.cc b/internal/platform/device_info_impl.cc index 79d5be7f..91c04021 100644 --- a/internal/platform/device_info_impl.cc +++ b/internal/platform/device_info_impl.cc @@ -44,32 +44,19 @@ api::DeviceInfo::OsType DeviceInfoImpl::GetOsType() const { } FilePath DeviceInfoImpl::GetDownloadPath() const { - std::optional path = device_info_impl_->GetDownloadPath(); - if (path.has_value()) { - return *path; - } - return Files::GetTemporaryDirectory(); + return device_info_impl_->GetDownloadPath(); } FilePath DeviceInfoImpl::GetAppDataPath() const { - std::optional path = device_info_impl_->GetLocalAppDataPath(); - if (path.has_value()) { - return *path; - } - return Files::GetTemporaryDirectory(); + return device_info_impl_->GetLocalAppDataPath(FilePath()); } FilePath DeviceInfoImpl::GetTemporaryPath() const { - std::optional path = device_info_impl_->GetTemporaryPath(); - if (path.has_value()) { - return *path; - } - return Files::GetTemporaryDirectory(); + return device_info_impl_->GetTemporaryPath(); } FilePath DeviceInfoImpl::GetLogPath() const { - std::optional path = device_info_impl_->GetLogPath(); - return path.value_or(GetTemporaryPath()); + return device_info_impl_->GetLogPath(); } std::optional DeviceInfoImpl::GetAvailableDiskSpaceInBytes( diff --git a/internal/platform/device_info_impl.h b/internal/platform/device_info_impl.h deleted file mode 100644 index fc5ba6fe..00000000 --- a/internal/platform/device_info_impl.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2021 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_DEVICE_INFO_IMPL_H_ -#define PLATFORM_PUBLIC_DEVICE_INFO_IMPL_H_ - -#include -#include -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "internal/base/file_path.h" -#include "internal/platform/device_info.h" -#include "internal/platform/implementation/device_info.h" -#include "internal/platform/implementation/platform.h" - -namespace nearby { - -class DeviceInfoImpl : public DeviceInfo { - public: - DeviceInfoImpl() - : device_info_impl_(api::ImplementationPlatform::CreateDeviceInfo()) {} - - std::string GetOsDeviceName() const override; - api::DeviceInfo::DeviceType GetDeviceType() const override; - api::DeviceInfo::OsType GetOsType() const override; - - FilePath GetDownloadPath() const override; - FilePath GetAppDataPath() const override; - FilePath GetTemporaryPath() const override; - FilePath GetLogPath() const override; - - std::optional GetAvailableDiskSpaceInBytes( - const FilePath& path) const override; - - bool IsScreenLocked() const override; - void RegisterScreenLockedListener( - absl::string_view listener_name, - std::function callback) override; - void UnregisterScreenLockedListener(absl::string_view listener_name) override; - - bool PreventSleep() override; - bool AllowSleep() override; - - private: - std::unique_ptr device_info_impl_; -}; -} // namespace nearby - -#endif // PLATFORM_PUBLIC_DEVICE_INFO_IMPL_H_ diff --git a/internal/platform/exception.h b/internal/platform/exception.h index 08345971..07dbfaac 100644 --- a/internal/platform/exception.h +++ b/internal/platform/exception.h @@ -15,6 +15,7 @@ #ifndef PLATFORM_BASE_EXCEPTION_H_ #define PLATFORM_BASE_EXCEPTION_H_ +#include #include #include "absl/meta/type_traits.h" @@ -82,7 +83,7 @@ class ExceptionOr { ExceptionOr(Exception exception) : exception_{exception} {} // NOLINT // If there exists explicit conversion from U to T, // then allow explicit conversion from ExceptionOr to ExceptionOr. - template ()})>> + template ()})>> explicit ExceptionOr(ExceptionOr value) { if (!value.ok()) { exception_ = value.GetException(); diff --git a/internal/platform/feature_flags.h b/internal/platform/feature_flags.h index cad1ba6a..a4483839 100644 --- a/internal/platform/feature_flags.h +++ b/internal/platform/feature_flags.h @@ -75,14 +75,6 @@ class FeatureFlags { // auto-resume 5. non-distance-constraint-recovery 6. payload_ack std::int32_t min_nc_version_supports_safe_to_disconnect = 1; std::int32_t min_nc_version_supports_auto_reconnect = 3; - absl::Duration safe_to_disconnect_reconnect_retry_delay_millis = - absl::Milliseconds(4000); - absl::Duration safe_to_disconnect_reconnect_timeout_millis = - absl::Milliseconds(15000); - std::int32_t safe_to_disconnect_reconnect_retry_attempts = 3; - absl::Duration - safe_to_disconnect_reconnect_skip_duplicated_endpoint_duration = - absl::Milliseconds(2000); // Android code won't be able to launch "payload_received_ack" feature for // in near future, so change "payload_received_ack" version from "2" to "5" // after auto-reconnect and auto-resume. diff --git a/internal/platform/flags/BUILD b/internal/platform/flags/BUILD index e0e875a0..334caa90 100644 --- a/internal/platform/flags/BUILD +++ b/internal/platform/flags/BUILD @@ -22,7 +22,6 @@ cc_library( ], visibility = [ "//connections:__subpackages__", - "//connections:partners", "//internal:__subpackages__", "//location/nearby/cpp:__subpackages__", "//location/nearby/testing:__subpackages__", diff --git a/internal/platform/flags/nearby_platform_feature_flags.h b/internal/platform/flags/nearby_platform_feature_flags.h index 924d80f7..0deadf4b 100644 --- a/internal/platform/flags/nearby_platform_feature_flags.h +++ b/internal/platform/flags/nearby_platform_feature_flags.h @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Mendel flags, auto-generated. DO NOT EDIT. #ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_FLAGS_NEARBY_PLATFORM_FEATURE_FLAGS_H_ #define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_FLAGS_NEARBY_PLATFORM_FEATURE_FLAGS_H_ @@ -28,50 +29,45 @@ constexpr absl::string_view kConfigPackage = "nearby"; // The Nearby Platform features. namespace nearby_platform_feature { - -// The maximum scanning times for available hotspots. -constexpr auto kWifiHotspotScanMaxRetries = - flags::Flag(kConfigPackage, "45415883", 3); - -// The maximum IP check times during Wi-Fi hotspot connection. -constexpr auto kWifiHotspotCheckIpMaxRetries = - flags::Flag(kConfigPackage, "45415884", 20); - -// The interval between 2 IP check attempts. -constexpr auto kWifiHotspotCheckIpIntervalMillis = - flags::Flag(kConfigPackage, "45415885", 500); - -// The maximum connection times to remote Wi-Fi hotspot. -constexpr auto kWifiHotspotConnectionMaxRetries = - flags::Flag(kConfigPackage, "45415886", 3); - -// The interval between 2 connectin attempts. -constexpr auto kWifiHotspotConnectionIntervalMillis = - flags::Flag(kConfigPackage, "45415887", 2000); - -// The connection timeout to remote Wi-Fi hotspot. -constexpr auto kWifiHotspotConnectionTimeoutMillis = - flags::Flag(kConfigPackage, "45415888", 10000); - -// Enable/Disable use of address candidates for hotspot upgrade in Windows. -constexpr auto kEnableHotspotAddressCandidates = - flags::Flag(kConfigPackage, "45739567", false); - +// Disable/Enable GATT feature in BLE v2. +constexpr auto kEnableBleV2Gatt = + flags::Flag(kConfigPackage, "45415180", true); +// Disable/Enable GATT feature on devices without BLE extended feature. +constexpr auto kEnableBleV2GattOnNonExtendedDevice = + flags::Flag(kConfigPackage, "45415267", true); // Enable/Disable Intel PIe SDK to query/set WIFI feature. constexpr auto kEnableIntelPieSdk = flags::Flag(kConfigPackage, "45428547", false); - -// Enable/Disable new Bluetooth refactor -constexpr auto kEnableNewBluetoothRefactor = - flags::Flag(kConfigPackage, "45615156", false); - -// The send buffer size of blocking socket +// Replace std::async with platform thread +constexpr auto kEnablePlatformThreadToNetwork = + flags::Flag(kConfigPackage, "45412711", true); +// Enable/Disable task scheduler for ScheduledExecutor and timer. +constexpr auto kEnableTaskScheduler = + flags::Flag(kConfigPackage, "45643835", true); +// Enable/Disable Wi-Fi hotspot native. +constexpr auto kEnableWifiHotspotNative = + flags::Flag(kConfigPackage, "45667396", true); +// The send buffer size of blocking socket. constexpr auto kSocketSendBufferSize = flags::Flag(kConfigPackage, "45673785", 524288); - -// Run scheduled executor callback on executor thread. -constexpr auto kRunScheduledExecutorCallbackOnExecutorThread = - flags::Flag(kConfigPackage, "45686494", false); +// The interval between 2 IP check attempts. +constexpr auto kWifiHotspotCheckIpIntervalMillis = + flags::Flag(kConfigPackage, "45415885", 500); +// The maximum IP check times during Wi-Fi hotspot connection. +constexpr auto kWifiHotspotCheckIpMaxRetries = + flags::Flag(kConfigPackage, "45415884", 10); +// The interval between 2 connectin attempts. +constexpr auto kWifiHotspotConnectionIntervalMillis = + flags::Flag(kConfigPackage, "45415887", 2000); +// The maximum connection times to remote WiFi hotspot. +constexpr auto kWifiHotspotConnectionMaxRetries = + flags::Flag(kConfigPackage, "45415886", 3); +// The connection timeout to remote Wi-Fi hotspot. +constexpr auto kWifiHotspotConnectionTimeoutMillis = + flags::Flag(kConfigPackage, "45415888", 10000); +// The max retry times to scan WiFi hotspots. +constexpr auto kWifiHotspotScanMaxRetries = + flags::Flag(kConfigPackage, "45415883", 3); } // namespace nearby_platform_feature } // namespace config_package_nearby diff --git a/internal/platform/implementation/BUILD b/internal/platform/implementation/BUILD index cf3879bb..c0c94231 100644 --- a/internal/platform/implementation/BUILD +++ b/internal/platform/implementation/BUILD @@ -17,69 +17,6 @@ load("@rules_cc//cc:cc_test.bzl", "cc_test") licenses(["notice"]) -cc_library( - name = "auth_status", - hdrs = ["auth_status.h"], - visibility = [ - "//internal/auth:__pkg__", - "//internal/platform/implementation:__subpackages__", - "//location/nearby/cpp/sharing/clients/cpp:__subpackages__", - ], -) - -cc_library( - name = "account_info", - hdrs = ["account_info.h"], - visibility = [ - "//internal/auth:__pkg__", - "//internal/platform/implementation:__subpackages__", - "//location/nearby/cpp/sharing/clients/cpp:__subpackages__", - ], -) - -cc_library( - name = "account_manager", - hdrs = ["account_manager.h"], - visibility = [ - "//internal/account:__pkg__", - "//internal/platform:__pkg__", - "//internal/platform/implementation:__subpackages__", - "//internal/test:__subpackages__", - "//location/nearby/cpp/sharing/clients/cpp:__subpackages__", - "//location/nearby/sharing/lib:__subpackages__", - "//location/nearby/sharing/sdk/quick_share_server:__pkg__", - "//sharing:__subpackages__", - ], - deps = [ - ":account_info", - ":signin_attempt", - "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings:string_view", - ], -) - -cc_library( - name = "signin_attempt", - hdrs = ["signin_attempt.h"], - visibility = [ - "//internal/account:__pkg__", - "//internal/auth:__pkg__", - "//internal/platform/implementation:__subpackages__", - "//internal/test:__subpackages__", - "//location/nearby/cpp/sharing/clients/cpp:__subpackages__", - "//location/nearby/sharing/sdk/quick_share_server:__pkg__", - "//sharing:__subpackages__", - ], - deps = [ - ":account_info", - ":auth_status", - "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/strings:string_view", - ], -) - cc_library( name = "types", hdrs = [ @@ -112,11 +49,12 @@ cc_library( "//internal/test:__subpackages__", "//location/nearby/analytics/cpp:__subpackages__", "//location/nearby/cpp/sharing:__subpackages__", - "//presence:__subpackages__", "//sharing:__subpackages__", + "//third_party/nearby/presence:__subpackages__", ], deps = [ "//internal/base:file_path", + "//internal/base:files", "//internal/crypto_cros", "//internal/platform:base", "//internal/platform:mac_address", @@ -149,6 +87,26 @@ cc_library( ], ) +cc_library( + name = "webrtc_platform", + hdrs = [ + "webrtc.h", + "webrtc_platform.h", + ], + compatible_with = ["//buildenv/target:non_prod"], + visibility = [ + "//:__subpackages__", + ], + deps = [ + "//connections/implementation/proto:offline_wire_formats_cc_proto", + "//internal/platform:base", + "//third_party/webrtc/files/stable/webrtc/api:peer_connection_interface", + "//third_party/webrtc/files/stable/webrtc/api:scoped_refptr", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/strings:string_view", + ], +) + cc_library( name = "comm", hdrs = [ @@ -162,30 +120,28 @@ cc_library( "http_loader.h", "psk_info.h", "upgrade_address_info.h", - "webrtc.h", "wifi.h", "wifi_direct.h", "wifi_hotspot.h", "wifi_lan.h", ], - copts = ["-DNO_WEBRTC"], visibility = [ "//connections/implementation:__subpackages__", "//internal/network:__subpackages__", "//internal/platform:__pkg__", "//internal/platform/implementation:__subpackages__", - "//presence:__subpackages__", - "//presence/implementation:__subpackages__", + "//third_party/nearby/presence:__subpackages__", + "//third_party/nearby/presence/implementation:__subpackages__", ], deps = [ - "//connections/implementation/proto:offline_wire_formats_cc_proto", "//internal/platform:base", "//internal/platform:cancellation_flag", "//internal/platform:mac_address", "//internal/platform:uuid", "//internal/proto:credential_cc_proto", "//internal/proto:local_credential_cc_proto", - # TODO: Support WebRTC + # "//third_party/webrtc/files/stable/webrtc/api:create_peerconnection_factory", # buildcleaner: keep + # "//third_party/webrtc/files/stable/webrtc/api:peer_connection_interface", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/functional:any_invocable", @@ -194,7 +150,6 @@ cc_library( "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time", - "@com_google_absl//absl/types:optional", ], ) @@ -203,7 +158,6 @@ cc_library( hdrs = [ "platform.h", ], - defines = ["NO_WEBRTC"], visibility = [ "//connections/implementation:__subpackages__", "//internal:__subpackages__", @@ -213,6 +167,7 @@ cc_library( "//location/nearby/analytics/cpp:__subpackages__", "//location/nearby/apps/better_together/plugins/preferences_native:__subpackages__", "//location/nearby/cpp/sharing:__subpackages__", + "//sharing/internal/impl/common:__subpackages__", ], deps = [ ":comm", @@ -229,6 +184,8 @@ cc_library( tags = ["keep_dep"], # Prevent build_cleaner from removing the dependency. visibility = [ "//:__subpackages__", + "//location/nearby/analytics/cpp:__subpackages__", + "//location/nearby/cpp:__subpackages__", "//location/nearby/sharing/lib:__subpackages__", ], deps = [ @@ -259,3 +216,16 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "device_info_test", + size = "small", + timeout = "moderate", + srcs = ["device_info_test.cc"], + deps = [ + ":types", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/internal/platform/implementation/account_info.h b/internal/platform/implementation/account_info.h deleted file mode 100644 index 2cb48535..00000000 --- a/internal/platform/implementation/account_info.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2024 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 THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_ACCOUNT_INFO_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_ACCOUNT_INFO_H_ - -#include - -namespace nearby { - -// Describes a Nearby account. The account class will have more properties -// and methods in the future based on the new feature added. -struct AccountInfo { - std::string id; // The unique identify of the account. - std::string display_name; - std::string family_name; - std::string given_name; - std::string picture_url; - std::string email; -}; - -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_ACCOUNT_INFO_H_ diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index 5a292734..ec25ff43 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -51,6 +51,18 @@ objc_library( ], ) +objc_library( + name = "apple_webrtc", + srcs = [ + "webrtc_platform.mm", + ], + deps = [ + "//connections/implementation/mediums/webrtc:webrtc_medium_impl", + "//internal/platform/implementation:webrtc_platform", + "//third_party/apple_frameworks:Foundation", + ], +) + objc_library( name = "apple", srcs = [ @@ -61,7 +73,6 @@ objc_library( "preferences_manager.mm", "scheduled_executor.mm", "timer.mm", - "webrtc.mm", "wifi_hotspot.mm", "wifi_lan.mm", ], @@ -70,7 +81,6 @@ objc_library( "device_info.h", "preferences_manager.h", "timer.h", - "webrtc.h", "wifi.h", "wifi_hotspot.h", "wifi_lan.h", @@ -102,6 +112,7 @@ objc_library( "//internal/base:file_path", "//internal/base:files", "//internal/base:masker", + "//internal/platform/implementation/apple/Flags", "//internal/platform/implementation/apple/Mediums/Hotspot", "//internal/account", "//internal/crypto_cros", @@ -109,10 +120,13 @@ objc_library( "//internal/platform:logging", "//internal/platform:types", "//internal/proto:tachyon_cc_proto", +<<<<<<< HEAD "//third_party/webrtc/files/stable/webrtc/api/task_queue:default_task_queue_factory", - "//third_party/webrtc/files/stable/webrtc/api:create_modular_peer_connection_factory", - "//third_party/webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", + # "//third_party/webrtc/files/stable/webrtc/api:create_modular_peer_connection_factory", + # "//third_party/webrtc/files/stable/webrtc/api:peer_connection_interface", "//third_party/webrtc/files/stable/webrtc/rtc_base:checks", +======= +>>>>>>> nearby/main "//internal/platform:base", "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", @@ -345,11 +359,11 @@ cc_test( deps = [ ":Platform_cc", "//internal/platform/implementation/g3:crypto", + "//third_party/gloop/thread/fiber", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", - "@com_google_nisaba//nisaba/port:thread_pool/fiber", ], ) diff --git a/internal/platform/implementation/apple/Flags/GNCFeatureFlags.h b/internal/platform/implementation/apple/Flags/GNCFeatureFlags.h index 7d75da5d..265904be 100644 --- a/internal/platform/implementation/apple/Flags/GNCFeatureFlags.h +++ b/internal/platform/implementation/apple/Flags/GNCFeatureFlags.h @@ -29,4 +29,16 @@ /** Checks whether BLE L2CAP refactor is enabled in the Nearby Connections SDK. */ @property(nonatomic, class, readonly) BOOL refactorBleL2capEnabled; +/** Checks whether shared peripheral manager is enabled in the Nearby Connections SDK. */ +@property(nonatomic, class, readonly) BOOL sharedPeripheralManagerEnabled; + +<<<<<<< HEAD +======= +/** Checks whether single copy read/write is enabled in the Nearby Connections SDK. */ +@property(nonatomic, class, readonly) BOOL singleCopyEnabled; + +/** Checks whether BLE server socket deadlock is fixed in the Nearby Connections SDK. */ +@property(nonatomic, class, readonly) BOOL fixBleServerSocketDeadlockEnabled; + +>>>>>>> nearby/main @end diff --git a/internal/platform/implementation/apple/Flags/GNCFeatureFlags.mm b/internal/platform/implementation/apple/Flags/GNCFeatureFlags.mm index 548518e8..dfa34cce 100644 --- a/internal/platform/implementation/apple/Flags/GNCFeatureFlags.mm +++ b/internal/platform/implementation/apple/Flags/GNCFeatureFlags.mm @@ -42,4 +42,24 @@ kRefactorBleL2cap); } ++ (BOOL)sharedPeripheralManagerEnabled { + return nearby::NearbyFlags::GetInstance().GetBoolFlag( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager); +} + +<<<<<<< HEAD +======= ++ (BOOL)singleCopyEnabled { + return nearby::NearbyFlags::GetInstance().GetBoolFlag( + nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy); +} + ++ (BOOL)fixBleServerSocketDeadlockEnabled { + return nearby::NearbyFlags::GetInstance().GetBoolFlag( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kFixBleServerSocketDeadlock); +} + +>>>>>>> nearby/main @end diff --git a/internal/platform/implementation/apple/Flags/Tests/GNCFeatureFlagsTest.mm b/internal/platform/implementation/apple/Flags/Tests/GNCFeatureFlagsTest.mm index 767eebfb..59784339 100644 --- a/internal/platform/implementation/apple/Flags/Tests/GNCFeatureFlagsTest.mm +++ b/internal/platform/implementation/apple/Flags/Tests/GNCFeatureFlagsTest.mm @@ -34,6 +34,13 @@ nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( nearby::connections::config_package_nearby::nearby_connections_feature::kEnableBleL2cap, false); + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature::kRefactorBleL2cap, + false); + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + false); [super tearDown]; } @@ -78,4 +85,34 @@ XCTAssertFalse([GNCFeatureFlags bleL2capEnabled]); } +- (void)testRefactorBleL2capEnabled_WhenFlagIsTrue { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature::kRefactorBleL2cap, + YES); + XCTAssertTrue([GNCFeatureFlags refactorBleL2capEnabled]); +} + +- (void)testRefactorBleL2capEnabled_WhenFlagIsFalse { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature::kRefactorBleL2cap, + NO); + XCTAssertFalse([GNCFeatureFlags refactorBleL2capEnabled]); +} + +- (void)testSharedPeripheralManagerEnabled_WhenFlagIsTrue { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + YES); + XCTAssertTrue([GNCFeatureFlags sharedPeripheralManagerEnabled]); +} + +- (void)testSharedPeripheralManagerEnabled_WhenFlagIsFalse { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + NO); + XCTAssertFalse([GNCFeatureFlags sharedPeripheralManagerEnabled]); +} + @end diff --git a/internal/platform/implementation/apple/Mediums/BLE/BUILD b/internal/platform/implementation/apple/Mediums/BLE/BUILD index 2a7a19f8..8f43a4e3 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/BUILD +++ b/internal/platform/implementation/apple/Mediums/BLE/BUILD @@ -39,6 +39,7 @@ objc_library( "GNCMConnection.m", "GNCPeripheral.m", "GNCPeripheralManager.m", + "GNCPeripheralManagerMultiplexer.m", "NSData+GNCBase85.mm", "NSData+GNCWebSafeBase64.m", ], @@ -59,6 +60,7 @@ objc_library( "GNCMConnection.h", "GNCPeripheral.h", "GNCPeripheralManager.h", + "GNCPeripheralManagerMultiplexer.h", "NSData+GNCBase85.h", "NSData+GNCWebSafeBase64.h", ], diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h index 1b94731a..5ee0cad3 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h @@ -15,6 +15,8 @@ #import #import +#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManager.h" + @class GNCBLEGATTCharacteristic; NS_ASSUME_NONNULL_BEGIN @@ -57,7 +59,18 @@ typedef void (^GNCStopAdvertisingCompletionHandler)(NSError *_Nullable error); * * @note The public APIs of this class are thread safe. */ -@interface GNCBLEGATTServer : NSObject +@interface GNCBLEGATTServer : NSObject + +/** + * Initializes the GATT server. + * + * @param peripheralManager The peripheral manager to use. + * @param queue The queue to use for delegate callbacks and internal operations. + */ +- (instancetype)initWithPeripheralManager:(nullable id)peripheralManager + queue:(nullable dispatch_queue_t)queue; + +- (instancetype)init NS_UNAVAILABLE; /** * Creates a characteristic and adds it to the GATT server. diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.m b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.m index 6193a7ad..5bfc6d5c 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.m +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.m @@ -59,34 +59,66 @@ static const int kMaxAdvertisementLengthOnIOS = 23; NSDictionary *_advertisementData; } -- (instancetype)init { +- (instancetype)initWithPeripheralManager:(nullable id)peripheralManager + queue:(nullable dispatch_queue_t)queue { self = [super init]; if (self) { - _queue = dispatch_queue_create(kGNCBLEGATTServerQueueLabel, DISPATCH_QUEUE_SERIAL); - _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:nil queue:_queue]; - // Set for @c GNCPeripheralManager to be able to forward callbacks. - _peripheralManager.peripheralDelegate = self; - _services = [[NSMutableDictionary alloc] init]; - _pendingCharacteristics = [[NSMutableDictionary alloc] init]; - _characteristicValues = [[NSMutableDictionary alloc] init]; - _advertisementData = nil; - } - return self; -} + _queue = queue ?: dispatch_queue_create(kGNCBLEGATTServerQueueLabel, DISPATCH_QUEUE_SERIAL); +<<<<<<< HEAD + if (GNCFeatureFlags.sharedPeripheralManagerEnabled) { + if (!peripheralManager) { + // In shared mode, the peripheral manager must be injected. + [NSException raise:NSInvalidArgumentException + format:@"Peripheral manager cannot be nil when shared manager is enabled."]; + } + _peripheralManager = peripheralManager; + // In shared mode, do NOT set the delegate. The Multiplexer handles callbacks. + } else { + // Legacy mode: Create a new manager if one isn't provided. + if (!peripheralManager) { + peripheralManager = [[CBPeripheralManager alloc] + initWithDelegate:self + queue:_queue + options:@{CBPeripheralManagerOptionShowPowerAlertKey : @NO}]; + } + _peripheralManager = peripheralManager; + // In legacy mode, we own the manager (or use the injected one as if we own it) and set the + // delegate. + _peripheralManager.peripheralDelegate = self; + } -// This is private and should only be used for tests. The provided peripheral manager must call -// delegate methods on the main queue. -- (instancetype)initWithPeripheralManager:(nullable id)peripheralManager { - self = [super init]; - if (self) { - _queue = dispatch_get_main_queue(); - _peripheralManager = peripheralManager; - // Set for @c GNCPeripheralManager to be able to forward callbacks. - _peripheralManager.peripheralDelegate = self; _services = [[NSMutableDictionary alloc] init]; _pendingCharacteristics = [[NSMutableDictionary alloc] init]; _characteristicValues = [[NSMutableDictionary alloc] init]; _advertisementData = nil; +======= + _services = [[NSMutableDictionary alloc] init]; + _pendingCharacteristics = [[NSMutableDictionary alloc] init]; + _characteristicValues = [[NSMutableDictionary alloc] init]; + _advertisementData = nil; + + if (GNCFeatureFlags.sharedPeripheralManagerEnabled) { + if (!peripheralManager) { + // In shared mode, the peripheral manager must be injected. + [NSException raise:NSInvalidArgumentException + format:@"Peripheral manager cannot be nil when shared manager is enabled."]; + } + _peripheralManager = peripheralManager; + // In shared mode, do NOT set the delegate. The Multiplexer handles callbacks. + } else { + // Legacy mode: Create a new manager if one isn't provided. + if (!peripheralManager) { + peripheralManager = [[CBPeripheralManager alloc] + initWithDelegate:nil + queue:_queue + options:@{CBPeripheralManagerOptionShowPowerAlertKey : @NO}]; + } + _peripheralManager = peripheralManager; + // In legacy mode, we own the manager (or use the injected one as if we own it) and set the + // delegate. + _peripheralManager.peripheralDelegate = self; + } +>>>>>>> nearby/main } return self; } @@ -342,6 +374,12 @@ static const int kMaxAdvertisementLengthOnIOS = 23; - (void)gnc_peripheralManager:(id)peripheral didReceiveReadRequest:(CBATTRequest *)request { dispatch_assert_queue(_queue); + if (!_services[request.characteristic.service.UUID]) { + // This server does not own the requested service. Ignore the request to allow other listeners + // (or future listeners) to handle it. + return; + } + NSData *value = _characteristicValues[request.characteristic.service.UUID][request.characteristic.UUID]; if (!value) { diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h index be602889..285bb0ce 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h @@ -15,6 +15,8 @@ #import #import +#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManager.h" + @class GNCBLEL2CAPStream; @protocol GNCPeripheralManager; @@ -42,7 +44,7 @@ typedef void (^GNCOpenL2CAPServerChannelOpendCompletionHandler)(GNCBLEL2CAPStrea * * @note The public APIs of this class are thread safe. */ -@interface GNCBLEL2CAPServer : NSObject +@interface GNCBLEL2CAPServer : NSObject // Represents a PSM (Protocol/Service Multiplexer) value for an L2CAP channel. @property(atomic, readonly) CBL2CAPPSM PSM; @@ -57,6 +59,8 @@ typedef void (^GNCOpenL2CAPServerChannelOpendCompletionHandler)(GNCBLEL2CAPStrea - (instancetype)initWithPeripheralManager:(nullable id)peripheralManager queue:(nullable dispatch_queue_t)queue; +- (instancetype)init NS_UNAVAILABLE; + /** * Starts listening for an L2CAP channel. * diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.m b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.m index e33f458a..31065d9f 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.m +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.m @@ -17,6 +17,7 @@ #import #import +#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h" #import "internal/platform/implementation/apple/Log/GNCLogger.h" #import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEError.h" #import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPStream.h" @@ -43,21 +44,25 @@ static char *const kGNCBLEL2CAPServerQueueLabel = "com.google.nearby.GNCBLEL2CAP BOOL _alreadyStartedWhenPeripheralPoweredOff; } -- (instancetype)init { - return [self initWithPeripheralManager:nil queue:nil]; -} - -// This is private and should only be used for tests. The provided peripheral manager must call -// delegate methods on the main queue. - (instancetype)initWithPeripheralManager:(nullable id)peripheralManager queue:(nullable dispatch_queue_t)queue { self = [super init]; if (self) { _queue = queue ?: dispatch_queue_create(kGNCBLEL2CAPServerQueueLabel, DISPATCH_QUEUE_SERIAL); - if (peripheralManager) { + if (GNCFeatureFlags.sharedPeripheralManagerEnabled) { + if (!peripheralManager) { + // In shared mode, the peripheral manager must be injected. + [NSException raise:NSInvalidArgumentException + format:@"Peripheral manager cannot be nil when shared manager is enabled."]; + } _peripheralManager = peripheralManager; - // Set for @c GNCPeripheralManager to be able to forward callbacks. - _peripheralManager.peripheralDelegate = self; + // In shared mode, do NOT set the delegate. The Multiplexer handles callbacks. + } else { + if (peripheralManager) { + _peripheralManager = peripheralManager; + // Set for @c GNCPeripheralManager to be able to forward callbacks. + _peripheralManager.peripheralDelegate = self; + } } } return self; @@ -70,22 +75,42 @@ static char *const kGNCBLEL2CAPServerQueueLabel = "com.google.nearby.GNCBLEL2CAP channelOpenedCompletionHandler { _psmPublishedCompletionHandler = [psmPublishedCompletionHandler copy]; _channelOpenedCompletionHandler = [channelOpenedCompletionHandler copy]; - if (!_queue) { - _psmPublishedCompletionHandler(0, [NSError errorWithDomain:GNCBLEErrorDomain - code:GNCBLEErrorL2CAPListeningOnQueueNil - userInfo:nil]); - return; - } - if (!_peripheralManager) { - // Lazy initialization to avoid system dialog on app startup before pairing. - _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:nil - queue:_queue - options:nil]; + if (GNCFeatureFlags.sharedPeripheralManagerEnabled) { + if (!_queue) { + if (_psmPublishedCompletionHandler) { + _psmPublishedCompletionHandler(0, + [NSError errorWithDomain:GNCBLEErrorDomain + code:GNCBLEErrorL2CAPListeningOnQueueNil + userInfo:nil]); + } + return; + } + if (!_peripheralManager) { + GNCLoggerError(@"[NEARBY] Peripheral manager must not be nil."); + return; + } + } else { + if (!_queue) { + _psmPublishedCompletionHandler(0, [NSError errorWithDomain:GNCBLEErrorDomain + code:GNCBLEErrorL2CAPListeningOnQueueNil + userInfo:nil]); + return; + } + if (!_peripheralManager) { + // Lazy initialization to avoid system dialog on app startup before pairing. + _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:nil + queue:_queue + options:nil]; - // Set for @c GNCPeripheralManager to be able to forward callbacks. - _peripheralManager.peripheralDelegate = self; + // Set for @c GNCPeripheralManager to be able to forward callbacks. + _peripheralManager.peripheralDelegate = self; + } } + if (_PSM > 0) { + GNCLoggerInfo((@"[NEARBY] Unpublish L2CAP channel with PSM: %@"), @(_PSM)); + [_peripheralManager unpublishL2CAPChannel:_PSM]; + } if (_peripheralManager.state == CBManagerStatePoweredOn) { // Bluetooth link is already encrypted, however encryption is not required here to avoid getting // insufficient authentication errors due to initialization order. diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h index 893fd32f..8e0df1fe 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h @@ -106,15 +106,6 @@ typedef void (^GNCGATTConnectionCompletionHandler)(GNCBLEGATTClient *_Nullable c */ - (instancetype)init; -/** - * Initializes the BLE medium with a custom central manager. - * - * @param centralManager The central manager to use for BLE operations. - * @param queue The queue to use for all internal operations. - */ -- (instancetype)initWithCentralManager:(id)centralManager - queue:(nullable dispatch_queue_t)queue; - /** The hardware supports BOTH advertising extensions and extended scans. */ @property(nonatomic, readonly) BOOL supportsExtendedAdvertisements; diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.m b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.m index a6276c3c..34656973 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.m +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.m @@ -22,10 +22,10 @@ #import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEError.h" #import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTClient.h" #import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h" -#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPClient.h" #import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h" #import "internal/platform/implementation/apple/Mediums/BLE/GNCCentralManager.h" #import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheral.h" +#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManagerMultiplexer.h" #import "internal/platform/implementation/apple/Mediums/BLE/NSData+GNCBase85.h" #import "internal/platform/implementation/apple/Mediums/BLE/NSData+GNCWebSafeBase64.h" @@ -40,22 +40,17 @@ static NSError *AlreadyScanningError() { return [NSError errorWithDomain:GNCBLEErrorDomain code:GNCBLEErrorAlreadyScanning userInfo:nil]; } -static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( - id _Nullable peripheralManager) { - if (!peripheralManager) { - return [[GNCBLEL2CAPServer alloc] init]; - } else { - return [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:peripheralManager - queue:dispatch_get_main_queue()]; - } -} - @interface GNCBLEMedium () +- (instancetype)initWithCentralManager:(id)centralManager + peripheralManager:(nullable id)peripheralManager + queue:(dispatch_queue_t)queue; @end @implementation GNCBLEMedium { dispatch_queue_t _queue; id _centralManager; + id _peripheralManager; + GNCPeripheralManagerMultiplexer *_multiplexer; // The active GATT server, or @nil if one hasn't been started yet. GNCBLEGATTServer *_server; @@ -99,21 +94,31 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( - (instancetype)init { dispatch_queue_t queue = dispatch_queue_create(kBLEMediumQueueLabel, DISPATCH_QUEUE_SERIAL); CBCentralManager *centralManager = - [[CBCentralManager alloc] initWithDelegate:self + [[CBCentralManager alloc] initWithDelegate:nil queue:queue options:@{CBCentralManagerOptionShowPowerAlertKey : @NO}]; - return [self initWithCentralManager:centralManager queue:queue]; + CBPeripheralManager *peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:nil + queue:queue]; + return [self initWithCentralManager:centralManager + peripheralManager:peripheralManager + queue:queue]; } // This is private and should only be used for tests. The provided central manager must call // delegate methods on the main queue. - (instancetype)initWithCentralManager:(id)centralManager - queue:(nullable dispatch_queue_t)queue { + peripheralManager:(nullable id)peripheralManager + queue:(dispatch_queue_t)queue { self = [super init]; if (self) { - _queue = queue ?: dispatch_get_main_queue(); + _queue = queue; _centralManager = centralManager; _centralManager.centralDelegate = self; + _peripheralManager = peripheralManager; + if (GNCFeatureFlags.sharedPeripheralManagerEnabled && _peripheralManager) { + _multiplexer = [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:_queue]; + _peripheralManager.peripheralDelegate = _multiplexer; + } _gattConnectionCompletionHandlers = [NSMutableDictionary dictionary]; _gattDisconnectionHandlers = [NSMutableDictionary dictionary]; _scanningServiceUUIDs = [NSMutableArray array]; @@ -160,11 +165,34 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( return NO; } +- (void)dealloc { + [_centralManager stopScan]; + _centralManager.centralDelegate = nil; + + [_peripheralManager stopAdvertising]; + _peripheralManager.peripheralDelegate = nil; +} + - (void)startAdvertisingData:(NSDictionary *)serviceData completionHandler:(nullable GNCStartAdvertisingCompletionHandler)completionHandler { dispatch_async(_queue, ^{ if (!_server) { - _server = [[GNCBLEGATTServer alloc] init]; + if (GNCFeatureFlags.sharedPeripheralManagerEnabled) { +<<<<<<< HEAD + // TODO (edwinwu): Implement shared peripheral manager. + // For now, raise an exception. + [NSException raise:NSInvalidArgumentException + format:@"Not implemented for shared manager is enabled."]; +======= + _server = [[GNCBLEGATTServer alloc] initWithPeripheralManager:_peripheralManager + queue:_queue]; + [_multiplexer addListener:_server]; +>>>>>>> nearby/main + } else { + // In legacy mode, we pass nil (or a separate manager) and do NOT add to multiplexer. + // GNCBLEGATTServer will create its own internal manager. + _server = [[GNCBLEGATTServer alloc] initWithPeripheralManager:nil queue:nil]; + } } [_server startAdvertisingData:serviceData completionHandler:completionHandler]; }); @@ -206,7 +234,7 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( [_scanningServiceUUIDs addObjectsFromArray:serviceUUIDs]; _advertisementFoundHandler = advertisementFoundHandler; - [self internalStartScanningIfPoweredOn]; + [self updateScanningState]; if (completionHandler) { completionHandler(nil); } @@ -227,7 +255,7 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( - (void)resumeMediumScanning:(nullable GNCStartScanningCompletionHandler)completionHandler { dispatch_async(_queue, ^{ - [self internalStartScanningIfPoweredOn]; + [self updateScanningState]; if (completionHandler) { completionHandler(nil); } @@ -238,7 +266,20 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( (nullable GNCGATTServerCompletionHandler)completionHandler { dispatch_async(_queue, ^{ if (!_server) { - _server = [[GNCBLEGATTServer alloc] init]; + if (GNCFeatureFlags.sharedPeripheralManagerEnabled) { +<<<<<<< HEAD + // TODO (edwinwu): Implement shared peripheral manager. + // For now, raise an exception. + [NSException raise:NSInvalidArgumentException + format:@"Not implemented for shared manager is enabled."]; +======= + _server = [[GNCBLEGATTServer alloc] initWithPeripheralManager:_peripheralManager + queue:_queue]; + [_multiplexer addListener:_server]; +>>>>>>> nearby/main + } else { + _server = [[GNCBLEGATTServer alloc] initWithPeripheralManager:nil queue:nil]; + } } if (completionHandler) { completionHandler(_server, nil); @@ -284,7 +325,29 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( (nullable id)peripheralManager { dispatch_async(_queue, ^{ if (!_l2capServer) { - _l2capServer = CreateL2CapServer(peripheralManager); + if (GNCFeatureFlags.sharedPeripheralManagerEnabled) { +<<<<<<< HEAD + // TODO (edwinwu): Implement shared peripheral manager. + // For now, raise an exception. + [NSException raise:NSInvalidArgumentException + format:@"Not implemented for shared manager is enabled."]; +======= + _l2capServer = [[GNCBLEL2CAPServer alloc] + initWithPeripheralManager:peripheralManager ?: _peripheralManager + queue:peripheralManager ? dispatch_get_main_queue() : _queue]; + // Only add to multiplexer if we are using the internal shared manager. + // If a specific manager was passed in (e.g. for testing?), we might still need logic here. + // But typically `peripheralManager` is nil in prod. + if (peripheralManager == nil || peripheralManager == _peripheralManager) { + [_multiplexer addListener:_l2capServer]; + } +>>>>>>> nearby/main + } else { + // Legacy mode + _l2capServer = [[GNCBLEL2CAPServer alloc] + initWithPeripheralManager:peripheralManager // Likely nil, so Server creates new one + queue:peripheralManager ? dispatch_get_main_queue() : nil]; + } } [_l2capServer startListeningChannelWithPSMPublishedCompletionHandler:psmPublishedCompletionHandler @@ -335,7 +398,7 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( #pragma mark - Internal -- (void)internalStartScanningIfPoweredOn { +- (void)updateScanningState { dispatch_assert_queue(_queue); // Scanning can only be done when powered on and must be restarted if bluetooth is turned off // then back on. This will be called anytime the central manager's state changes, so @@ -463,7 +526,7 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( return; } dispatch_assert_queue(_queue); - [self internalStartScanningIfPoweredOn]; + [self updateScanningState]; } - (void)gnc_centralManager:(id)central @@ -480,6 +543,7 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( didConnectPeripheral:(id)peripheral { dispatch_assert_queue(_queue); [self cancelConnectionTimeout]; + if (_l2capPSM > 0) { [self internalOpenL2CAPChannel:peripheral]; return; @@ -525,6 +589,7 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( didDisconnectPeripheral:(id)peripheral error:(nullable NSError *)error { dispatch_assert_queue(_queue); + GNCGATTDisconnectionHandler handler = _gattDisconnectionHandlers[peripheral.identifier]; _gattDisconnectionHandlers[peripheral.identifier] = nil; if (handler) { diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCCentralManager.m b/internal/platform/implementation/apple/Mediums/BLE/GNCCentralManager.m index a86bfee6..7b642fce 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCCentralManager.m +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCCentralManager.m @@ -22,8 +22,10 @@ NS_ASSUME_NONNULL_BEGIN @implementation CBCentralManager (GNCCentralManagerAdditions) - (void)setCentralDelegate:(nullable id)centralDelegate { - NSAssert([centralDelegate conformsToProtocol:@protocol(CBCentralManagerDelegate)], - @"centralDelegate must conform to protocol CBCentralManagerDelegate"); + if (centralDelegate) { + NSAssert([centralDelegate conformsToProtocol:@protocol(CBCentralManagerDelegate)], + @"centralDelegate must conform to protocol CBCentralManagerDelegate"); + } self.delegate = (id)centralDelegate; } diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCMBleUtils.h b/internal/platform/implementation/apple/Mediums/BLE/GNCMBleUtils.h index d5bc59d6..38bba6e2 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCMBleUtils.h +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCMBleUtils.h @@ -90,9 +90,11 @@ NSData *_Nullable GNCMGenerateBLEL2CAPPacket(GNCMBLEL2CAPCommand command, NSData /** * Calls the completion handler with (a) YES if the GNSSocket connected, or (b) NO if it failed to - * connect for any reason. The completion handler is called on the main queue. + * connect for any reason. The completion handler is called on the given queue. If the queue is nil, + * the completion handler is called on the main queue. */ -void GNCMWaitForConnection(GNSSocket *socket, GNCMBoolHandler completion); +void GNCMWaitForConnection(GNSSocket *socket, dispatch_queue_t _Nullable queue, + GNCMBoolHandler completion); #ifdef __cplusplus } // extern "C" diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCMBleUtils.mm b/internal/platform/implementation/apple/Mediums/BLE/GNCMBleUtils.mm index 6c1c518d..c582ff11 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCMBleUtils.mm +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCMBleUtils.mm @@ -235,18 +235,20 @@ NSData *_Nullable GNCMGenerateBLEL2CAPPacket(GNCMBLEL2CAPCommand command, NSData @end -void GNCMWaitForConnection(GNSSocket *socket, GNCMBoolHandler completion) { +void GNCMWaitForConnection(GNSSocket *socket, dispatch_queue_t _Nullable queue, + GNCMBoolHandler completion) { // This function passes YES to the completion when the socket has successfully connected, and // otherwise passes NO to the completion after a timeout of several seconds. We shouldn't retain // the completion after it's been called, so store it in a __block variable and nil it out once // the socket has connected. __block GNCMBoolHandler completionRef = completion; + dispatch_queue_t targetQueue = queue ?: dispatch_get_main_queue(); // The delegate listens for the socket connection callbacks. It's retained by the block passed to // dispatch_after below, so it will live long enough to do its job. GNCMBleSocketDelegate *delegate = [GNCMBleSocketDelegate delegateWithConnectedHandler:^(BOOL didConnect) { - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(targetQueue, ^{ if (completionRef) completionRef(didConnect); completionRef = nil; }); @@ -254,9 +256,10 @@ void GNCMWaitForConnection(GNSSocket *socket, GNCMBoolHandler completion) { socket.delegate = delegate; dispatch_after( dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kBleSocketConnectionTimeout * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ + targetQueue, ^{ (void)delegate; // make sure it's retained until the timeout if (completionRef) completionRef(NO); + completionRef = nil; }); } diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManagerMultiplexer.h b/internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManagerMultiplexer.h new file mode 100644 index 00000000..74129d02 --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManagerMultiplexer.h @@ -0,0 +1,49 @@ +// Copyright 2026 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. + +#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * A multiplexer that forwards @c CBPeripheralManagerDelegate and @c GNCPeripheralManagerDelegate + */ +@interface GNCPeripheralManagerMultiplexer : NSObject + +/** + * Initializes the multiplexer. + * + * @param callbackQueue The queue to use for forwarding delegate callbacks. + */ +- (instancetype)initWithCallbackQueue:(dispatch_queue_t)callbackQueue NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +/** + * Adds a listener to the multiplexer. Listeners are held weakly. + * + * @param listener The listener to add. + */ +- (void)addListener:(id)listener; + +/** + * Removes a listener from the multiplexer. + * + * @param listener The listener to remove. + */ +- (void)removeListener:(id)listener; + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManagerMultiplexer.m b/internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManagerMultiplexer.m new file mode 100644 index 00000000..56d69774 --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManagerMultiplexer.m @@ -0,0 +1,194 @@ +// Copyright 2026 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. + +#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManagerMultiplexer.h" + +#import +#import + +#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation GNCPeripheralManagerMultiplexer { + NSHashTable> *_listeners; + dispatch_queue_t _callbackQueue; + dispatch_queue_t _syncQueue; +} + +- (instancetype)initWithCallbackQueue:(dispatch_queue_t)callbackQueue { + self = [super init]; + if (self) { + _listeners = [NSHashTable weakObjectsHashTable]; + _callbackQueue = callbackQueue; + _syncQueue = dispatch_queue_create("com.google.nearby.GNCPeripheralManagerMultiplexerSync", + DISPATCH_QUEUE_SERIAL); + } + return self; +} + +- (instancetype)init { + return [self initWithCallbackQueue:dispatch_get_main_queue()]; +} + +- (void)addListener:(id)listener { + dispatch_async(_syncQueue, ^{ + [self->_listeners addObject:listener]; + }); +} + +- (void)removeListener:(id)listener { + dispatch_async(_syncQueue, ^{ + [self->_listeners removeObject:listener]; + }); +} + +- (NSArray> *)allListeners { + __block NSArray> *listeners; + dispatch_sync(_syncQueue, ^{ + listeners = [self->_listeners allObjects]; + }); + return listeners; +} + +#pragma mark - GNCPeripheralManagerDelegate + +- (void)gnc_peripheralManagerDidUpdateState:(id)peripheral { + NSArray> *listeners = [self allListeners]; + dispatch_async(_callbackQueue, ^{ + for (id listener in listeners) { + [listener gnc_peripheralManagerDidUpdateState:peripheral]; + } + }); +} + +- (void)gnc_peripheralManagerDidStartAdvertising:(id)peripheral + error:(nullable NSError *)error { + NSArray> *listeners = [self allListeners]; + dispatch_async(_callbackQueue, ^{ + for (id listener in listeners) { + if ([listener respondsToSelector:@selector(gnc_peripheralManagerDidStartAdvertising:error:)]) { + [listener gnc_peripheralManagerDidStartAdvertising:peripheral error:error]; + } + } + }); +} + +- (void)gnc_peripheralManager:(id)peripheral + didAddService:(CBService *)service + error:(nullable NSError *)error { + NSArray> *listeners = [self allListeners]; + dispatch_async(_callbackQueue, ^{ + for (id listener in listeners) { + if ([listener respondsToSelector:@selector(gnc_peripheralManager:didAddService:error:)]) { + [listener gnc_peripheralManager:peripheral didAddService:service error:error]; + } + } + }); +} + +- (void)gnc_peripheralManager:(id)peripheral + didReceiveReadRequest:(CBATTRequest *)request { + NSArray> *listeners = [self allListeners]; + dispatch_async(_callbackQueue, ^{ + for (id listener in listeners) { + if ([listener respondsToSelector:@selector(gnc_peripheralManager:didReceiveReadRequest:)]) { + [listener gnc_peripheralManager:peripheral didReceiveReadRequest:request]; + } + } + }); +} + +- (void)gnc_peripheralManager:(id)peripheral + didPublishL2CAPChannel:(CBL2CAPPSM)PSM + error:(nullable NSError *)error { + NSArray> *listeners = [self allListeners]; + dispatch_async(_callbackQueue, ^{ + for (id listener in listeners) { + if ([listener respondsToSelector:@selector(gnc_peripheralManager:didPublishL2CAPChannel:error:)]) { + [listener gnc_peripheralManager:peripheral didPublishL2CAPChannel:PSM error:error]; + } + } + }); +} + +- (void)gnc_peripheralManager:(id)peripheral + didUnpublishL2CAPChannel:(CBL2CAPPSM)PSM + error:(NSError *)error { + NSArray> *listeners = [self allListeners]; + dispatch_async(_callbackQueue, ^{ + for (id listener in listeners) { + if ([listener respondsToSelector:@selector(gnc_peripheralManager:didUnpublishL2CAPChannel:error:)]) { + [listener gnc_peripheralManager:peripheral didUnpublishL2CAPChannel:PSM error:error]; + } + } + }); +} + +- (void)gnc_peripheralManager:(id)peripheral + didOpenL2CAPChannel:(nullable CBL2CAPChannel *)channel + error:(nullable NSError *)error { + NSArray> *listeners = [self allListeners]; + dispatch_async(_callbackQueue, ^{ + for (id listener in listeners) { + if ([listener respondsToSelector:@selector(gnc_peripheralManager:didOpenL2CAPChannel:error:)]) { + [listener gnc_peripheralManager:peripheral didOpenL2CAPChannel:channel error:error]; + } + } + }); +} + +#pragma mark - CBPeripheralManagerDelegate + +- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral { + [self gnc_peripheralManagerDidUpdateState:peripheral]; +} + +- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral + error:(nullable NSError *)error { + [self gnc_peripheralManagerDidStartAdvertising:peripheral error:error]; +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral + didAddService:(CBService *)service + error:(nullable NSError *)error { + [self gnc_peripheralManager:peripheral didAddService:service error:error]; +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral + didReceiveReadRequest:(CBATTRequest *)request { + [self gnc_peripheralManager:peripheral didReceiveReadRequest:request]; +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral + didPublishL2CAPChannel:(CBL2CAPPSM)PSM + error:(nullable NSError *)error { + [self gnc_peripheralManager:peripheral didPublishL2CAPChannel:PSM error:error]; +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral + didUnpublishL2CAPChannel:(CBL2CAPPSM)PSM + error:(nullable NSError *)error { + [self gnc_peripheralManager:peripheral didUnpublishL2CAPChannel:PSM error:error]; +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral + didOpenL2CAPChannel:(nullable CBL2CAPChannel *)channel + error:(nullable NSError *)error { + [self gnc_peripheralManager:peripheral didOpenL2CAPChannel:channel error:error]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLE/Sockets/BUILD b/internal/platform/implementation/apple/Mediums/BLE/Sockets/BUILD index 97954b96..0146d5a8 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Sockets/BUILD +++ b/internal/platform/implementation/apple/Mediums/BLE/Sockets/BUILD @@ -30,9 +30,6 @@ objc_library( ]) + [ "Source/GNSCentral.h", ], - copts = [ - "-Wno-enum-compare", # TODO(b/418286948): Remove this when the error is fixed. - ], deps = [ ":Shared", "//internal/platform/implementation/apple/Log:GNCLogger", @@ -51,9 +48,6 @@ objc_library( ]) + [ "Source/GNSPeripheral.h", ], - copts = [ - "-Wno-enum-compare", # TODO(b/418286948): Remove this when the error is fixed. - ], deps = [ ":Shared", "//internal/platform/implementation/apple/Log:GNCLogger", diff --git a/internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Peripheral/GNSPeripheralManager.m b/internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Peripheral/GNSPeripheralManager.m index d58d1759..af6e7e8b 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Peripheral/GNSPeripheralManager.m +++ b/internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Peripheral/GNSPeripheralManager.m @@ -371,7 +371,7 @@ static NSTimeInterval gKBTCrashLoopMaxTimeBetweenResetting = 15.f; } - (void)updateBTCrashLoopHeuristic { - NSAssert(_cbPeripheralManager.state == CBCentralManagerStateResetting, @"Unexpected CB state %@", + NSAssert(_cbPeripheralManager.state == CBManagerStateResetting, @"Unexpected CB state %@", CBManagerStateString(_cbPeripheralManager.state)); NSDate *now = [NSDate date]; if ([now timeIntervalSinceDate:_btCrashLastResettingDate] > diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/BUILD b/internal/platform/implementation/apple/Mediums/BLE/Tests/BUILD index 86e1be56..520b9014 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/BUILD +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/BUILD @@ -26,13 +26,13 @@ objc_library( srcs = [ "GNCBLEGATTCharacteristicTest.mm", "GNCBLEGATTClientTest.m", - "GNCBLEGATTServerTest.m", + "GNCBLEGATTServerTest.mm", "GNCBLEL2CAPClientTest.m", "GNCBLEL2CAPConnectionTest.m", "GNCBLEL2CAPFakeInputOutputStream.m", - "GNCBLEL2CAPServerTest.m", + "GNCBLEL2CAPServerTest.mm", "GNCBLEL2CAPStreamTest.m", - "GNCBLEMediumTest.m", + "GNCBLEMediumTest.mm", "GNCFakeBLEGATTServer.m", "GNCFakeBLEMedium.m", "GNCFakeCBL2CAPChannel.m", @@ -44,6 +44,7 @@ objc_library( "GNCMBleUtilsTest.m", "GNCMConnectionsTest.m", "GNCMFakeConnection.mm", + "GNCPeripheralManagerMultiplexerTest.m", "GNCPeripheralManagerTest.m", "GNCPeripheralTest.m", "NSData+GNCBase85Test.m", @@ -66,6 +67,8 @@ objc_library( "GNCMFakeConnection.h", ], deps = [ + "//connections/implementation/flags:connections_flags", + "//internal/flags:nearby_flags", "//internal/platform/implementation/apple", # buildcleaner: keep "//internal/platform/implementation/apple/Mediums/BLE", "//internal/platform/implementation/apple/Mediums/BLE/Sockets:Shared", diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServerTest.m b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServerTest.mm similarity index 60% rename from internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServerTest.m rename to internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServerTest.mm index f6bfac51..8e4882fd 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServerTest.m +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServerTest.mm @@ -22,6 +22,9 @@ #import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServer+Testing.h" #import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h" +#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "internal/flags/nearby_flags.h" + static NSString *const kServiceUUID1 = @"0000FEF3-0000-1000-8000-00805F9B34FB"; static NSString *const kServiceUUID2 = @"0000FEF4-0000-1000-8000-00805F9B34FB"; static NSString *const kCharacteristicUUID1 = @"00000000-0000-3000-8000-000000000000"; @@ -34,11 +37,60 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 #pragma mark - Create Characteristic -- (void)testCreateCharacteristic { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; +- (void)tearDown { + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); + [super tearDown]; +} - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; +- (void)testInit_setsDelegateCorrectlyBasedOnFlag { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEGATTServer *server = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + + if (enabled.boolValue) { + // In shared mode, functionality is delegated to the multiplexer, so the server should NOT + // self-assign as delegate. + XCTAssertNil(fakePeripheralManager.peripheralDelegate); + } else { + // In legacy mode, the server owns the manager and sets itself as delegate. + XCTAssertEqual(fakePeripheralManager.peripheralDelegate, server); + } + } +} + +- (void)testInit_throwsWithNilManagerWhenFlagEnabled { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + true); + + XCTAssertThrowsSpecificNamed( + [[GNCBLEGATTServer alloc] initWithPeripheralManager:(id)nil + queue:dispatch_get_main_queue()], + NSException, NSInvalidArgumentException); +} + +- (void)testCreateCharacteristic { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -62,13 +114,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testCreateMultipleCharacteristicsForOneService { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -105,13 +167,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [self waitForExpectations:@[ expectation1, expectation2 ] timeout:3]; XCTAssertEqual(fakePeripheralManager.services.count, 1); XCTAssertEqual(fakePeripheralManager.services[0].characteristics.count, 2); + } } - (void)testCreateDuplicateCharacteristics { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -140,13 +212,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [self waitForExpectations:@[ expectation ] timeout:3]; XCTAssertEqual(fakePeripheralManager.services.count, 1); XCTAssertEqual(fakePeripheralManager.services[0].characteristics.count, 1); + } } - (void)testCreateDuplicatePendingCharacteristics { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Create characteristic."]; @@ -171,13 +253,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testCreateCharacteristicNotPoweredOn { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Create characteristic."]; @@ -198,13 +290,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testCreateCharacteristicServiceFailure { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } fakePeripheralManager.didAddServiceError = [NSError errorWithDomain:@"fake" code:0 userInfo:nil]; [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -228,15 +330,25 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } #pragma mark - Read Request - (void)testReadRequest { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -265,14 +377,24 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 characteristic:characteristicUUID]; [self waitForExpectations:@[ fakePeripheralManager.respondToRequestSuccessExpectation ] - timeout:0]; + timeout:3]; + } } -- (void)testReadRequestInvalidService { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; +- (void)testReadRequestInvalidCharacteristic { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -296,73 +418,106 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [self waitForExpectations:@[ expectation ] timeout:3]; + CBUUID *invalidCharacteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID2]; + + [fakePeripheralManager + simulatePeripheralManagerDidReceiveReadRequestForService:serviceUUID + characteristic:invalidCharacteristicUUID]; + + [self waitForExpectations:@[ fakePeripheralManager.respondToRequestErrorExpectation ] timeout:3]; + } +} + +- (void)testReadRequestInvalidService { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Create characteristic."]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + [gattServer createCharacteristicWithServiceID:serviceUUID + characteristicUUID:characteristicUUID + permissions:CBAttributePermissionsReadable + properties:CBCharacteristicPropertyRead + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; + CBUUID *invalidServiceUUID = [CBUUID UUIDWithString:kServiceUUID2]; + // Expectation that we *should not* receive a response. + fakePeripheralManager.respondToRequestSuccessExpectation.inverted = YES; + fakePeripheralManager.respondToRequestErrorExpectation.inverted = YES; + [fakePeripheralManager simulatePeripheralManagerDidReceiveReadRequestForService:invalidServiceUUID characteristic:characteristicUUID]; - [self waitForExpectations:@[ fakePeripheralManager.respondToRequestErrorExpectation ] timeout:0]; -} - -- (void)testReadRequestInvalidCharacteristic { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; - - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"Create and update characteristic."]; - - CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; - CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; - [gattServer createCharacteristicWithServiceID:serviceUUID - characteristicUUID:characteristicUUID - permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyRead - completionHandler:^(GNCBLEGATTCharacteristic *characteristic, - NSError *error) { - [gattServer updateCharacteristic:characteristic - value:[NSData data] - completionHandler:^(NSError *error) { - [expectation fulfill]; - }]; - }]; - - [self waitForExpectations:@[ expectation ] timeout:3]; - - CBUUID *invalidCharacteristicUUID = - [CBUUID UUIDWithString:kCharacteristicUUID2]; - - [fakePeripheralManager - simulatePeripheralManagerDidReceiveReadRequestForService:serviceUUID - characteristic:invalidCharacteristicUUID]; - - [self waitForExpectations:@[ fakePeripheralManager.respondToRequestErrorExpectation ] timeout:0]; + [self waitForExpectations:@[ + fakePeripheralManager.respondToRequestSuccessExpectation, + fakePeripheralManager.respondToRequestErrorExpectation + ] + timeout:3]; + } } #pragma mark - Stop - (void)testStop { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } - [gattServer stop]; + [gattServer stop]; - XCTAssertEqual(fakePeripheralManager.services.count, 0); + XCTAssertEqual(fakePeripheralManager.services.count, 0); + } } #pragma mark - Start Advertising - (void)testStartAdvertisingNoServiceData { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -377,13 +532,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingEmptyServiceData { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -402,13 +567,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingShortServiceData { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -429,13 +604,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertising20ByteServiceData { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -457,13 +642,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingLongServiceData { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -482,13 +677,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingWithEmojiServiceData { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -510,13 +715,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingMultipleServices { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -536,13 +751,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingNotPoweredOn { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; @@ -556,13 +781,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingStartFailure { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } fakePeripheralManager.didStartAdvertisingError = [NSError errorWithDomain:@"fake" code:0 @@ -581,13 +816,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingAlreadyAdvertising { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } fakePeripheralManager.didStartAdvertisingError = [NSError errorWithDomain:@"fake" code:0 @@ -608,13 +853,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartStopStartAdvertising { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -648,6 +903,9 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } @end + + diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.m b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.m deleted file mode 100644 index 2380d287..00000000 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.m +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2025 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. - -#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h" - -#import -#import -#import - -#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServer+Testing.h" -#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h" - -static const NSTimeInterval kTestTimeout = 1.0; - -@interface GNCBLEL2CAPServerTest : XCTestCase -@end - -@implementation GNCBLEL2CAPServerTest - -#pragma mark Tests - -- (void)testInit { - GNCBLEL2CAPServer *l2capServer = [[GNCBLEL2CAPServer alloc] init]; - XCTAssertNotNil(l2capServer); - XCTAssertNil([l2capServer valueForKey:@"_peripheralManager"]); -} - -- (void)testPublishL2CAPChannelAndOpenChannelWhenStartListeningChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *psmPublishedExpectation = - [[XCTestExpectation alloc] initWithDescription:@"PSM published."]; - XCTestExpectation *channelOpenedexpectation = - [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, nil); - XCTAssertEqual(PSM, fakePeripheralManager.PSM); - [psmPublishedExpectation fulfill]; - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error) { - XCTAssertNil(error); - XCTAssertNotNil(stream); - [channelOpenedexpectation fulfill]; - }]; - [self waitForExpectations:@[ psmPublishedExpectation, channelOpenedexpectation ] - timeout:kTestTimeout]; - XCTAssertNotNil([l2capServer valueForKey:@"l2CAPChannel"]); - XCTAssertNotNil([l2capServer valueForKey:@"l2CAPStream"]); -} - -- (void)testFailedToPublishL2CAPChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - fakePeripheralManager.didPublishL2CAPChannelError = [NSError errorWithDomain:@"fake" - code:0 - userInfo:nil]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *psmPublishedExpectation = - [[XCTestExpectation alloc] initWithDescription:@"PSM published with error."]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, fakePeripheralManager.didPublishL2CAPChannelError); - XCTAssertEqual(PSM, 0); - [psmPublishedExpectation fulfill]; - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - [self waitForExpectations:@[ psmPublishedExpectation ] timeout:kTestTimeout]; -} - -- (void)testPoweredOffUnpublishesChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, nil); - XCTAssertEqual(PSM, fakePeripheralManager.PSM); - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOff]; - - [self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout]; -} - -- (void)testPeripheralManagerDidUpdateStatePoweredOffUnpublishesChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - fakePeripheralManager.state = CBManagerStatePoweredOn; - [(id)l2capServer - peripheralManagerDidUpdateState:(CBPeripheralManager *)fakePeripheralManager]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, nil); - XCTAssertEqual(PSM, fakePeripheralManager.PSM); - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - - fakePeripheralManager.state = CBManagerStatePoweredOff; - [(id)l2capServer - peripheralManagerDidUpdateState:(CBPeripheralManager *)fakePeripheralManager]; - - [self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout]; -} - -- (void)testStartPeripheralManagerInitiallyOff { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *psmPublishedExpectation = - [[XCTestExpectation alloc] initWithDescription:@"PSM published."]; - XCTestExpectation *channelOpenedexpectation = - [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, nil); - XCTAssertEqual(PSM, fakePeripheralManager.PSM); - [psmPublishedExpectation fulfill]; - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error) { - [channelOpenedexpectation fulfill]; - }]; - - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - [self waitForExpectations:@[ psmPublishedExpectation, channelOpenedexpectation ] - timeout:kTestTimeout]; -} - -- (void)testClose { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, nil); - XCTAssertEqual(PSM, fakePeripheralManager.PSM); - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - - [l2capServer close]; - - XCTAssertEqual([l2capServer PSM], 0); -} - -- (void)testCloseDoesNotUnpublishesChannelIfNotConnected { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - fakePeripheralManager.unpublishExpectation.inverted = YES; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - - [l2capServer close]; - - [self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout]; -} - -- (void)testFailedToOpenL2CAPChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - fakePeripheralManager.didOpenL2CAPChannelError = [NSError errorWithDomain:@"fake" - code:0 - userInfo:nil]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *channelOpenedexpectation = - [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, nil); - XCTAssertEqual(PSM, fakePeripheralManager.PSM); - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error) { - XCTAssertNil(stream); - XCTAssertEqual(error, fakePeripheralManager.didOpenL2CAPChannelError); - [channelOpenedexpectation fulfill]; - }]; - [self waitForExpectations:@[ channelOpenedexpectation ] timeout:kTestTimeout]; - XCTAssertNil([l2capServer valueForKey:@"l2CAPChannel"]); - XCTAssertNil([l2capServer valueForKey:@"l2CAPStream"]); -} - -- (void)testPeripheralManagerDidUnpublishL2CAPChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - - [(id)l2capServer - peripheralManager:(CBPeripheralManager *)fakePeripheralManager - didUnpublishL2CAPChannel:l2capServer.PSM - error:[NSError errorWithDomain:@"fake" code:0 userInfo:nil]]; - - XCTAssertEqual(l2capServer.PSM, 0); -} - -- (void)testCloseL2CAPChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *channelOpenedexpectation = - [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error) { - [channelOpenedexpectation fulfill]; - }]; - [self waitForExpectations:@[ channelOpenedexpectation ] timeout:kTestTimeout]; - - [l2capServer closeL2CAPChannel]; - - XCTAssertNil([l2capServer valueForKey:@"l2CAPChannel"]); - XCTAssertNil([l2capServer valueForKey:@"l2CAPStream"]); -} - -- (void)testPeripheralManagerDidPublishL2CAPChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"completion called"]; - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(PSM, 1); - XCTAssertNil(error); - [expectation fulfill]; - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - [(id)l2capServer - peripheralManager:(CBPeripheralManager *)fakePeripheralManager - didPublishL2CAPChannel:1 - error:nil]; - [self waitForExpectations:@[ expectation ] timeout:kTestTimeout]; - XCTAssertEqual(l2capServer.PSM, 1); -} - -- (void)testPeripheralManagerDidPublishL2CAPChannelWithError { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"completion called"]; - NSError *publishError = [NSError errorWithDomain:@"test" code:0 userInfo:nil]; - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(PSM, 0); - XCTAssertEqualObjects(error, publishError); - [expectation fulfill]; - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - [(id)l2capServer - peripheralManager:(CBPeripheralManager *)fakePeripheralManager - didPublishL2CAPChannel:0 - error:publishError]; - [self waitForExpectations:@[ expectation ] timeout:kTestTimeout]; - XCTAssertEqual(l2capServer.PSM, 0); -} - -@end diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.mm b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.mm new file mode 100644 index 00000000..2d2fabda --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.mm @@ -0,0 +1,485 @@ +// Copyright 2025 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. + +#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h" + +#import +#import +#import + +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServer+Testing.h" +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h" + +#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "internal/flags/nearby_flags.h" + +static const NSTimeInterval kTestTimeout = 1.0; + +@interface GNCBLEL2CAPServerTest : XCTestCase +@end + +@implementation GNCBLEL2CAPServerTest + +#pragma mark Tests + +- (void)tearDown { + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); + [super tearDown]; +} + +- (void)testInit_setsDelegateCorrectlyBasedOnFlag { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *server = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + + if (enabled.boolValue) { + // In shared mode, functionality is delegated to the multiplexer, so the server should NOT + // self-assign as delegate. + XCTAssertNil(fakePeripheralManager.peripheralDelegate); + } else { + // In legacy mode, the server owns the manager and sets itself as delegate. + XCTAssertEqual(fakePeripheralManager.peripheralDelegate, server); + } + } +} + +- (void)testInit_throwsWithNilManagerWhenFlagEnabled { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + true); + + XCTAssertThrowsSpecificNamed( + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:(id)nil + queue:dispatch_get_main_queue()], + NSException, NSInvalidArgumentException); +} + +- (void)testPublishL2CAPChannelAndOpenChannelWhenStartListeningChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *psmPublishedExpectation = + [[XCTestExpectation alloc] initWithDescription:@"PSM published."]; + XCTestExpectation *channelOpenedexpectation = + [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, nil); + XCTAssertEqual(PSM, fakePeripheralManager.PSM); + [psmPublishedExpectation fulfill]; + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error) { + XCTAssertNil(error); + XCTAssertNotNil(stream); + [channelOpenedexpectation fulfill]; + }]; + [self waitForExpectations:@[ psmPublishedExpectation, channelOpenedexpectation ] + timeout:kTestTimeout]; + XCTAssertNotNil([l2capServer valueForKey:@"l2CAPChannel"]); + XCTAssertNotNil([l2capServer valueForKey:@"l2CAPStream"]); + } +} + +- (void)testFailedToPublishL2CAPChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + fakePeripheralManager.didPublishL2CAPChannelError = [NSError errorWithDomain:@"fake" + code:0 + userInfo:nil]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *psmPublishedExpectation = + [[XCTestExpectation alloc] initWithDescription:@"PSM published with error."]; + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, fakePeripheralManager.didPublishL2CAPChannelError); + XCTAssertEqual(PSM, 0); + [psmPublishedExpectation fulfill]; + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + [self waitForExpectations:@[ psmPublishedExpectation ] timeout:kTestTimeout]; + } +} + +- (void)testPoweredOffUnpublishesChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, nil); + XCTAssertEqual(PSM, fakePeripheralManager.PSM); + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOff]; + + [self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout]; + } +} + +- (void)testPeripheralManagerDidUpdateStatePoweredOffUnpublishesChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + fakePeripheralManager.state = CBManagerStatePoweredOn; + [(id)l2capServer + peripheralManagerDidUpdateState:(CBPeripheralManager *)fakePeripheralManager]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, nil); + XCTAssertEqual(PSM, fakePeripheralManager.PSM); + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + + fakePeripheralManager.state = CBManagerStatePoweredOff; + [(id)l2capServer + peripheralManagerDidUpdateState:(CBPeripheralManager *)fakePeripheralManager]; + + [self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout]; + } +} + +- (void)testStartPeripheralManagerInitiallyOff { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *psmPublishedExpectation = + [[XCTestExpectation alloc] initWithDescription:@"PSM published."]; + XCTestExpectation *channelOpenedexpectation = + [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, nil); + XCTAssertEqual(PSM, fakePeripheralManager.PSM); + [psmPublishedExpectation fulfill]; + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error) { + [channelOpenedexpectation fulfill]; + }]; + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + [self waitForExpectations:@[ psmPublishedExpectation, channelOpenedexpectation ] + timeout:kTestTimeout]; + } +} + +- (void)testClose { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, nil); + XCTAssertEqual(PSM, fakePeripheralManager.PSM); + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + + [l2capServer close]; + + XCTAssertEqual([l2capServer PSM], 0); + } +} + +- (void)testCloseDoesNotUnpublishesChannelIfNotConnected { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + fakePeripheralManager.unpublishExpectation.inverted = YES; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + [l2capServer close]; + + [self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout]; + } +} + +- (void)testFailedToOpenL2CAPChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + fakePeripheralManager.didOpenL2CAPChannelError = [NSError errorWithDomain:@"fake" + code:0 + userInfo:nil]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *channelOpenedexpectation = + [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, nil); + XCTAssertEqual(PSM, fakePeripheralManager.PSM); + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error) { + XCTAssertNil(stream); + XCTAssertEqual(error, fakePeripheralManager.didOpenL2CAPChannelError); + [channelOpenedexpectation fulfill]; + }]; + [self waitForExpectations:@[ channelOpenedexpectation ] timeout:kTestTimeout]; + XCTAssertNil([l2capServer valueForKey:@"l2CAPChannel"]); + XCTAssertNil([l2capServer valueForKey:@"l2CAPStream"]); + } +} + +- (void)testPeripheralManagerDidUnpublishL2CAPChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + + [(id)l2capServer + peripheralManager:(CBPeripheralManager *)fakePeripheralManager + didUnpublishL2CAPChannel:l2capServer.PSM + error:[NSError errorWithDomain:@"fake" code:0 userInfo:nil]]; + + XCTAssertEqual(l2capServer.PSM, 0); + } +} + +- (void)testCloseL2CAPChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *channelOpenedexpectation = + [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error) { + [channelOpenedexpectation fulfill]; + }]; + [self waitForExpectations:@[ channelOpenedexpectation ] timeout:kTestTimeout]; + + [l2capServer closeL2CAPChannel]; + + XCTAssertNil([l2capServer valueForKey:@"l2CAPChannel"]); + XCTAssertNil([l2capServer valueForKey:@"l2CAPStream"]); + } +} + +- (void)testPeripheralManagerDidPublishL2CAPChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"completion called"]; + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(PSM, 1); + XCTAssertNil(error); + [expectation fulfill]; + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + [(id)l2capServer + peripheralManager:(CBPeripheralManager *)fakePeripheralManager + didPublishL2CAPChannel:1 + error:nil]; + [self waitForExpectations:@[ expectation ] timeout:kTestTimeout]; + XCTAssertEqual(l2capServer.PSM, 1); + } +} + +- (void)testPeripheralManagerDidPublishL2CAPChannelWithError { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"completion called"]; + NSError *publishError = [NSError errorWithDomain:@"test" code:0 userInfo:nil]; + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(PSM, 0); + XCTAssertEqualObjects(error, publishError); + [expectation fulfill]; + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + [(id)l2capServer + peripheralManager:(CBPeripheralManager *)fakePeripheralManager + didPublishL2CAPChannel:0 + error:publishError]; + [self waitForExpectations:@[ expectation ] timeout:kTestTimeout]; + XCTAssertEqual(l2capServer.PSM, 0); + } +} + +@end + diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMedium+Testing.h b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMedium+Testing.h index 5da28b57..9761a796 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMedium+Testing.h +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMedium+Testing.h @@ -24,16 +24,19 @@ NS_ASSUME_NONNULL_BEGIN @interface GNCBLEMedium (Testing) /** - * Creates a BLE Medium with a provided central manager. + * Creates a BLE Medium with a provided central manager and peripheral manager. * - * This is only exposed for testing and can be used to inject a fake central manager. + * This is only exposed for tests and can be used to inject a fake central manager and + * peripheral manager. * * @param centralManager The central manager instance. + * @param peripheralManager The peripheral manager instance. * @param queue The queue to run on, this must match the queue that the central manager's delegate - * is running on. Defaults to the main queue when @c nil. + * is running on. */ - (instancetype)initWithCentralManager:(id)centralManager - queue:(nullable dispatch_queue_t)queue; + peripheralManager:(nullable id)peripheralManager + queue:(dispatch_queue_t)queue; - (NSDictionary *)decodeAdvertisementData: (NSDictionary *)advertisementData; diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMediumTest.m b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMediumTest.m deleted file mode 100644 index 10b623d9..00000000 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMediumTest.m +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright 2023 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. - -#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h" - -#import -#import -#import - -#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTClient.h" -#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h" -#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheral.h" -#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPClient+Testing.h" -#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMedium+Testing.h" -#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCentralManager.h" -#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.h" -#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h" - -static NSString *const kServiceUUID = @"0000FEF3-0000-1000-8000-00805F9B34FB"; - -@interface GNCBLEMediumTest : XCTestCase -@end - -@implementation GNCBLEMediumTest - -#pragma mark - Supports Extended Advertisements - -- (void)testSupportsExtendedAdvertisements { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - - XCTAssertFalse([medium supportsExtendedAdvertisements]); -} - -#pragma mark - Start Scanning - -- (void)testStartScanning { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTestExpectation *startScanningExpectation = - [[XCTestExpectation alloc] initWithDescription:@"Start scanning."]; - XCTestExpectation *advertisementFoundExpectation = - [[XCTestExpectation alloc] initWithDescription:@"Advertisement found."]; - CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; - - [fakeCentralManager simulateCentralManagerDidUpdateState:CBManagerStatePoweredOn]; - - [medium startScanningForService:serviceUUID - advertisementFoundHandler:^(id peripheral, - NSDictionary *data) { - NSDictionary *expected = @{ - serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding], - }; - XCTAssertEqualObjects(expected, data); - [advertisementFoundExpectation fulfill]; - } - completionHandler:^(NSError *error) { - XCTAssertNil(error); - [startScanningExpectation fulfill]; - }]; - - [self waitForExpectations:@[ startScanningExpectation ] timeout:3]; - - XCTAssertEqualObjects(@[ serviceUUID ], fakeCentralManager.serviceUUIDs); - - [fakeCentralManager - simulateCentralManagerDidDiscoverPeripheral:[[GNCFakePeripheral alloc] init] - advertisementData:@{ - CBAdvertisementDataLocalNameKey : @"dGVzdA", - CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ], - }]; - - [self waitForExpectations:@[ advertisementFoundExpectation ] timeout:3]; -} - -- (void)testAlreadyScanning { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"Start scanning."]; - - [medium startScanningForService:[CBUUID UUIDWithString:kServiceUUID] - advertisementFoundHandler:^(id peripheral, - NSDictionary *data) { - } - completionHandler:nil]; - - [medium startScanningForService:[CBUUID UUIDWithString:kServiceUUID] - advertisementFoundHandler:^(id peripheral, - NSDictionary *data) { - } - completionHandler:^(NSError *error) { - XCTAssertNotNil(error); - [expectation fulfill]; - }]; - - [self waitForExpectations:@[ expectation ] timeout:3]; -} - -- (void)testStartStopStartScanning { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"Start scanning."]; - - CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; - - [medium startScanningForService:serviceUUID - advertisementFoundHandler:^(id peripheral, - NSDictionary *data) { - } - completionHandler:^(NSError *error) { - XCTAssertNil(error); - [medium stopScanningWithCompletionHandler:^(NSError *error) { - XCTAssertNil(error); - [medium startScanningForService:serviceUUID - advertisementFoundHandler:^(id peripheral, - NSDictionary *data) { - } - completionHandler:^(NSError *error) { - XCTAssertNil(error); - [expectation fulfill]; - }]; - }]; - }]; - - [self waitForExpectations:@[ expectation ] timeout:3]; -} - -#pragma mark - Decode Advertisement Data - -- (void)testDecodeAndroidStyleAdvertisementData { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; - - NSDictionary *expected = @{ - serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding], - }; - - NSDictionary *data = @{ - CBAdvertisementDataServiceDataKey : @{ - serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding], - }, - }; - NSDictionary *actual = [medium decodeAdvertisementData:data]; - - XCTAssertEqualObjects(expected, actual); -} - -- (void)testDecodeAndroidStyleAdvertisementDataWithLocalName { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; - - NSDictionary *expected = @{ - serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding], - }; - - NSDictionary *data = @{ - CBAdvertisementDataLocalNameKey : @"Nearby", // Just happens to be base64 decodable. - CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ], - CBAdvertisementDataServiceDataKey : @{ - serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding], - }, - }; - NSDictionary *actual = [medium decodeAdvertisementData:data]; - - XCTAssertEqualObjects(expected, actual); -} - -- (void)testDecodeAppleStyleAdvertisementData { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; - - NSDictionary *expected = @{ - serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding], - }; - - NSDictionary *data = @{ - CBAdvertisementDataLocalNameKey : @"dGVzdA", - CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ], - }; - NSDictionary *actual = [medium decodeAdvertisementData:data]; - - XCTAssertEqualObjects(expected, actual); -} - -- (void)testDecodeInvalidAdvertisementData { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; - - NSDictionary *data = @{ - CBAdvertisementDataLocalNameKey : @"!@#$", - CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ], - }; - NSDictionary *actual = [medium decodeAdvertisementData:data]; - - XCTAssertEqualObjects(@{}, actual); -} - -- (void)testDecodeEmptyAdvertisementData { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - - NSDictionary *actual = [medium decodeAdvertisementData:@{}]; - - XCTAssertEqualObjects(@{}, actual); -} - -#pragma mark - Start GATT Server - -- (void)testStartGATTServer { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"Start GATT server."]; - - [medium startGATTServerWithCompletionHandler:^(GNCBLEGATTServer *server, NSError *error) { - XCTAssertNotNil(server); - XCTAssertNil(error); - [expectation fulfill]; - }]; - - [self waitForExpectations:@[ expectation ] timeout:3]; -} - -#pragma mark - Start Advertising - -- (void)testStartAdvertising { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; - - // Start advertising is fully covered with @c GNCBLEGATTServer tests. We are passing invalid - // advertising data here so we can test code paths relevant to @c GNCBLEMedium, but bail early - // enough to avoid making actual CoreBluetooth calls. - [medium startAdvertisingData:@{} - completionHandler:^(NSError *error) { - XCTAssertNotNil(error); - [expectation fulfill]; - }]; - - [self waitForExpectations:@[ expectation ] timeout:3]; -} - -- (void)testStopAdvertising { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"Stop advertising."]; - - // Stop advertising is fully covered with @c GNCBLEGATTServer tests. We are only testing stopping - // without having started which tests the code paths relevant to @c GNCBLEMedium. - [medium stopAdvertisingWithCompletionHandler:^(NSError *error) { - XCTAssertNil(error); - [expectation fulfill]; - }]; - - [self waitForExpectations:@[ expectation ] timeout:3]; -} - -#pragma mark - Open L2CAP Channel - -- (void)testOpenL2CAPServerSocket { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTestExpectation *psmPublishedexpectation = - [[XCTestExpectation alloc] initWithDescription:@"PSM published."]; - XCTestExpectation *channelOpenedexpectation = - [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; - - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - // Open L2CAP server is fully covered with @c GNCBLEL2CAPServer tests. - [medium - openL2CAPServerWithPSMPublishedCompletionHandler:^(uint16_t PSM, NSError *error) { - XCTAssertEqual(PSM, fakePeripheralManager.PSM); - XCTAssertNil(error); - [psmPublishedexpectation fulfill]; - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *stream, NSError *error) { - XCTAssertNil(error); - [channelOpenedexpectation fulfill]; - } - peripheralManager:fakePeripheralManager]; - - [self waitForExpectations:@[ psmPublishedexpectation ] timeout:0.1]; - [self waitForExpectations:@[ channelOpenedexpectation ] timeout:0.5]; -} - -- (void)testSuccessfulOpenL2CAPChannel { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"Open L2CAP channel."]; - - GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; - GNCBLEL2CAPClient *l2capClient = - [[GNCBLEL2CAPClient alloc] initWithQueue:nil - requestDisconnectionHandler:^(id _Nonnull peripheral){ - }]; - [medium setL2CAPClient:l2capClient]; - [medium openL2CAPChannelWithPSM:123 - peripheral:fakePeripheral - completionHandler:^(GNCBLEL2CAPStream *_Nullable stream, NSError *_Nullable error) { - [expectation fulfill]; - }]; - - [self waitForExpectations:@[ expectation ] timeout:3]; -} - -#pragma mark - Connect - -- (void)testSuccessfulConnect { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Connect."]; - - [medium connectToGATTServerForPeripheral:[[GNCFakePeripheral alloc] init] - disconnectionHandler:nil - completionHandler:^(GNCBLEGATTClient *client, NSError *error) { - XCTAssertNotNil(client); - XCTAssertNil(error); - [expectation fulfill]; - }]; - - [self waitForExpectations:@[ expectation ] timeout:3]; -} - -- (void)testFailedConnect { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Connect."]; - - fakeCentralManager.didFailToConnectPeripheralError = [NSError errorWithDomain:@"fake" - code:0 - userInfo:nil]; - - [medium connectToGATTServerForPeripheral:[[GNCFakePeripheral alloc] init] - disconnectionHandler:nil - completionHandler:^(GNCBLEGATTClient *client, NSError *error) { - XCTAssertNil(client); - XCTAssertNotNil(error); - [expectation fulfill]; - }]; - - [self waitForExpectations:@[ expectation ] timeout:3]; -} - -- (void)testDisconnect { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTestExpectation *disconnectExpectation = - [[XCTestExpectation alloc] initWithDescription:@"Disconnect."]; - - GNCFakePeripheral *peripheral = [[GNCFakePeripheral alloc] init]; - - [medium connectToGATTServerForPeripheral:peripheral - disconnectionHandler:^() { - [disconnectExpectation fulfill]; - } - completionHandler:^(GNCBLEGATTClient *client, NSError *error) { - XCTAssertNotNil(client); - XCTAssertNil(error); - [client disconnect]; - }]; - - [self waitForExpectations:@[ disconnectExpectation ] timeout:3]; -} - -- (void)testRetrievePeripheralWithIdentifier_exists { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTAssertNotNil( - [medium retrievePeripheralWithIdentifier: - [[NSUUID alloc] initWithUUIDString:@"11111111-1111-1111-1111-111111111111"]]); -} - -- (void)testRetrievePeripheralWithIdentifier_doesNotExist { - GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; - GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager queue:nil]; - XCTAssertNil( - [medium retrievePeripheralWithIdentifier: - [[NSUUID alloc] initWithUUIDString:@"11111111-1111-1111-1111-111111111112"]]); -} - -@end diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMediumTest.mm b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMediumTest.mm new file mode 100644 index 00000000..49e0a462 --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMediumTest.mm @@ -0,0 +1,621 @@ +// Copyright 2023 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. + +#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h" + +#import +#import +#import + +#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTClient.h" +#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h" +#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheral.h" +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPClient+Testing.h" +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMedium+Testing.h" +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCentralManager.h" +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.h" +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h" + +#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "internal/flags/nearby_flags.h" + +static NSString *const kServiceUUID = @"0000FEF3-0000-1000-8000-00805F9B34FB"; + +@interface GNCBLEMediumTest : XCTestCase +@end + +@implementation GNCBLEMediumTest + +- (void)tearDown { + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); + [super tearDown]; +} + +- (void)testInit_allocatesMultiplexerWhenFlagEnabled { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + + id multiplexer = [medium valueForKey:@"_multiplexer"]; + if (enabled.boolValue) { + XCTAssertNotNil(multiplexer); + XCTAssertEqual(fakePeripheralManager.peripheralDelegate, multiplexer); + } else { + XCTAssertNil(multiplexer); + XCTAssertNil(fakePeripheralManager.peripheralDelegate); + } + } +} + +#pragma mark - Supports Extended Advertisements + +- (void)testSupportsExtendedAdvertisements { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + + XCTAssertFalse([medium supportsExtendedAdvertisements]); + } +} + +#pragma mark - Start Scanning + +- (void)testStartScanning { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + XCTestExpectation *startScanningExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Start scanning."]; + XCTestExpectation *advertisementFoundExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Advertisement found."]; + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; + + [fakeCentralManager simulateCentralManagerDidUpdateState:CBManagerStatePoweredOn]; + + [medium startScanningForService:serviceUUID + advertisementFoundHandler:^(id peripheral, + NSDictionary *data) { + NSDictionary *expected = @{ + serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding], + }; + XCTAssertEqualObjects(expected, data); + [advertisementFoundExpectation fulfill]; + } + completionHandler:^(NSError *error) { + XCTAssertNil(error); + [startScanningExpectation fulfill]; + }]; + + [self waitForExpectations:@[ startScanningExpectation ] timeout:3]; + + XCTAssertEqualObjects(@[ serviceUUID ], fakeCentralManager.serviceUUIDs); + + [fakeCentralManager + simulateCentralManagerDidDiscoverPeripheral:[[GNCFakePeripheral alloc] init] + advertisementData:@{ + CBAdvertisementDataLocalNameKey : @"dGVzdA", + CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ], + }]; + + [self waitForExpectations:@[ advertisementFoundExpectation ] timeout:3]; + } +} + +- (void)testAlreadyScanning { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start scanning."]; + + [medium startScanningForService:[CBUUID UUIDWithString:kServiceUUID] + advertisementFoundHandler:^(id peripheral, + NSDictionary *data) { + } + completionHandler:nil]; + + [medium startScanningForService:[CBUUID UUIDWithString:kServiceUUID] + advertisementFoundHandler:^(id peripheral, + NSDictionary *data) { + } + completionHandler:^(NSError *error) { + XCTAssertNotNil(error); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; + } +} + +- (void)testStartStopStartScanning { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start scanning."]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; + + [medium startScanningForService:serviceUUID + advertisementFoundHandler:^(id peripheral, + NSDictionary *data) { + } + completionHandler:^(NSError *error) { + XCTAssertNil(error); + [medium stopScanningWithCompletionHandler:^(NSError *error) { + XCTAssertNil(error); + [medium startScanningForService:serviceUUID + advertisementFoundHandler:^(id peripheral, + NSDictionary *data) { + } + completionHandler:^(NSError *error) { + XCTAssertNil(error); + [expectation fulfill]; + }]; + }]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; + } +} + +#pragma mark - Decode Advertisement Data + +- (void)testDecodeAndroidStyleAdvertisementData { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; + + NSDictionary *expected = @{ + serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding], + }; + + NSDictionary *data = @{ + CBAdvertisementDataServiceDataKey : @{ + serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding], + }, + }; + NSDictionary *actual = [medium decodeAdvertisementData:data]; + + XCTAssertEqualObjects(expected, actual); + } +} + +- (void)testDecodeAndroidStyleAdvertisementDataWithLocalName { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; + + NSDictionary *expected = @{ + serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding], + }; + + NSDictionary *data = @{ + CBAdvertisementDataLocalNameKey : @"Nearby", // Just happens to be base64 decodable. + CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ], + CBAdvertisementDataServiceDataKey : @{ + serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding], + }, + }; + NSDictionary *actual = [medium decodeAdvertisementData:data]; + + XCTAssertEqualObjects(expected, actual); + } +} + +- (void)testDecodeAppleStyleAdvertisementData { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; + + NSDictionary *expected = @{ + serviceUUID : [@"test" dataUsingEncoding:NSUTF8StringEncoding], + }; + + NSDictionary *data = @{ + CBAdvertisementDataLocalNameKey : @"dGVzdA", + CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ], + }; + NSDictionary *actual = [medium decodeAdvertisementData:data]; + + XCTAssertEqualObjects(expected, actual); + } +} + +- (void)testDecodeInvalidAdvertisementData { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID]; + + NSDictionary *data = @{ + CBAdvertisementDataLocalNameKey : @"!@#$", + CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ], + }; + NSDictionary *actual = [medium decodeAdvertisementData:data]; + + XCTAssertEqualObjects(@{}, actual); + } +} + +- (void)testDecodeEmptyAdvertisementData { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + + NSDictionary *actual = [medium decodeAdvertisementData:@{}]; + + XCTAssertEqualObjects(@{}, actual); + } +} + +#pragma mark - Start GATT Server + +- (void)testStartGATTServer { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start GATT server."]; + + [medium startGATTServerWithCompletionHandler:^(GNCBLEGATTServer *server, NSError *error) { + XCTAssertNotNil(server); + XCTAssertNil(error); + + // Verify internal structure based on flag + id mediumManager = [medium valueForKey:@"_peripheralManager"]; + id serverManager = [server valueForKey:@"_peripheralManager"]; + + if (enabled.boolValue) { + XCTAssertEqual(mediumManager, serverManager); + id multiplexer = [medium valueForKey:@"_multiplexer"]; + XCTAssertEqual([mediumManager peripheralDelegate], multiplexer); + } else { + XCTAssertNotEqual(mediumManager, serverManager); + id manager = [server valueForKey:@"_peripheralManager"]; + XCTAssertEqual([manager peripheralDelegate], server); + } + + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; + } +} + +#pragma mark - Start Advertising + +- (void)testStartAdvertising { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; + + // Start advertising is fully covered with @c GNCBLEGATTServer tests. We are passing invalid + // advertising data here so we can test code paths relevant to @c GNCBLEMedium, but bail early + // enough to avoid making actual CoreBluetooth calls. + [medium startAdvertisingData:@{} + completionHandler:^(NSError *error) { + XCTAssertNotNil(error); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; + } +} + +- (void)testStopAdvertising { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Stop advertising."]; + + // Stop advertising is fully covered with @c GNCBLEGATTServer tests. We are only testing stopping + // without having started which tests the code paths relevant to @c GNCBLEMedium. + [medium stopAdvertisingWithCompletionHandler:^(NSError *error) { + XCTAssertNil(error); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; + } +} + +#pragma mark - Open L2CAP Channel + +- (void)testOpenL2CAPServerSocket { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + XCTestExpectation *psmPublishedexpectation = + [[XCTestExpectation alloc] initWithDescription:@"PSM published."]; + XCTestExpectation *channelOpenedexpectation = + [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + // Open L2CAP server is fully covered with @c GNCBLEL2CAPServer tests. + [medium + openL2CAPServerWithPSMPublishedCompletionHandler:^(uint16_t PSM, NSError *error) { + XCTAssertEqual(PSM, fakePeripheralManager.PSM); + XCTAssertNil(error); + [psmPublishedexpectation fulfill]; + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *stream, NSError *error) { + XCTAssertNil(error); + [channelOpenedexpectation fulfill]; + } + peripheralManager:fakePeripheralManager]; + + [self waitForExpectations:@[ psmPublishedexpectation ] timeout:0.1]; + [self waitForExpectations:@[ channelOpenedexpectation ] timeout:0.5]; + } +} + +- (void)testSuccessfulOpenL2CAPChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Open L2CAP channel."]; + + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + GNCBLEL2CAPClient *l2capClient = + [[GNCBLEL2CAPClient alloc] initWithQueue:nil + requestDisconnectionHandler:^(id _Nonnull peripheral){ + }]; + [medium setL2CAPClient:l2capClient]; + [medium openL2CAPChannelWithPSM:123 + peripheral:fakePeripheral + completionHandler:^(GNCBLEL2CAPStream *_Nullable stream, NSError *_Nullable error) { + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; + } +} + +#pragma mark - Connect + +- (void)testSuccessfulConnect { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Connect."]; + + [medium connectToGATTServerForPeripheral:[[GNCFakePeripheral alloc] init] + disconnectionHandler:nil + completionHandler:^(GNCBLEGATTClient *client, NSError *error) { + XCTAssertNotNil(client); + XCTAssertNil(error); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; + } +} + +- (void)testFailedConnect { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Connect."]; + + fakeCentralManager.didFailToConnectPeripheralError = [NSError errorWithDomain:@"fake" + code:0 + userInfo:nil]; + + [medium connectToGATTServerForPeripheral:[[GNCFakePeripheral alloc] init] + disconnectionHandler:nil + completionHandler:^(GNCBLEGATTClient *client, NSError *error) { + XCTAssertNil(client); + XCTAssertNotNil(error); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; + } +} + +- (void)testDisconnect { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + XCTestExpectation *disconnectExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Disconnect."]; + + GNCFakePeripheral *peripheral = [[GNCFakePeripheral alloc] init]; + + [medium connectToGATTServerForPeripheral:peripheral + disconnectionHandler:^() { + [disconnectExpectation fulfill]; + } + completionHandler:^(GNCBLEGATTClient *client, NSError *error) { + XCTAssertNotNil(client); + XCTAssertNil(error); + [client disconnect]; + }]; + + [self waitForExpectations:@[ disconnectExpectation ] timeout:3]; + } +} + +- (void)testRetrievePeripheralWithIdentifier_exists { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + XCTAssertNotNil( + [medium retrievePeripheralWithIdentifier: + [[NSUUID alloc] initWithUUIDString:@"11111111-1111-1111-1111-111111111111"]]); + } +} + +- (void)testRetrievePeripheralWithIdentifier_doesNotExist { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEMedium *medium = [[GNCBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + XCTAssertNil( + [medium retrievePeripheralWithIdentifier: + [[NSUUID alloc] initWithUUIDString:@"11111111-1111-1111-1111-111111111112"]]); + } +} + +@end diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCBL2CAPChannel.h b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCBL2CAPChannel.h index 69a1b791..465b3120 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCBL2CAPChannel.h +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCBL2CAPChannel.h @@ -29,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN /** The socket file descriptor for the L2CAP channel. */ @property(nonatomic, readonly) int socketFD; /** The PSM (Protocol/Service Multiplexer) of the L2CAP channel. */ -@property(nonatomic, readonly) CBL2CAPPSM PSM; +@property(nonatomic, readwrite) CBL2CAPPSM PSM; @end diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.m b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.m index 912974df..3f18bf6a 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.m +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.m @@ -140,6 +140,7 @@ static const uint16_t kPSM = 192; GNCFakeCBL2CAPChannel *fakeChannel = [[GNCFakeCBL2CAPChannel alloc] init]; fakeChannel.inputStream = fakeStream.inputStream; fakeChannel.outputStream = fakeStream.outputStream; + fakeChannel.PSM = _PSM; [_peripheralDelegate gnc_peripheralManager:self didOpenL2CAPChannel:(CBL2CAPChannel *)fakeChannel error:_didOpenL2CAPChannelError]; diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCMBleUtilsTest.m b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCMBleUtilsTest.m index b1c08d95..11ca0c50 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCMBleUtilsTest.m +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCMBleUtilsTest.m @@ -131,7 +131,7 @@ static const NSTimeInterval kWaitForConnectionTimeout = 6.0; // Allow for the 5 GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init]; XCTestExpectation *expectation = [self expectationWithDescription:@"Connection success"]; - GNCMWaitForConnection((GNSSocket *)fakeSocket, ^(BOOL flag) { + GNCMWaitForConnection((GNSSocket *)fakeSocket, nil, ^(BOOL flag) { XCTAssertTrue(flag); [expectation fulfill]; }); @@ -142,12 +142,31 @@ static const NSTimeInterval kWaitForConnectionTimeout = 6.0; // Allow for the 5 [self waitForExpectationsWithTimeout:kTimeout handler:nil]; } +- (void)testWaitForConnection_CustomQueue { + GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init]; + dispatch_queue_t customQueue = dispatch_queue_create("com.google.nearby.testQueue", DISPATCH_QUEUE_SERIAL); + + XCTestExpectation *expectation = [self expectationWithDescription:@"Connection success on custom queue"]; + GNCMWaitForConnection((GNSSocket *)fakeSocket, customQueue, ^(BOOL flag) { + XCTAssertTrue(flag); + // Verify that we are on the custom queue + const char *label = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL); + XCTAssertEqual(strcmp(label, "com.google.nearby.testQueue"), 0); + [expectation fulfill]; + }); + + // Simulate the connection callback + [fakeSocket simulateSocketDidConnect]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; +} + - (void)testWaitForConnection_Failure_Disconnect { GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init]; XCTestExpectation *expectation = [self expectationWithDescription:@"Connection failed on disconnect"]; - GNCMWaitForConnection((GNSSocket *)fakeSocket, ^(BOOL flag) { + GNCMWaitForConnection((GNSSocket *)fakeSocket, nil, ^(BOOL flag) { XCTAssertFalse(flag); [expectation fulfill]; }); @@ -164,7 +183,7 @@ static const NSTimeInterval kWaitForConnectionTimeout = 6.0; // Allow for the 5 XCTestExpectation *expectation = [self expectationWithDescription:@"Connection failed on timeout"]; - GNCMWaitForConnection((GNSSocket *)fakeSocket, ^(BOOL flag) { + GNCMWaitForConnection((GNSSocket *)fakeSocket, nil, ^(BOOL flag) { XCTAssertFalse(flag); [expectation fulfill]; }); diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCPeripheralManagerMultiplexerTest.m b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCPeripheralManagerMultiplexerTest.m new file mode 100644 index 00000000..394eb4cc --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCPeripheralManagerMultiplexerTest.m @@ -0,0 +1,495 @@ +// Copyright 2026 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. + +#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManagerMultiplexer.h" + +#import +#import +#import + +#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManager.h" +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h" + +@interface GNCPeripheralManagerMultiplexerTest : XCTestCase +@end + +@interface FakePeripheralManagerDelegate : NSObject +@property(nonatomic) XCTestExpectation *expectation; +@property(nonatomic) BOOL didUpdateStateCalled; +@property(nonatomic) CBManagerState state; + +@property(nonatomic) BOOL didStartAdvertisingCalled; +@property(nonatomic) NSError *startAdvertisingError; + +@property(nonatomic) BOOL didAddServiceCalled; +@property(nonatomic) CBService *addedService; +@property(nonatomic) NSError *addServiceError; + +@property(nonatomic) BOOL didReceiveReadRequestCalled; +@property(nonatomic) CBATTRequest *readRequest; + +@property(nonatomic) BOOL didPublishL2CAPChannelCalled; +@property(nonatomic) CBL2CAPPSM publishedPSM; +@property(nonatomic) NSError *publishL2CAPChannelError; + +@property(nonatomic) BOOL didUnpublishL2CAPChannelCalled; +@property(nonatomic) CBL2CAPPSM unpublishedPSM; +@property(nonatomic) NSError *unpublishL2CAPChannelError; + +@property(nonatomic) BOOL didOpenL2CAPChannelCalled; +@property(nonatomic) CBL2CAPChannel *openedChannel; +@property(nonatomic) NSError *openL2CAPChannelError; + +@end + +@implementation FakePeripheralManagerDelegate + +- (void)gnc_peripheralManagerDidUpdateState:(id)peripheral { + _didUpdateStateCalled = YES; + _state = peripheral.state; + if (_expectation) { + [_expectation fulfill]; + } +} + +- (void)gnc_peripheralManagerDidStartAdvertising:(id)peripheral + error:(nullable NSError *)error { + _didStartAdvertisingCalled = YES; + _startAdvertisingError = error; + if (_expectation) { + [_expectation fulfill]; + } +} + +- (void)gnc_peripheralManager:(id)peripheral + didAddService:(CBService *)service + error:(nullable NSError *)error { + _didAddServiceCalled = YES; + _addedService = service; + _addServiceError = error; + if (_expectation) { + [_expectation fulfill]; + } +} + +- (void)gnc_peripheralManager:(id)peripheral + didReceiveReadRequest:(CBATTRequest *)request { + _didReceiveReadRequestCalled = YES; + _readRequest = request; + if (_expectation) { + [_expectation fulfill]; + } +} + +- (void)gnc_peripheralManager:(id)peripheral + didPublishL2CAPChannel:(CBL2CAPPSM)PSM + error:(nullable NSError *)error { + _didPublishL2CAPChannelCalled = YES; + _publishedPSM = PSM; + _publishL2CAPChannelError = error; + if (_expectation) { + [_expectation fulfill]; + } +} + +- (void)gnc_peripheralManager:(id)peripheral + didUnpublishL2CAPChannel:(CBL2CAPPSM)PSM + error:(NSError *)error { + _didUnpublishL2CAPChannelCalled = YES; + _unpublishedPSM = PSM; + _unpublishL2CAPChannelError = error; + if (_expectation) { + [_expectation fulfill]; + } +} + +- (void)gnc_peripheralManager:(id)peripheral + didOpenL2CAPChannel:(nullable CBL2CAPChannel *)channel + error:(nullable NSError *)error { + _didOpenL2CAPChannelCalled = YES; + _openedChannel = channel; + _openL2CAPChannelError = error; + if (_expectation) { + [_expectation fulfill]; + } +} + +- (void)peripheralManagerDidUpdateState:(nonnull CBPeripheralManager *)peripheral { + _didUpdateStateCalled = YES; + _state = peripheral.state; + if (_expectation) { + [_expectation fulfill]; + } +} + +- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral + error:(nullable NSError *)error { + _didStartAdvertisingCalled = YES; + _startAdvertisingError = error; + if (_expectation) { + [_expectation fulfill]; + } +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral + didAddService:(CBService *)service + error:(nullable NSError *)error { + _didAddServiceCalled = YES; + _addedService = service; + _addServiceError = error; + if (_expectation) { + [_expectation fulfill]; + } +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral + didReceiveReadRequest:(CBATTRequest *)request { + _didReceiveReadRequestCalled = YES; + _readRequest = request; + if (_expectation) { + [_expectation fulfill]; + } +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral + didPublishL2CAPChannel:(CBL2CAPPSM)PSM + error:(nullable NSError *)error { + _didPublishL2CAPChannelCalled = YES; + _publishedPSM = PSM; + _publishL2CAPChannelError = error; + if (_expectation) { + [_expectation fulfill]; + } +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral + didUnpublishL2CAPChannel:(CBL2CAPPSM)PSM + error:(nullable NSError *)error { + _didUnpublishL2CAPChannelCalled = YES; + _unpublishedPSM = PSM; + _unpublishL2CAPChannelError = error; + if (_expectation) { + [_expectation fulfill]; + } +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral + didOpenL2CAPChannel:(nullable CBL2CAPChannel *)channel + error:(nullable NSError *)error { + _didOpenL2CAPChannelCalled = YES; + _openedChannel = channel; + _openL2CAPChannelError = error; + if (_expectation) { + [_expectation fulfill]; + } +} + +@end + +@implementation GNCPeripheralManagerMultiplexerTest + +- (void)testMultiplexerForwardsCallbacks { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate1 = [[FakePeripheralManagerDelegate alloc] init]; + FakePeripheralManagerDelegate *delegate2 = [[FakePeripheralManagerDelegate alloc] init]; + + delegate1.expectation = [self expectationWithDescription:@"Delegate 1 called"]; + delegate2.expectation = [self expectationWithDescription:@"Delegate 2 called"]; + + [multiplexer addListener:delegate1]; + [multiplexer addListener:delegate2]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + fakeManager.state = CBManagerStatePoweredOn; + + [multiplexer gnc_peripheralManagerDidUpdateState:fakeManager]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + + XCTAssertTrue(delegate1.didUpdateStateCalled); + XCTAssertTrue(delegate2.didUpdateStateCalled); + XCTAssertEqual(delegate1.state, CBManagerStatePoweredOn); + XCTAssertEqual(delegate2.state, CBManagerStatePoweredOn); +} + +- (void)testMultiplexerRemovesListener { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate1 = [[FakePeripheralManagerDelegate alloc] init]; + + delegate1.expectation = [self expectationWithDescription:@"Delegate 1 called"]; + + [multiplexer addListener:delegate1]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + fakeManager.state = CBManagerStatePoweredOn; + + [multiplexer removeListener:delegate1]; + [multiplexer gnc_peripheralManagerDidUpdateState:fakeManager]; + + // We expect delegate1 NOT to be called. + // Since removals are async, we wait a bit to ensure it had a chance (or didn't). + XCTWaiterResult result = [XCTWaiter waitForExpectations:@[ delegate1.expectation ] timeout:0.5]; + XCTAssertEqual(result, XCTWaiterResultTimedOut); + XCTAssertFalse(delegate1.didUpdateStateCalled); +} + +- (void)testMultiplexerForwardsDidStartAdvertising { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init]; + delegate.expectation = [self expectationWithDescription:@"Delegate called"]; + [multiplexer addListener:delegate]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + NSError *error = [NSError errorWithDomain:@"test" code:1 userInfo:nil]; + + [multiplexer gnc_peripheralManagerDidStartAdvertising:fakeManager error:error]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(delegate.didStartAdvertisingCalled); + XCTAssertEqualObjects(delegate.startAdvertisingError, error); +} + +- (void)testMultiplexerForwardsDidAddService { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init]; + delegate.expectation = [self expectationWithDescription:@"Delegate called"]; + [multiplexer addListener:delegate]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + CBMutableService *service = + [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"180D"] primary:YES]; + NSError *error = [NSError errorWithDomain:@"test" code:2 userInfo:nil]; + + [multiplexer gnc_peripheralManager:fakeManager didAddService:service error:error]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(delegate.didAddServiceCalled); + XCTAssertEqualObjects(delegate.addedService, service); + XCTAssertEqualObjects(delegate.addServiceError, error); +} + +- (void)testMultiplexerForwardsDidReceiveReadRequest { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init]; + delegate.expectation = [self expectationWithDescription:@"Delegate called"]; + [multiplexer addListener:delegate]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + id request = [NSNull null]; // Use NSNull or any object as placeholder since we can't create + // CBATTRequest + + [multiplexer gnc_peripheralManager:fakeManager didReceiveReadRequest:request]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(delegate.didReceiveReadRequestCalled); + XCTAssertEqual(delegate.readRequest, request); +} + +- (void)testMultiplexerForwardsDidPublishL2CAPChannel { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init]; + delegate.expectation = [self expectationWithDescription:@"Delegate called"]; + [multiplexer addListener:delegate]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + CBL2CAPPSM psm = 42; + NSError *error = [NSError errorWithDomain:@"test" code:3 userInfo:nil]; + + [multiplexer gnc_peripheralManager:fakeManager didPublishL2CAPChannel:psm error:error]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(delegate.didPublishL2CAPChannelCalled); + XCTAssertEqual(delegate.publishedPSM, psm); + XCTAssertEqualObjects(delegate.publishL2CAPChannelError, error); +} + +- (void)testMultiplexerForwardsDidUnpublishL2CAPChannel { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init]; + delegate.expectation = [self expectationWithDescription:@"Delegate called"]; + [multiplexer addListener:delegate]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + CBL2CAPPSM psm = 42; + NSError *error = [NSError errorWithDomain:@"test" code:4 userInfo:nil]; + + [multiplexer gnc_peripheralManager:fakeManager didUnpublishL2CAPChannel:psm error:error]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(delegate.didUnpublishL2CAPChannelCalled); + XCTAssertEqual(delegate.unpublishedPSM, psm); + XCTAssertEqualObjects(delegate.unpublishL2CAPChannelError, error); +} + +- (void)testMultiplexerForwardsDidOpenL2CAPChannel { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init]; + delegate.expectation = [self expectationWithDescription:@"Delegate called"]; + [multiplexer addListener:delegate]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + id channel = [NSNull null]; // Placeholder + NSError *error = [NSError errorWithDomain:@"test" code:5 userInfo:nil]; + + [multiplexer gnc_peripheralManager:fakeManager didOpenL2CAPChannel:channel error:error]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(delegate.didOpenL2CAPChannelCalled); + XCTAssertEqual(delegate.openedChannel, channel); + XCTAssertEqualObjects(delegate.openL2CAPChannelError, error); +} + +- (void)testCBPeripheralManagerDelegateDidUpdateState { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init]; + delegate.expectation = [self expectationWithDescription:@"Delegate called"]; + [multiplexer addListener:delegate]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + fakeManager.state = CBManagerStatePoweredOn; + + [multiplexer peripheralManagerDidUpdateState:(CBPeripheralManager *)fakeManager]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(delegate.didUpdateStateCalled); + XCTAssertEqual(delegate.state, CBManagerStatePoweredOn); +} + +- (void)testCBPeripheralManagerDelegateDidStartAdvertising { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init]; + delegate.expectation = [self expectationWithDescription:@"Delegate called"]; + [multiplexer addListener:delegate]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + NSError *error = [NSError errorWithDomain:@"test" code:10 userInfo:nil]; + + [multiplexer peripheralManagerDidStartAdvertising:(CBPeripheralManager *)fakeManager error:error]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(delegate.didStartAdvertisingCalled); + XCTAssertEqualObjects(delegate.startAdvertisingError, error); +} + +- (void)testCBPeripheralManagerDelegateDidAddService { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init]; + delegate.expectation = [self expectationWithDescription:@"Delegate called"]; + [multiplexer addListener:delegate]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + CBMutableService *service = + [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"180F"] primary:YES]; + NSError *error = [NSError errorWithDomain:@"test" code:11 userInfo:nil]; + + [multiplexer peripheralManager:(CBPeripheralManager *)fakeManager didAddService:service error:error]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(delegate.didAddServiceCalled); + XCTAssertEqualObjects(delegate.addedService, service); + XCTAssertEqualObjects(delegate.addServiceError, error); +} + +- (void)testCBPeripheralManagerDelegateDidReceiveReadRequest { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init]; + delegate.expectation = [self expectationWithDescription:@"Delegate called"]; + [multiplexer addListener:delegate]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + id request = [NSNull null]; + + [multiplexer peripheralManager:(CBPeripheralManager *)fakeManager didReceiveReadRequest:request]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(delegate.didReceiveReadRequestCalled); + XCTAssertEqual(delegate.readRequest, request); +} + +- (void)testCBPeripheralManagerDelegateDidPublishL2CAPChannel { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init]; + delegate.expectation = [self expectationWithDescription:@"Delegate called"]; + [multiplexer addListener:delegate]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + CBL2CAPPSM psm = 100; + NSError *error = [NSError errorWithDomain:@"test" code:13 userInfo:nil]; + + [multiplexer peripheralManager:(CBPeripheralManager *)fakeManager + didPublishL2CAPChannel:psm + error:error]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(delegate.didPublishL2CAPChannelCalled); + XCTAssertEqual(delegate.publishedPSM, psm); + XCTAssertEqualObjects(delegate.publishL2CAPChannelError, error); +} + +- (void)testCBPeripheralManagerDelegateDidUnpublishL2CAPChannel { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init]; + delegate.expectation = [self expectationWithDescription:@"Delegate called"]; + [multiplexer addListener:delegate]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + CBL2CAPPSM psm = 101; + NSError *error = [NSError errorWithDomain:@"test" code:14 userInfo:nil]; + + [multiplexer peripheralManager:(CBPeripheralManager *)fakeManager + didUnpublishL2CAPChannel:psm + error:error]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(delegate.didUnpublishL2CAPChannelCalled); + XCTAssertEqual(delegate.unpublishedPSM, psm); + XCTAssertEqualObjects(delegate.unpublishL2CAPChannelError, error); +} + +- (void)testCBPeripheralManagerDelegateDidOpenL2CAPChannel { + GNCPeripheralManagerMultiplexer *multiplexer = + [[GNCPeripheralManagerMultiplexer alloc] initWithCallbackQueue:dispatch_get_main_queue()]; + FakePeripheralManagerDelegate *delegate = [[FakePeripheralManagerDelegate alloc] init]; + delegate.expectation = [self expectationWithDescription:@"Delegate called"]; + [multiplexer addListener:delegate]; + + GNCFakePeripheralManager *fakeManager = [[GNCFakePeripheralManager alloc] init]; + id channel = [NSNull null]; + NSError *error = [NSError errorWithDomain:@"test" code:15 userInfo:nil]; + + [multiplexer peripheralManager:(CBPeripheralManager *)fakeManager + didOpenL2CAPChannel:channel + error:error]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(delegate.didOpenL2CAPChannelCalled); + XCTAssertEqual(delegate.openedChannel, channel); + XCTAssertEqualObjects(delegate.openL2CAPChannelError, error); +} + +@end diff --git a/internal/platform/implementation/apple/Mediums/WiFiCommon/BUILD b/internal/platform/implementation/apple/Mediums/WiFiCommon/BUILD index 43313086..59dfe33c 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiCommon/BUILD +++ b/internal/platform/implementation/apple/Mediums/WiFiCommon/BUILD @@ -32,7 +32,7 @@ objc_library( "GNCNWFramework.m", "GNCNWFrameworkError.m", "GNCNWFrameworkServerSocket.m", - "GNCNWFrameworkSocket.m", + "GNCNWFrameworkSocket.mm", "GNCNWListenerImpl.m", "GNCNWParameters.m", ], diff --git a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h index 1893dbd6..df429dd8 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h +++ b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h @@ -26,4 +26,5 @@ typedef NS_ERROR_ENUM(GNCNWFrameworkErrorDomain, GNCNWFrameworkError){ GNCNWFrameworkErrorUnknown, GNCNWFrameworkErrorTimedOut, GNCNWFrameworkErrorDuplicateDiscovererForServiceType, + GNCNWFrameworkErrorNotConnected, }; diff --git a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.h b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.h index 4336418c..a13b2774 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.h +++ b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.h @@ -15,6 +15,12 @@ #import #import +#ifdef __cplusplus +#include +#include +#endif + + @protocol GNCNWConnection; @interface GNCNWFrameworkSocket : NSObject @@ -44,6 +50,21 @@ */ - (nullable NSData *)readMaxLength:(NSUInteger)length error:(NSError **_Nullable)error; +/** + * Reads the requested amount of bytes from the connection and converts it to a string. + * + * Blocks execution until the bytes have been read or an error occurs. + * + * @param length The number of bytes to read. + * @param[out] error Error that will be populated on failure. A read may return non-nil data along + * with an error. This normally happens if the data read is shorter than the + * requested length. + */ +#ifdef __cplusplus +- (std::optional)readStringWithMaxLength:(NSUInteger)length + error:(NSError **_Nullable)error; +#endif + /** * Writes the given data to the connection. * @@ -54,6 +75,17 @@ */ - (BOOL)write:(NSData *)data error:(NSError **_Nullable)error; +/** + * Writes raw bytes to the connection. + * + * @param bytes The buffer to write. + * @param length The number of bytes to write. + * @param error Error that will be populated on failure. + */ +- (BOOL)writeBytes:(const void *)bytes + length:(NSUInteger)length + error:(NSError **_Nullable)error; + /** * Gracefully closes the connection to remote endpoint. * diff --git a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.m b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.mm similarity index 55% rename from internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.m rename to internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.mm index 76d6c601..001fe2e0 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.m +++ b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.mm @@ -17,6 +17,9 @@ #import #import +#include +#include + #import "internal/platform/implementation/apple/Log/GNCLogger.h" #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWConnection.h" #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h" @@ -85,6 +88,52 @@ static const NSTimeInterval kConnectionWriteTimeout = 5.0; // 5 seconds timeout return blockResult; } +- (std::optional)readStringWithMaxLength:(NSUInteger)length error:(NSError **)error { + if (!self.connection) { + if (error) { + *error = [NSError errorWithDomain:GNCNWFrameworkErrorDomain + code:GNCNWFrameworkErrorNotConnected + userInfo:nil]; + } + return std::nullopt; + } + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block std::string resultString; + __block NSError *blockError = nil; + __block BOOL contentReceived = NO; + + [self.connection + receiveMessageWithMinLength:(uint32_t)length + maxLength:(uint32_t)length + completionHandler:^(dispatch_data_t _Nullable content, + nw_content_context_t _Nullable context, bool isComplete, + nw_error_t _Nullable receiveError) { + if (receiveError) { + blockError = (__bridge_transfer NSError *)nw_error_copy_cf_error(receiveError); + } + if (content) { + contentReceived = YES; + // OPTIMIZATION: Copy directly from dispatch_data_t into std::string + resultString.reserve(dispatch_data_get_size(content)); + dispatch_data_apply(content, ^bool(dispatch_data_t region, size_t offset, + const void *buffer, size_t size) { + resultString.append((const char *)buffer, size); + return true; + }); + } + dispatch_semaphore_signal(semaphore); + }]; + + // Block the current thread until the network callback completes. + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + + if (error != nil) { + *error = blockError; + } + return (!contentReceived) ? std::nullopt : std::make_optional(std::move(resultString)); +} + - (BOOL)write:(NSData *)data error:(NSError **)error { if (!self.connection) { if (error) { @@ -136,6 +185,50 @@ static const NSTimeInterval kConnectionWriteTimeout = 5.0; // 5 seconds timeout return signaled && blockSuccess; } +- (BOOL)writeBytes:(const void *)bytes length:(NSUInteger)length error:(NSError **)error { + if (!self.connection) { + if (error) { + *error = [NSError errorWithDomain:GNCNWFrameworkErrorDomain + code:GNCNWFrameworkErrorNotConnected + userInfo:nil]; + } + return NO; + } + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + + __block NSError *blockError = nil; + + // OPTIMIZATION: Use DISPATCH_DATA_DESTRUCTOR_DEFAULT to perform a + // single copy into a GCD-managed buffer. No NSData required. + // TODO: edwinwu - Investigate to see if it is worth to make it zero-copy by replacing + // DISPATCH_DATA_DESTRUCTOR_DEFAULT with a custom empty destructor: + // dispatch_data_t dispatchData = dispatch_data_create(bytes, length, nil, ^{ + // // Zero-copy: ownership remains with the caller. + // }); + dispatch_data_t dispatchData = + dispatch_data_create(bytes, length, nil, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + + [self.connection sendData:dispatchData + context:NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT + isComplete:NO + completionHandler:^(nw_error_t _Nullable sendError) { + if (sendError) { + blockError = (__bridge_transfer NSError *)nw_error_copy_cf_error(sendError); + } + dispatch_semaphore_signal(semaphore); + }]; + + // Wait until signaled or the 5-second timeout passes + intptr_t waitResult = dispatch_semaphore_wait( + semaphore, + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kConnectionWriteTimeout * NSEC_PER_SEC))); + if (error != nil) { + *error = blockError; + } + return (waitResult == 0) && (blockError == nil); +} + - (void)close { [_connection cancel]; _connection = nil; diff --git a/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/BUILD b/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/BUILD index 57afaee9..5aaa96ad 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/BUILD +++ b/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/BUILD @@ -29,7 +29,7 @@ objc_library( "GNCFakeNWConnection.m", "GNCFakeNWFramework.m", "GNCFakeNWFrameworkServerSocket.m", - "GNCFakeNWFrameworkSocket.m", + "GNCFakeNWFrameworkSocket.mm", "GNCFakeNWListener.m", ], hdrs = [ @@ -57,7 +57,7 @@ objc_library( "GNCNWBrowserImplTest.m", "GNCNWConnectionImplTest.m", "GNCNWFrameworkServerSocketTest.m", - "GNCNWFrameworkSocketTest.m", + "GNCNWFrameworkSocketTest.mm", "GNCNWFrameworkTest.m", "GNCNWListenerImplTest.m", "GNCNWParametersTest.m", diff --git a/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWFrameworkSocket.m b/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWFrameworkSocket.mm similarity index 65% rename from internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWFrameworkSocket.m rename to internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWFrameworkSocket.mm index 6bdaf67c..38428fb6 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWFrameworkSocket.m +++ b/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWFrameworkSocket.mm @@ -44,6 +44,25 @@ return [NSData data]; } +- (std::optional)readStringWithMaxLength:(NSUInteger)length error:(NSError **)error { + if (self.readError) { + if (error) *error = self.readError; + return std::nullopt; + } + if (self.dataToRead) { + NSData *data = self.dataToRead; + self.dataToRead = nil; + NSUInteger actualLength = MIN(length, data.length); + if (data.length > actualLength) { + self.dataToRead = + [data subdataWithRange:NSMakeRange(actualLength, data.length - actualLength)]; + } + NSData *returnData = [data subdataWithRange:NSMakeRange(0, actualLength)]; + return std::string((const char *)returnData.bytes, returnData.length); + } + return std::string(); +} + - (BOOL)write:(NSData *)data error:(NSError **)error { if (self.writeError) { if (error) { @@ -55,6 +74,17 @@ return YES; } +- (BOOL)writeBytes:(const void *)bytes length:(NSUInteger)length error:(NSError **)error { + if (self.writeError) { + if (error) { + *error = self.writeError; + } + return NO; + } + [self.writtenData appendBytes:bytes length:length]; + return YES; +} + - (void)close { self.isClosed = YES; } diff --git a/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCNWFrameworkSocketTest.m b/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCNWFrameworkSocketTest.mm similarity index 61% rename from internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCNWFrameworkSocketTest.m rename to internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCNWFrameworkSocketTest.mm index e194a0f4..bce7847b 100644 --- a/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCNWFrameworkSocketTest.m +++ b/internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCNWFrameworkSocketTest.mm @@ -17,6 +17,9 @@ #import #import +#include +#include + #import "internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWConnection.h" NS_ASSUME_NONNULL_BEGIN @@ -75,6 +78,38 @@ NS_ASSUME_NONNULL_BEGIN XCTAssertNil(error); } +- (void)testReadStringWithMaxLength_Success { + NSError *error = nil; + NSString *testString = @"testData"; + NSData *testData = [testString dataUsingEncoding:NSUTF8StringEncoding]; + dispatch_data_t dispatchData = dispatch_data_create(testData.bytes, testData.length, dispatch_get_main_queue(), ^{}); + _fakeConnection.dataToReceive = dispatchData; + + std::optional receivedString = [_socket readStringWithMaxLength:testData.length error:&error]; + + XCTAssertTrue(receivedString.has_value()); + XCTAssertEqualObjects(@(receivedString.value().c_str()), testString); + XCTAssertNil(error); +} + +- (void)testReadStringWithMaxLength_Error { + NSError *error = nil; + _fakeConnection.simulateReceiveFailure = YES; + + std::optional receivedString = [_socket readStringWithMaxLength:10 error:&error]; + + XCTAssertFalse(receivedString.has_value()); + XCTAssertNil(error); // Fake doesn't produce an NSError +} + +- (void)testReadStringWithMaxLength_Zero { + NSError *error = nil; + std::optional receivedString = [_socket readStringWithMaxLength:0 error:&error]; + + XCTAssertFalse(receivedString.has_value()); + XCTAssertNil(error); +} + - (void)testWrite_Success { NSError *error = nil; NSString *testString = @"testData"; @@ -97,6 +132,28 @@ NS_ASSUME_NONNULL_BEGIN XCTAssertFalse(result); } +- (void)testWriteBytes_Success { + NSError *error = nil; + NSString *testString = @"testData"; + NSData *testData = [testString dataUsingEncoding:NSUTF8StringEncoding]; + + BOOL result = [_socket writeBytes:testData.bytes length:testData.length error:&error]; + + XCTAssertTrue(result); + XCTAssertNil(error); +} + +- (void)testWriteBytes_Error { + NSError *error = nil; + NSString *testString = @"testData"; + NSData *testData = [testString dataUsingEncoding:NSUTF8StringEncoding]; + _fakeConnection.simulateSendFailure = YES; + + BOOL result = [_socket writeBytes:testData.bytes length:testData.length error:&error]; + + XCTAssertFalse(result); +} + - (void)testClose { XCTAssertFalse(_fakeConnection.cancelCalled); [_socket close]; @@ -105,6 +162,7 @@ NS_ASSUME_NONNULL_BEGIN NSError *error = nil; XCTAssertNil([_socket readMaxLength:10 error:&error]); XCTAssertFalse([_socket write:[NSData data] error:&error]); + XCTAssertFalse([_socket writeBytes:"test" length:4 error:&error]); } @end diff --git a/internal/platform/implementation/apple/Tests/BUILD b/internal/platform/implementation/apple/Tests/BUILD index b27b3c4b..32369963 100644 --- a/internal/platform/implementation/apple/Tests/BUILD +++ b/internal/platform/implementation/apple/Tests/BUILD @@ -56,8 +56,10 @@ objc_library( "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", "//internal/platform/implementation:types", + "//internal/platform/implementation:webrtc_platform", "//internal/platform/implementation/apple", # buildcleaner: keep "//internal/platform/implementation/apple:Shared", + "//internal/platform/implementation/apple:apple_webrtc", # buildcleaner: keep "//internal/platform/implementation/apple:ble_v2", "//internal/platform/implementation/apple:network_utils", "//internal/platform/implementation/apple/Flags", diff --git a/internal/platform/implementation/apple/Tests/GNCAwdlMediumTest.mm b/internal/platform/implementation/apple/Tests/GNCAwdlMediumTest.mm index 4ea125a8..6eeec152 100644 --- a/internal/platform/implementation/apple/Tests/GNCAwdlMediumTest.mm +++ b/internal/platform/implementation/apple/Tests/GNCAwdlMediumTest.mm @@ -18,6 +18,9 @@ #include +#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "internal/flags/nearby_flags.h" + #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h" #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFramework.h" #import "internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWConnection.h" @@ -45,6 +48,7 @@ static const int kTestPort = 1234; - (void)tearDown { _awdlMedium.reset(); + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); [super tearDown]; } @@ -128,6 +132,10 @@ static const int kTestPort = 1234; } - (void)testSocketAndStream { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + ::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy, + false); + // Create a server socket. std::unique_ptr serverSocket = _awdlMedium->ListenForService(kTestPort); @@ -167,6 +175,49 @@ static const int kTestPort = 1234; XCTAssertTrue(fakeServerSocket.isClosed); } +- (void)testSocketAndStream_SingleCopyEnabled { + // Enable the flag. + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + ::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy, + true); + + // Create a server socket. + std::unique_ptr serverSocket = + _awdlMedium->ListenForService(kTestPort); + XCTAssertTrue(serverSocket != nullptr); + + GNCFakeNWFrameworkServerSocket* fakeServerSocket = + (GNCFakeNWFrameworkServerSocket*)_fakeNWFramework.serverSockets[0]; + GNCFakeNWConnection* connection = [[GNCFakeNWConnection alloc] init]; + GNCFakeNWFrameworkSocket* fakeSocket = + [[GNCFakeNWFrameworkSocket alloc] initWithConnection:connection]; + fakeServerSocket.socketToReturnOnAccept = fakeSocket; + + // Accept a client socket. + std::unique_ptr clientSocket = serverSocket->Accept(); + XCTAssertTrue(clientSocket != nullptr); + + // Test input stream with optimized single-copy read. + nearby::InputStream& inputStream = clientSocket->GetInputStream(); + fakeSocket.dataToRead = [@"optimized awdl data" dataUsingEncoding:NSUTF8StringEncoding]; + // "optimized awdl data" is 19 bytes. + nearby::ExceptionOr readData = inputStream.Read(19); + + XCTAssertTrue(readData.ok()); + XCTAssertEqual(std::string(readData.result()), "optimized awdl data"); + + // Test output stream. + nearby::OutputStream& outputStream = clientSocket->GetOutputStream(); + absl::string_view writeData("write data"); + XCTAssertTrue(outputStream.Write(writeData).Ok()); + XCTAssertEqualObjects(fakeSocket.writtenData, + [@"write data" dataUsingEncoding:NSUTF8StringEncoding]); + + // Clean up. + XCTAssertTrue(clientSocket->Close().Ok()); + XCTAssertTrue(serverSocket->Close().Ok()); +} + - (void)testServerSocketGetIPAddress { // Create a server socket. std::unique_ptr serverSocket = @@ -195,6 +246,39 @@ static const int kTestPort = 1234; XCTAssertTrue(serverSocket->Close().Ok()); } +- (void)testOutputStreamWrite_SingleCopyEnabled { + // Enable the flag. + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + ::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy, + true); + + // Create a server socket and accept a client. + std::unique_ptr serverSocket = + _awdlMedium->ListenForService(kTestPort); + GNCFakeNWFrameworkServerSocket* fakeServerSocket = + (GNCFakeNWFrameworkServerSocket*)_fakeNWFramework.serverSockets[0]; + GNCFakeNWConnection* connection = [[GNCFakeNWConnection alloc] init]; + GNCFakeNWFrameworkSocket* fakeSocket = + [[GNCFakeNWFrameworkSocket alloc] initWithConnection:connection]; + fakeServerSocket.socketToReturnOnAccept = fakeSocket; + std::unique_ptr clientSocket = serverSocket->Accept(); + XCTAssertTrue(clientSocket != nullptr); + + // Test output stream. + nearby::OutputStream& outputStream = clientSocket->GetOutputStream(); + absl::string_view writeData("optimized write data"); + XCTAssertTrue(outputStream.Write(writeData).Ok()); + XCTAssertEqualObjects(fakeSocket.writtenData, + [@"optimized write data" dataUsingEncoding:NSUTF8StringEncoding]); + + // Clean up. + XCTAssertTrue(clientSocket->Close().Ok()); + XCTAssertTrue(serverSocket->Close().Ok()); + + // Reset the flag. + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); +} + - (void)testOutputStreamClose { // Create a server socket and accept a client. std::unique_ptr serverSocket = diff --git a/internal/platform/implementation/apple/Tests/GNCDeviceInfoTest.mm b/internal/platform/implementation/apple/Tests/GNCDeviceInfoTest.mm index 39988866..edaf54ad 100644 --- a/internal/platform/implementation/apple/Tests/GNCDeviceInfoTest.mm +++ b/internal/platform/implementation/apple/Tests/GNCDeviceInfoTest.mm @@ -49,27 +49,20 @@ } - (void)testGetDownloadPath { - XCTAssertNotNil(@(_deviceInfo->GetDownloadPath().value().GetPath().c_str())); + XCTAssertNotNil(@(_deviceInfo->GetDownloadPath().GetPath().c_str())); } - (void)testGetLocalAppDataPath { - XCTAssertNotNil(@(_deviceInfo->GetLocalAppDataPath().value().GetPath().c_str())); -} - -- (void)testGetCommonAppDataPath { - XCTAssertNotNil(@(_deviceInfo->GetCommonAppDataPath().value().GetPath().c_str())); + XCTAssertNotNil( + @(_deviceInfo->GetLocalAppDataPath(nearby::FilePath("sub_path")).GetPath().c_str())); } - (void)testGetTemporaryPath { - XCTAssertNotNil(@(_deviceInfo->GetTemporaryPath().value().GetPath().c_str())); + XCTAssertNotNil(@(_deviceInfo->GetTemporaryPath().GetPath().c_str())); } - (void)testGetLogPath { - XCTAssertNotNil(@(_deviceInfo->GetLogPath().value().GetPath().c_str())); -} - -- (void)testGetCrashDumpPath { - XCTAssertNotNil(@(_deviceInfo->GetCrashDumpPath().value().GetPath().c_str())); + XCTAssertNotNil(@(_deviceInfo->GetLogPath().GetPath().c_str())); } - (void)testIsScreenLocked { diff --git a/internal/platform/implementation/apple/Tests/GNCMultiThreadExecutorTest.mm b/internal/platform/implementation/apple/Tests/GNCMultiThreadExecutorTest.mm index 886ff646..2d794f65 100644 --- a/internal/platform/implementation/apple/Tests/GNCMultiThreadExecutorTest.mm +++ b/internal/platform/implementation/apple/Tests/GNCMultiThreadExecutorTest.mm @@ -93,7 +93,7 @@ using MultiThreadExecutor = ::nearby::api::SubmittableExecutor; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0); XCTestExpectation *expectation = [self expectationWithDescription:@"finished"]; - const int kRunnableCount = 1000; + const int kRunnableCount = 100; for (int i = 0; i < kRunnableCount; i++) { executor->Execute([self]() { self.counter++; }); } diff --git a/internal/platform/implementation/apple/Tests/GNCPlatformTest.mm b/internal/platform/implementation/apple/Tests/GNCPlatformTest.mm index cfaaebac..22afb8c1 100644 --- a/internal/platform/implementation/apple/Tests/GNCPlatformTest.mm +++ b/internal/platform/implementation/apple/Tests/GNCPlatformTest.mm @@ -13,6 +13,7 @@ // limitations under the License. #include "internal/platform/implementation/platform.h" +#include "internal/platform/implementation/webrtc_platform.h" #import #import @@ -320,7 +321,7 @@ void GNCEnsureFileAtPath(std::string path) { } - (void)testCreateWebRtcMedium { - auto webrtc_medium = nearby::api::ImplementationPlatform::CreateWebRtcMedium(); + auto webrtc_medium = nearby::api::WebRtcImplementationPlatform::CreateWebRtcMedium(); XCTAssertNotEqual(webrtc_medium.get(), nullptr); } diff --git a/internal/platform/implementation/apple/Tests/GNCSingleThreadExecutorTest.mm b/internal/platform/implementation/apple/Tests/GNCSingleThreadExecutorTest.mm index e5857cb8..8e5e1dbf 100644 --- a/internal/platform/implementation/apple/Tests/GNCSingleThreadExecutorTest.mm +++ b/internal/platform/implementation/apple/Tests/GNCSingleThreadExecutorTest.mm @@ -76,20 +76,14 @@ using SingleThreadExecutor = ::nearby::api::SubmittableExecutor; // Tests that shutting down an existing task allows to complete. - (void)testShutdownToAllowExistingTaskComplete { std::unique_ptr executor([self executor]); - - dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_TARGET_QUEUE_DEFAULT, 0); XCTestExpectation *expectation = [self expectationWithDescription:@"finished"]; - - executor->Execute([self]() { self.counter++; }); - - executor->Shutdown(); - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), queue, ^{ - XCTAssertEqual(self.counter, 1); + executor->Execute([self, expectation]() { + self.counter++; [expectation fulfill]; }); - - [self waitForExpectationsWithTimeout:0.5 handler:nil]; + executor->Shutdown(); + [self waitForExpectationsWithTimeout:1.0 handler:nil]; + XCTAssertEqual(self.counter, 1); } @end diff --git a/internal/platform/implementation/apple/Tests/GNCTimerTest.mm b/internal/platform/implementation/apple/Tests/GNCTimerTest.mm index 269bdf0f..32c895f1 100644 --- a/internal/platform/implementation/apple/Tests/GNCTimerTest.mm +++ b/internal/platform/implementation/apple/Tests/GNCTimerTest.mm @@ -65,7 +65,7 @@ auto timer = std::make_unique(); std::atomic fireCount = 0; - XCTAssertTrue(timer->Create(10, 10, [&]() { + XCTAssertTrue(timer->Create(100, 100, [&]() { if (fireCount.fetch_add(1) == 1) { dispatch_async(dispatch_get_main_queue(), ^{ [expectation fulfill]; @@ -73,9 +73,9 @@ } })); - [self waitForExpectationsWithTimeout:1.0 handler:nil]; + [self waitForExpectationsWithTimeout:2.0 handler:nil]; XCTAssertTrue(timer->Stop()); - XCTAssertEqual(fireCount.load(), 2); + XCTAssertGreaterThanOrEqual(fireCount.load(), 2); } - (void)testRestart { diff --git a/internal/platform/implementation/apple/Tests/GNCWifiHotspotMediumTest.mm b/internal/platform/implementation/apple/Tests/GNCWifiHotspotMediumTest.mm index 59687ab1..8c2e7803 100644 --- a/internal/platform/implementation/apple/Tests/GNCWifiHotspotMediumTest.mm +++ b/internal/platform/implementation/apple/Tests/GNCWifiHotspotMediumTest.mm @@ -19,8 +19,11 @@ #include +#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "internal/flags/nearby_flags.h" #include "internal/platform/byte_array.h" #include "internal/platform/exception.h" + #import "internal/platform/implementation/apple/Mediums/CoreLocation/CLLocationManager/Fake/CLLocationManagerFake.h" #import "internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotMedium.h" #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h" @@ -59,8 +62,8 @@ const char kIPAddress[] = "192.168.1.2"; _medium.locationManager = _fakeLocationManager; _hotspotMedium = std::make_unique(_medium); _service_address = { - .address = {static_cast(192), static_cast(168), 1, 2}, - .port = 1234, + .address = {static_cast(192), static_cast(168), 1, 2}, + .port = 1234, }; } @@ -98,6 +101,10 @@ const char kIPAddress[] = "192.168.1.2"; } - (void)testInputStreamRead { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + ::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy, + false); + nearby::CancellationFlag cancellationFlag; std::unique_ptr socket = _hotspotMedium->ConnectToService(_service_address, &cancellationFlag); @@ -110,6 +117,28 @@ const char kIPAddress[] = "192.168.1.2"; XCTAssertTrue(readData.ok()); XCTAssertEqual(readData.result().size(), 4); XCTAssertEqual(strncmp(readData.result().data(), "Test", 4), 0); + + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); +} + +- (void)testInputStreamRead_SingleCopyEnabled { + // Enable the flag + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + ::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy, + true); + + nearby::CancellationFlag cancellationFlag; + std::unique_ptr socket = + _hotspotMedium->ConnectToService(_service_address, &cancellationFlag); + GNCFakeNWFrameworkSocket *fakeSocket = _fakeNWFramework.sockets.firstObject; + fakeSocket.dataToRead = [@"HotspotOpt" dataUsingEncoding:NSUTF8StringEncoding]; + + nearby::ExceptionOr readData = socket->GetInputStream().Read(10); + + XCTAssertTrue(readData.ok()); + XCTAssertEqual(std::string(readData.result()), "HotspotOpt"); + + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); } - (void)testInputStreamClose { @@ -125,6 +154,10 @@ const char kIPAddress[] = "192.168.1.2"; } - (void)testOutputStreamWrite { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + ::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy, + false); + nearby::CancellationFlag cancellationFlag; std::unique_ptr socket = _hotspotMedium->ConnectToService(_service_address, &cancellationFlag); @@ -136,6 +169,29 @@ const char kIPAddress[] = "192.168.1.2"; XCTAssertTrue(writeResult.Ok()); XCTAssertEqualObjects(fakeSocket.writtenData, data); + + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); +} + +- (void)testOutputStreamWrite_SingleCopyEnabled { + // Enable the flag + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + ::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy, + true); + + nearby::CancellationFlag cancellationFlag; + std::unique_ptr socket = + _hotspotMedium->ConnectToService(_service_address, &cancellationFlag); + GNCFakeNWFrameworkSocket *fakeSocket = _fakeNWFramework.sockets.firstObject; + NSData *data = [@"TestDataOpt" dataUsingEncoding:NSUTF8StringEncoding]; + absl::string_view data_str(reinterpret_cast(data.bytes), data.length); + + nearby::Exception writeResult = socket->GetOutputStream().Write(data_str); + + XCTAssertTrue(writeResult.Ok()); + XCTAssertEqualObjects(fakeSocket.writtenData, data); + + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); } - (void)testSocketClose { diff --git a/internal/platform/implementation/apple/Tests/GNCWifiLanMediumTest.mm b/internal/platform/implementation/apple/Tests/GNCWifiLanMediumTest.mm index 1d581fb2..fd633d60 100644 --- a/internal/platform/implementation/apple/Tests/GNCWifiLanMediumTest.mm +++ b/internal/platform/implementation/apple/Tests/GNCWifiLanMediumTest.mm @@ -18,6 +18,9 @@ #include +#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "internal/flags/nearby_flags.h" + #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h" #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFramework.h" #import "internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWConnection.h" @@ -118,6 +121,10 @@ } - (void)testSocketAndStream { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + ::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy, + false); + // Create a server socket. std::unique_ptr serverSocket = _wifiLanMedium->ListenForService(1234); @@ -155,6 +162,38 @@ // Test closing the server socket. XCTAssertTrue(serverSocket->Close().Ok()); XCTAssertTrue(fakeServerSocket.isClosed); + // Reset the flag. + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); +} + +- (void)testSocketAndStream_SingleCopyEnabled { + // Enable the flag + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + ::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy, + true); + + // Create a server socket and accept a client. + std::unique_ptr serverSocket = + _wifiLanMedium->ListenForService(1234); + GNCFakeNWFrameworkServerSocket* fakeServerSocket = + (GNCFakeNWFrameworkServerSocket*)_fakeNWFramework.serverSockets[0]; + GNCFakeNWConnection* connection = [[GNCFakeNWConnection alloc] init]; + GNCFakeNWFrameworkSocket* fakeSocket = + [[GNCFakeNWFrameworkSocket alloc] initWithConnection:connection]; + fakeServerSocket.socketToReturnOnAccept = fakeSocket; + + std::unique_ptr clientSocket = serverSocket->Accept(); + nearby::InputStream& inputStream = clientSocket->GetInputStream(); + + // Test optimized single-copy read. + fakeSocket.dataToRead = [@"optimized data" dataUsingEncoding:NSUTF8StringEncoding]; + nearby::ExceptionOr readData = inputStream.Read(14); + + XCTAssertTrue(readData.ok()); + XCTAssertEqual(std::string(readData.result()), "optimized data"); + + // Reset the flag. + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); } - (void)testServerSocketGetIPAddress { @@ -185,6 +224,39 @@ XCTAssertTrue(serverSocket->Close().Ok()); } +- (void)testOutputStreamWrite_SingleCopyEnabled { + // Enable the flag + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + ::nearby::connections::config_package_nearby::nearby_connections_feature::kEnableSingleCopy, + true); + + // Create a server socket and accept a client. + std::unique_ptr serverSocket = + _wifiLanMedium->ListenForService(1234); + GNCFakeNWFrameworkServerSocket* fakeServerSocket = + (GNCFakeNWFrameworkServerSocket*)_fakeNWFramework.serverSockets[0]; + GNCFakeNWConnection* connection = [[GNCFakeNWConnection alloc] init]; + GNCFakeNWFrameworkSocket* fakeSocket = + [[GNCFakeNWFrameworkSocket alloc] initWithConnection:connection]; + fakeServerSocket.socketToReturnOnAccept = fakeSocket; + + std::unique_ptr clientSocket = serverSocket->Accept(); + nearby::OutputStream& outputStream = clientSocket->GetOutputStream(); + + // Test optimized single-copy write. + absl::string_view writeData("optimized data"); + XCTAssertTrue(outputStream.Write(writeData).Ok()); + XCTAssertEqualObjects(fakeSocket.writtenData, + [@"optimized data" dataUsingEncoding:NSUTF8StringEncoding]); + + // Clean up. + XCTAssertTrue(clientSocket->Close().Ok()); + XCTAssertTrue(serverSocket->Close().Ok()); + + // Reset the flag. + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); +} + - (void)testOutputStreamClose { // Create a server socket and accept a client. std::unique_ptr serverSocket = diff --git a/internal/platform/implementation/apple/Tests/UtilsTest.mm b/internal/platform/implementation/apple/Tests/UtilsTest.mm index cabf5d3d..5c513c1f 100644 --- a/internal/platform/implementation/apple/Tests/UtilsTest.mm +++ b/internal/platform/implementation/apple/Tests/UtilsTest.mm @@ -56,8 +56,9 @@ using ::nearby::ObjCStringFromCppString; - (void)testUUIDStringFromNSUUID { NSString *uuidString = @"E621E1F8-C36C-495A-93FC-0C247A3E6E5F"; NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString]; - std::string expectedCppString = [uuidString UTF8String]; - XCTAssertEqual(nearby::UUIDStringFromNSUUID(uuid), expectedCppString); + XCTAssert(nearby::UUIDStringFromNSUUID(uuid) == + std::string([uuidString UTF8String], + [uuidString lengthOfBytesUsingEncoding:NSUTF8StringEncoding])); } - (void)testBluetoothUUIDConversions { diff --git a/internal/platform/implementation/apple/Tests/ble_gatt_client_test.mm b/internal/platform/implementation/apple/Tests/ble_gatt_client_test.mm index c5969c5f..ae1066a8 100644 --- a/internal/platform/implementation/apple/Tests/ble_gatt_client_test.mm +++ b/internal/platform/implementation/apple/Tests/ble_gatt_client_test.mm @@ -210,19 +210,6 @@ XCTAssertFalse(result); } -- (void)testSetCharacteristicSubscriptionReturnsFalse { - GNCBLEGATTCharacteristic *characteristic = - [[GNCBLEGATTCharacteristic alloc] initWithUUID:[CBUUID UUIDWithString:@"B2B4"] - serviceUUID:[CBUUID UUIDWithString:@"FEF3"] - permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyNotify]; - nearby::api::ble::GattCharacteristic cppCharacteristic = - nearby::apple::CPPGATTCharacteristicFromObjC(characteristic); - BOOL result = _gattClient->SetCharacteristicSubscription(cppCharacteristic, true, - [](absl::string_view value) {}); - XCTAssertFalse(result); -} - - (void)testDisconnectWhenFlagEnabled { nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( nearby::connections::config_package_nearby::nearby_connections_feature:: diff --git a/internal/platform/implementation/apple/Tests/ble_l2cap_server_socket_test.mm b/internal/platform/implementation/apple/Tests/ble_l2cap_server_socket_test.mm index 88b47282..2be79381 100644 --- a/internal/platform/implementation/apple/Tests/ble_l2cap_server_socket_test.mm +++ b/internal/platform/implementation/apple/Tests/ble_l2cap_server_socket_test.mm @@ -50,8 +50,9 @@ - (void)testBleL2capServerSocketAccept { XCTestExpectation *expectation = [self expectationWithDescription:@"accept"]; + nearby::apple::BleL2capServerSocket *serverSocket = _serverSocket.get(); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - std::unique_ptr clientSocket = _serverSocket->Accept(); + std::unique_ptr clientSocket = serverSocket->Accept(); XCTAssertNotEqual(clientSocket.get(), nullptr); [expectation fulfill]; }); @@ -76,8 +77,9 @@ - (void)testBleL2capServerSocketClose { XCTestExpectation *expectation = [self expectationWithDescription:@"close"]; + nearby::apple::BleL2capServerSocket *serverSocket = _serverSocket.get(); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - std::unique_ptr clientSocket = _serverSocket->Accept(); + std::unique_ptr clientSocket = serverSocket->Accept(); XCTAssertEqual(clientSocket.get(), nullptr); [expectation fulfill]; }); diff --git a/internal/platform/implementation/apple/Tests/ble_medium_test.mm b/internal/platform/implementation/apple/Tests/ble_medium_test.mm index b628c3f3..2994284c 100644 --- a/internal/platform/implementation/apple/Tests/ble_medium_test.mm +++ b/internal/platform/implementation/apple/Tests/ble_medium_test.mm @@ -23,9 +23,7 @@ #include #include -#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTClient.h" -#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h" -#import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPClient.h" +#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h" #import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.h" #import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheral.h" #import "internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Central/GNSCentralManager.h" @@ -33,9 +31,13 @@ #import "internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Peripheral/GNSPeripheralManager.h" #import "internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Peripheral/GNSPeripheralServiceManager.h" #import "internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Shared/GNSSocket.h" +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEMedium+Testing.h" #import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeBLEGATTServer.h" #import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeBLEMedium.h" +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeCentralManager.h" #import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheral.h" +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h" +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeSocket.h" #include "internal/platform/implementation/apple/ble_utils.h" #include "internal/platform/implementation/ble.h" #import "third_party/objective_c/ocmock/v3/Source/OCMock/OCMock.h" @@ -45,11 +47,16 @@ namespace apple { class BleMediumPeer { public: - static void SetSocketCentralManager(BleMedium *ble_medium, GNSCentralManager *manager) { - ble_medium->socketCentralManager_ = manager; + static void SetPeripheralManagerFactory(BleMedium *ble_medium, + BleMedium::PeripheralManagerFactory factory) { + ble_medium->peripheral_manager_factory_ = std::move(factory); } - static void SetSocketPeripheralManager(BleMedium *ble_medium, GNSPeripheralManager *manager) { - ble_medium->socketPeripheralManager_ = manager; + static void SetCentralManagerFactory(BleMedium *ble_medium, + BleMedium::CentralManagerFactory factory) { + ble_medium->central_manager_factory_ = std::move(factory); + } + static GNSPeripheralServiceManager *GetSocketPeripheralServiceManager(BleMedium *ble_medium) { + return ble_medium->socketPeripheralServiceManager_; } }; @@ -71,7 +78,11 @@ static const char *const kTestServiceID = "TestServiceID"; - (void)setUp { [super setUp]; - _fakeGNCBLEMedium = [[GNCFakeBLEMedium alloc] init]; + GNCFakeCentralManager *fakeCentralManager = [[GNCFakeCentralManager alloc] init]; + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + _fakeGNCBLEMedium = [[GNCFakeBLEMedium alloc] initWithCentralManager:fakeCentralManager + peripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; _medium = std::make_unique((GNCBLEMedium *)_fakeGNCBLEMedium); } @@ -79,6 +90,28 @@ static const char *const kTestServiceID = "TestServiceID"; [super tearDown]; } +- (void)testOpenServerSocket_UsesFactoryForInitialization { + __block BOOL factoryWasCalled = NO; + id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]); + OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any] + bleServiceAddedCompletion:[OCMArg any]]) + .andDo(^(GNSPeripheralManager *localSelf, GNSPeripheralServiceManager *manager, + void (^completion)(NSError *error)) { + completion(nil); + }); + + nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() { + factoryWasCalled = YES; + return mockPeripheralManager; + }); + + // This call should trigger the factory inside BleMedium. + auto server_socket = _medium->OpenServerSocket(kTestServiceID); + + XCTAssertTrue(factoryWasCalled, @"BleMedium should have requested the manager from the factory."); + XCTAssertNotEqual(server_socket.get(), nullptr); +} + #pragma mark - Advertising Tests - (void)testStartAdvertising_Success { @@ -225,8 +258,13 @@ static const char *const kTestServiceID = "TestServiceID"; #pragma mark - GATT Server Tests - (void)testStartGattServer_Success { - _fakeGNCBLEMedium.fakeGATTServer = [[GNCFakeBLEGATTServer alloc] init]; - +<<<<<<< HEAD + _fakeGNCBLEMedium.fakeGATTServer = + [[GNCFakeBLEGATTServer alloc] initWithPeripheralManager:nil queue:nil]; +======= + _fakeGNCBLEMedium.fakeGATTServer = [[GNCFakeBLEGATTServer alloc] initWithPeripheralManager:nil + queue:nil]; +>>>>>>> nearby/main auto gatt_server = _medium->StartGattServer({}); XCTAssertNotEqual(gatt_server.get(), nullptr); @@ -422,7 +460,9 @@ static const char *const kTestServiceID = "TestServiceID"; id mockCentralManager = OCMClassMock([GNSCentralManager class]); OCMStub([mockCentralManager retrieveCentralPeerWithIdentifier:fakePeripheral.identifier]) .andReturn(nil); - nearby::apple::BleMediumPeer::SetSocketCentralManager(_medium.get(), mockCentralManager); + nearby::apple::BleMediumPeer::SetCentralManagerFactory(_medium.get(), ^(CBUUID *uuid) { + return mockCentralManager; + }); auto socket = _medium->Connect(kTestServiceID, nearby::api::ble::TxPowerLevel::kUltraLow, fakePeripheral.identifier.hash, nullptr); @@ -460,7 +500,9 @@ static const char *const kTestServiceID = "TestServiceID"; id mockCentralManager = OCMClassMock([GNSCentralManager class]); OCMStub([mockCentralManager retrieveCentralPeerWithIdentifier:fakePeripheral.identifier]) .andReturn(mockCentralPeerManager); - nearby::apple::BleMediumPeer::SetSocketCentralManager(_medium.get(), mockCentralManager); + nearby::apple::BleMediumPeer::SetCentralManagerFactory(_medium.get(), ^(CBUUID *uuid) { + return mockCentralManager; + }); auto socket = _medium->Connect(kTestServiceID, nearby::api::ble::TxPowerLevel::kUltraLow, fakePeripheral.identifier.hash, nullptr); @@ -470,7 +512,10 @@ static const char *const kTestServiceID = "TestServiceID"; #pragma mark - Server Socket Tests -- (void)testOpenServerSocket_Success { +- (void)testOpenServerSocket_Success_LegacyPath { + id mockFeatureFlags = OCMClassMock([GNCFeatureFlags class]); + OCMStub([mockFeatureFlags fixBleServerSocketDeadlockEnabled]).andReturn(NO); + id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]); OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any] bleServiceAddedCompletion:[OCMArg any]]) @@ -478,11 +523,97 @@ static const char *const kTestServiceID = "TestServiceID"; void (^completion)(NSError *error)) { completion(nil); }); - nearby::apple::BleMediumPeer::SetSocketPeripheralManager(_medium.get(), mockPeripheralManager); + nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() { + return mockPeripheralManager; + }); auto server_socket = _medium->OpenServerSocket(kTestServiceID); XCTAssertNotEqual(server_socket.get(), nullptr); + + GNSPeripheralServiceManager *serviceManager = + nearby::apple::BleMediumPeer::GetSocketPeripheralServiceManager(_medium.get()); + XCTAssertNotNil(serviceManager); +} + +- (void)testOpenServerSocket_Success_OptimizedPath { + id mockFeatureFlags = OCMClassMock([GNCFeatureFlags class]); + OCMStub([mockFeatureFlags fixBleServerSocketDeadlockEnabled]).andReturn(YES); + + id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]); + OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any] + bleServiceAddedCompletion:[OCMArg any]]) + .andDo(^(GNSPeripheralManager *localSelf, GNSPeripheralServiceManager *manager, + void (^completion)(NSError *error)) { + completion(nil); + }); + nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() { + return mockPeripheralManager; + }); + + auto server_socket = _medium->OpenServerSocket(kTestServiceID); + + XCTAssertNotEqual(server_socket.get(), nullptr); + + GNSPeripheralServiceManager *serviceManager = + nearby::apple::BleMediumPeer::GetSocketPeripheralServiceManager(_medium.get()); + XCTAssertNotNil(serviceManager); +} + +- (void)testOpenServerSocket_OptimizedPath_AcceptSocketAfterClose { + id mockFeatureFlags = OCMClassMock([GNCFeatureFlags class]); + OCMStub([mockFeatureFlags fixBleServerSocketDeadlockEnabled]).andReturn(YES); + + id mockPeripheralManager = OCMClassMock([GNSPeripheralManager class]); + OCMStub([mockPeripheralManager addPeripheralServiceManager:[OCMArg any] + bleServiceAddedCompletion:[OCMArg any]]) + .andDo(^(GNSPeripheralManager *localSelf, GNSPeripheralServiceManager *manager, + void (^completion)(NSError *error)) { + completion(nil); + }); + nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() { + return mockPeripheralManager; + }); + + auto server_socket = _medium->OpenServerSocket(kTestServiceID); + XCTAssertNotEqual(server_socket.get(), nullptr); + __block BOOL (^capturedHandler)(GNSSocket *) = nil; + id mockServiceManagerClass = OCMClassMock([GNSPeripheralServiceManager class]); + OCMStub([mockServiceManagerClass alloc]).andReturn(mockServiceManagerClass); + OCMStub([mockServiceManagerClass initWithBleServiceUUID:[OCMArg any] + addPairingCharacteristic:NO + shouldAcceptSocketHandler:[OCMArg any]]) + .andDo(^(NSInvocation *invocation) { + BOOL (^handler)(GNSSocket *); + [invocation getArgument:&handler atIndex:4]; + capturedHandler = handler; + }) + .andReturn(mockServiceManagerClass); + + auto server_socket_for_handler_capture = _medium->OpenServerSocket(kTestServiceID); + XCTAssertNotEqual(server_socket_for_handler_capture.get(), nullptr); + XCTAssertNotNil(capturedHandler); + + // Invoke the shouldAcceptSocketHandler with a fake socket. + GNCFakeSocket *fakeSocket = [[GNCFakeSocket alloc] init]; + BOOL result = capturedHandler((GNSSocket *)fakeSocket); + XCTAssertTrue(result); + + // Close the server_socket. This triggers the close notifier, setting server_socket_ptr_ to null. + server_socket_for_handler_capture->Close(); + + // Now simulate the connection completing. It should safely ignore the connection because + // server_socket_ptr_ is null, preventing use-after-free or deadlocks. + [fakeSocket simulateSocketDidConnect]; + + // Since we use dispatch_async internally for connection callback, give it a small amount of time + // to process so we know it didn't crash. + XCTestExpectation *expectation2 = [self expectationWithDescription:@"Wait for async execution"]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + [expectation2 fulfill]; + }); + [self waitForExpectations:@[ expectation2 ] timeout:1.0]; } - (void)testOpenServerSocket_Failure { @@ -493,7 +624,9 @@ static const char *const kTestServiceID = "TestServiceID"; void (^completion)(NSError *error)) { completion([NSError errorWithDomain:@"test" code:0 userInfo:nil]); }); - nearby::apple::BleMediumPeer::SetSocketPeripheralManager(_medium.get(), mockPeripheralManager); + nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() { + return mockPeripheralManager; + }); auto server_socket = _medium->OpenServerSocket(kTestServiceID); @@ -508,7 +641,9 @@ static const char *const kTestServiceID = "TestServiceID"; void (^completion)(NSError *error)){ // Do not call completion to simulate timeout. }); - nearby::apple::BleMediumPeer::SetSocketPeripheralManager(_medium.get(), mockPeripheralManager); + nearby::apple::BleMediumPeer::SetPeripheralManagerFactory(_medium.get(), ^() { + return mockPeripheralManager; + }); auto server_socket = _medium->OpenServerSocket(kTestServiceID); @@ -646,18 +781,20 @@ static const char *const kTestServiceID = "TestServiceID"; NSDictionary *serviceData = @{[CBUUID UUIDWithString:kTestServiceUUIDString] : [NSData dataWithBytes:"test" length:4]}; - __block XCTestExpectation *expectation1 = [self expectationWithDescription:@"Callback 1"]; + XCTestExpectation *expectation1 = [self expectationWithDescription:@"Callback 1"]; XCTestExpectation *expectation2 = [self expectationWithDescription:@"Callback 2"]; expectation2.inverted = YES; // Should NOT be called. + auto callback1_fulfilled = std::make_shared>(false); + nearby::api::ble::BleMedium::ScanCallback callback = { .advertisement_found_cb = std::function( - ^(nearby::api::ble::BlePeripheral::UniqueId peripheral_id, - const nearby::api::ble::BleAdvertisementData &advertisement) { - if ([expectation1.description isEqualToString:@"Callback 1"]) { + [callback1_fulfilled, expectation1, expectation2]( + nearby::api::ble::BlePeripheral::UniqueId peripheral_id, + const nearby::api::ble::BleAdvertisementData &advertisement) { + if (!callback1_fulfilled->exchange(true)) { [expectation1 fulfill]; - expectation1 = nil; // Prevent double fulfillment } else { [expectation2 fulfill]; } diff --git a/internal/platform/implementation/apple/Tests/ble_server_socket_test.mm b/internal/platform/implementation/apple/Tests/ble_server_socket_test.mm index 1b887880..a9574f96 100644 --- a/internal/platform/implementation/apple/Tests/ble_server_socket_test.mm +++ b/internal/platform/implementation/apple/Tests/ble_server_socket_test.mm @@ -42,8 +42,9 @@ - (void)testBleServerSocketAccept { XCTestExpectation *expectation = [self expectationWithDescription:@"accept"]; + nearby::apple::BleServerSocket *serverSocket = _serverSocket.get(); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - std::unique_ptr clientSocket = _serverSocket->Accept(); + std::unique_ptr clientSocket = serverSocket->Accept(); XCTAssertNotEqual(clientSocket.get(), nullptr); [expectation fulfill]; }); @@ -57,8 +58,9 @@ - (void)testBleServerSocketClose { XCTestExpectation *expectation = [self expectationWithDescription:@"close"]; + nearby::apple::BleServerSocket *serverSocket = _serverSocket.get(); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - std::unique_ptr clientSocket = _serverSocket->Accept(); + std::unique_ptr clientSocket = serverSocket->Accept(); XCTAssertEqual(clientSocket.get(), nullptr); [expectation fulfill]; }); diff --git a/internal/platform/implementation/apple/atomic_boolean_test.cc b/internal/platform/implementation/apple/atomic_boolean_test.cc index 26c6b4f7..cd86bf4d 100644 --- a/internal/platform/implementation/apple/atomic_boolean_test.cc +++ b/internal/platform/implementation/apple/atomic_boolean_test.cc @@ -15,7 +15,7 @@ #include "internal/platform/implementation/apple/atomic_boolean.h" #include "gtest/gtest.h" -#include "thread/fiber/fiber.h" +#include "third_party/gloop/thread/fiber/fiber.h" namespace nearby { namespace apple { diff --git a/internal/platform/implementation/apple/atomic_uint32_test.cc b/internal/platform/implementation/apple/atomic_uint32_test.cc index 54c2ac79..5fe2917f 100644 --- a/internal/platform/implementation/apple/atomic_uint32_test.cc +++ b/internal/platform/implementation/apple/atomic_uint32_test.cc @@ -15,7 +15,7 @@ #include "internal/platform/implementation/apple/atomic_uint32.h" #include "gtest/gtest.h" -#include "thread/fiber/fiber.h" +#include "third_party/gloop/thread/fiber/fiber.h" namespace nearby { namespace apple { diff --git a/internal/platform/implementation/apple/awdl.mm b/internal/platform/implementation/apple/awdl.mm index f23b61b5..8720d1e9 100644 --- a/internal/platform/implementation/apple/awdl.mm +++ b/internal/platform/implementation/apple/awdl.mm @@ -19,6 +19,7 @@ #include #include +#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h" #import "internal/platform/implementation/apple/Log/GNCLogger.h" #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h" #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFramework.h" @@ -35,12 +36,22 @@ AwdlInputStream::AwdlInputStream(GNCNWFrameworkSocket* socket) : socket_(socket) ExceptionOr AwdlInputStream::Read(std::int64_t size) { NSError* error = nil; - NSData* data = [socket_ readMaxLength:size error:&error]; - if (data == nil) { - GNCLoggerError(@"Error reading socket: %@", error); - return {Exception::kIo}; + if (GNCFeatureFlags.singleCopyEnabled) { + auto result = [socket_ readStringWithMaxLength:size error:&error]; + if (!result.has_value()) { + GNCLoggerError(@"Error reading socket: %@", error); + return {Exception::kIo}; + } + // OPTIMIZATION: Zero-copy transfer from std::string to ByteArray + return ExceptionOr{ByteArray(std::move(result.value()))}; + } else { + NSData* data = [socket_ readMaxLength:size error:&error]; + if (data == nil) { + GNCLoggerError(@"Error reading socket: %@", error); + return {Exception::kIo}; + } + return ExceptionOr{ByteArray((const char*)data.bytes, data.length)}; } - return ExceptionOr{ByteArray((const char*)data.bytes, data.length)}; } Exception AwdlInputStream::Close() { @@ -55,7 +66,15 @@ AwdlOutputStream::AwdlOutputStream(GNCNWFrameworkSocket* socket) : socket_(socke Exception AwdlOutputStream::Write(absl::string_view data) { NSError* error = nil; - BOOL result = [socket_ write:[NSData dataWithBytes:data.data() length:data.size()] error:&error]; + BOOL result = NO; + + if (GNCFeatureFlags.singleCopyEnabled) { + // OPTIMIZATION: Write raw bytes directly, avoiding NSData creation. + result = [socket_ writeBytes:data.data() length:data.size() error:&error]; + } else { + result = [socket_ write:[NSData dataWithBytes:data.data() length:data.size()] error:&error]; + } + if (!result) { GNCLoggerError(@"Error writing socket: %@", error); return {Exception::kIo}; diff --git a/internal/platform/implementation/apple/ble_gatt_client.h b/internal/platform/implementation/apple/ble_gatt_client.h index f375586c..1be7ae1e 100644 --- a/internal/platform/implementation/apple/ble_gatt_client.h +++ b/internal/platform/implementation/apple/ble_gatt_client.h @@ -66,16 +66,6 @@ class GattClient : public api::ble::GattClient { bool WriteCharacteristic(const api::ble::GattCharacteristic &characteristic, absl::string_view value, api::ble::GattClient::WriteType type) override; - // Enable or disable notifications/indications for a given characteristic. - // - // Once notifications are enabled for a characteristic, on_characteristic_changed_cb will be - // triggered if the remote device indicates that the given characteristic has changed. - // - // Returns whether or not the subscription was successful. - bool SetCharacteristicSubscription( - const api::ble::GattCharacteristic &characteristic, bool enable, - absl::AnyInvocable on_characteristic_changed_cb) override; - // Disconnects an established connection, or cancels a connection attempt currently in progress. void Disconnect() override; diff --git a/internal/platform/implementation/apple/ble_gatt_client.mm b/internal/platform/implementation/apple/ble_gatt_client.mm index 655878cf..4b1a07f2 100644 --- a/internal/platform/implementation/apple/ble_gatt_client.mm +++ b/internal/platform/implementation/apple/ble_gatt_client.mm @@ -121,13 +121,6 @@ bool GattClient::WriteCharacteristic(const api::ble::GattCharacteristic &charact return false; } -// TODO(b/290385712): Implement. -bool GattClient::SetCharacteristicSubscription( - const api::ble::GattCharacteristic &characteristic, bool enable, - absl::AnyInvocable on_characteristic_changed_cb) { - return false; -} - void GattClient::Disconnect() { // There seems to be an issue between some iOS<>Android device pairs where the Android device will // not connect to the iOS device if the iOS device disconnects and then attempts to reconnect. diff --git a/internal/platform/implementation/apple/ble_l2cap_socket.mm b/internal/platform/implementation/apple/ble_l2cap_socket.mm index e44c6728..3cf2f2a3 100644 --- a/internal/platform/implementation/apple/ble_l2cap_socket.mm +++ b/internal/platform/implementation/apple/ble_l2cap_socket.mm @@ -14,9 +14,11 @@ #import "internal/platform/implementation/apple/ble_l2cap_socket.h" +#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h" #import "internal/platform/implementation/apple/Log/GNCLogger.h" #import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPConnection.h" #import "internal/platform/implementation/apple/utils.h" + #include "internal/platform/implementation/ble.h" namespace nearby { @@ -52,40 +54,81 @@ BleL2capInputStream::~BleL2capInputStream() { } ExceptionOr BleL2capInputStream::Read(std::int64_t size) { - // Block until either (a) the connection has been closed, (b) we have enough data to return. - NSData *dataToReturn; - [condition_ lock]; - while (true) { - // Check if the stream has been closed or severed. - if (!newDataPackets_) break; + if (GNCFeatureFlags.singleCopyEnabled) { + std::string dataToReturn; + bool success = false; - if (newDataPackets_.count > 0) { - // Add the packet data to the accumulated data. - for (NSData *data in newDataPackets_) { - if (data.length > 0) { - [accumulatedData_ appendData:data]; + [condition_ lock]; + while (true) { + // Check if the stream has been closed or severed. + if (!newDataPackets_) break; + + if (newDataPackets_.count > 0) { + // Add the packet data to the accumulated data. + for (NSData *data in newDataPackets_) { + if (data.length > 0) { + [accumulatedData_ appendData:data]; + } } + [newDataPackets_ removeAllObjects]; } - [newDataPackets_ removeAllObjects]; + + if (accumulatedData_.length > 0) { + std::int64_t sizeToReturn = + (accumulatedData_.length < size) ? accumulatedData_.length : size; + NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn); + + // Copy bytes directly into std::string, avoiding [NSData subdataWithRange:] + dataToReturn.assign((const char *)accumulatedData_.bytes, sizeToReturn); + [accumulatedData_ replaceBytesInRange:range withBytes:nil length:0]; + + success = true; + break; + } + [condition_ wait]; } + [condition_ unlock]; - if (accumulatedData_.length > 0) { - // Return up to |size| bytes of the data. - std::int64_t sizeToReturn = (accumulatedData_.length < size) ? accumulatedData_.length : size; - NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn); - dataToReturn = [accumulatedData_ subdataWithRange:range]; - [accumulatedData_ replaceBytesInRange:range withBytes:nil length:0]; - break; + if (success) { + // OPTIMIZATION: Zero-copy transfer from std::string to ByteArray + return ExceptionOr{ByteArray(std::move(dataToReturn))}; + } else { + return ExceptionOr{Exception::kIo}; } - - [condition_ wait]; - } - [condition_ unlock]; - - if (dataToReturn) { - return ExceptionOr{ByteArray((const char *)dataToReturn.bytes, dataToReturn.length)}; } else { - return ExceptionOr{Exception::kIo}; + // Legacy Path + NSData *dataToReturn; + [condition_ lock]; + while (true) { + if (!newDataPackets_) break; + + if (newDataPackets_.count > 0) { + for (NSData *data in newDataPackets_) { + if (data.length > 0) { + [accumulatedData_ appendData:data]; + } + } + [newDataPackets_ removeAllObjects]; + } + + if (accumulatedData_.length > 0) { + std::int64_t sizeToReturn = + (accumulatedData_.length < size) ? accumulatedData_.length : size; + NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn); + dataToReturn = [accumulatedData_ subdataWithRange:range]; + [accumulatedData_ replaceBytesInRange:range withBytes:nil length:0]; + break; + } + [condition_ wait]; + } + [condition_ unlock]; + + if (dataToReturn) { + return ExceptionOr{ + ByteArray((const char *)dataToReturn.bytes, dataToReturn.length)}; + } else { + return ExceptionOr{Exception::kIo}; + } } } @@ -111,7 +154,17 @@ Exception BleL2capOutputStream::Write(absl::string_view data) { return {Exception::kIo}; } - NSMutableData *packet = [NSMutableData dataWithBytes:data.data() length:data.size()]; + NSData *packet; + if (GNCFeatureFlags.singleCopyEnabled) { + // OPTIMIZATION: Use DISPATCH_DATA_DESTRUCTOR_DEFAULT to perform a + // single copy into a GCD-managed buffer. No NSData required. + dispatch_data_t dispatchData = + dispatch_data_create(data.data(), data.size(), nil, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + // dispatch_data_t is toll-free bridged to NSData + packet = (NSData *)dispatchData; + } else { + packet = [NSMutableData dataWithBytes:data.data() length:data.size()]; + } // Send the data, blocking until the completion handler is called. __block BOOL isComplete = NO; diff --git a/internal/platform/implementation/apple/ble_medium.h b/internal/platform/implementation/apple/ble_medium.h index 7c052c43..34695757 100644 --- a/internal/platform/implementation/apple/ble_medium.h +++ b/internal/platform/implementation/apple/ble_medium.h @@ -21,6 +21,7 @@ #import +#include #include #include #include @@ -50,6 +51,10 @@ class BleMedium : public api::ble::BleMedium { friend class BleMediumPeer; public: + // Define factory types for managers. + using PeripheralManagerFactory = std::function; + using CentralManagerFactory = std::function; + BleMedium(); // For testing only. explicit BleMedium(GNCBLEMedium *medium); @@ -207,9 +212,20 @@ class BleMedium : public api::ble::BleMedium { NSDictionary *service_data); NSDate *GetLastTimestampToCleanExpiredAdvertisementPackets(); + // Opens a BLE server socket based on service ID with deadlock safety. + std::unique_ptr OpenServerSocketWithDeadlockSafety( + const std::string &service_id); + + // Opens a BLE server socket based on service ID using the legacy implementation. + std::unique_ptr OpenServerSocketLegacy(const std::string &service_id); + // The executor for handling callbacks. apple::SingleThreadExecutor callback_executor_; + // Factories for lazy initialization + PeripheralManagerFactory peripheral_manager_factory_ = nullptr; + CentralManagerFactory central_manager_factory_ = nullptr; + GNCBLEMedium *medium_; PeripheralsMap peripherals_; @@ -229,14 +245,22 @@ class BleMedium : public api::ble::BleMedium { GNSPeripheralServiceManager *socketPeripheralServiceManager_; GNSPeripheralManager *socketPeripheralManager_; - GNSCentralManager *socketCentralManager_; + + absl::Mutex scanning_mutex_; + GNSCentralManager *socketCentralManager_ ABSL_GUARDED_BY(scanning_mutex_); // Used for the blocking version of StartAdvertising and only has an advertisement found callback. - api::ble::BleMedium::ScanCallback scan_cb_; + std::shared_ptr scan_cb_ ABSL_GUARDED_BY(scanning_mutex_); // Used for the async version of StartAdvertising and has both an advertisement found and result // callback. - api::ble::BleMedium::ScanningCallback scanning_cb_; + std::shared_ptr scanning_cb_ + ABSL_GUARDED_BY(scanning_mutex_); + // Used for the BleServerSocket. + absl::Mutex server_socket_mutex_; + BleServerSocket *server_socket_ptr_ ABSL_GUARDED_BY(server_socket_mutex_) = nullptr; + + // Used for the L2CAP server socket. absl::Mutex l2cap_server_socket_mutex_; BleL2capServerSocket *l2cap_server_socket_ptr_ = nullptr; diff --git a/internal/platform/implementation/apple/ble_medium.mm b/internal/platform/implementation/apple/ble_medium.mm index afefac0b..1120f8b6 100644 --- a/internal/platform/implementation/apple/ble_medium.mm +++ b/internal/platform/implementation/apple/ble_medium.mm @@ -173,11 +173,19 @@ void BleMedium::HandleAdvertisementFound(id peripheral, } #endif - if (scanning_cb_.advertisement_found_cb) { - scanning_cb_.advertisement_found_cb(unique_id, data); + std::shared_ptr scanning_cb; + std::shared_ptr scan_cb; + { + absl::MutexLock lock(&scanning_mutex_); + scanning_cb = scanning_cb_; + scan_cb = scan_cb_; } - if (scan_cb_.advertisement_found_cb) { - scan_cb_.advertisement_found_cb(unique_id, data); + + if (scanning_cb && scanning_cb->advertisement_found_cb) { + scanning_cb->advertisement_found_cb(unique_id, data); + } + if (scan_cb && scan_cb->advertisement_found_cb) { + scan_cb->advertisement_found_cb(unique_id, data); } } @@ -185,7 +193,17 @@ std::unique_ptr BleMedium::StartScanning( const Uuid &service_uuid, api::ble::TxPowerLevel tx_power_level, api::ble::BleMedium::ScanningCallback callback) { CBUUID *serviceUUID = CBUUID128FromCPP(service_uuid); - scanning_cb_ = std::move(callback); + + { + absl::MutexLock lock(&scanning_mutex_); + scanning_cb_ = std::make_shared(std::move(callback)); + + if (central_manager_factory_) { + socketCentralManager_ = central_manager_factory_(serviceUUID); + } else { + socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID]; + } + } // Clear the map of discovered peripherals only when we are starting a new scan. If we cleared the // map every time we stopped a scan, we would not be able to connect to peripherals that we @@ -193,8 +211,10 @@ std::unique_ptr BleMedium::StartScanning( peripherals_.Clear(); ClearAdvertisementPacketsMap(); - socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID]; - [socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]]; + { + absl::MutexLock lock(&scanning_mutex_); + [socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]]; + } dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block NSError *blockError = nil; @@ -207,8 +227,13 @@ std::unique_ptr BleMedium::StartScanning( } completionHandler:^(NSError *error) { blockError = error; - if (scanning_cb_.start_scanning_result) { - scanning_cb_.start_scanning_result( + std::shared_ptr scanning_cb; + { + absl::MutexLock lock(&scanning_mutex_); + scanning_cb = scanning_cb_; + } + if (scanning_cb && scanning_cb->start_scanning_result) { + scanning_cb->start_scanning_result( error == nil ? absl::OkStatus() : absl::InternalError(error.localizedDescription.UTF8String)); } @@ -218,8 +243,13 @@ std::unique_ptr BleMedium::StartScanning( dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); if (dispatch_semaphore_wait(semaphore, timeout) != 0) { GNCLoggerError(@"Start scanning operation timed out."); - if (scanning_cb_.start_scanning_result) { - scanning_cb_.start_scanning_result(absl::DeadlineExceededError("Start scanning timed out")); + std::shared_ptr scanning_cb; + { + absl::MutexLock lock(&scanning_mutex_); + scanning_cb = scanning_cb_; + } + if (scanning_cb && scanning_cb->start_scanning_result) { + scanning_cb->start_scanning_result(absl::DeadlineExceededError("Start scanning timed out")); } return nullptr; } @@ -239,7 +269,17 @@ std::unique_ptr BleMedium::StartScanning( bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble::TxPowerLevel tx_power_level, api::ble::BleMedium::ScanCallback callback) { CBUUID *serviceUUID = CBUUID128FromCPP(service_uuid); - scan_cb_ = std::move(callback); + + { + absl::MutexLock lock(&scanning_mutex_); + scan_cb_ = std::make_shared(std::move(callback)); + + if (central_manager_factory_) { + socketCentralManager_ = central_manager_factory_(serviceUUID); + } else { + socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID]; + } + } // Clear the map of discovered peripherals only when we are starting a new scan. If we cleared the // map every time we stopped a scan, we would not be able to connect to peripherals that we @@ -247,8 +287,10 @@ bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble::TxPowerLevel t peripherals_.Clear(); ClearAdvertisementPacketsMap(); - socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID]; - [socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]]; + { + absl::MutexLock lock(&scanning_mutex_); + [socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]]; + } dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block NSError *blockError = nil; @@ -286,7 +328,16 @@ bool BleMedium::StartMultipleServicesScanning(const std::vector &service_u [serviceUUIDs addObject:CBUUID128FromCPP(service_uuid)]; } - scan_cb_ = std::move(callback); + { + absl::MutexLock lock(&scanning_mutex_); + scan_cb_ = std::make_shared(std::move(callback)); + + if (central_manager_factory_) { + socketCentralManager_ = central_manager_factory_(serviceUUIDs[0]); + } else { + socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUIDs[0]]; + } + } // Clear the map of discovered peripherals only when we are starting a new scan. If we cleared the // map every time we stopped a scan, we would not be able to connect to peripherals that we @@ -294,8 +345,10 @@ bool BleMedium::StartMultipleServicesScanning(const std::vector &service_u peripherals_.Clear(); ClearAdvertisementPacketsMap(); - socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUIDs[0]]; - [socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUIDs[0] ]]; + { + absl::MutexLock lock(&scanning_mutex_); + [socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUIDs[0] ]]; + } dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block NSError *blockError = nil; @@ -321,7 +374,12 @@ bool BleMedium::StartMultipleServicesScanning(const std::vector &service_u } bool BleMedium::StopScanning() { - [socketCentralManager_ stopNoScanMode]; + { + absl::MutexLock lock(&scanning_mutex_); + [socketCentralManager_ stopNoScanMode]; + scan_cb_ = nullptr; + scanning_cb_ = nullptr; + } dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block NSError *blockError = nil; @@ -448,24 +506,118 @@ std::unique_ptr BleMedium::ConnectToGattServer( // TODO(b/293336684): Old Weave code that need to be deleted once shared Weave is complete. std::unique_ptr BleMedium::OpenServerSocket( const std::string &service_id) { + if (GNCFeatureFlags.fixBleServerSocketDeadlockEnabled) { + return OpenServerSocketWithDeadlockSafety(service_id); + } else { + return OpenServerSocketLegacy(service_id); + } +} + +std::unique_ptr BleMedium::OpenServerSocketWithDeadlockSafety( + const std::string &service_id) { auto server_socket = std::make_unique(); - __block auto server_socket_ptr = server_socket.get(); if (socketPeripheralManager_ == nil) { - socketPeripheralManager_ = [[GNSPeripheralManager alloc] initWithAdvertisedName:nil - restoreIdentifier:nil]; + if (peripheral_manager_factory_) { + socketPeripheralManager_ = peripheral_manager_factory_(); + } else { + socketPeripheralManager_ = [[GNSPeripheralManager alloc] initWithAdvertisedName:nil + restoreIdentifier:nil]; + } } - if (socketPeripheralManager_ == nil) { - GNCLoggerError(@"Failed to create peripheral manager."); - return nullptr; + // Fix for b/494335036 (Registry + Background Queue) + { + absl::MutexLock lock(server_socket_mutex_); + server_socket_ptr_ = server_socket.get(); } + server_socket->SetCloseNotifier([this]() { + absl::MutexLock lock(server_socket_mutex_); + server_socket_ptr_ = nullptr; + }); socketPeripheralServiceManager_ = [[GNSPeripheralServiceManager alloc] initWithBleServiceUUID:[CBUUID UUIDWithString:kWeaveServiceUUID] addPairingCharacteristic:NO shouldAcceptSocketHandler:^BOOL(GNSSocket *socket) { - GNCMWaitForConnection(socket, ^(BOOL didConnect) { + // Optimized Path: Use background queue and registry validation. + GNCMWaitForConnection(socket, connection_callback_queue_, ^(BOOL didConnect) { + GNCMBleConnection *connection = + [GNCMBleConnection connectionWithSocket:socket + serviceID:nil + expectedIntroPacket:YES + callbackQueue:connection_callback_queue_]; + + auto socket_wrapper = std::make_unique(connection); + socket_wrapper->SetCloseNotifier( + [socketPeripheralManager = socketPeripheralManager_, + serviceUUID = socketPeripheralServiceManager_.serviceUUID]() { + [socketPeripheralManager + removePeripheralServiceManagerForServiceUUID:serviceUUID + bleServiceRemovedCompletion:^(NSError *_Nullable error) { + GNCLoggerInfo(@"BleSocket is removed peripheral manager."); + }]; + }); + + connection.connectionHandlers = socket_wrapper->GetInputStream().GetConnectionHandlers(); + + // Fix: Verify the BleServerSocket still exists before calling Connect(). + // This prevents the use-after-free/deadlock reported in b/494335036. + absl::MutexLock lock(server_socket_mutex_); + if (server_socket_ptr_) { + server_socket_ptr_->Connect(std::move(socket_wrapper)); + GNCLoggerInfo(@"BleServerSocket is created with connection"); + } else { + GNCLoggerWarning(@"BleServerSocket was destroyed; ignoring connection."); + } + }); + return YES; + }]; + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block NSError *blockError = nil; + [socketPeripheralManager_ addPeripheralServiceManager:socketPeripheralServiceManager_ + bleServiceAddedCompletion:^(NSError *error) { + if (error != nil) { + GNCLoggerError(@"Failed to add Weave service: %@", error); + blockError = error; + } + dispatch_semaphore_signal(semaphore); + }]; + [socketPeripheralManager_ start]; + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); + if (dispatch_semaphore_wait(semaphore, timeout) != 0) { + GNCLoggerError(@"OpenServerSocket operation timed out."); + return nullptr; + } + if (blockError != nil) { + return nullptr; + } + return std::move(server_socket); +} + +std::unique_ptr BleMedium::OpenServerSocketLegacy( + const std::string &service_id) { + auto server_socket = std::make_unique(); + + if (socketPeripheralManager_ == nil) { + if (peripheral_manager_factory_) { + socketPeripheralManager_ = peripheral_manager_factory_(); + } else { + socketPeripheralManager_ = [[GNSPeripheralManager alloc] initWithAdvertisedName:nil + restoreIdentifier:nil]; + } + } + + // Raw pointer for closure capture in the legacy path (risks use-after-free). + BleServerSocket *server_socket_ptr = server_socket.get(); + + socketPeripheralServiceManager_ = [[GNSPeripheralServiceManager alloc] + initWithBleServiceUUID:[CBUUID UUIDWithString:kWeaveServiceUUID] + addPairingCharacteristic:NO + shouldAcceptSocketHandler:^BOOL(GNSSocket *socket) { + // Legacy Path: Verbatim copy of original code (blocks Main Thread). + GNCMWaitForConnection(socket, nil, ^(BOOL didConnect) { GNCMBleConnection *connection = [GNCMBleConnection connectionWithSocket:socket // This must be nil as the advertiser even though we @@ -588,8 +740,12 @@ std::unique_ptr BleMedium::Connect( return nullptr; } - GNSCentralPeerManager *updatedCentralPeerManager = - [socketCentralManager_ retrieveCentralPeerWithIdentifier:peripheral.identifier]; + GNSCentralPeerManager *updatedCentralPeerManager; + { + absl::MutexLock lock(&scanning_mutex_); + updatedCentralPeerManager = + [socketCentralManager_ retrieveCentralPeerWithIdentifier:peripheral.identifier]; + } if (!updatedCentralPeerManager) { return nullptr; } @@ -603,7 +759,14 @@ std::unique_ptr BleMedium::Connect( dispatch_semaphore_signal(semaphore); return; } - GNCMWaitForConnection(nssocket, ^(BOOL didConnect) { + + // Suggestion: Use the connection callback queue instead of nil + dispatch_queue_t targetQueue = + GNCFeatureFlags.fixBleServerSocketDeadlockEnabled + ? connection_callback_queue_ + : nil; + + GNCMWaitForConnection(nssocket, targetQueue, ^(BOOL didConnect) { if (!didConnect) { dispatch_semaphore_signal(semaphore); return; diff --git a/internal/platform/implementation/apple/ble_socket.mm b/internal/platform/implementation/apple/ble_socket.mm index e34eb9e1..3ea6daf2 100644 --- a/internal/platform/implementation/apple/ble_socket.mm +++ b/internal/platform/implementation/apple/ble_socket.mm @@ -14,13 +14,13 @@ #import "internal/platform/implementation/apple/ble_socket.h" -#include "internal/platform/implementation/ble.h" - +#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h" #import "internal/platform/implementation/apple/Mediums/BLE/GNCMBleConnection.h" #import "internal/platform/implementation/apple/ble_peripheral.h" #import "internal/platform/implementation/apple/ble_utils.h" #import "internal/platform/implementation/apple/utils.h" +#include "internal/platform/implementation/ble.h" // TODO(b/293336684): Remove this file when shared Weave is complete. namespace nearby { @@ -55,45 +55,94 @@ BleInputStream::~BleInputStream() { } ExceptionOr BleInputStream::Read(std::int64_t size) { - // Block until either (a) the connection has been closed, (b) we have enough data to return. - NSData *dataToReturn; - [condition_ lock]; - while (true) { - // Check if the stream has been closed or severed. - if (!newDataPackets_) break; + if (GNCFeatureFlags.singleCopyEnabled) { + std::string dataToReturn; + bool success = false; - if (newDataPackets_.count > 0) { - // Add the packet data to the accumulated data. - for (NSData *data in newDataPackets_) { - if (data.length > 0) { - [accumulatedData_ appendData:data]; + [condition_ lock]; + while (true) { + // Check if the stream has been closed or severed. + if (!newDataPackets_) break; + + if (newDataPackets_.count > 0) { + // Add the packet data to the accumulated data. + for (NSData *data in newDataPackets_) { + if (data.length > 0) { + [accumulatedData_ appendData:data]; + } } + [newDataPackets_ removeAllObjects]; } - [newDataPackets_ removeAllObjects]; + + if ((size == -1) && (accumulatedData_.length > 0)) { + // Return all of the data. + dataToReturn.assign((const char *)accumulatedData_.bytes, accumulatedData_.length); + accumulatedData_ = [NSMutableData data]; + success = true; + break; + } else if (accumulatedData_.length > 0) { + // Return up to |size| bytes of the data. + std::int64_t sizeToReturn = + (accumulatedData_.length < size) ? accumulatedData_.length : size; + NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn); + // Copy bytes directly into std::string, avoiding [NSData subdataWithRange:] + dataToReturn.assign((const char *)accumulatedData_.bytes, sizeToReturn); + [accumulatedData_ replaceBytesInRange:range withBytes:nil length:0]; + success = true; + break; + } + + [condition_ wait]; } + [condition_ unlock]; - if ((size == -1) && (accumulatedData_.length > 0)) { - // Return all of the data. - dataToReturn = accumulatedData_; - accumulatedData_ = [NSMutableData data]; - break; - } else if (accumulatedData_.length > 0) { - // Return up to |size| bytes of the data. - std::int64_t sizeToReturn = (accumulatedData_.length < size) ? accumulatedData_.length : size; - NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn); - dataToReturn = [accumulatedData_ subdataWithRange:range]; - [accumulatedData_ replaceBytesInRange:range withBytes:nil length:0]; - break; + if (success) { + // OPTIMIZATION: Zero-copy transfer from std::string to ByteArray + return ExceptionOr{ByteArray(std::move(dataToReturn))}; + } else { + return ExceptionOr{Exception::kIo}; } - - [condition_ wait]; - } - [condition_ unlock]; - - if (dataToReturn) { - return ExceptionOr(ByteArrayFromNSData(dataToReturn)); } else { - return ExceptionOr{Exception::kIo}; + // Legacy path + NSData *dataToReturn; + [condition_ lock]; + while (true) { + // Check if the stream has been closed or severed. + if (!newDataPackets_) break; + + if (newDataPackets_.count > 0) { + for (NSData *data in newDataPackets_) { + if (data.length > 0) { + [accumulatedData_ appendData:data]; + } + } + [newDataPackets_ removeAllObjects]; + } + + if ((size == -1) && (accumulatedData_.length > 0)) { + // Return all of the data. + dataToReturn = accumulatedData_; + accumulatedData_ = [NSMutableData data]; + break; + } else if (accumulatedData_.length > 0) { + // Return up to |size| bytes of the data. + std::int64_t sizeToReturn = + (accumulatedData_.length < size) ? accumulatedData_.length : size; + NSRange range = NSMakeRange(0, (NSUInteger)sizeToReturn); + dataToReturn = [accumulatedData_ subdataWithRange:range]; + [accumulatedData_ replaceBytesInRange:range withBytes:nil length:0]; + break; + } + + [condition_ wait]; + } + [condition_ unlock]; + + if (dataToReturn) { + return ExceptionOr(ByteArrayFromNSData(dataToReturn)); + } else { + return ExceptionOr{Exception::kIo}; + } } } @@ -119,7 +168,17 @@ Exception BleOutputStream::Write(absl::string_view data) { return {Exception::kIo}; } - NSMutableData *packet = [NSMutableData dataWithBytes:data.data() length:data.size()]; + NSData *packet; + if (GNCFeatureFlags.singleCopyEnabled) { + // OPTIMIZATION: Use DISPATCH_DATA_DESTRUCTOR_DEFAULT to perform a + // single copy into a GCD-managed buffer. No NSData required. + dispatch_data_t dispatchData = + dispatch_data_create(data.data(), data.size(), nil, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + // dispatch_data_t is toll-free bridged to NSData + packet = (NSData *)dispatchData; + } else { + packet = [NSMutableData dataWithBytes:data.data() length:data.size()]; + } // Send the data, blocking until the completion handler is called. __block bool isComplete = NO; diff --git a/internal/platform/implementation/apple/condition_variable_test.cc b/internal/platform/implementation/apple/condition_variable_test.cc index 846ad438..4b7af71b 100644 --- a/internal/platform/implementation/apple/condition_variable_test.cc +++ b/internal/platform/implementation/apple/condition_variable_test.cc @@ -16,8 +16,8 @@ #include "gtest/gtest.h" #include "absl/time/clock.h" +#include "third_party/gloop/thread/fiber/fiber.h" #include "internal/platform/implementation/apple/mutex.h" -#include "thread/fiber/fiber.h" namespace nearby { namespace apple { diff --git a/internal/platform/implementation/apple/count_down_latch_test.cc b/internal/platform/implementation/apple/count_down_latch_test.cc index 3b26af10..0f164f7a 100644 --- a/internal/platform/implementation/apple/count_down_latch_test.cc +++ b/internal/platform/implementation/apple/count_down_latch_test.cc @@ -18,7 +18,7 @@ #include "gtest/gtest.h" #include "absl/time/time.h" -#include "thread/fiber/fiber.h" +#include "third_party/gloop/thread/fiber/fiber.h" namespace nearby { namespace apple { diff --git a/internal/platform/implementation/apple/device_info.h b/internal/platform/implementation/apple/device_info.h index ce436201..e42b2b16 100644 --- a/internal/platform/implementation/apple/device_info.h +++ b/internal/platform/implementation/apple/device_info.h @@ -34,17 +34,13 @@ class DeviceInfo : public api::DeviceInfo { api::DeviceInfo::OsType GetOsType() const override; - std::optional GetDownloadPath() const override; + FilePath GetDownloadPath() const override; - std::optional GetLocalAppDataPath() const override; + FilePath GetLocalAppDataPath(FilePath sub_path) const override; - std::optional GetCommonAppDataPath() const override; + FilePath GetTemporaryPath() const override; - std::optional GetTemporaryPath() const override; - - std::optional GetLogPath() const override; - - std::optional GetCrashDumpPath() const override; + FilePath GetLogPath() const override; bool IsScreenLocked() const override; diff --git a/internal/platform/implementation/apple/device_info.mm b/internal/platform/implementation/apple/device_info.mm index 08dc3536..c66f3148 100644 --- a/internal/platform/implementation/apple/device_info.mm +++ b/internal/platform/implementation/apple/device_info.mm @@ -79,7 +79,7 @@ api::DeviceInfo::OsType DeviceInfo::GetOsType() const { #endif } -std::optional DeviceInfo::GetDownloadPath() const { +FilePath DeviceInfo::GetDownloadPath() const { NSFileManager *manager = [NSFileManager defaultManager]; NSError *error = nil; @@ -90,30 +90,24 @@ std::optional DeviceInfo::GetDownloadPath() const { error:&error]; if (!downloadsURL) { GNCLoggerError(@"Failed to get download path: %@", error); - return std::nullopt; + return GetTemporaryPath(); } return FilePath(absl::string_view([downloadsURL.path cString])); } -std::optional DeviceInfo::GetLocalAppDataPath() const { - return FilePath(absl::string_view([GNCLocalAppDataPath().path cString])); +FilePath DeviceInfo::GetLocalAppDataPath(FilePath sub_path) const { + return FilePath(absl::string_view([GNCLocalAppDataPath().path cString])).append(sub_path); } -std::optional DeviceInfo::GetCommonAppDataPath() const { return GetLocalAppDataPath(); } - -std::optional DeviceInfo::GetTemporaryPath() const { +FilePath DeviceInfo::GetTemporaryPath() const { return FilePath(absl::string_view([NSTemporaryDirectory() cString])); } -std::optional DeviceInfo::GetLogPath() const { +FilePath DeviceInfo::GetLogPath() const { return FilePath(absl::string_view([GNCLogPath().path cString])); } -std::optional DeviceInfo::GetCrashDumpPath() const { - return FilePath(absl::string_view([GNCCrashDumpPath().path cString])); -} - bool DeviceInfo::IsScreenLocked() const { return false; } void DeviceInfo::RegisterScreenLockedListener( diff --git a/internal/platform/implementation/apple/mutex_test.cc b/internal/platform/implementation/apple/mutex_test.cc index 4ebadce0..2ad2fce0 100644 --- a/internal/platform/implementation/apple/mutex_test.cc +++ b/internal/platform/implementation/apple/mutex_test.cc @@ -18,7 +18,7 @@ #include "absl/base/thread_annotations.h" #include "absl/synchronization/notification.h" #include "absl/time/time.h" -#include "thread/fiber/fiber.h" +#include "third_party/gloop/thread/fiber/fiber.h" namespace nearby { namespace apple { diff --git a/internal/platform/implementation/apple/platform.mm b/internal/platform/implementation/apple/platform.mm index 5f95d4d6..e11b524b 100644 --- a/internal/platform/implementation/apple/platform.mm +++ b/internal/platform/implementation/apple/platform.mm @@ -45,10 +45,6 @@ #include "internal/platform/implementation/shared/file.h" #include "internal/platform/payload_id.h" -#ifndef NO_WEBRTC -#import "internal/platform/implementation/apple/webrtc.h" -#endif - namespace nearby { namespace api { @@ -205,12 +201,6 @@ std::unique_ptr ImplementationPlatform::CreateWifiDirectMedium return nullptr; } -#ifndef NO_WEBRTC -std::unique_ptr ImplementationPlatform::CreateWebRtcMedium() { - return std::make_unique(); -} -#endif - std::unique_ptr ImplementationPlatform::CreateAppLifecycleMonitor( std::function state_updated_callback) { #if TARGET_OS_IPHONE diff --git a/internal/platform/implementation/apple/utils.mm b/internal/platform/implementation/apple/utils.mm index 386e1036..e517310d 100644 --- a/internal/platform/implementation/apple/utils.mm +++ b/internal/platform/implementation/apple/utils.mm @@ -27,11 +27,15 @@ bool CppBoolFromObjCBool(BOOL b) { return b ? true : false; } char CharFromNSNumber(NSNumber* n) { return n.charValue; } NSString* ObjCStringFromCppString(absl::string_view s) { - return [NSString stringWithUTF8String:s.data()]; + return [[NSString alloc] initWithBytes:s.data() length:s.size() encoding:NSUTF8StringEncoding]; } std::string CppStringFromObjCString(NSString* s) { - return std::string([s UTF8String], [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + if (!s) return std::string(); + const char* cstr = [s UTF8String]; + if (!cstr) return std::string(); + NSUInteger len = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + return std::string(cstr, len); } NSData* NSDataFromByteArray(ByteArray byteArray) { diff --git a/internal/platform/implementation/apple/webrtc.h b/internal/platform/implementation/apple/webrtc.h deleted file mode 100644 index eb3af040..00000000 --- a/internal/platform/implementation/apple/webrtc.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2025 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_APPLE_WEBRTC_H_ -#define PLATFORM_IMPL_APPLE_WEBRTC_H_ - -#ifndef NO_WEBRTC - -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/webrtc.h" -#include "webrtc/api/peer_connection_interface.h" - -namespace nearby::apple { - -class WebRtcMedium : public api::WebRtcMedium { - public: - ~WebRtcMedium() override = default; - - // Gets the default two-letter country code associated with current locale. - // For example, en_US locale resolves to "US". - // This follows the ISO 3166-1 Alpha-2 standard. - std::string GetDefaultCountryCode() override; - - // Creates and returns a new webrtc::PeerConnectionInterface object via - // |callback|. - void CreatePeerConnection(webrtc::PeerConnectionObserver* observer, - PeerConnectionCallback callback) override; - - // Creates and returns a new webrtc::PeerConnectionInterface object via - // |callback| with |PeerConnectionFactoryInterface::Options|. - void CreatePeerConnection( - std::optional options, - webrtc::PeerConnectionObserver* observer, - PeerConnectionCallback callback) override; - - // Returns a signaling messenger for sending WebRTC signaling messages. - std::unique_ptr GetSignalingMessenger( - absl::string_view self_id, - const location::nearby::connections::LocationHint& location_hint) - override; -}; - -} // namespace nearby::apple - -#endif // #ifndef NO_WEBRTC - -#endif // PLATFORM_IMPL_APPLE_WEBRTC_H_ diff --git a/internal/platform/implementation/apple/webrtc.mm b/internal/platform/implementation/apple/webrtc.mm deleted file mode 100644 index a7a50f79..00000000 --- a/internal/platform/implementation/apple/webrtc.mm +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2025 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 NO_WEBRTC - -#include "internal/platform/implementation/apple/webrtc.h" - -#import - -#include -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/strings/string_view.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/crypto.h" -#include "internal/platform/logging.h" -#include "internal/platform/tachyon_express_signaling_messenger.h" -#include "internal/proto/tachyon.pb.h" -#include "internal/proto/tachyon_enums.proto.h" -#include "webrtc/api/create_modular_peer_connection_factory.h" -#include "webrtc/api/task_queue/default_task_queue_factory.h" - -namespace nearby::apple { - -std::string WebRtcMedium::GetDefaultCountryCode() { - NSString* countryCode = [NSLocale.currentLocale objectForKey:NSLocaleCountryCode]; - if (countryCode) { - return std::string([countryCode UTF8String]); - } - return "US"; -} - -void WebRtcMedium::CreatePeerConnection(webrtc::PeerConnectionObserver* observer, - PeerConnectionCallback callback) { - CreatePeerConnection(std::nullopt, observer, std::move(callback)); -} - -void WebRtcMedium::CreatePeerConnection( - std::optional options, - webrtc::PeerConnectionObserver* observer, PeerConnectionCallback callback) { - webrtc::PeerConnectionInterface::RTCConfiguration rtc_config; - rtc_config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; - // TODO: b/261663238 - Add the TURN servers and go beyond the default servers. - webrtc::PeerConnectionInterface::IceServer ice_server; - ice_server.urls.emplace_back("stun:stun.l.google.com:19302"); - ice_server.urls.emplace_back("stun:stun1.l.google.com:19302"); - ice_server.urls.emplace_back("stun:stun2.l.google.com:19302"); - ice_server.urls.emplace_back("stun:stun3.l.google.com:19302"); - ice_server.urls.emplace_back("stun:stun4.l.google.com:19302"); - rtc_config.servers.push_back(ice_server); - - std::unique_ptr signaling_thread = webrtc::Thread::Create(); - signaling_thread->SetName("signaling_thread", nullptr); - if (!signaling_thread->Start()) { - callback(/*peer_connection=*/nullptr); - return; - } - - webrtc::PeerConnectionDependencies dependencies(observer); - webrtc::PeerConnectionFactoryDependencies factory_dependencies; - factory_dependencies.signaling_thread = signaling_thread.release(); - - webrtc::scoped_refptr peer_connection_factory = - webrtc::CreateModularPeerConnectionFactory(std::move(factory_dependencies)); - if (options.has_value()) { - peer_connection_factory->SetOptions(options.value()); - } - webrtc::RTCErrorOr> - peer_connection_or_error = - peer_connection_factory->CreatePeerConnectionOrError(rtc_config, std::move(dependencies)); - if (peer_connection_or_error.ok()) { - callback(peer_connection_or_error.MoveValue()); - } else { - callback(/*peer_connection=*/nullptr); - } -} - -std::unique_ptr WebRtcMedium::GetSignalingMessenger( - absl::string_view self_id, const location::nearby::connections::LocationHint& location_hint) { - return std::make_unique(self_id, location_hint); -} - -} // namespace nearby::apple - -#endif // #ifndef NO_WEBRTC diff --git a/internal/platform/implementation/apple/webrtc_platform.mm b/internal/platform/implementation/apple/webrtc_platform.mm new file mode 100644 index 00000000..8cc97a86 --- /dev/null +++ b/internal/platform/implementation/apple/webrtc_platform.mm @@ -0,0 +1,39 @@ +// Copyright 2026 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/webrtc_platform.h" + +#import + +#include + +#include "connections/implementation/mediums/webrtc/webrtc_medium_impl.h" + +namespace nearby { +namespace api { + +std::unique_ptr WebRtcImplementationPlatform::CreateWebRtcMedium() { + return std::make_unique(); +} + +std::string WebRtcImplementationPlatform::GetDefaultCountryCode() { + NSString* countryCode = [NSLocale.currentLocale objectForKey:NSLocaleCountryCode]; + if (countryCode) { + return std::string([countryCode UTF8String]); + } + return "US"; +} + +} // namespace api +} // namespace nearby diff --git a/internal/platform/implementation/apple/wifi_hotspot.mm b/internal/platform/implementation/apple/wifi_hotspot.mm index 0ec2e846..d2772330 100644 --- a/internal/platform/implementation/apple/wifi_hotspot.mm +++ b/internal/platform/implementation/apple/wifi_hotspot.mm @@ -23,6 +23,8 @@ #include #include "internal/base/masker.h" #include "internal/platform/cancellation_flag_listener.h" + +#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h" #import "internal/platform/implementation/apple/Log/GNCLogger.h" #import "internal/platform/implementation/apple/Mediums/Hotspot/GNCHotspotMedium.h" #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h" @@ -43,12 +45,22 @@ WifiHotspotInputStream::WifiHotspotInputStream(GNCNWFrameworkSocket* socket) : s ExceptionOr WifiHotspotInputStream::Read(std::int64_t size) { NSError* error = nil; - NSData* data = [socket_ readMaxLength:size error:&error]; - if (data == nil) { - GNCLoggerError(@"Error reading socket: %@", error); - return {Exception::kIo}; + if (GNCFeatureFlags.singleCopyEnabled) { + auto result = [socket_ readStringWithMaxLength:size error:&error]; + if (!result.has_value()) { + GNCLoggerError(@"Error reading socket: %@", error); + return {Exception::kIo}; + } + // OPTIMIZATION: Zero-copy transfer from std::string to ByteArray + return ExceptionOr{ByteArray(std::move(result.value()))}; + } else { + NSData* data = [socket_ readMaxLength:size error:&error]; + if (data == nil) { + GNCLoggerError(@"Error reading socket: %@", error); + return {Exception::kIo}; + } + return ExceptionOr{ByteArray((const char*)data.bytes, data.length)}; } - return ExceptionOr{ByteArray((const char*)data.bytes, data.length)}; } Exception WifiHotspotInputStream::Close() { @@ -63,7 +75,15 @@ WifiHotspotOutputStream::WifiHotspotOutputStream(GNCNWFrameworkSocket* socket) : Exception WifiHotspotOutputStream::Write(absl::string_view data) { NSError* error = nil; - BOOL result = [socket_ write:[NSData dataWithBytes:data.data() length:data.size()] error:&error]; + BOOL result = NO; + + if (GNCFeatureFlags.singleCopyEnabled) { + // OPTIMIZATION: Write raw bytes directly, avoiding NSData creation. + result = [socket_ writeBytes:data.data() length:data.size() error:&error]; + } else { + result = [socket_ write:[NSData dataWithBytes:data.data() length:data.size()] error:&error]; + } + if (!result) { GNCLoggerError(@"Error writing socket: %@", error); return {Exception::kIo}; @@ -150,7 +170,7 @@ std::unique_ptr WifiHotspotMedium::ConnectToService( } // 4 bytes IP address format. NSData* host_ip_address = [NSData dataWithBytes:service_address.address.data() - length:service_address.address.size()]; + length:service_address.address.size()]; host = [GNCIPv4Address addressFromData:host_ip_address]; GNCLoggerInfo(@"Connect to Hotspot host server: %@", [host dottedRepresentation]); diff --git a/internal/platform/implementation/apple/wifi_lan.mm b/internal/platform/implementation/apple/wifi_lan.mm index 5f57ba52..984d44de 100644 --- a/internal/platform/implementation/apple/wifi_lan.mm +++ b/internal/platform/implementation/apple/wifi_lan.mm @@ -19,6 +19,7 @@ #include #include +#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h" #import "internal/platform/implementation/apple/Log/GNCLogger.h" #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h" #import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFramework.h" @@ -35,12 +36,22 @@ WifiLanInputStream::WifiLanInputStream(GNCNWFrameworkSocket* socket) : socket_(s ExceptionOr WifiLanInputStream::Read(std::int64_t size) { NSError* error = nil; - NSData* data = [socket_ readMaxLength:size error:&error]; - if (data == nil) { - GNCLoggerError(@"Error reading socket: %@", error); - return {Exception::kIo}; + if (GNCFeatureFlags.singleCopyEnabled) { + auto result = [socket_ readStringWithMaxLength:size error:&error]; + if (!result.has_value()) { + GNCLoggerError(@"Error reading socket: %@", error); + return {Exception::kIo}; + } + // OPTIMIZATION: Zero-copy transfer from std::string to ByteArray + return ExceptionOr{ByteArray(std::move(result.value()))}; + } else { + NSData* data = [socket_ readMaxLength:size error:&error]; + if (data == nil) { + GNCLoggerError(@"Error reading socket: %@", error); + return {Exception::kIo}; + } + return ExceptionOr{ByteArray((const char*)data.bytes, data.length)}; } - return ExceptionOr{ByteArray((const char*)data.bytes, data.length)}; } Exception WifiLanInputStream::Close() { @@ -55,7 +66,15 @@ WifiLanOutputStream::WifiLanOutputStream(GNCNWFrameworkSocket* socket) : socket_ Exception WifiLanOutputStream::Write(absl::string_view data) { NSError* error = nil; - BOOL result = [socket_ write:[NSData dataWithBytes:data.data() length:data.size()] error:&error]; + BOOL result = NO; + + if (GNCFeatureFlags.singleCopyEnabled) { + // OPTIMIZATION: Write raw bytes directly, avoiding NSData creation. + result = [socket_ writeBytes:data.data() length:data.size() error:&error]; + } else { + result = [socket_ write:[NSData dataWithBytes:data.data() length:data.size()] error:&error]; + } + if (!result) { GNCLoggerError(@"Error writing socket: %@", error); return {Exception::kIo}; diff --git a/internal/platform/implementation/auth_status.h b/internal/platform/implementation/auth_status.h deleted file mode 100644 index 00227836..00000000 --- a/internal/platform/implementation/auth_status.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2024 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 THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_AUTH_STATUS_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_AUTH_STATUS_H_ - -namespace nearby { - -enum AuthStatus { - AUTH_STATUS_UNSPECIFIED = 0, - // Request completed successfully, the results should be in the correct order - // up to the given count. - SUCCESS = 1, - - // Request encountered a generic error. - GENERIC_ERROR = 2, - - // Request as specified is not supported. - UNSUPPORTED = 3, - - // Request failed and should be retried soon. - TEMPORARILY_UNAVAILABLE = 4, - - // Request failed due to an unavailable resource. - UNAVAILABLE_RESOURCE = 5, - - // The request failed due to an invalid argument. - INVALID_ARGUMENT = 6, - - // In case the status could not be retrieved. - UNKNOWN_STATUS = 7, - - // Currently used as a way to signal an ETag mismatch. - PRECONDITION_FAILED = 8, - - // Exclusively used to report when user did not consent to required scopes. - // Do NOT use this for another other scenarios. - PERMISSION_DENIED = 9, - - // The resource exists, but the requested attribute of it does not. - MISSING_ATTRIBUTE = 10, - - // The method was interrupted and the caller should exit the current unit of - // work immediately. - INTERRUPTED = 11, - - // User signed in with an unexpected account. - SIGNED_IN_WITH_WRONG_ACCOUNT = 12, - - // Used when data cannot be parsed properly. - PARSE_ERROR = 13, - - // Used to report that the local HTTP server for receiving the authorization - // code cannot be created. - CANT_CREATE_AUTH_SERVER = 14, - - // Used to report that the system browser for authenticating the user cannot - // be open. - CANT_OPEN_BROWSER_FOR_AUTH = 15, - - // Used to report that the authorization code cannot be received. - CANT_RECEIVE_AUTH_CODE = 16, - - // Used to report that the account is blocked (e.g. CAA). - ACCOUNT_BLOCKED = 17, - - // Receiving the authorization code failed because it took longer than the - // timeout. - AUTH_CODE_TIMEOUT_EXCEEDED = 18, - - // Used to report when user presses the cancel button during login process. - USER_CANCELED = 19, -}; - -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_AUTH_STATUS_H_ diff --git a/internal/platform/implementation/ble.h b/internal/platform/implementation/ble.h index 00866e1e..d2f31276 100644 --- a/internal/platform/implementation/ble.h +++ b/internal/platform/implementation/ble.h @@ -29,7 +29,6 @@ #include "absl/status/statusor.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "internal/platform/byte_array.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/exception.h" @@ -155,26 +154,26 @@ struct GattCharacteristic { Property property; // overloading operator for enum class Permission and Property - friend inline Permission operator|(Permission a, Permission b) { + friend Permission operator|(Permission a, Permission b) { return static_cast(static_cast(a) | static_cast(b)); } - friend inline Permission operator&(Permission a, Permission b) { + friend Permission operator&(Permission a, Permission b) { return static_cast(static_cast(a) & static_cast(b)); } - friend inline Permission& operator|=(Permission& a, Permission b) { + friend Permission& operator|=(Permission& a, Permission b) { a = a | b; return a; } - friend inline Property operator|(Property a, Property b) { + friend Property operator|(Property a, Property b) { return static_cast(static_cast(a) | static_cast(b)); } - friend inline Property operator&(Property a, Property b) { + friend Property operator&(Property a, Property b) { return static_cast(static_cast(a) & static_cast(b)); } - friend inline Property& operator|=(Property& a, Property b) { + friend Property& operator|=(Property& a, Property b) { a = a | b; return a; } @@ -231,13 +230,13 @@ class GattClient { // It is okay for duplicate services to exist, as long as the specified // characteristic UUID is unique among all services of the same UUID. // NOLINTNEXTLINE(google3-legacy-absl-backports) - virtual absl::optional GetCharacteristic( + virtual std::optional GetCharacteristic( const Uuid& service_uuid, const Uuid& characteristic_uuid) = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#readCharacteristic(android.bluetooth.BluetoothGattCharacteristic) // https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#getValue() // NOLINTNEXTLINE(google3-legacy-absl-backports) - virtual absl::optional ReadCharacteristic( + virtual std::optional ReadCharacteristic( const GattCharacteristic& characteristic) = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#setValue(byte[]) @@ -248,14 +247,6 @@ class GattClient { virtual bool WriteCharacteristic(const GattCharacteristic& characteristic, absl::string_view value, WriteType type) = 0; - // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic,%20boolean) - // - // Enable or disable notifications/indications for a given characteristic. - virtual bool SetCharacteristicSubscription( - const GattCharacteristic& characteristic, bool enable, - absl::AnyInvocable - on_characteristic_changed_cb) = 0; - // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#disconnect() virtual void Disconnect() = 0; }; @@ -281,7 +272,7 @@ class GattServer { // more information about this descriptor, please go to: // https://www.bluetooth.com/specifications/Gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.Gatt.client_characteristic_configuration.xml // NOLINTNEXTLINE(google3-legacy-absl-backports) - virtual absl::optional CreateCharacteristic( + virtual std::optional CreateCharacteristic( const Uuid& service_uuid, const Uuid& characteristic_uuid, GattCharacteristic::Permission permission, GattCharacteristic::Property property) = 0; diff --git a/internal/platform/implementation/device_info.h b/internal/platform/implementation/device_info.h index 19aca037..3d3e859a 100644 --- a/internal/platform/implementation/device_info.h +++ b/internal/platform/implementation/device_info.h @@ -15,12 +15,14 @@ #ifndef PLATFORM_API_DEVICE_INFO_H_ #define PLATFORM_API_DEVICE_INFO_H_ +#include #include #include #include #include "absl/strings/string_view.h" #include "internal/base/file_path.h" +#include "internal/base/files.h" namespace nearby { namespace api { @@ -46,12 +48,15 @@ class DeviceInfo { virtual OsType GetOsType() const = 0; // Gets known paths of current user. - virtual std::optional GetDownloadPath() const = 0; - virtual std::optional GetLocalAppDataPath() const = 0; - virtual std::optional GetCommonAppDataPath() const = 0; - virtual std::optional GetTemporaryPath() const = 0; - virtual std::optional GetLogPath() const = 0; - virtual std::optional GetCrashDumpPath() const = 0; + virtual FilePath GetDownloadPath() const = 0; + virtual FilePath GetLocalAppDataPath(FilePath sub_path) const = 0; + virtual FilePath GetTemporaryPath() const = 0; + virtual FilePath GetLogPath() const = 0; + + virtual std::optional GetAvailableDiskSpaceInBytes( + const FilePath& path) const { + return Files::GetAvailableDiskSpaceInBytes(path); + }; // Monitor screen status virtual bool IsScreenLocked() const = 0; @@ -66,6 +71,24 @@ class DeviceInfo { virtual bool AllowSleep() = 0; }; +template +void AbslStringify(Sink& sink, DeviceInfo::DeviceType device_type) { + switch (device_type) { + case DeviceInfo::DeviceType::kUnknown: + sink.Append("Unknown"); + return; + case DeviceInfo::DeviceType::kPhone: + sink.Append("Phone"); + return; + case DeviceInfo::DeviceType::kTablet: + sink.Append("Tablet"); + return; + case DeviceInfo::DeviceType::kLaptop: + sink.Append("PC"); + return; + } +} + } // namespace api } // namespace nearby diff --git a/presence/device_motion.cc b/internal/platform/implementation/device_info_test.cc similarity index 56% rename from presence/device_motion.cc rename to internal/platform/implementation/device_info_test.cc index 8b79e3f6..cae28209 100644 --- a/presence/device_motion.cc +++ b/internal/platform/implementation/device_info_test.cc @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2026 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,17 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "presence/device_motion.h" +#include "internal/platform/implementation/device_info.h" -namespace nearby { -namespace presence { +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" -DeviceMotion::DeviceMotion(MotionType motion_type, float confidence) noexcept - : motion_type_(motion_type), confidence_(confidence) {} -DeviceMotion::MotionType DeviceMotion::GetMotionType() const { - return motion_type_; +namespace nearby::api { +namespace { + +TEST(DeviceInfoTest, DeviceTypeToStringTest) { + DeviceInfo::DeviceType type = DeviceInfo::DeviceType::kPhone; + EXPECT_EQ(absl::StrCat(type), "Phone"); } -float DeviceMotion::GetConfidence() const { return confidence_; } -} // namespace presence -} // namespace nearby +} // namespace +} // namespace nearby::api diff --git a/internal/platform/implementation/g3/BUILD b/internal/platform/implementation/g3/BUILD index f2a980a2..6fb81f20 100644 --- a/internal/platform/implementation/g3/BUILD +++ b/internal/platform/implementation/g3/BUILD @@ -85,6 +85,7 @@ cc_library( "bluetooth_adapter.cc", "bluetooth_classic.cc", "credential_storage_impl.cc", + "webrtc.cc", "wifi_direct.cc", "wifi_hotspot.cc", "wifi_lan.cc", @@ -96,6 +97,7 @@ cc_library( "bluetooth_classic.h", "credential_storage_impl.h", "socket_base.h", + "webrtc.h", "wifi.h", "wifi_direct.h", "wifi_hotspot.h", @@ -112,11 +114,14 @@ cc_library( "//internal/platform:types", "//internal/platform:uuid", "//internal/platform/implementation:comm", + "//internal/platform/implementation:webrtc_platform", "//internal/platform/implementation:wifi_utils", "//internal/proto:credential_cc_proto", "//third_party/webrtc/files/stable/webrtc/api:create_modular_peer_connection_factory", - "//third_party/webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", + "//third_party/webrtc/files/stable/webrtc/api:peer_connection_interface", "//third_party/webrtc/files/stable/webrtc/api:scoped_refptr", + "//third_party/webrtc/files/stable/webrtc/rtc_base:checks", + "//third_party/webrtc/files/stable/webrtc/rtc_base:threading", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", @@ -177,8 +182,8 @@ cc_library( testonly = True, srcs = [ "platform.cc", + "webrtc_platform.cc", ], - defines = ["NO_WEBRTC"], visibility = [ "//connections:__subpackages__", "//connections:partners", @@ -191,10 +196,9 @@ cc_library( "//internal/preferences:__subpackages__", "//internal/proto/analytics:__subpackages__", "//internal/weave:__subpackages__", - "//location/nearby/cpp:__subpackages__", "//location/nearby/sharing/sdk:__subpackages__", - "//presence:__subpackages__", "//sharing:__subpackages__", + "//third_party/nearby/presence:__subpackages__", ], deps = [ ":comm", @@ -208,14 +212,16 @@ cc_library( "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", "//internal/platform/implementation:types", + "//internal/platform/implementation:webrtc_platform", "//internal/platform/implementation/shared:count_down_latch", "//internal/platform/implementation/shared:file", + "//third_party/gloop/thread", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", - "@com_google_nisaba//nisaba/port:thread_pool", ], + alwayslink = 1, ) cc_library( diff --git a/internal/platform/implementation/g3/ble.cc b/internal/platform/implementation/g3/ble.cc index 9e1282b5..573d2cf1 100644 --- a/internal/platform/implementation/g3/ble.cc +++ b/internal/platform/implementation/g3/ble.cc @@ -679,33 +679,6 @@ bool BleMedium::GattClient::WriteCharacteristic( return status.ok(); } -bool BleMedium::GattClient::SetCharacteristicSubscription( - const api::ble::GattCharacteristic& characteristic, bool enable, - absl::AnyInvocable - on_characteristic_changed_cb) { - absl::MutexLock lock(mutex_); - if (!is_connection_alive_) { - return false; - } - Borrowed borrowed = gatt_server_.Borrow(); - if (!borrowed) { - return false; - } - BleMedium::GattServer* gatt_server = - static_cast(*borrowed); - LOG(INFO) << "G3 Ble SetCharacteristicSubscription, characteristic=(" - << characteristic.service_uuid.Get16BitAsString() << "," - << std::string(characteristic.uuid) << "), enable = " << enable; - if (enable) { - return gatt_server->AddCharacteristicSubscription( - peripheral_id_, characteristic, - std::move(on_characteristic_changed_cb)); - } else { - return gatt_server->RemoveCharacteristicSubscription(peripheral_id_, - characteristic); - } -} - void BleMedium::GattClient::Disconnect() { bool was_alive = is_connection_alive_.exchange(false); if (!was_alive) return; diff --git a/internal/platform/implementation/g3/ble.h b/internal/platform/implementation/g3/ble.h index 86c6daf8..e3eacca3 100644 --- a/internal/platform/implementation/g3/ble.h +++ b/internal/platform/implementation/g3/ble.h @@ -282,11 +282,6 @@ class BleMedium : public api::ble::BleMedium { absl::string_view value, api::ble::GattClient::WriteType write_type) override; - bool SetCharacteristicSubscription( - const api::ble::GattCharacteristic& characteristic, bool enable, - absl::AnyInvocable - on_characteristic_changed_cb) override; - void Disconnect() override; void OnServerDisconnected(); diff --git a/internal/platform/implementation/g3/device_info.h b/internal/platform/implementation/g3/device_info.h index 5ff18863..6754d46f 100644 --- a/internal/platform/implementation/g3/device_info.h +++ b/internal/platform/implementation/g3/device_info.h @@ -45,33 +45,24 @@ class DeviceInfo : public api::DeviceInfo { return api::DeviceInfo::OsType::kChromeOs; } - std::optional GetDownloadPath() const override { + FilePath GetDownloadPath() const override { return Files::GetTemporaryDirectory(); } - std::optional GetLocalAppDataPath() const override { + FilePath GetLocalAppDataPath(FilePath sub_path) const override { if (MediumEnvironment::Instance() .GetEnvironmentConfig() .use_temporary_directory_for_app_path) { - return Files::GetTemporaryDirectory(); + return Files::GetTemporaryDirectory().append(sub_path); } - - return GetAppDataPath(); + return GetAppDataPath().append(sub_path); } - std::optional GetCommonAppDataPath() const override { + FilePath GetTemporaryPath() const override { return Files::GetTemporaryDirectory(); } - std::optional GetTemporaryPath() const override { - return Files::GetTemporaryDirectory(); - } - - std::optional GetLogPath() const override { - return Files::GetTemporaryDirectory(); - } - - std::optional GetCrashDumpPath() const override { + FilePath GetLogPath() const override { return Files::GetTemporaryDirectory(); } diff --git a/internal/platform/implementation/g3/platform.cc b/internal/platform/implementation/g3/platform.cc index 02c5afda..dfa62fae 100644 --- a/internal/platform/implementation/g3/platform.cc +++ b/internal/platform/implementation/g3/platform.cc @@ -14,7 +14,6 @@ #include "internal/platform/implementation/platform.h" -#include #include #include #include @@ -25,6 +24,7 @@ #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "third_party/gloop/thread/thread.h" #include "internal/base/file_path.h" #include "internal/base/files.h" #include "internal/platform/implementation/app_lifecycle_monitor.h" @@ -55,11 +55,6 @@ #include "internal/platform/logging.h" #include "internal/platform/os_name.h" #include "internal/platform/payload_id.h" -#include "thread/thread.h" -#ifndef NO_WEBRTC -#include "internal/platform/implementation/g3/webrtc.h" -#include "internal/platform/implementation/webrtc.h" -#endif #include "internal/platform/implementation/g3/atomic_boolean.h" #include "internal/platform/implementation/g3/atomic_reference.h" #include "internal/platform/implementation/g3/ble.h" @@ -80,7 +75,6 @@ #include "internal/platform/implementation/g3/wifi_lan.h" #include "internal/platform/implementation/shared/file.h" #include "internal/platform/implementation/wifi.h" -#include "internal/platform/medium_environment.h" namespace nearby { namespace api { @@ -219,16 +213,6 @@ ImplementationPlatform::CreateWifiDirectMedium() { return std::make_unique(); } -#ifndef NO_WEBRTC -std::unique_ptr ImplementationPlatform::CreateWebRtcMedium() { - if (MediumEnvironment::Instance().GetEnvironmentConfig().webrtc_enabled) { - return std::make_unique(); - } else { - return nullptr; - } -} -#endif - std::unique_ptr ImplementationPlatform::CreateAppLifecycleMonitor( std::function diff --git a/internal/platform/implementation/g3/scheduled_executor.cc b/internal/platform/implementation/g3/scheduled_executor.cc index f82d6f7b..34e7fed9 100644 --- a/internal/platform/implementation/g3/scheduled_executor.cc +++ b/internal/platform/implementation/g3/scheduled_executor.cc @@ -16,16 +16,15 @@ #include #include -#include #include #include "absl/strings/str_format.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" #include "internal/platform/implementation/cancelable.h" +#include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" #include "internal/platform/runnable.h" -#include "internal/test/fake_clock.h" namespace nearby { namespace g3 { @@ -66,20 +65,13 @@ class ScheduledCancelable : public api::Cancelable { } // namespace ScheduledExecutor::ScheduledExecutor() { - std::optional fake_clock = - MediumEnvironment::Instance().GetSimulatedClock(); - if (fake_clock.has_value()) { - name_ = absl::StrFormat("G3 scheduled executor %p", this); - (*fake_clock)->AddObserver(name_, [this]() { RunReadyTasks(); }); - } + name_ = absl::StrFormat("G3 scheduled executor %p", this); + MediumEnvironment::Instance().AddSimulatedClockObserver( + name_, [this]() { RunReadyTasks(); }); } ScheduledExecutor::~ScheduledExecutor() { - std::optional fake_clock = - MediumEnvironment::Instance().GetSimulatedClock(); - if (fake_clock.has_value()) { - (*fake_clock)->RemoveObserver(name_); - } + MediumEnvironment::Instance().RemoveSimulatedClockObserver(name_); executor_.Shutdown(); } @@ -96,10 +88,10 @@ std::shared_ptr ScheduledExecutor::Schedule( runnable(); } }; - std::optional fake_clock = - MediumEnvironment::Instance().GetSimulatedClock(); - if (fake_clock.has_value()) { - absl::Time trigger_time = (*fake_clock)->Now() + delay; + if (MediumEnvironment::Instance() + .GetEnvironmentConfig() + .use_simulated_clock) { + absl::Time trigger_time = MediumEnvironment::Instance().Now() + delay; absl::MutexLock lock(mutex_); tasks_.insert(std::pair>( trigger_time, std::make_unique(std::move(task)))); @@ -110,15 +102,12 @@ std::shared_ptr ScheduledExecutor::Schedule( } void ScheduledExecutor::RunReadyTasks() { - std::optional fake_clock = - MediumEnvironment::Instance().GetSimulatedClock(); if (executor_.InShutdown()) { return; } - if (!fake_clock.has_value()) { - return; - } - absl::Time current_time = (*fake_clock)->Now(); + CHECK( + MediumEnvironment::Instance().GetEnvironmentConfig().use_simulated_clock); + absl::Time current_time = MediumEnvironment::Instance().Now(); absl::MutexLock lock(mutex_); for (auto it = tasks_.begin(); it != tasks_.end();) { if (it->first <= current_time) { diff --git a/internal/platform/implementation/g3/system_clock.cc b/internal/platform/implementation/g3/system_clock.cc index b3cc53c1..430ba5a4 100644 --- a/internal/platform/implementation/g3/system_clock.cc +++ b/internal/platform/implementation/g3/system_clock.cc @@ -15,26 +15,21 @@ #include "internal/platform/implementation/system_clock.h" #include "absl/time/clock.h" +#include "absl/time/time.h" #include "internal/platform/exception.h" #include "internal/platform/medium_environment.h" -#include "internal/test/fake_clock.h" namespace nearby { absl::Time SystemClock::ElapsedRealtime() { - absl::optional fake_clock = - MediumEnvironment::Instance().GetSimulatedClock(); - if (fake_clock.has_value()) { - return (*fake_clock)->Now(); - } - return absl::Now(); + return MediumEnvironment::Instance().Now(); } Exception SystemClock::Sleep(absl::Duration duration) { - absl::optional fake_clock = - MediumEnvironment::Instance().GetSimulatedClock(); - if (fake_clock.has_value()) { - (*fake_clock)->FastForward(duration); + if (MediumEnvironment::Instance() + .GetEnvironmentConfig() + .use_simulated_clock) { + MediumEnvironment::Instance().FastForward(duration); } else { absl::SleepFor(duration); } diff --git a/internal/platform/implementation/g3/webrtc.cc b/internal/platform/implementation/g3/webrtc.cc index c8385c0c..da43fa31 100644 --- a/internal/platform/implementation/g3/webrtc.cc +++ b/internal/platform/implementation/g3/webrtc.cc @@ -24,10 +24,11 @@ #include "internal/platform/byte_array.h" #include "internal/platform/implementation/webrtc.h" #include "internal/platform/medium_environment.h" -#include "webrtc/api/create_modular_peer_connection_factory.h" -#include "webrtc/api/peer_connection_interface.h" -#include "webrtc/api/scoped_refptr.h" -#include "webrtc/rtc_base/checks.h" +#include "third_party/webrtc/files/stable/webrtc/api/create_modular_peer_connection_factory.h" +#include "third_party/webrtc/files/stable/webrtc/api/peer_connection_interface.h" +#include "third_party/webrtc/files/stable/webrtc/api/scoped_refptr.h" +#include "third_party/webrtc/files/stable/webrtc/rtc_base/checks.h" +#include "third_party/webrtc/files/stable/webrtc/rtc_base/thread.h" namespace nearby { namespace g3 { @@ -60,8 +61,6 @@ void WebRtcSignalingMessenger::StopReceivingMessages() { WebRtcMedium::~WebRtcMedium() { single_thread_executor_.Shutdown(); } -std::string WebRtcMedium::GetDefaultCountryCode() { return "US"; } - void WebRtcMedium::CreatePeerConnection( webrtc::PeerConnectionObserver* observer, PeerConnectionCallback callback) { CreatePeerConnection(std::nullopt, observer, std::move(callback)); diff --git a/internal/platform/implementation/g3/webrtc.h b/internal/platform/implementation/g3/webrtc.h index 048dabf2..943e27f6 100644 --- a/internal/platform/implementation/g3/webrtc.h +++ b/internal/platform/implementation/g3/webrtc.h @@ -23,7 +23,7 @@ #include "internal/platform/byte_array.h" #include "internal/platform/implementation/webrtc.h" #include "internal/platform/implementation/g3/single_thread_executor.h" -#include "webrtc/api/peer_connection_interface.h" +#include "third_party/webrtc/files/stable/webrtc/api/peer_connection_interface.h" namespace nearby { namespace g3 { @@ -59,8 +59,6 @@ class WebRtcMedium : public api::WebRtcMedium { WebRtcMedium() = default; ~WebRtcMedium() override; - std::string GetDefaultCountryCode() override; - // Creates and returns a new webrtc::PeerConnectionInterface object via // |callback|. void CreatePeerConnection(webrtc::PeerConnectionObserver* observer, diff --git a/internal/platform/implementation/g3/webrtc_platform.cc b/internal/platform/implementation/g3/webrtc_platform.cc new file mode 100644 index 00000000..227f2e69 --- /dev/null +++ b/internal/platform/implementation/g3/webrtc_platform.cc @@ -0,0 +1,39 @@ +// Copyright 2026 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/webrtc_platform.h" + +#include +#include + +#include "internal/platform/implementation/g3/webrtc.h" +#include "internal/platform/implementation/webrtc.h" +#include "internal/platform/medium_environment.h" + +namespace nearby::api { + +std::unique_ptr +WebRtcImplementationPlatform::CreateWebRtcMedium() { + if (MediumEnvironment::Instance().GetEnvironmentConfig().webrtc_enabled) { + return std::make_unique(); + } else { + return nullptr; + } +} + +std::string WebRtcImplementationPlatform::GetDefaultCountryCode() { + return "US"; +} + +} // namespace nearby::api diff --git a/internal/platform/implementation/g3/wifi.h b/internal/platform/implementation/g3/wifi.h index f1439ace..48e287b6 100644 --- a/internal/platform/implementation/g3/wifi.h +++ b/internal/platform/implementation/g3/wifi.h @@ -17,6 +17,9 @@ #include +#include "absl/base/thread_annotations.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "absl/synchronization/mutex.h" #include "internal/platform/implementation/wifi.h" #include "internal/platform/medium_environment.h" diff --git a/internal/platform/implementation/g3/wifi_direct.cc b/internal/platform/implementation/g3/wifi_direct.cc index 5b154f47..0241bf99 100644 --- a/internal/platform/implementation/g3/wifi_direct.cc +++ b/internal/platform/implementation/g3/wifi_direct.cc @@ -135,12 +135,12 @@ bool WifiDirectMedium::StartWifiDirect( WifiDirectCredentials* wifi_direct_credentials) { absl::MutexLock lock(mutex_); - std::string service_name = absl::StrCat("NC-", Prng().NextUint32()); - wifi_direct_credentials->SetServiceName(service_name); + std::string device_name = absl::StrCat("NC-", Prng().NextUint32()); + wifi_direct_credentials->SetDeviceName(device_name); std::string pin = absl::StrFormat("%04x", Prng().NextUint32()); wifi_direct_credentials->SetPin(pin); - LOG(INFO) << "G3 StartWifiDirect GO: service_name:" << service_name + LOG(INFO) << "G3 StartWifiDirect GO: device_name:" << device_name << ", pin:" << pin; auto& env = MediumEnvironment::Instance(); @@ -165,13 +165,13 @@ bool WifiDirectMedium::ConnectWifiDirect( const WifiDirectCredentials& wifi_direct_credentials) { absl::MutexLock lock(mutex_); - LOG(INFO) << "G3 ConnectWifiDirect : service_name:" - << wifi_direct_credentials.GetServiceName() + LOG(INFO) << "G3 ConnectWifiDirect : device_name:" + << wifi_direct_credentials.GetDeviceName() << ", pin:" << wifi_direct_credentials.GetPin(); auto& env = MediumEnvironment::Instance(); auto* remote_medium = static_cast( - env.GetWifiDirectMedium(wifi_direct_credentials.GetServiceName(), "")); + env.GetWifiDirectMedium(wifi_direct_credentials.GetDeviceName(), "")); if (!remote_medium) { env.UpdateWifiDirectMediumForStartOrConnect(*this, &wifi_direct_credentials, /*is_go=*/false, diff --git a/internal/platform/implementation/g3/wifi_direct.h b/internal/platform/implementation/g3/wifi_direct.h index fe9ec31d..9d0a8ea5 100644 --- a/internal/platform/implementation/g3/wifi_direct.h +++ b/internal/platform/implementation/g3/wifi_direct.h @@ -185,7 +185,7 @@ class WifiDirectMedium : public api::WifiDirectMedium { // Returns the supported WifiDirect auth types. std::vector GetSupportedWifiDirectAuthTypes() const override { - return {WifiDirectAuthType::WIFI_DIRECT_WITH_PIN}; + return {WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME}; } private: diff --git a/internal/platform/implementation/linux/ble_gatt_client.cc b/internal/platform/implementation/linux/ble_gatt_client.cc index 1c876f85..727643f7 100644 --- a/internal/platform/implementation/linux/ble_gatt_client.cc +++ b/internal/platform/implementation/linux/ble_gatt_client.cc @@ -172,62 +172,6 @@ bool GattClient::WriteCharacteristic( return success; } -bool GattClient::SetCharacteristicSubscription( - const api::ble::GattCharacteristic &characteristic, bool enable, - absl::AnyInvocable - on_characteristic_changed_cb) { - LOG(INFO) << __func__ << ": " - << (enable ? "Enabling" : "Disabling") - << " subscription for characteristic '" - << absl::Substitute("$0", characteristic) << "'"; - absl::MutexLock lock(&characteristics_mutex_); - if (characteristics_.count(characteristic) == 0) { - LOG(ERROR) << __func__ << ": Unknown characteristic '" - << absl::Substitute("$0", characteristic) << "'"; - return false; - } - - if (enable) { - auto subbed_chr = gatt_discovery_->GetSubscribedCharacteristic( - peripheral_object_path_, characteristic.service_uuid, - characteristic.uuid, std::move(on_characteristic_changed_cb)); - if (subbed_chr == nullptr) { - LOG(INFO) << __func__ - << ": Failed to get subscribed characteristic client."; - return false; - } - try { - subbed_chr->StartNotify(); - } catch (const sdbus::Error &e) { - DBUS_LOG_METHOD_CALL_ERROR(subbed_chr, "StartNotify", e); - return false; - } - characteristics_[characteristic] = std::move(subbed_chr); - } else if (std::holds_alternative< - std::unique_ptr>( - characteristics_[characteristic])) { - auto chr = gatt_discovery_->GetCharacteristic(peripheral_object_path_, - characteristic.service_uuid, - characteristic.uuid); - if (chr == nullptr) { - LOG(INFO) << __func__ - << ": Failed to get characteristic client for unsubscribe."; - return false; - } - try { - chr->StopNotify(); - } catch (const sdbus::Error &e) { - DBUS_LOG_METHOD_CALL_ERROR(chr, "StopNotify", e); - return false; - } - - characteristics_[characteristic] = std::move(chr); - } - LOG(INFO) << __func__ << ": Subscription update succeeded for characteristic '" - << absl::Substitute("$0", characteristic) << "'"; - return true; -} - void GattClient::Disconnect() { LOG(INFO) << __func__ << ": Disconnecting GATT client for peripheral " diff --git a/internal/platform/implementation/linux/ble_gatt_client.h b/internal/platform/implementation/linux/ble_gatt_client.h index 1f755229..bb41a97b 100644 --- a/internal/platform/implementation/linux/ble_gatt_client.h +++ b/internal/platform/implementation/linux/ble_gatt_client.h @@ -182,14 +182,6 @@ class GattClient : public api::ble::GattClient { absl::string_view value, WriteType type) override ABSL_LOCKS_EXCLUDED(characteristics_mutex_); - // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic,%20boolean) - // - // Enable or disable notifications/indications for a given characteristic. - bool SetCharacteristicSubscription( - const api::ble::GattCharacteristic &characteristic, bool enable, - absl::AnyInvocable - on_characteristic_changed_cb) override - ABSL_LOCKS_EXCLUDED(characteristics_mutex_); // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#disconnect() void Disconnect() override; diff --git a/internal/platform/implementation/linux/ble_l2cap_connection_test.cc b/internal/platform/implementation/linux/ble_l2cap_connection_test.cc new file mode 100644 index 00000000..537c6fba --- /dev/null +++ b/internal/platform/implementation/linux/ble_l2cap_connection_test.cc @@ -0,0 +1,98 @@ +// Copyright 2025 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/linux/ble_l2cap_connection.h" + +#include +#include + +#include +#include + +#include "internal/platform/byte_array.h" +#include "gtest/gtest.h" + +namespace nearby { +namespace linux { +namespace { + +std::string WrapFrame(absl::string_view payload) { + return std::string{ + static_cast((payload.size() >> 24) & 0xFF), + static_cast((payload.size() >> 16) & 0xFF), + static_cast((payload.size() >> 8) & 0xFF), + static_cast(payload.size() & 0xFF)} + + std::string(payload); +} + +class BleL2capConnectionTest : public ::testing::Test { + protected: + void SetUp() override { + ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, fds_), 0); + peer_fd_ = fds_[1]; + streams_ = CreateBleL2capConnectionStreams(fds_[0]); + } + + void TearDown() override { + if (streams_.input) streams_.input->Close(); + if (streams_.output) streams_.output->Close(); + if (peer_fd_ >= 0) close(peer_fd_); + } + + int fds_[2]{-1, -1}; + int peer_fd_{-1}; + BleL2capConnectionStreams streams_; +}; + +TEST_F(BleL2capConnectionTest, ReadReceivesExactBytesFromPeer) { + std::string message = "hello into l2cap socket"; + std::string framed_message = WrapFrame(message); + + ASSERT_EQ(write(peer_fd_, framed_message.data(), framed_message.size()), + static_cast(framed_message.size())); + + auto out = streams_.input->Read(message.size()).GetResult(); + EXPECT_EQ(out, ByteArray(message)); + EXPECT_EQ(out.AsStringView(), message); +} + +TEST_F(BleL2capConnectionTest, WriteSendsExactBytesToPeer) { + std::string message = "hello from l2cap socket"; + std::string framed_message = WrapFrame(message); + + EXPECT_EQ(streams_.output->Write(message).value, Exception::kSuccess); + + std::vector received(framed_message.size()); + ssize_t n = read(peer_fd_, received.data(), received.size()); + + ASSERT_EQ(n, static_cast(framed_message.size())); + EXPECT_EQ(std::string(received.begin(), received.end()), framed_message); +} + +TEST_F(BleL2capConnectionTest, SkipSkipsBytesFromPeer) { + std::string message = "hello there"; + std::string framed_message = WrapFrame(message); + + ASSERT_EQ(write(peer_fd_, framed_message.data(), framed_message.size()), + static_cast(framed_message.size())); + + EXPECT_EQ(streams_.input->Skip(6).result(), 6); + + auto out = streams_.input->Read(5).GetResult(); + EXPECT_EQ(out, ByteArray("there")); +} + +} // namespace +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/ble_l2cap_socket.cc b/internal/platform/implementation/linux/ble_l2cap_socket.cc index a12ad0a7..5debcf74 100644 --- a/internal/platform/implementation/linux/ble_l2cap_socket.cc +++ b/internal/platform/implementation/linux/ble_l2cap_socket.cc @@ -14,121 +14,103 @@ #include "internal/platform/implementation/linux/ble_l2cap_socket.h" -#include -#include -#include - #include #include -#include -#include +#include #include -#include +#include "absl/strings/escaping.h" #include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "internal/platform/implementation/crypto.h" #include "internal/platform/logging.h" -#include "proto/mediums/ble_frames.pb.h" namespace nearby { namespace linux { -BleL2capInputStream::~BleL2capInputStream() { - Close(); +namespace { + +constexpr int kFrameHeaderSize = 4; +constexpr int kMaxFrameSize = 1024 * 1024; + +std::string EncodeFrameLength(uint32_t length) { + return std::string{ + static_cast((length >> 24) & 0xFF), + static_cast((length >> 16) & 0xFF), + static_cast((length >> 8) & 0xFF), + static_cast(length & 0xFF), + }; +} + +uint32_t DecodeFrameLength(absl::string_view header) { + return (static_cast(static_cast(header[0])) << 24) | + (static_cast(static_cast(header[1])) << 16) | + (static_cast(static_cast(header[2])) << 8) | + static_cast(static_cast(header[3])); +} + +} // namespace + +Exception BleL2capOutputStream::Write(absl::string_view data) { + if (data.size() > std::numeric_limits::max()) { + return {Exception::kIo}; + } + + std::string frame = EncodeFrameLength(static_cast(data.size())); + frame.append(data.data(), data.size()); + return stream_.Write(frame); +} + +Exception BleL2capOutputStream::Flush() { + return stream_.Flush(); } ExceptionOr BleL2capInputStream::Read(std::int64_t size) { - std::vector buffer(size); + if (size <= 0) { + return ExceptionOr(ByteArray(std::string())); + } - pollfd pfds[1]; - pfds[0].fd = fd_raw_->get(); - pfds[0].events = POLLIN; - ssize_t rcvd = 0; - - while (rcvd < size) { - int r = poll(pfds, 1, -1); - if (r < 0) { - if (errno == EINTR) { - continue; - } - return Exception{Exception::kIo}; + while (pending_.empty()) { + ExceptionOr packet = stream_.Read(kMaxFrameSize); + if (!packet.ok()) { + return {Exception::kIo}; } - if (pfds[0].revents & POLLIN) { - auto r = recv(fd_raw_->get(), buffer.data() + rcvd, size - rcvd, 0); - if (r < 0) { - return Exception{Exception::kIo}; + + std::string packet_data = packet.result().string_data(); + + if (packet_data.empty()) { + return {Exception::kIo}; + } + + wire_buffer_.append(packet_data); + + while (wire_buffer_.size() >= kFrameHeaderSize) { + uint32_t frame_length = DecodeFrameLength(absl::string_view( + wire_buffer_.data(), kFrameHeaderSize)); + if (frame_length > kMaxFrameSize) { + LOG(ERROR) << __func__ << ": invalid L2CAP frame length " + << frame_length; + return {Exception::kIo}; } - rcvd += r; + + size_t full_frame_size = kFrameHeaderSize + frame_length; + if (wire_buffer_.size() < full_frame_size) { + break; + } + + pending_.append(wire_buffer_.data() + kFrameHeaderSize, frame_length); + wire_buffer_.erase(0, full_frame_size); + + if (!pending_.empty()) { + break; + } + + continue; } } - return ExceptionOr{ByteArray(std::string(buffer.begin(), buffer.end()))}; + size_t bytes_to_return = std::min(static_cast(size), pending_.size()); + std::string out = pending_.substr(0, bytes_to_return); + pending_.erase(0, bytes_to_return); + return ExceptionOr(ByteArray(std::move(out))); } - -Exception BleL2capInputStream::Close() { - if (!fd_raw_->isValid()) return {Exception::kSuccess}; - fd_raw_->reset(); - return {Exception::kSuccess}; -} -BleL2capOutputStream::~BleL2capOutputStream() { - Close(); -} - -Exception BleL2capOutputStream::Write(absl::string_view data) { - pollfd pfds[1]; - pfds[0].fd = fd_raw_->get(); - pfds[0].events = POLLOUT; - ssize_t sent = 0; - - while (sent < data.size()) { - int r = poll(pfds, 1, -1); - if (r < 0) { - if (errno == EINTR) { - continue; - } - return Exception{Exception::kIo}; - } - if (pfds[0].revents & POLLOUT) { - auto r = send(fd_raw_->get(), data.data() + sent, data.size(), 0); - if (r < 0) { - return Exception{Exception::kIo}; - } - sent += r; - } - } - return {Exception::kSuccess}; -} - -Exception BleL2capOutputStream::Close() { - if (!fd_raw_->isValid()) return {Exception::kSuccess}; - fd_raw_->reset(); - return {Exception::kSuccess}; -} - -BleL2capSocket::BleL2capSocket(int fd, - api::ble::BlePeripheral::UniqueId peripheral_id, - std::string service_id) - : fd_(std::make_shared(fd)), - input_stream_(std::make_unique(fd_)), - output_stream_(std::make_unique(fd_)), - peripheral_id_(peripheral_id) {} - -BleL2capSocket::~BleL2capSocket() { - Close(); -} - -Exception BleL2capSocket::Close() { - if (!fd_->isValid()) return {Exception::kIo}; - fd_->reset(); - return {Exception::kSuccess}; -} - -void BleL2capSocket::SetCloseNotifier(absl::AnyInvocable notifier) {} - -bool BleL2capSocket::IsClosed() const { - return closed_; -} - } // namespace linux } // namespace nearby diff --git a/internal/platform/implementation/linux/ble_l2cap_socket.h b/internal/platform/implementation/linux/ble_l2cap_socket.h index d13ba98c..65ad2a75 100644 --- a/internal/platform/implementation/linux/ble_l2cap_socket.h +++ b/internal/platform/implementation/linux/ble_l2cap_socket.h @@ -17,11 +17,11 @@ #include "dbus.h" - -#include +#include #include #include #include +#include #include "absl/functional/any_invocable.h" #include "absl/synchronization/mutex.h" @@ -29,70 +29,81 @@ #include "internal/platform/byte_array.h" #include "internal/platform/exception.h" #include "internal/platform/implementation/ble.h" -#include "internal/platform/input_stream.h" -#include "internal/platform/output_stream.h" +#include "internal/platform/implementation/linux/stream.h" namespace nearby { namespace linux { -// TODO: use linux stream instead of bespoke l2cap input/output stream - class BleL2capSocket; -class BleL2capInputStream final : public InputStream { +class BleL2capInputStream : public nearby::InputStream { public: - explicit BleL2capInputStream(std::shared_ptr fd_raw_): fd_raw_(std::move(fd_raw_)) {}; - ~BleL2capInputStream() override; + explicit BleL2capInputStream(int fd) : stream_(fd) {} ExceptionOr Read(std::int64_t size) override; - Exception Close() override; + Exception Close() override { + if (closed_) { + return {Exception::kSuccess}; + } + closed_ = true; + return stream_.Close(); + } -private: - std::shared_ptr fd_raw_; + private: + linux::InputStream stream_; + std::string wire_buffer_; + std::string pending_; + bool closed_ = false; }; -class BleL2capOutputStream final : public OutputStream { -public: - explicit BleL2capOutputStream(std::shared_ptr fd_raw_): fd_raw_(std::move(fd_raw_)) {}; - ~BleL2capOutputStream() override; +class BleL2capOutputStream : public nearby::OutputStream { + public: + explicit BleL2capOutputStream(int fd) : stream_(fd) {} Exception Write(absl::string_view data) override; - Exception Flush() override { return {Exception::kSuccess}; } - Exception Close() override; - -private: - std::shared_ptr fd_raw_; + Exception Flush() override; + Exception Close() override { + if (closed_) { + return {Exception::kSuccess}; + } + closed_ = true; + return stream_.Close(); + } + private: + linux::OutputStream stream_; + bool closed_ = false; }; class BleL2capSocket final : public api::ble::BleL2capSocket { public: - BleL2capSocket(int fd, api::ble::BlePeripheral::UniqueId peripheral_id, - std::string service_id = ""); - ~BleL2capSocket() override; + std::string service_id = "") + : fd_(fd), + output_stream_(fd_), + input_stream_(fd_), + peripheral_id_(peripheral_id) {}; - InputStream& GetInputStream() override { return *input_stream_; } - OutputStream& GetOutputStream() override { return *output_stream_; } - Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); - void SetCloseNotifier(absl::AnyInvocable notifier) override - ABSL_LOCKS_EXCLUDED(mutex_); + nearby::InputStream& GetInputStream() override { return input_stream_; } + nearby::OutputStream& GetOutputStream() override { return output_stream_; } + Exception Close() override { + input_stream_.Close(); + output_stream_.Close(); + if (fd_ >= 0) { + close(fd_); + fd_ = -1; + } + + return Exception{Exception::kSuccess}; + }; api::ble::BlePeripheral::UniqueId GetRemotePeripheralId() override { return peripheral_id_; } - bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_); - private: - friend class BleL2capInputStream; - friend class BleL2capOutputStream; - - mutable absl::Mutex mutex_; - mutable absl::Mutex io_mutex_; - bool closed_ ABSL_GUARDED_BY(mutex_) = false; - std::shared_ptr fd_ ; - std::unique_ptr input_stream_; - std::unique_ptr output_stream_; + int fd_; + BleL2capOutputStream output_stream_; + BleL2capInputStream input_stream_; api::ble::BlePeripheral::UniqueId peripheral_id_; }; diff --git a/internal/platform/implementation/linux/ble_l2cap_socket_test.cc b/internal/platform/implementation/linux/ble_l2cap_socket_test.cc index fdc64ba7..8f81f8bb 100644 --- a/internal/platform/implementation/linux/ble_l2cap_socket_test.cc +++ b/internal/platform/implementation/linux/ble_l2cap_socket_test.cc @@ -31,6 +31,16 @@ namespace nearby { namespace linux { namespace { + +std::string WrapFrame(absl::string_view payload) { + return std::string{ + static_cast((payload.size() >> 24) & 0xFF), + static_cast((payload.size() >> 16) & 0xFF), + static_cast((payload.size() >> 8) & 0xFF), + static_cast(payload.size() & 0xFF)} + + std::string(payload); +} + class BleL2capSocketTest : public ::testing::Test { protected: void SetUp() override { @@ -57,31 +67,32 @@ void SetUp() override { std::unique_ptr socket_; }; TEST_F(BleL2capSocketTest, ReturnsInputAndOutputStreams) { - InputStream& input = socket_->GetInputStream(); - OutputStream& output = socket_->GetOutputStream(); + nearby::InputStream& input = socket_->GetInputStream(); + nearby::OutputStream& output = socket_->GetOutputStream(); EXPECT_NE(&input, nullptr); EXPECT_NE(&output, nullptr); } TEST_F(BleL2capSocketTest, ReturnsSameStreamInstancesAcrossCalls) { - InputStream& input1 = socket_->GetInputStream(); - InputStream& input2 = socket_->GetInputStream(); + nearby::InputStream& input1 = socket_->GetInputStream(); + nearby::InputStream& input2 = socket_->GetInputStream(); - OutputStream& output1 = socket_->GetOutputStream(); - OutputStream& output2 = socket_->GetOutputStream(); + nearby::OutputStream& output1 = socket_->GetOutputStream(); + nearby::OutputStream& output2 = socket_->GetOutputStream(); EXPECT_EQ(&input1, &input2); EXPECT_EQ(&output1, &output2); } TEST_F(BleL2capSocketTest, ReadReceivesExactBytesFromPeer) { std::string message = "hello into l2cap socket"; + std::string framed_message = WrapFrame(message); ASSERT_EQ( - write(peer_fd_, message.data(), message.size()), - static_cast(message.size()) + write(peer_fd_, framed_message.data(), framed_message.size()), + static_cast(framed_message.size()) ); - InputStream& input = socket_->GetInputStream(); + nearby::InputStream& input = socket_->GetInputStream(); std::vector buffer(message.size()); auto out = input.Read(buffer.size()).GetResult(); @@ -98,31 +109,33 @@ TEST_F(BleL2capSocketTest, ReadReceivesExactBytesFromPeer) { } TEST_F(BleL2capSocketTest, WriteSendsExactBytesToPeer) { std::string message = "hello from l2cap socket"; + std::string framed_message = WrapFrame(message); - OutputStream& output = socket_->GetOutputStream(); + nearby::OutputStream& output = socket_->GetOutputStream(); EXPECT_EQ(output.Write(message).value, Exception::kSuccess); - std::vector received(message.size()); + std::vector received(framed_message.size()); ssize_t n = read(peer_fd_, received.data(), received.size()); - ASSERT_EQ(n, static_cast(message.size())); + ASSERT_EQ(n, static_cast(framed_message.size())); EXPECT_EQ( std::string(received.begin(), received.end()), - message + framed_message ); } TEST_F(BleL2capSocketTest, SkipSkipsBytesFromPeer) { std::string message = "hello there"; + std::string framed_message = WrapFrame(message); ASSERT_EQ( - write(peer_fd_, message.data(), message.size()), - static_cast(message.size()) + write(peer_fd_, framed_message.data(), framed_message.size()), + static_cast(framed_message.size()) ); - InputStream& input = socket_->GetInputStream(); + nearby::InputStream& input = socket_->GetInputStream(); EXPECT_EQ(input.Skip(6).result(), 6); @@ -132,6 +145,26 @@ TEST_F(BleL2capSocketTest, SkipSkipsBytesFromPeer) { TEST_F(BleL2capSocketTest, CloseReturnsSuccess) { EXPECT_EQ(socket_->Close().value, Exception::kSuccess); } + +TEST(BleL2capSocketSeqpacketTest, ReadReceivesHeaderAndPayloadFromOnePacket) { + int fds[2]{-1, -1}; + ASSERT_EQ(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds), 0); + + auto socket = std::make_unique(fds[0], 1); + int peer_fd = fds[1]; + + std::string message = "\x03"; + std::string framed_message = WrapFrame(message); + + ASSERT_EQ(write(peer_fd, framed_message.data(), framed_message.size()), + static_cast(framed_message.size())); + + auto out = socket->GetInputStream().Read(1).GetResult(); + EXPECT_EQ(out, ByteArray(message)); + + socket->Close(); + close(peer_fd); +} } // namespace } // namespace linux } // namespace nearby diff --git a/internal/platform/implementation/linux/ble_v2_socket.cc b/internal/platform/implementation/linux/ble_v2_socket.cc index d29791f4..ab3bc725 100644 --- a/internal/platform/implementation/linux/ble_v2_socket.cc +++ b/internal/platform/implementation/linux/ble_v2_socket.cc @@ -242,18 +242,19 @@ void BleV2Socket::SetGattClient( }); // Subscribe to RX characteristic to receive data - bool subscribed = gatt_client_->SetCharacteristicSubscription( - rx_char_, /*enable=*/true, - [this](absl::string_view value) { - if (!IsClosed()) { - ByteArray data(value.data(), value.size()); - input_stream_.ReceiveData(data); - } - }); - if (!subscribed) { - LOG(ERROR) << "Failed to subscribe to RX characteristic"; - } + //bool subscribed = gatt_client_->SetCharacteristicSubscription( + // rx_char_, /*enable=*/true, + // [this](absl::string_view value) { + // if (!IsClosed()) { + // ByteArray data(value.data(), value.size()); + // input_stream_.ReceiveData(data); + // } + // }); + + //if (!subscribed) { + // LOG(ERROR) << "Failed to subscribe to RX characteristic"; + //} LOG(INFO) << "BLE socket configured with GATT client, RX: " << std::string(rx_char.uuid) diff --git a/internal/platform/implementation/linux/bluetooth_classic_socket.h b/internal/platform/implementation/linux/bluetooth_classic_socket.h index a1353713..d1e14443 100644 --- a/internal/platform/implementation/linux/bluetooth_classic_socket.h +++ b/internal/platform/implementation/linux/bluetooth_classic_socket.h @@ -21,6 +21,7 @@ #include #include +#include #include #include "absl/synchronization/mutex.h" @@ -38,20 +39,27 @@ class BluetoothSocket final : public api::BluetoothSocket { public: BluetoothSocket(std::shared_ptr device, sdbus::UnixFd fd) - :fd_(fd), device_(std::move(device)), output_stream_(fd_), input_stream_(fd_) {} + : fd_(fd.release()), + device_(std::move(device)), + output_stream_(fd_), + input_stream_(fd_) {} InputStream &GetInputStream() override { return input_stream_; } OutputStream &GetOutputStream() override { return output_stream_; } Exception Close() override { input_stream_.Close(); output_stream_.Close(); + if (fd_ >= 0) { + close(fd_); + fd_ = -1; + } return Exception{Exception::kSuccess}; } api::BluetoothDevice *GetRemoteDevice() override { return device_.get(); }; private: - sdbus::UnixFd fd_; + int fd_; std::shared_ptr device_; OutputStream output_stream_; InputStream input_stream_; diff --git a/internal/platform/implementation/linux/bluez_le_advertisement.cc b/internal/platform/implementation/linux/bluez_le_advertisement.cc index c58b55ca..e2cdaed6 100644 --- a/internal/platform/implementation/linux/bluez_le_advertisement.cc +++ b/internal/platform/implementation/linux/bluez_le_advertisement.cc @@ -43,14 +43,16 @@ LEAdvertisement::LEAdvertisement( std::vector data_bytes(data.size()); const auto* bytes = data.data(); - service_uuids_.push_back(uuid_string); + if (is_extended_advertisement_){ + service_uuids_.push_back(uuid_string); + } // service_uuids_.push_back("0000FE2C-0000-1000-8000-00805F9B34FB"); // service_uuids_.push_back("0000FE2C-0000-1000-8000-00805F9B34FB"); for (size_t i = 0; i < data.size(); i++) { data_bytes[i] = bytes[i]; } - // LOG(INFO)<< __func__ << ": " << uuid_string; - // LOG(INFO)<< __func__ << ": " << BytesToHexString(data_bytes); + LOG(INFO)<< __func__ << ": " << uuid_string; + LOG(INFO)<< __func__ << ": " << BytesToHexString(data_bytes); service_data_.insert({uuid_string, std::move(sdbus::Variant(data_bytes))}); // service_data_.insert({"0000FE2C-0000-1000-8000-00805F9B34FB", std::move(data_bytes)}); } diff --git a/internal/platform/implementation/linux/device_info.cc b/internal/platform/implementation/linux/device_info.cc index a47e961a..919102e5 100644 --- a/internal/platform/implementation/linux/device_info.cc +++ b/internal/platform/implementation/linux/device_info.cc @@ -93,7 +93,7 @@ api::DeviceInfo::DeviceType DeviceInfo::GetDeviceType() const { } -std::optional DeviceInfo::GetDownloadPath() const { +FilePath DeviceInfo::GetDownloadPath() const { char *dir = getenv("XDG_DOWNLOAD_DIR"); if (dir == nullptr) { return FilePath("/tmp"); @@ -101,7 +101,7 @@ std::optional DeviceInfo::GetDownloadPath() const { return FilePath(std::string(dir)); } -std::optional DeviceInfo::GetLocalAppDataPath() const { +FilePath DeviceInfo::GetLocalAppDataPath(FilePath sub_path) const { char *dir = getenv("XDG_CONFIG_HOME"); if (dir == nullptr) { return FilePath("/tmp"); @@ -109,7 +109,7 @@ std::optional DeviceInfo::GetLocalAppDataPath() const { return FilePath(std::string((std::filesystem::path(std::string(dir)) / "Google Nearby"))); } -std::optional DeviceInfo::GetTemporaryPath() const { +FilePath DeviceInfo::GetTemporaryPath() const { char *dir = getenv("XDG_RUNTIME_PATH"); if (dir == nullptr) { return FilePath("/tmp"); @@ -117,7 +117,7 @@ std::optional DeviceInfo::GetTemporaryPath() const { return FilePath(std::string(std::filesystem::path(std::string(dir)) / "Google Nearby")); } -std::optional DeviceInfo::GetLogPath() const { +FilePath DeviceInfo::GetLogPath() const { char *dir = getenv("XDG_STATE_HOME"); if (dir == nullptr) { return FilePath("/tmp"); @@ -125,13 +125,6 @@ std::optional DeviceInfo::GetLogPath() const { return FilePath(std::string(std::filesystem::path(std::string(dir)) / "Google Nearby" / "logs")); } -std::optional DeviceInfo::GetCrashDumpPath() const { - char *dir = getenv("XDG_STATE_HOME"); - if (dir == nullptr) { - return FilePath("/tmp"); - } - return FilePath(std::string(std::filesystem::path(std::string(dir)) / "Google Nearby" / "crashes")); -} bool DeviceInfo::IsScreenLocked() const { try { diff --git a/internal/platform/implementation/linux/device_info.h b/internal/platform/implementation/linux/device_info.h index fa0913ff..0f0d8acf 100644 --- a/internal/platform/implementation/linux/device_info.h +++ b/internal/platform/implementation/linux/device_info.h @@ -129,14 +129,10 @@ class DeviceInfo final : public api::DeviceInfo { return api::DeviceInfo::OsType::kWindows; // Or ChromeOS? } - std::optional GetDownloadPath() const override; - std::optional GetLocalAppDataPath() const override; - std::optional GetCommonAppDataPath() const override { - return std::nullopt; - }; - std::optional GetTemporaryPath() const override; - std::optional GetLogPath() const override; - std::optional GetCrashDumpPath() const override; + nearby::FilePath GetDownloadPath() const override; + nearby::FilePath GetLocalAppDataPath(nearby::FilePath sub_path) const override; + nearby::FilePath GetTemporaryPath() const override; + nearby::FilePath GetLogPath() const override; bool IsScreenLocked() const override; void RegisterScreenLockedListener( diff --git a/internal/platform/implementation/linux/file_path.cc b/internal/platform/implementation/linux/file_path.cc index 5fb469f4..68fee2d9 100644 --- a/internal/platform/implementation/linux/file_path.cc +++ b/internal/platform/implementation/linux/file_path.cc @@ -56,8 +56,8 @@ std::wstring FilePath::GetDownloadPathInternal(std::wstring parent_folder, auto nearby_path = info.GetDownloadPath(); std::optional download_path = - nearby_path ? std::optional( - std::filesystem::path(nearby_path->ToString())) + !nearby_path.IsEmpty() ? std::optional( + std::filesystem::path(nearby_path.ToString())) : std::nullopt; std::string base_path; diff --git a/internal/platform/implementation/linux/preferences_manager.cc b/internal/platform/implementation/linux/preferences_manager.cc index 9779cefd..1a39ca9b 100644 --- a/internal/platform/implementation/linux/preferences_manager.cc +++ b/internal/platform/implementation/linux/preferences_manager.cc @@ -42,7 +42,7 @@ PreferencesManager::PreferencesManager(absl::string_view file_path) : api::PreferencesManager() { std::optional path = nearby::api::ImplementationPlatform::CreateDeviceInfo() - ->GetLocalAppDataPath(); + ->GetLocalAppDataPath(nearby::FilePath()); if (!path.has_value()) { path = FilePath("/tmp"); } diff --git a/internal/platform/implementation/linux/stream.cc b/internal/platform/implementation/linux/stream.cc index bf25e427..cd457159 100644 --- a/internal/platform/implementation/linux/stream.cc +++ b/internal/platform/implementation/linux/stream.cc @@ -15,11 +15,11 @@ #include #include #include -#include -#include #include #include +#include +#include "absl/strings/escaping.h" #include "internal/platform/byte_array.h" #include "internal/platform/exception.h" #include "internal/platform/implementation/linux/stream.h" @@ -33,7 +33,7 @@ ExceptionOr InputStream::Read(std::int64_t size) { return ExceptionOr(ByteArray(std::string())); } - if (!fd_ || !fd_->isValid()) { + if (closed_ || fd_ < 0) { return {Exception::kIo}; } @@ -42,7 +42,7 @@ ExceptionOr InputStream::Read(std::int64_t size) { while (true) { pollfd pfd{}; - pfd.fd = fd_->get(); + pfd.fd = fd_; pfd.events = POLLIN; int poll_result = poll(&pfd, 1, -1); @@ -63,8 +63,7 @@ ExceptionOr InputStream::Read(std::int64_t size) { } if (pfd.revents & (POLLIN | POLLHUP)) { - ssize_t bytes_read = - recv(fd_->get(), buffer.data(), buffer.size(), 0); + ssize_t bytes_read = recv(fd_, buffer.data(), buffer.size(), 0); if (bytes_read > 0) { buffer.resize(static_cast(bytes_read)); @@ -93,17 +92,18 @@ ExceptionOr InputStream::Read(std::int64_t size) { } Exception InputStream::Close() { - if (!fd_->isValid()) return Exception{Exception::kIo}; - fd_.reset(); - return {}; + if (closed_ || fd_ < 0) return Exception{Exception::kSuccess}; + closed_ = true; + shutdown(fd_, SHUT_RD); + return Exception{Exception::kSuccess}; } Exception OutputStream::Write(absl::string_view data) { - if (!fd_ || !fd_->isValid()) { + if (closed_ || fd_ < 0) { return {Exception::kIo}; } - const int fd = fd_->get(); + const int fd = fd_; size_t sent = 0; while (sent < data.size()) { @@ -117,8 +117,7 @@ Exception OutputStream::Write(absl::string_view data) { } while (poll_result < 0 && errno == EINTR); if (poll_result < 0) { - LOG(ERROR) << __func__ - << ": poll failed: " << std::strerror(errno); + LOG(ERROR) << __func__ << ": poll failed: " << std::strerror(errno); return {Exception::kIo}; } @@ -132,11 +131,7 @@ Exception OutputStream::Write(absl::string_view data) { continue; } - ssize_t n = send( - fd, - data.data() + sent, - data.size() - sent, - MSG_NOSIGNAL); + ssize_t n = send(fd, data.data() + sent, data.size() - sent, MSG_NOSIGNAL); if (n > 0) { sent += static_cast(n); @@ -158,23 +153,22 @@ Exception OutputStream::Write(absl::string_view data) { continue; } - LOG(ERROR) << __func__ - << ": error writing to fd: " << std::strerror(errno); + LOG(ERROR) << __func__ << ": error writing to fd: " << std::strerror(errno); return {Exception::kIo}; } return {Exception::kSuccess}; } -Exception OutputStream::Flush() { return Exception{Exception::kSuccess}; } +Exception OutputStream::Flush() { + return Exception{Exception::kSuccess}; +} Exception OutputStream::Close() { - if (!fd_->isValid()) return Exception{Exception::kIo}; - - auto ret = close(fd_->get()) < 0 ? Exception{Exception::kIo} - : Exception{Exception::kSuccess}; - fd_.reset(); - return ret; + if (closed_ || fd_ < 0) return Exception{Exception::kSuccess}; + closed_ = true; + shutdown(fd_, SHUT_WR); + return Exception{Exception::kSuccess}; } } // namespace linux diff --git a/internal/platform/implementation/linux/stream.h b/internal/platform/implementation/linux/stream.h index b4b42f19..4223a8e0 100644 --- a/internal/platform/implementation/linux/stream.h +++ b/internal/platform/implementation/linux/stream.h @@ -15,10 +15,6 @@ #ifndef PLATFORM_IMPL_LINUX_STREAM_H_ #define PLATFORM_IMPL_LINUX_STREAM_H_ -#include - -#include - #include "internal/platform/input_stream.h" #include "internal/platform/output_stream.h" @@ -26,26 +22,28 @@ namespace nearby { namespace linux { class InputStream : public nearby::InputStream { public: - explicit InputStream(sdbus::UnixFd fd) : fd_(std::make_shared(fd)){}; + explicit InputStream(int fd) : fd_(fd){}; ExceptionOr Read(std::int64_t size) override; Exception Close() override; private: - std::shared_ptr fd_; + int fd_; + bool closed_ = false; }; class OutputStream : public nearby::OutputStream { public: - explicit OutputStream(sdbus::UnixFd fd) : fd_(std::make_shared(fd)){}; + explicit OutputStream(int fd) : fd_(fd){}; Exception Write(absl::string_view data) override; Exception Flush() override; Exception Close() override; private: - std::shared_ptr fd_; + int fd_; + bool closed_ = false; }; } // namespace linux diff --git a/internal/platform/implementation/linux/tcp_server_socket.h b/internal/platform/implementation/linux/tcp_server_socket.h index a2e983d7..cb016fc9 100644 --- a/internal/platform/implementation/linux/tcp_server_socket.h +++ b/internal/platform/implementation/linux/tcp_server_socket.h @@ -18,9 +18,10 @@ #include #include #include +#include +#include #include - -#include +#include #include "internal/platform/exception.h" #include "internal/platform/implementation/linux/stream.h" @@ -30,8 +31,8 @@ namespace nearby { namespace linux { class TCPSocket { public: - explicit TCPSocket(const sdbus::UnixFd& fd) - : closed_(false), output_stream_(fd), input_stream_(fd) {} + explicit TCPSocket(int fd) + : fd_(fd), closed_(false), output_stream_(fd), input_stream_(fd) {} static std::optional Connect(const std::string& ip_address, int port) { @@ -54,10 +55,11 @@ class TCPSocket { if (ret < 0) { LOG(ERROR) << __func__ << ": Error connecting to socket: " << std::strerror(errno); + close(sock); return std::nullopt; } - return TCPSocket(sdbus::UnixFd(sock)); + return TCPSocket(sock); } InputStream& GetInputStream() { return input_stream_; } @@ -69,11 +71,16 @@ class TCPSocket { closed_ = true; input_stream_.Close(); output_stream_.Close(); + if (fd_ >= 0) { + close(fd_); + fd_ = -1; + } return {Exception::kSuccess}; }; private: + int fd_; bool closed_; OutputStream output_stream_; @@ -125,19 +132,23 @@ class TCPServerSocket { socklen_t len = sizeof(addr); auto conn = - accept(fd_.get(), reinterpret_cast(&addr), &len); + accept(fd_, reinterpret_cast(&addr), &len); if (conn < 0) { LOG(ERROR) << __func__ << ": Error accepting incoming connections on socket " - << fd_.get() << ": " << std::strerror(errno); + << fd_ << ": " << std::strerror(errno); return std::nullopt; } - return TCPSocket(sdbus::UnixFd(conn)); + return TCPSocket(conn); }; Exception Close() { - int fd = fd_.release(); + if (fd_ < 0) { + return {Exception::kSuccess}; + } + int fd = fd_; + fd_ = -1; shutdown(fd, SHUT_RDWR); auto ret = close(fd); if (ret < 0) { @@ -153,11 +164,11 @@ class TCPServerSocket { struct sockaddr_in sin; socklen_t len = sizeof(sin); auto ret = - getsockname(fd_.get(), reinterpret_cast(&sin), &len); + getsockname(fd_, reinterpret_cast(&sin), &len); if (ret < 0) { LOG(ERROR) << __func__ << ": Error getting information for socket " - << fd_.get() << ": " << std::strerror(errno); + << fd_ << ": " << std::strerror(errno); return 0; } @@ -165,7 +176,7 @@ class TCPServerSocket { } private: - sdbus::UnixFd fd_; + int fd_; }; } // namespace linux } // namespace nearby diff --git a/internal/platform/implementation/linux/tests/BUILD b/internal/platform/implementation/linux/tests/BUILD new file mode 100644 index 00000000..32cfae4a --- /dev/null +++ b/internal/platform/implementation/linux/tests/BUILD @@ -0,0 +1,37 @@ +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") + +cc_library( + +) + +cc_test( + name = "linux_connections_test", + size = "small", + srcs = [ + "atomic_boolean_test.cc", + "atomic_reference_test.cc", + "mutex_test.cc", + "utils_test.cc", + ], + tags = ["notap"], + deps = [ + "//internal/platform/implementation/linux:comm", + "//internal/platform/implementation/linux:crypto", + "//internal/platform/implementation/linux:linux", + "//internal/platform/implementation/linux:test_utils", + "//internal/platform/implementation/linux:types", + "//internal/platform:base", + "//internal/platform/implementation:comm", + "//internal/platform/implementation:platform", + "//internal/platform/implementation:types", + "//internal/platform/implementation/shared:count_down_latch", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_absl//absl/types:span", + "@com_google_googletest//:gtest_main", + "@nlohmann_json//:json", + ], +) diff --git a/internal/platform/implementation/linux/wifi_hotspot_socket.h b/internal/platform/implementation/linux/wifi_hotspot_socket.h index e94587ff..27fda3e7 100644 --- a/internal/platform/implementation/linux/wifi_hotspot_socket.h +++ b/internal/platform/implementation/linux/wifi_hotspot_socket.h @@ -18,12 +18,14 @@ #include "internal/platform/implementation/linux/stream.h" #include "internal/platform/implementation/wifi_hotspot.h" +#include + namespace nearby { namespace linux { class WifiHotspotSocket : public api::WifiHotspotSocket { public: explicit WifiHotspotSocket(int connection_fd) - : fd_(sdbus::UnixFd(connection_fd)), + : fd_(connection_fd), output_stream_(fd_), input_stream_(fd_) {} @@ -32,12 +34,16 @@ class WifiHotspotSocket : public api::WifiHotspotSocket { Exception Close() override { input_stream_.Close(); output_stream_.Close(); + if (fd_ >= 0) { + close(fd_); + fd_ = -1; + } return Exception{Exception::kSuccess}; }; private: - sdbus::UnixFd fd_; + int fd_; OutputStream output_stream_; InputStream input_stream_; }; diff --git a/internal/platform/implementation/platform.h b/internal/platform/implementation/platform.h index f1ac4ac1..44ee1851 100644 --- a/internal/platform/implementation/platform.h +++ b/internal/platform/implementation/platform.h @@ -39,15 +39,10 @@ #include "internal/platform/implementation/log_message.h" #include "internal/platform/implementation/mutex.h" #include "internal/platform/implementation/output_file.h" +#include "internal/platform/implementation/preferences_manager.h" #include "internal/platform/implementation/scheduled_executor.h" #include "internal/platform/implementation/submittable_executor.h" #include "internal/platform/implementation/timer.h" -#ifndef NO_WEBRTC -#include "internal/platform/implementation/webrtc.h" -#endif -#ifndef NEARBY_CHROMIUM -#include "internal/platform/implementation/preferences_manager.h" -#endif #include "internal/platform/implementation/wifi.h" #include "internal/platform/implementation/wifi_direct.h" #include "internal/platform/implementation/wifi_hotspot.h" @@ -136,10 +131,6 @@ class ImplementationPlatform { static std::unique_ptr CreateWifiHotspotMedium(); static std::unique_ptr CreateWifiDirectMedium(); static std::unique_ptr CreateTimer(); - static std::unique_ptr CreateDeviceInfo(); -#ifndef NO_WEBRTC - static std::unique_ptr CreateWebRtcMedium(); -#endif #if defined(NEARBY_CHROMIUM) static std::unique_ptr CreateAppLifecycleMonitor( @@ -147,11 +138,17 @@ class ImplementationPlatform { state_updated_callback) { return nullptr; } + static std::unique_ptr + CreatePreferencesManager(absl::string_view path) { + return nullptr; + } #else static std::unique_ptr CreateAppLifecycleMonitor( std::function state_updated_callback); -#endif + static std::unique_ptr + CreatePreferencesManager(absl::string_view path); + static std::unique_ptr CreateDeviceInfo(); // Gets HTTP response from remote server. // @@ -161,15 +158,6 @@ class ImplementationPlatform { // return WebResponse if HTTP status code between 200 and 300. // other cases will return absl Status in error. static absl::StatusOr SendRequest(const WebRequest& request); - -#if defined(NEARBY_CHROMIUM) - static std::unique_ptr - CreatePreferencesManager(absl::string_view path) { - return nullptr; - } -#else - static std::unique_ptr - CreatePreferencesManager(absl::string_view path); #endif }; diff --git a/internal/platform/implementation/shared/file.h b/internal/platform/implementation/shared/file.h index fbf7bfd0..ddc71828 100644 --- a/internal/platform/implementation/shared/file.h +++ b/internal/platform/implementation/shared/file.h @@ -15,7 +15,6 @@ #ifndef PLATFORM_IMPL_SHARED_FILE_H_ #define PLATFORM_IMPL_SHARED_FILE_H_ -#include #include #include #include @@ -23,6 +22,7 @@ #include "absl/strings/string_view.h" #include "absl/time/time.h" +#include "internal/platform/byte_array.h" #include "internal/platform/exception.h" #include "internal/platform/implementation/input_file.h" #include "internal/platform/implementation/output_file.h" @@ -49,7 +49,7 @@ class IOFile final : public api::InputFile, public api::OutputFile { void SetLastModifiedTime(absl::Time last_modified_time) override; private: - explicit IOFile(absl::string_view file_path) : path_(file_path) {}; + explicit IOFile(absl::string_view file_path) : path_(file_path) {} void OpenForRead(); void OpenForWrite(); diff --git a/internal/platform/implementation/signin_attempt.h b/internal/platform/implementation/signin_attempt.h deleted file mode 100644 index aa631a54..00000000 --- a/internal/platform/implementation/signin_attempt.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2024 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 THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_SIGNIN_ATTEMPT_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_SIGNIN_ATTEMPT_H_ - -#include - -#include "absl/functional/any_invocable.h" -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/account_info.h" -#include "internal/platform/implementation/auth_status.h" - -namespace nearby { - -class SigninAttempt { - public: - SigninAttempt() = default; - virtual ~SigninAttempt() = default; - - // Starts a new sign-in attempt. - // `callback` is called with the status of the request, client_id, - // client_secret, and account_info if the request is successful. Returns the - // auth url if the request is successful. - virtual std::string Start( - absl::AnyInvocable - callback) = 0; - - // Tears down the machinery set up to request auth tokens, including the HTTP - // server. - virtual void Close() = 0; -}; - -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_SIGNIN_ATTEMPT_H_ diff --git a/internal/platform/implementation/webrtc.h b/internal/platform/implementation/webrtc.h index 57bd14d7..90938bfa 100644 --- a/internal/platform/implementation/webrtc.h +++ b/internal/platform/implementation/webrtc.h @@ -15,17 +15,15 @@ #ifndef PLATFORM_API_WEBRTC_H_ #define PLATFORM_API_WEBRTC_H_ -#ifndef NO_WEBRTC - #include #include -#include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "connections/implementation/proto/offline_wire_formats.pb.h" #include "internal/platform/byte_array.h" -#include "webrtc/api/peer_connection_interface.h" +#include "third_party/webrtc/files/stable/webrtc/api/peer_connection_interface.h" +#include "third_party/webrtc/files/stable/webrtc/api/scoped_refptr.h" namespace nearby { namespace api { @@ -53,10 +51,6 @@ class WebRtcMedium { virtual ~WebRtcMedium() = default; - // Gets the default two-letter country code associated with current locale. - // For example, en_US locale resolves to "US". - virtual std::string GetDefaultCountryCode() = 0; - // Creates and returns a new webrtc::PeerConnectionInterface object via // |callback|. virtual void CreatePeerConnection(webrtc::PeerConnectionObserver* observer, @@ -78,6 +72,4 @@ class WebRtcMedium { } // namespace api } // namespace nearby -#endif - #endif // PLATFORM_API_WEBRTC_H_ diff --git a/internal/platform/implementation/webrtc_platform.h b/internal/platform/implementation/webrtc_platform.h new file mode 100644 index 00000000..a765179f --- /dev/null +++ b/internal/platform/implementation/webrtc_platform.h @@ -0,0 +1,37 @@ +// Copyright 2026 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_API_WEBRTC_PLATFORM_H_ +#define PLATFORM_API_WEBRTC_PLATFORM_H_ + +#include +#include + +#include "internal/platform/implementation/webrtc.h" + +namespace nearby::api { + +class WebRtcImplementationPlatform { + public: + static std::unique_ptr CreateWebRtcMedium(); + + // Gets the default two-letter country code associated with current locale. + // For example, en_US locale resolves to "US". + // This follows the ISO 3166-1 Alpha-2 standard. + static std::string GetDefaultCountryCode(); +}; + +} // namespace nearby::api + +#endif // PLATFORM_API_WEBRTC_PLATFORM_H_ diff --git a/internal/platform/implementation/wifi_direct.h b/internal/platform/implementation/wifi_direct.h index adfcf55f..6b6112bc 100644 --- a/internal/platform/implementation/wifi_direct.h +++ b/internal/platform/implementation/wifi_direct.h @@ -17,12 +17,12 @@ #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/exception.h" #include "internal/platform/input_stream.h" @@ -121,7 +121,7 @@ class WifiDirectMedium { virtual bool DisconnectWifiDirect() = 0; // Returns the port range as a pair of min and max port. - virtual absl::optional> + virtual std::optional> GetDynamicPortRange() = 0; // Returns the supported WifiDirect auth types. diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD index a8357302..94698475 100644 --- a/internal/platform/implementation/windows/BUILD +++ b/internal/platform/implementation/windows/BUILD @@ -74,9 +74,7 @@ cc_library( ], defines = ["_SILENCE_CLANG_COROUTINE_MESSAGE"], tags = ["windows"], - visibility = [ - "//sharing/internal/impl/windows:__pkg__", - ], + visibility = ["//visibility:private"], deps = [ ":device_paths", ":string_utils", @@ -151,10 +149,7 @@ cc_library( "WINVER=_WIN32_WINNT_WIN10", ], tags = ["windows"], - visibility = [ - "//:__subpackages__", - "//location/nearby:__subpackages__", - ], + visibility = ["//:__subpackages__"], deps = [ ":socket_address", ":string_utils", @@ -175,10 +170,7 @@ cc_library( ], compatible_with = ["//buildenv/target:non_prod"], tags = ["windows"], - visibility = [ - "//:__subpackages__", - "//location/nearby:__subpackages__", - ], + visibility = ["//location/nearby:__subpackages__"], ) cc_library( @@ -191,10 +183,7 @@ cc_library( ], compatible_with = ["//buildenv/target:non_prod"], tags = ["windows"], - visibility = [ - "//:__subpackages__", - "//location/nearby:__subpackages__", - ], + visibility = ["//location/nearby:__subpackages__"], deps = [ ":scoped_wlan_memory", "//internal/platform:logging", @@ -243,6 +232,18 @@ cc_library( ], ) +cc_library( + name = "webrtc_platform", + srcs = ["webrtc_platform.cc"], + tags = ["windows"], + deps = [ + "//connections/implementation/mediums/webrtc:webrtc_medium_impl", + "//internal/platform:logging", + "//internal/platform/implementation:webrtc_platform", + ], + alwayslink = True, +) + cc_library( name = "windows", srcs = [ @@ -268,7 +269,6 @@ cc_library( "preferences_manager.cc", "preferences_repository.cc", "system_clock.cc", - "webrtc.cc", "wifi_direct_medium.cc", "wifi_direct_server_socket.cc", "wifi_direct_socket.cc", @@ -301,7 +301,6 @@ cc_library( "nearby_server_socket.h", "preferences_manager.h", "preferences_repository.h", - "webrtc.h", "wifi.h", "wifi_direct.h", "wifi_hotspot.h", @@ -316,6 +315,9 @@ cc_library( "-DNO_INTEL_PIE", "-D_WIN32_WINNT=_WIN32_WINNT_WIN10 -DWINVER=_WIN32_WINNT_WIN10", ], + linkopts = [ + "iphlpapi.lib", + ], tags = ["windows"], visibility = [ "//chrome/chromeos/assistant/data_migration/lib:__pkg__", @@ -323,8 +325,8 @@ cc_library( "//connections:partners", "//internal/platform:__subpackages__", "//location/nearby:__subpackages__", - "//presence:__subpackages__", "//sharing:__subpackages__", + "//third_party/nearby/presence:__subpackages__", ], deps = [ ":crypto", # build_cleaner: keep @@ -358,11 +360,6 @@ cc_library( "//internal/platform/implementation/shared:count_down_latch", "//internal/platform/implementation/windows/generated:types", "//third_party/intel/pie", - "//third_party/webrtc/files/stable/webrtc/api:create_modular_peer_connection_factory", - "//third_party/webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", - "//third_party/webrtc/files/stable/webrtc/api:rtc_error", - "//third_party/webrtc/files/stable/webrtc/api:scoped_refptr", - "//third_party/webrtc/files/stable/webrtc/rtc_base:threading", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/container:flat_hash_map", @@ -374,7 +371,6 @@ cc_library( "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", - "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", "@com_google_protobuf//:protobuf", "@com_google_protobuf//json", @@ -396,9 +392,7 @@ cc_library( "WINVER=_WIN32_WINNT_WIN10", ], tags = ["windows"], - visibility = [ - "//:__subpackages__", - ], + visibility = ["//visibility:private"], deps = [ "//internal/platform:base", "//internal/platform:logging", @@ -460,7 +454,6 @@ cc_test( "thread_pool_test.cc", "timer_test.cc", "utils_test.cc", - "webrtc_test.cc", "wifi_direct_test.cc", "wifi_hotspot_test.cc", "wifi_medium_test.cc", diff --git a/internal/platform/implementation/windows/ble_gatt_client.cc b/internal/platform/implementation/windows/ble_gatt_client.cc index 83ed7695..78244f1b 100644 --- a/internal/platform/implementation/windows/ble_gatt_client.cc +++ b/internal/platform/implementation/windows/ble_gatt_client.cc @@ -17,7 +17,6 @@ #include #include -#include #include #include #include @@ -30,7 +29,6 @@ #include "absl/strings/str_join.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" -#include "absl/types/optional.h" #include "internal/platform/implementation/ble.h" #include "internal/platform/implementation/windows/bluetooth_adapter.h" #include "internal/platform/implementation/windows/utils.h" @@ -261,7 +259,7 @@ bool BleGattClient::DiscoverServiceAndCharacteristics( return false; } -absl::optional BleGattClient::GetCharacteristic( +std::optional BleGattClient::GetCharacteristic( const Uuid& service_uuid, const Uuid& characteristic_uuid) { absl::MutexLock lock(mutex_); VLOG(1) << __func__ << ": Stared to get characteristic UUID=" @@ -273,7 +271,7 @@ absl::optional BleGattClient::GetCharacteristic( if (!gatt_characteristic.has_value()) { LOG(ERROR) << __func__ << ": Failed to get native GATT characteristic."; - return absl::nullopt; + return std::nullopt; } api::ble::GattCharacteristic result; @@ -323,10 +321,10 @@ absl::optional BleGattClient::GetCharacteristic( << error.code() << ": " << winrt::to_string(error.message()); } - return absl::nullopt; + return std::nullopt; } -absl::optional BleGattClient::ReadCharacteristic( +std::optional BleGattClient::ReadCharacteristic( const api::ble::GattCharacteristic& characteristic) { absl::MutexLock lock(mutex_); VLOG(1) << __func__ @@ -338,7 +336,7 @@ absl::optional BleGattClient::ReadCharacteristic( if (!gatt_characteristic.has_value()) { LOG(ERROR) << __func__ << ": Failed to get native GATT characteristic."; - return absl::nullopt; + return std::nullopt; } GattReadResult result = @@ -347,7 +345,7 @@ absl::optional BleGattClient::ReadCharacteristic( LOG(ERROR) << __func__ << ": Failed to read GATT characteristic with error: " << GattCommunicationStatusToString(result.Status()); - return absl::nullopt; + return std::nullopt; } IBuffer buffer = result.Value(); @@ -377,7 +375,7 @@ absl::optional BleGattClient::ReadCharacteristic( << error.code() << ": " << winrt::to_string(error.message()); } - return absl::nullopt; + return std::nullopt; } bool BleGattClient::WriteCharacteristic( @@ -431,80 +429,6 @@ bool BleGattClient::WriteCharacteristic( return false; } -bool BleGattClient::SetCharacteristicSubscription( - const api::ble::GattCharacteristic& characteristic, bool enable, - absl::AnyInvocable - on_characteristic_changed_cb) { - absl::MutexLock lock(mutex_); - VLOG(1) << __func__ << ": Started to set Characteristic Subscription."; - GattClientCharacteristicConfigurationDescriptorValue gcccd_value = - GattClientCharacteristicConfigurationDescriptorValue::None; - if ((characteristic.property & Property::kNotify) != Property::kNone) { - gcccd_value = GattClientCharacteristicConfigurationDescriptorValue::Notify; - } else if ((characteristic.property & Property::kIndicate) != - Property::kNone) { - gcccd_value = - GattClientCharacteristicConfigurationDescriptorValue::Indicate; - } else { - LOG(WARNING) << "Characeristic: " << std::string(characteristic.uuid) - << " supports neither notifications nor indications."; - return false; - } - - std::optional gatt_characteristic; - - gatt_characteristic = - native_characteristic_map_[characteristic].native_characteristic; - - if (!gatt_characteristic.has_value()) { - LOG(ERROR) << __func__ << ": Failed to get native GATT characteristic."; - return false; - } - - // Write characteristic configuration descriptor - if (!WriteCharacteristicConfigurationDescriptor( - gatt_characteristic.value(), - enable - ? gcccd_value - : GattClientCharacteristicConfigurationDescriptorValue::None)) { - return false; - } - - // Set value changed handler - try { - if (enable) { - native_characteristic_map_[characteristic].on_characteristic_changed_cb = - std::move(on_characteristic_changed_cb); - native_characteristic_map_[characteristic].notification_token = - gatt_characteristic->ValueChanged( - [&](GattCharacteristic const& native_characteristic, - GattValueChangedEventArgs args) { - BleGattClient::OnCharacteristicValueChanged(characteristic, - args); - }); - - if (!native_characteristic_map_[characteristic].notification_token) { - LOG(ERROR) << __func__ << ": Failed to add value change handler."; - return false; - } - } else if (native_characteristic_map_[characteristic].notification_token) { - gatt_characteristic->ValueChanged(std::exchange( - native_characteristic_map_[characteristic].notification_token, {})); - } - LOG(ERROR) << __func__ << ": Successfully set Characteristic Subscription."; - return true; - } catch (std::exception exception) { - LOG(ERROR) << __func__ << ": Failed to set Characteristic Subscription." - << exception.what(); - } catch (const winrt::hresult_error& error) { - LOG(ERROR) << __func__ - << ": Failed to set Characteristic Subscription." - " WinRT exception: " - << error.code() << ": " << winrt::to_string(error.message()); - } - return false; -} - void BleGattClient::Disconnect() { absl::MutexLock lock(mutex_); try { @@ -533,12 +457,12 @@ std::optional BleGattClient::GetNativeCharacteristic( try { if (ble_device_ == nullptr) { LOG(ERROR) << __func__ << ": BLE device is disconnected."; - return absl::nullopt; + return std::nullopt; } if (gatt_devices_services_result_ == nullptr) { LOG(ERROR) << __func__ << ": No available GATT services."; - return absl::nullopt; + return std::nullopt; } for (const auto& service : gatt_devices_services_result_.Services()) { @@ -579,7 +503,7 @@ std::optional BleGattClient::GetNativeCharacteristic( << error.code() << ": " << winrt::to_string(error.message()); } - return absl::nullopt; + return std::nullopt; } bool BleGattClient::WriteCharacteristicConfigurationDescriptor( diff --git a/internal/platform/implementation/windows/ble_gatt_client.h b/internal/platform/implementation/windows/ble_gatt_client.h index 651001e1..2f14eca1 100644 --- a/internal/platform/implementation/windows/ble_gatt_client.h +++ b/internal/platform/implementation/windows/ble_gatt_client.h @@ -17,8 +17,6 @@ #include -#include -#include #include #include #include @@ -28,8 +26,6 @@ #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" -#include "absl/types/optional.h" -#include "internal/platform/byte_array.h" #include "internal/platform/implementation/ble.h" #include "internal/platform/uuid.h" #include "winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h" @@ -50,11 +46,11 @@ class BleGattClient : public api::ble::GattClient { const std::vector& characteristic_uuids) override ABSL_LOCKS_EXCLUDED(mutex_); - absl::optional GetCharacteristic( + std::optional GetCharacteristic( const Uuid& service_uuid, const Uuid& characteristic_uuid) override ABSL_LOCKS_EXCLUDED(mutex_); - absl::optional ReadCharacteristic( + std::optional ReadCharacteristic( const api::ble::GattCharacteristic& characteristic) override ABSL_LOCKS_EXCLUDED(mutex_); @@ -63,11 +59,6 @@ class BleGattClient : public api::ble::GattClient { api::ble::GattClient::WriteType write_type) override ABSL_LOCKS_EXCLUDED(mutex_); - bool SetCharacteristicSubscription( - const api::ble::GattCharacteristic& characteristic, bool enable, - absl::AnyInvocable - on_characteristic_changed_cb) override ABSL_LOCKS_EXCLUDED(mutex_); - void Disconnect() override ABSL_LOCKS_EXCLUDED(mutex_); private: diff --git a/internal/platform/implementation/windows/ble_gatt_server.cc b/internal/platform/implementation/windows/ble_gatt_server.cc index d3c2a939..cbb540a7 100644 --- a/internal/platform/implementation/windows/ble_gatt_server.cc +++ b/internal/platform/implementation/windows/ble_gatt_server.cc @@ -31,7 +31,6 @@ #include "absl/synchronization/mutex.h" #include "absl/time/clock.h" #include "absl/time/time.h" -#include "absl/types/optional.h" #include "internal/platform/byte_array.h" #include "internal/platform/implementation/ble.h" #include "internal/platform/implementation/bluetooth_adapter.h" @@ -114,7 +113,7 @@ BleGattServer::BleGattServer(api::BluetoothAdapter* adapter, DCHECK(adapter_ != nullptr); } -absl::optional +std::optional BleGattServer::CreateCharacteristic( const Uuid& service_uuid, const Uuid& characteristic_uuid, api::ble::GattCharacteristic::Permission permission, @@ -126,7 +125,7 @@ BleGattServer::CreateCharacteristic( if (!service_uuid_.IsEmpty() && service_uuid_ != service_uuid) { LOG(ERROR) << __func__ << ": Only support one GATT service for now."; - return absl::nullopt; + return std::nullopt; } service_uuid_ = service_uuid; diff --git a/internal/platform/implementation/windows/ble_gatt_server.h b/internal/platform/implementation/windows/ble_gatt_server.h index 78ed86b1..52832a9e 100644 --- a/internal/platform/implementation/windows/ble_gatt_server.h +++ b/internal/platform/implementation/windows/ble_gatt_server.h @@ -18,6 +18,7 @@ #include #include +#include #include #include "absl/base/thread_annotations.h" @@ -27,7 +28,6 @@ #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "absl/synchronization/notification.h" -#include "absl/types/optional.h" #include "internal/platform/byte_array.h" #include "internal/platform/implementation/ble.h" #include "internal/platform/implementation/bluetooth_adapter.h" @@ -47,7 +47,7 @@ class BleGattServer : public api::ble::GattServer { BleGattServer(api::BluetoothAdapter* adapter, api::ble::ServerGattConnectionCallback callback); ~BleGattServer() override = default; - absl::optional CreateCharacteristic( + std::optional CreateCharacteristic( const Uuid& service_uuid, const Uuid& characteristic_uuid, api::ble::GattCharacteristic::Permission permission, api::ble::GattCharacteristic::Property property) override diff --git a/internal/platform/implementation/windows/bluetooth_adapter.cc b/internal/platform/implementation/windows/bluetooth_adapter.cc index 9bef1fae..fb0a8396 100644 --- a/internal/platform/implementation/windows/bluetooth_adapter.cc +++ b/internal/platform/implementation/windows/bluetooth_adapter.cc @@ -32,6 +32,11 @@ #include #include +// Remove LogSeverity macro defined in setupapi.h +#if defined(LogSeverity) +#undef LogSeverity +#endif + #include #include #include diff --git a/internal/platform/implementation/windows/bluetooth_classic_medium.cc b/internal/platform/implementation/windows/bluetooth_classic_medium.cc index 7dbaf96a..87a80ffd 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_medium.cc +++ b/internal/platform/implementation/windows/bluetooth_classic_medium.cc @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -39,7 +40,6 @@ #include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h" #include "internal/platform/implementation/windows/generated/winrt/base.h" #include "internal/platform/implementation/windows/utils.h" -#include "internal/platform/implementation/windows/wifi_lan.h" #include "internal/platform/logging.h" #include "internal/platform/mac_address.h" @@ -72,6 +72,16 @@ constexpr wchar_t kBluetoothSelector[] = L"System.Devices.Aep.ProtocolId:=\"{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}" L"\""; +// The Id of the Service Name SDP attribute +constexpr uint16_t SdpServiceNameAttributeId = 0x100; + +// The SDP Type of the Service Name SDP attribute. +// The first byte in the SDP Attribute encodes the SDP Attribute Type as +// follows: +// - the Attribute Type size in the least significant 3 bits, +// - the SDP Attribute Type value in the most significant 5 bits. +constexpr char SdpServiceNameAttributeType = (4 << 3) | 5; + void DumpDeviceInformation( const IMapView& properties) { if (!kEnableDumpDeviceInfomation) { @@ -462,17 +472,17 @@ bool BluetoothClassicMedium::CheckSdp(RfcommDeviceService requested_service) { } auto attributes = requested_service.GetSdpRawAttributesAsync().get(); - if (!attributes.HasKey(Constants::SdpServiceNameAttributeId)) { + if (!attributes.HasKey(SdpServiceNameAttributeId)) { LOG(ERROR) << __func__ << ": Missing SdpServiceNameAttributeId."; return false; } - auto attribute_reader = DataReader::FromBuffer( - attributes.Lookup(Constants::SdpServiceNameAttributeId)); + auto attribute_reader = + DataReader::FromBuffer(attributes.Lookup(SdpServiceNameAttributeId)); auto attribute_type = attribute_reader.ReadByte(); - if (attribute_type != Constants::SdpServiceNameAttributeType) { + if (attribute_type != SdpServiceNameAttributeType) { LOG(ERROR) << __func__ << ": Missing SdpServiceNameAttributeType."; return false; } @@ -958,7 +968,7 @@ bool BluetoothClassicMedium::InitializeServiceSdpAttributes( auto sdp_writer = DataWriter(); // Write the Service Name Attribute. - sdp_writer.WriteByte(Constants::SdpServiceNameAttributeType); + sdp_writer.WriteByte(SdpServiceNameAttributeType); // The length of the UTF-8 encoded Service Name SDP Attribute. sdp_writer.WriteByte(service_name.size()); @@ -968,8 +978,8 @@ bool BluetoothClassicMedium::InitializeServiceSdpAttributes( sdp_writer.WriteString(winrt::to_hstring(service_name)); // Set the SDP Attribute on the RFCOMM Service Provider. - rfcomm_provider.SdpRawAttributes().Insert( - Constants::SdpServiceNameAttributeId, sdp_writer.DetachBuffer()); + rfcomm_provider.SdpRawAttributes().Insert(SdpServiceNameAttributeId, + sdp_writer.DetachBuffer()); return true; } catch (...) { diff --git a/internal/platform/implementation/windows/device_info.cc b/internal/platform/implementation/windows/device_info.cc index ba05e5d3..348f7ce6 100644 --- a/internal/platform/implementation/windows/device_info.cc +++ b/internal/platform/implementation/windows/device_info.cc @@ -30,27 +30,11 @@ #include "internal/platform/implementation/windows/device_paths.h" #include "internal/platform/implementation/windows/string_utils.h" #include "internal/platform/implementation/windows/utils.h" -#include "winrt/Windows.Foundation.Collections.h" -#include "winrt/Windows.Foundation.h" -#include "winrt/Windows.System.h" namespace nearby::windows { -using IInspectable = winrt::Windows::Foundation::IInspectable; -using KnownUserProperties = winrt::Windows::System::KnownUserProperties; -using User = winrt::Windows::System::User; -using UserType = winrt::Windows::System::UserType; -using UserAuthenticationStatus = - winrt::Windows::System::UserAuthenticationStatus; - using ::nearby::windows::string_utils::WideStringToString; -template -using IVectorView = winrt::Windows::Foundation::Collections::IVectorView; - -template -using IAsyncOperation = winrt::Windows::Foundation::IAsyncOperation; - std::optional DeviceInfo::GetOsDeviceName() const { std::optional device_name = GetDnsHostName(); if (device_name.has_value()) { @@ -60,7 +44,6 @@ std::optional DeviceInfo::GetOsDeviceName() const { } api::DeviceInfo::DeviceType DeviceInfo::GetDeviceType() const { - // TODO(b/230132370): return correct device type on the Windows platform. return api::DeviceInfo::DeviceType::kLaptop; } @@ -68,7 +51,7 @@ api::DeviceInfo::OsType DeviceInfo::GetOsType() const { return api::DeviceInfo::OsType::kWindows; } -std::optional DeviceInfo::GetDownloadPath() const { +FilePath DeviceInfo::GetDownloadPath() const { PWSTR path; HRESULT result = SHGetKnownFolderPath(FOLDERID_Downloads, KF_FLAG_DEFAULT, nullptr, &path); @@ -79,37 +62,19 @@ std::optional DeviceInfo::GetDownloadPath() const { } CoTaskMemFree(path); - return std::nullopt; -} - -std::optional DeviceInfo::GetLocalAppDataPath() const { - return nearby::platform::windows::GetLocalAppDataPath(FilePath()); -} - -std::optional DeviceInfo::GetCommonAppDataPath() const { - PWSTR path; - HRESULT result = SHGetKnownFolderPath(FOLDERID_ProgramData, KF_FLAG_DEFAULT, - /*hToken=*/nullptr, &path); - if (result == S_OK) { - std::wstring common_app_data_path{path}; - CoTaskMemFree(path); - return FilePath(std::wstring_view(common_app_data_path)); - } - - CoTaskMemFree(path); - return std::nullopt; -} - -std::optional DeviceInfo::GetTemporaryPath() const { return Files::GetTemporaryDirectory(); } -std::optional DeviceInfo::GetLogPath() const { - return nearby::platform::windows::GetLogPath(); +FilePath DeviceInfo::GetLocalAppDataPath(FilePath sub_path) const { + return nearby::platform::windows::GetLocalAppDataPath(sub_path); } -std::optional DeviceInfo::GetCrashDumpPath() const { - return nearby::platform::windows::GetCrashDumpPath(); +FilePath DeviceInfo::GetTemporaryPath() const { + return Files::GetTemporaryDirectory(); +} + +FilePath DeviceInfo::GetLogPath() const { + return nearby::platform::windows::GetLogPath(); } bool DeviceInfo::IsScreenLocked() const { diff --git a/internal/platform/implementation/windows/device_info.h b/internal/platform/implementation/windows/device_info.h index 1b4cfe99..8de0d388 100644 --- a/internal/platform/implementation/windows/device_info.h +++ b/internal/platform/implementation/windows/device_info.h @@ -37,12 +37,10 @@ class DeviceInfo : public api::DeviceInfo { api::DeviceInfo::DeviceType GetDeviceType() const override; api::DeviceInfo::OsType GetOsType() const override; - std::optional GetDownloadPath() const override; - std::optional GetLocalAppDataPath() const override; - std::optional GetCommonAppDataPath() const override; - std::optional GetTemporaryPath() const override; - std::optional GetLogPath() const override; - std::optional GetCrashDumpPath() const override; + FilePath GetDownloadPath() const override; + FilePath GetLocalAppDataPath(FilePath sub_path) const override; + FilePath GetTemporaryPath() const override; + FilePath GetLogPath() const override; bool IsScreenLocked() const override; void RegisterScreenLockedListener( diff --git a/internal/platform/implementation/windows/device_info_test.cc b/internal/platform/implementation/windows/device_info_test.cc index 6431e5ea..295992a6 100644 --- a/internal/platform/implementation/windows/device_info_test.cc +++ b/internal/platform/implementation/windows/device_info_test.cc @@ -40,23 +40,20 @@ TEST(DeviceInfo, GetOsType) { } TEST(DeviceInfo, DISABLED_GetLocalAppDataPath) { - EXPECT_TRUE(DeviceInfo().GetLocalAppDataPath().has_value()); + EXPECT_FALSE( + DeviceInfo().GetLocalAppDataPath(FilePath("sub_path")).IsEmpty()); } TEST(DeviceInfo, DISABLED_GetDownloadPath) { - EXPECT_TRUE(DeviceInfo().GetDownloadPath().has_value()); + EXPECT_FALSE(DeviceInfo().GetDownloadPath().IsEmpty()); } TEST(DeviceInfo, DISABLED_GetTemporaryPath) { - EXPECT_TRUE(DeviceInfo().GetTemporaryPath().has_value()); + EXPECT_FALSE(DeviceInfo().GetTemporaryPath().IsEmpty()); } TEST(DeviceInfo, DISABLED_GetLogPath) { - EXPECT_TRUE(DeviceInfo().GetLogPath().has_value()); -} - -TEST(DeviceInfo, DISABLED_GetCrashDumpPath) { - EXPECT_TRUE(DeviceInfo().GetCrashDumpPath().has_value()); + EXPECT_FALSE(DeviceInfo().GetLogPath().IsEmpty()); } TEST(DeviceInfo, DISABLED_IsScreenLocked) { diff --git a/internal/platform/implementation/windows/platform.cc b/internal/platform/implementation/windows/platform.cc index 4d896be9..20004c26 100644 --- a/internal/platform/implementation/windows/platform.cc +++ b/internal/platform/implementation/windows/platform.cc @@ -259,10 +259,6 @@ ImplementationPlatform::CreateWifiDirectMedium() { return std::make_unique(); } -std::unique_ptr ImplementationPlatform::CreateWebRtcMedium() { - return nullptr; -} - std::unique_ptr ImplementationPlatform::CreateAppLifecycleMonitor( std::function diff --git a/internal/platform/implementation/windows/preferences_repository_test.cc b/internal/platform/implementation/windows/preferences_repository_test.cc index 0ef8de90..473e5105 100644 --- a/internal/platform/implementation/windows/preferences_repository_test.cc +++ b/internal/platform/implementation/windows/preferences_repository_test.cc @@ -44,7 +44,8 @@ TEST(PreferencesRepository, LoadWithBadPath) { TEST(PreferencesRepository, RecoverFromBadPreferences) { std::optional app_data_path = - api::ImplementationPlatform::CreateDeviceInfo()->GetLocalAppDataPath(); + api::ImplementationPlatform::CreateDeviceInfo()->GetLocalAppDataPath( + FilePath()); ASSERT_TRUE(app_data_path.has_value()); FilePath full_path = app_data_path->append(FilePath(kPreferencesPath)); FilePath full_name = app_data_path->append(FilePath(kPreferencesFileName)); @@ -63,7 +64,8 @@ TEST(PreferencesRepository, RecoverFromBadPreferences) { TEST(PreferencesRepository, SaveAndLoadPreferences) { std::optional app_data_path = - api::ImplementationPlatform::CreateDeviceInfo()->GetLocalAppDataPath(); + api::ImplementationPlatform::CreateDeviceInfo()->GetLocalAppDataPath( + FilePath()); ASSERT_TRUE(app_data_path.has_value()); FilePath full_path = app_data_path->append(FilePath(kPreferencesPath)); FilePath full_name = app_data_path->append(FilePath(kPreferencesFileName)); @@ -86,7 +88,8 @@ TEST(PreferencesRepository, SaveAndLoadPreferences) { TEST(PreferencesRepository, LoadFromBackup) { std::optional app_data_path = - api::ImplementationPlatform::CreateDeviceInfo()->GetLocalAppDataPath(); + api::ImplementationPlatform::CreateDeviceInfo()->GetLocalAppDataPath( + FilePath()); ASSERT_TRUE(app_data_path.has_value()); FilePath full_path = app_data_path->append(FilePath(kPreferencesPath)); FilePath full_name = app_data_path->append(FilePath(kPreferencesFileName)); @@ -123,7 +126,8 @@ TEST(PreferencesRepository, LoadFromBackup) { TEST(PreferencesRepository, RecoverFromCorruption) { std::optional app_data_path = - api::ImplementationPlatform::CreateDeviceInfo()->GetLocalAppDataPath(); + api::ImplementationPlatform::CreateDeviceInfo()->GetLocalAppDataPath( + FilePath()); ASSERT_TRUE(app_data_path.has_value()); FilePath full_path = app_data_path->append(FilePath(kPreferencesPath)); FilePath full_name = app_data_path->append(FilePath(kPreferencesFileName)); diff --git a/internal/platform/implementation/windows/scheduled_executor.cc b/internal/platform/implementation/windows/scheduled_executor.cc index d1bcbf12..87792ff8 100644 --- a/internal/platform/implementation/windows/scheduled_executor.cc +++ b/internal/platform/implementation/windows/scheduled_executor.cc @@ -45,17 +45,7 @@ std::shared_ptr ScheduledExecutor::Schedule( return nullptr; } - if (NearbyFlags::GetInstance().GetBoolFlag( - platform::config_package_nearby::nearby_platform_feature:: - kRunScheduledExecutorCallbackOnExecutorThread)) { - return task_scheduler_.Schedule( - [this, runnable = std::move(runnable)]() mutable { - Execute(std::move(runnable)); - }, - duration); - } else { - return task_scheduler_.Schedule(std::move(runnable), duration); - } + return task_scheduler_.Schedule(std::move(runnable), duration); } void ScheduledExecutor::Execute(Runnable&& runnable) { diff --git a/internal/platform/implementation/windows/utils.cc b/internal/platform/implementation/windows/utils.cc index 62fa4562..cfabecb3 100644 --- a/internal/platform/implementation/windows/utils.cc +++ b/internal/platform/implementation/windows/utils.cc @@ -16,13 +16,15 @@ // clang-format off #include -#include -#include -#include #include #include // clang-format on +// Remove LogSeverity macro defined in setupapi.h +#if defined(LogSeverity) +#undef LogSeverity +#endif + // Standard C/C++ headers #include #include @@ -33,9 +35,6 @@ #include // Nearby connections headers -#include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/implementation/crypto.h" #include "internal/platform/implementation/windows/string_utils.h" #include "internal/platform/logging.h" #include "internal/platform/uuid.h" @@ -44,127 +43,8 @@ #include "winrt/base.h" namespace nearby::windows { -namespace { -void AddIpUnicastAddresses(IP_ADAPTER_UNICAST_ADDRESS* unicast_addresses, - std::vector& addresses) { - std::string address; - while (unicast_addresses != nullptr) { - DWORD size = INET6_ADDRSTRLEN; // Max IP address length. - address.resize(size); - if (WSAAddressToStringA(unicast_addresses->Address.lpSockaddr, - unicast_addresses->Address.iSockaddrLength, - /*lpProtocolInfo=*/nullptr, address.data(), - &size) != 0) { - LOG(ERROR) << __func__ << ": Cannot convert address to string."; - continue; - } - address.resize(size); - addresses.push_back(address); - unicast_addresses = unicast_addresses->Next; - } -} - -void GetIpAddresses(int family, std::vector& wifi_addresses, - std::vector& ethernet_addresses, - std::vector& other_addresses) { - static constexpr int kDefaultBufferSize = 15 * 1024; // default to 15K buffer - static constexpr int kMaxBufferSize = - 45 * 1024; // Try to increase buffer 2 times. - static constexpr ULONG kDefaultFlags = - GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | - GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME; - ULONG buffer_size = 0; - // A string to own the memory for IP_ADAPTER_ADDRESSES. - std::string address_buffer; - ULONG error_code = ERROR_NO_DATA; - IP_ADAPTER_ADDRESSES* addresses = nullptr; - do { - buffer_size += kDefaultBufferSize; - address_buffer.reserve(buffer_size); - addresses = reinterpret_cast(address_buffer.data()); - error_code = GetAdaptersAddresses( - family, kDefaultFlags, /*reserved=*/nullptr, addresses, &buffer_size); - } while (error_code == ERROR_BUFFER_OVERFLOW && - buffer_size <= kMaxBufferSize); - if (error_code != ERROR_NO_DATA && error_code != NO_ERROR) { - LOG(ERROR) << __func__ - << ": Cannot get adapter addresses. Error code: " << error_code; - return; - } - if (error_code == ERROR_NO_DATA) { - LOG(INFO) << __func__ << ": No IPv4 addresses found."; - return; - } - IP_ADAPTER_ADDRESSES* next_address = addresses; - while (next_address != nullptr) { - if (next_address->OperStatus == IfOperStatusUp) { - if (next_address->IfType == IF_TYPE_ETHERNET_CSMACD) { - VLOG(1) << "Found ethernet adater: " << next_address->AdapterName - << " index: " << next_address->IfIndex - << " v6 index: " << next_address->Ipv6IfIndex; - AddIpUnicastAddresses(next_address->FirstUnicastAddress, - ethernet_addresses); - } else if (next_address->IfType == IF_TYPE_IEEE80211) { - VLOG(1) << "Found wifi adapter: " << next_address->AdapterName - << " index: " << next_address->IfIndex - << " v6 index: " << next_address->Ipv6IfIndex; - AddIpUnicastAddresses(next_address->FirstUnicastAddress, - wifi_addresses); - } else if (next_address->IfType != IF_TYPE_SOFTWARE_LOOPBACK) { - // Skip loopback interfaces. - VLOG(1) << "Found other adapter: " << next_address->AdapterName; - AddIpUnicastAddresses(next_address->FirstUnicastAddress, - other_addresses); - } - } - next_address = next_address->Next; - } -} - -} // namespace - -std::string ipaddr_4bytes_to_dotdecimal_string( - absl::string_view ipaddr_4bytes) { - if (ipaddr_4bytes.size() != 4) { - return {}; - } - - in_addr address; - address.S_un.S_un_b.s_b1 = ipaddr_4bytes[0]; - address.S_un.S_un_b.s_b2 = ipaddr_4bytes[1]; - address.S_un.S_un_b.s_b3 = ipaddr_4bytes[2]; - address.S_un.S_un_b.s_b4 = ipaddr_4bytes[3]; - char* ipv4_address = inet_ntoa(address); - if (ipv4_address == nullptr) { - return {}; - } - - return std::string(ipv4_address); -} - -std::string ipaddr_dotdecimal_to_4bytes_string(std::string ipv4_s) { - if (ipv4_s.empty()) { - return {}; - } - - in_addr address; - address.S_un.S_addr = inet_addr(ipv4_s.c_str()); - char ipv4_b[5]; - ipv4_b[0] = address.S_un.S_un_b.s_b1; - ipv4_b[1] = address.S_un.S_un_b.s_b2; - ipv4_b[2] = address.S_un.S_un_b.s_b3; - ipv4_b[3] = address.S_un.S_un_b.s_b4; - ipv4_b[4] = 0; - - return std::string(ipv4_b, 4); -} - -std::vector GetIpv4Addresses() { - std::vector result; - GetIpAddresses(AF_INET, result, result, result); - return result; -} +using winrt::Windows::Foundation::IInspectable; Uuid winrt_guid_to_nearby_uuid(const ::winrt::guid& guid) { int64_t data1 = guid.Data1; @@ -209,11 +89,6 @@ bool is_nearby_uuid_equal_to_winrt_guid(const Uuid& uuid, return uuid == winrt_guid_to_nearby_uuid(guid); } -ByteArray Sha256(absl::string_view input, size_t size) { - ByteArray hash = nearby::Crypto::Sha256(input); - return ByteArray{hash.data(), size}; -} - bool InspectableReader::ReadBoolean(IInspectable inspectable) { if (inspectable == nullptr) { return false; diff --git a/internal/platform/implementation/windows/utils.h b/internal/platform/implementation/windows/utils.h index 52483dd9..b3910c6a 100644 --- a/internal/platform/implementation/windows/utils.h +++ b/internal/platform/implementation/windows/utils.h @@ -23,8 +23,6 @@ #include #include -#include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" #include "internal/platform/uuid.h" #include "winrt/Windows.Foundation.h" #include "winrt/base.h" @@ -32,17 +30,6 @@ namespace nearby { namespace windows { -using winrt::Windows::Foundation::IInspectable; - -std::string ipaddr_4bytes_to_dotdecimal_string(absl::string_view ipaddr_4bytes); -std::string ipaddr_dotdecimal_to_4bytes_string(std::string ipv4_s); - -// Helpers to windows platform -ByteArray Sha256(absl::string_view input, size_t size); - -// Reads the IPv4 addresses -std::vector GetIpv4Addresses(); - // Help methods to convert between Uuid and winrt::guid Uuid winrt_guid_to_nearby_uuid(const ::winrt::guid& guid); winrt::guid nearby_uuid_to_winrt_guid(Uuid uuid); @@ -57,31 +44,18 @@ std::optional GetDnsHostName(); // Returns true if the system has an Intel Wi-Fi adapter. bool IsIntelWifiAdapter(); -namespace Constants { -// The Id of the Service Name SDP attribute -const uint16_t SdpServiceNameAttributeId = 0x100; - -// The SDP Type of the Service Name SDP attribute. -// The first byte in the SDP Attribute encodes the SDP Attribute Type as -// follows: -// - the Attribute Type size in the least significant 3 bits, -// - the SDP Attribute Type value in the most significant 5 bits. -const char SdpServiceNameAttributeType = (4 << 3) | 5; - -// Possible values for the adapter type. Refer to: -// https://learn.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_info -const uint16_t kInterfaceTypeEthernet = 6; -const uint16_t kInterfaceTypeWifi = 71; -} // namespace Constants - class InspectableReader { public: - static bool ReadBoolean(IInspectable inspectable); - static uint16_t ReadUint16(IInspectable inspectable); - static uint32_t ReadUint32(IInspectable inspectable); - static std::string ReadString(IInspectable inspectable); - static std::vector ReadStringArray(IInspectable inspectable); - static GUID ReadGuid(IInspectable inspectable); + static bool ReadBoolean(winrt::Windows::Foundation::IInspectable inspectable); + static uint16_t ReadUint16( + winrt::Windows::Foundation::IInspectable inspectable); + static uint32_t ReadUint32( + winrt::Windows::Foundation::IInspectable inspectable); + static std::string ReadString( + winrt::Windows::Foundation::IInspectable inspectable); + static std::vector ReadStringArray( + winrt::Windows::Foundation::IInspectable inspectable); + static GUID ReadGuid(winrt::Windows::Foundation::IInspectable inspectable); }; } // namespace windows diff --git a/internal/platform/implementation/windows/utils_test.cc b/internal/platform/implementation/windows/utils_test.cc index 54bb7bea..5ee19153 100644 --- a/internal/platform/implementation/windows/utils_test.cc +++ b/internal/platform/implementation/windows/utils_test.cc @@ -24,8 +24,6 @@ #include #include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" #include "internal/platform/implementation/windows/string_utils.h" #include "internal/platform/logging.h" #include "internal/platform/uuid.h" @@ -38,62 +36,8 @@ namespace { using ::winrt::Windows::Foundation::IInspectable; using ::winrt::Windows::Foundation::PropertyValue; -constexpr absl::string_view kIpDotdecimal{"192.168.1.37"}; -constexpr char kIp4Bytes[] = {(char)192, (char)168, (char)1, (char)37}; - } // namespace -TEST(UtilsTests, Ip4BytesToDotdecimal) { - std::string result = - ipaddr_4bytes_to_dotdecimal_string(absl::string_view(kIp4Bytes, 4)); - - EXPECT_EQ(result, kIpDotdecimal); -} - -TEST(UtilsTests, Ip4BytesToDotdecimalInvalid) { - std::string result = ipaddr_4bytes_to_dotdecimal_string(absl::string_view()); - EXPECT_TRUE(result.empty()); -} - -TEST(UtilsTests, IpDotdecimalTo4Bytes) { - std::string result = - ipaddr_dotdecimal_to_4bytes_string(std::string(kIpDotdecimal)); - - EXPECT_EQ(result, std::string(kIp4Bytes, 4)); -} - -TEST(UtilsTests, IpDotdecimalTo4BytesEmpty) { - std::string result = ipaddr_dotdecimal_to_4bytes_string(""); - EXPECT_TRUE(result.empty()); -} - -TEST(UtilsTests, IpDotdecimalTo4BytesInvalid) { - std::string result = ipaddr_dotdecimal_to_4bytes_string("192.168.1.256"); - // inet_addr returns INADDR_NONE for invalid address. - char expected[] = {(char)255, (char)255, (char)255, (char)255}; - EXPECT_EQ(result, std::string(expected, 4)); -} - -TEST(UtilsTests, Sha256) { - std::string input = "Hello World"; - // sha256("Hello World") - const char expected_sha256[] = { - (char)0xa5, (char)0x91, (char)0xa6, (char)0xd4, (char)0x0b, (char)0xf4, - (char)0x20, (char)0x40, (char)0x4a, (char)0x01, (char)0x17, (char)0x33, - (char)0xcf, (char)0xb7, (char)0xb1, (char)0x90, (char)0xd6, (char)0x2c, - (char)0x65, (char)0xbf, (char)0x0b, (char)0xcd, (char)0xa3, (char)0x2b, - (char)0x57, (char)0xb2, (char)0x77, (char)0xd9, (char)0xad, (char)0x9f, - (char)0x14, (char)0x6e}; - - ByteArray result = Sha256(input, 32); - EXPECT_EQ(result.size(), 32); - EXPECT_EQ(memcmp(result.data(), expected_sha256, 32), 0); - - result = Sha256(input, 16); - EXPECT_EQ(result.size(), 16); - EXPECT_EQ(memcmp(result.data(), expected_sha256, 16), 0); -} - TEST(UtilsTests, ConvertBetweenWinrtGuidAndNearbyUuidSuccessfully) { Uuid uuid(0x123e4567e89b12d3, 0xa456426614174000); winrt::guid guid("{123e4567-e89b-12d3-a456-426614174000}"); @@ -157,16 +101,6 @@ TEST(UtilsTests, InspectableReader_ReadStringArray) { std::invalid_argument); } -TEST(UtilsTests, GetIpv4Addresses) { - LOG(ERROR) << "GetIpv4Addresses"; - std::vector addresses = GetIpv4Addresses(); - EXPECT_FALSE(addresses.empty()); - for (const auto& address : addresses) { - LOG(ERROR) << "address: " << address; - } - LOG(ERROR) << "GetIpv4Addresses done"; -} - TEST(UtilsTests, GetDnsHostName) { std::optional host_name = GetDnsHostName(); ASSERT_TRUE(host_name.has_value()); diff --git a/internal/platform/implementation/windows/webrtc.cc b/internal/platform/implementation/windows/webrtc.cc deleted file mode 100644 index 85fa989d..00000000 --- a/internal/platform/implementation/windows/webrtc.cc +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2023 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/windows/webrtc.h" - -#include - -#include -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/webrtc.h" -#include "internal/platform/logging.h" -#include "internal/platform/tachyon_express_signaling_messenger.h" -#include "webrtc/api/create_modular_peer_connection_factory.h" -#include "webrtc/api/peer_connection_interface.h" -#include "webrtc/api/rtc_error.h" -#include "webrtc/api/scoped_refptr.h" -#include "webrtc/rtc_base/thread.h" - -namespace nearby::windows { - -std::string WebRtcMedium::GetDefaultCountryCode() { - wchar_t systemGeoName[LOCALE_NAME_MAX_LENGTH]; - - if (!GetUserDefaultGeoName(systemGeoName, LOCALE_NAME_MAX_LENGTH)) { - LOG(ERROR) << __func__ - << ": Failed to GetUserDefaultGeoName: " << ". Fall back to US."; - return "US"; - } - std::wstring wideGeo(systemGeoName); - std::string systemGeoNameString(wideGeo.begin(), wideGeo.end()); - VLOG(1) << "GetUserDefaultGeoName() returns: " << systemGeoNameString; - return systemGeoNameString; -} - -void WebRtcMedium::CreatePeerConnection( - webrtc::PeerConnectionObserver* observer, PeerConnectionCallback callback) { - CreatePeerConnection(std::nullopt, observer, std::move(callback)); -} - -void WebRtcMedium::CreatePeerConnection( - std::optional options, - webrtc::PeerConnectionObserver* observer, PeerConnectionCallback callback) { - webrtc::PeerConnectionInterface::RTCConfiguration rtc_config; - rtc_config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; - // TODO: b/261663238 - Add the TURN servers and go beyond the default servers. - webrtc::PeerConnectionInterface::IceServer ice_server; - ice_server.urls.emplace_back("stun:stun.l.google.com:19302"); - ice_server.urls.emplace_back("stun:stun1.l.google.com:19302"); - ice_server.urls.emplace_back("stun:stun2.l.google.com:19302"); - ice_server.urls.emplace_back("stun:stun3.l.google.com:19302"); - ice_server.urls.emplace_back("stun:stun4.l.google.com:19302"); - rtc_config.servers.push_back(ice_server); - - std::unique_ptr signaling_thread = webrtc::Thread::Create(); - signaling_thread->SetName("signaling_thread", nullptr); - if (!signaling_thread->Start()) { - callback(/*peer_connection=*/nullptr); - return; - } - - webrtc::PeerConnectionDependencies dependencies(observer); - webrtc::PeerConnectionFactoryDependencies factory_dependencies; - factory_dependencies.signaling_thread = signaling_thread.release(); - - webrtc::scoped_refptr - peer_connection_factory = webrtc::CreateModularPeerConnectionFactory( - std::move(factory_dependencies)); - if (options.has_value()) { - peer_connection_factory->SetOptions(options.value()); - } - webrtc::RTCErrorOr> - peer_connection_or_error = - peer_connection_factory->CreatePeerConnectionOrError( - rtc_config, std::move(dependencies)); - if (peer_connection_or_error.ok()) { - callback(peer_connection_or_error.MoveValue()); - } else { - callback(/*peer_connection=*/nullptr); - } -} - -std::unique_ptr -WebRtcMedium::GetSignalingMessenger( - absl::string_view self_id, - const location::nearby::connections::LocationHint& location_hint) { - return std::make_unique(self_id, - location_hint); -} - -} // namespace nearby::windows diff --git a/internal/platform/implementation/windows/webrtc.h b/internal/platform/implementation/windows/webrtc.h deleted file mode 100644 index 2c5b3fb3..00000000 --- a/internal/platform/implementation/windows/webrtc.h +++ /dev/null @@ -1,58 +0,0 @@ -// 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. - -#ifndef PLATFORM_IMPL_WINDOWS_WEBRTC_H_ -#define PLATFORM_IMPL_WINDOWS_WEBRTC_H_ - -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/webrtc.h" -#include "webrtc/api/peer_connection_interface.h" - -namespace nearby::windows { - -class WebRtcMedium : public api::WebRtcMedium { - public: - ~WebRtcMedium() override = default; - - // Gets the default two-letter country code associated with current locale. - // For example, en_US locale resolves to "US". - // This follows the ISO 3166-1 Alpha-2 standard. - std::string GetDefaultCountryCode() override; - - // Creates and returns a new webrtc::PeerConnectionInterface object via - // |callback|. - void CreatePeerConnection(webrtc::PeerConnectionObserver* observer, - PeerConnectionCallback callback) override; - - // Creates and returns a new webrtc::PeerConnectionInterface object via - // |callback| with |PeerConnectionFactoryInterface::Options|. - void CreatePeerConnection( - std::optional options, - webrtc::PeerConnectionObserver* observer, - PeerConnectionCallback callback) override; - - // Returns a signaling messenger for sending WebRTC signaling messages. - std::unique_ptr GetSignalingMessenger( - absl::string_view self_id, - const location::nearby::connections::LocationHint& location_hint) - override; -}; - -} // namespace nearby::windows - -#endif // PLATFORM_IMPL_WINDOWS_WEBRTC_H_ diff --git a/internal/platform/implementation/windows/webrtc_platform.cc b/internal/platform/implementation/windows/webrtc_platform.cc new file mode 100644 index 00000000..fe7bc8fa --- /dev/null +++ b/internal/platform/implementation/windows/webrtc_platform.cc @@ -0,0 +1,47 @@ +// Copyright 2026 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/webrtc_platform.h" + +#include + +#include +#include + +#include "internal/platform/logging.h" +#include "internal/platform/implementation/webrtc.h" +#include "connections/implementation/mediums/webrtc/webrtc_medium_impl.h" + +namespace nearby::api { + +std::unique_ptr +WebRtcImplementationPlatform::CreateWebRtcMedium() { + return std::make_unique(); +} + +std::string WebRtcImplementationPlatform::GetDefaultCountryCode() { + wchar_t systemGeoName[LOCALE_NAME_MAX_LENGTH]; + + if (!GetUserDefaultGeoName(systemGeoName, LOCALE_NAME_MAX_LENGTH)) { + LOG(ERROR) << __func__ + << ": Failed to GetUserDefaultGeoName: " << ". Fall back to US."; + return "US"; + } + std::wstring wideGeo(systemGeoName); + std::string systemGeoNameString(wideGeo.begin(), wideGeo.end()); + VLOG(1) << "GetUserDefaultGeoName() returns: " << systemGeoNameString; + return systemGeoNameString; +} + +} // namespace nearby::api diff --git a/internal/platform/implementation/windows/webrtc_test.cc b/internal/platform/implementation/windows/webrtc_test.cc deleted file mode 100644 index 708e0efc..00000000 --- a/internal/platform/implementation/windows/webrtc_test.cc +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2023 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/windows/webrtc.h" - -#include -#include -#include - -#include "gtest/gtest.h" -#include "internal/platform/implementation/webrtc.h" -#include "webrtc/api/data_channel_interface.h" -#include "webrtc/api/jsep.h" -#include "webrtc/api/peer_connection_interface.h" -#include "webrtc/api/scoped_refptr.h" - -namespace nearby { -namespace windows { - -class MockPeerConnectionObserver : public webrtc::PeerConnectionObserver { - public: - void OnSignalingChange( - webrtc::PeerConnectionInterface::SignalingState new_state) override {} - - void OnDataChannel(webrtc::scoped_refptr - data_channel) override {} - - void OnIceGatheringChange( - webrtc::PeerConnectionInterface::IceGatheringState new_state) override {} - - void OnIceCandidate(const webrtc::IceCandidate* candidate) override {} -}; - -location::nearby::connections::LocationHint GetCountryCodeLocationHint( - const std::string& country_code) { - auto location_hint = location::nearby::connections::LocationHint(); - location_hint.set_location(country_code); - location_hint.set_format( - location::nearby::connections::LocationStandard::ISO_3166_1_ALPHA_2); - return location_hint; -} - -TEST(WebrtcTest, CountryCodeDefault) { - WebRtcMedium medium; - std::string result = medium.GetDefaultCountryCode(); - EXPECT_EQ(result, "US"); -} - -TEST(WebrtcTest, CreatePeerConnectionSucceeds) { - auto observer = std::make_unique(); - WebRtcMedium medium; - medium.CreatePeerConnection( - std::nullopt, observer.get(), - [](webrtc::scoped_refptr - peer_connection) mutable { - if (!peer_connection) { - FAIL() << "Peer connection should have been non-null"; - return; - } - }); -} - -TEST(WebrtcTest, GetSignalingMessengerSucceeds) { - WebRtcMedium medium; - std::unique_ptr messenger = - medium.GetSignalingMessenger("US", GetCountryCodeLocationHint("US")); - EXPECT_TRUE(messenger); -} - -} // namespace windows -} // namespace nearby diff --git a/internal/platform/implementation/windows/wifi_direct.h b/internal/platform/implementation/windows/wifi_direct.h index 276ee488..982fa6ba 100644 --- a/internal/platform/implementation/windows/wifi_direct.h +++ b/internal/platform/implementation/windows/wifi_direct.h @@ -17,15 +17,13 @@ // Windows headers #include +#include #include // Standard C/C++ headers -#include -#include #include +#include #include -#include -#include // Nearby connections headers #include "absl/base/nullability.h" @@ -35,6 +33,7 @@ #include "absl/synchronization/mutex.h" #include "absl/types/optional.h" #include "internal/platform/cancellation_flag.h" +#include "internal/platform/count_down_latch.h" #include "internal/platform/exception.h" #include "internal/platform/implementation/wifi_direct.h" #include "internal/platform/implementation/windows/nearby_client_socket.h" @@ -46,8 +45,9 @@ #include "internal/platform/wifi_credential.h" // WinRT headers +#include "internal/platform/implementation/windows/generated/winrt/base.h" #include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Enumeration.h" -#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.WiFiDirect.Services.h" +#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.WiFiDirect.h" #include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h" #include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.h" #include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.h" @@ -55,33 +55,55 @@ #include "internal/platform/implementation/windows/generated/winrt/Windows.Security.Cryptography.h" #include "internal/platform/implementation/windows/generated/winrt/Windows.Storage.Streams.h" #include "internal/platform/implementation/windows/generated/winrt/Windows.System.h" -#include "internal/platform/implementation/windows/generated/winrt/base.h" namespace nearby::windows { +// Windows.Devices.WiFiDirect Namespace contains classes that support connecting +// to associated Wi-Fi Direct devices and associated endpoints for PCs, tablets, +// and phones. +// https://learn.microsoft.com/en-us/uwp/api/windows.devices.wifidirect?view=winrt-22000 using ::winrt::event_token; using ::winrt::fire_and_forget; +using ::winrt::Windows::Devices::WiFiDirect:: + WiFiDirectAdvertisementListenStateDiscoverability; +using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectAdvertisementPublisher; +using ::winrt::Windows::Devices::WiFiDirect:: + WiFiDirectAdvertisementPublisherStatus; +using ::winrt::Windows::Devices::WiFiDirect:: + WiFiDirectAdvertisementPublisherStatusChangedEventArgs; +using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectConfigurationMethod; +using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectConnectionListener; +using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectConnectionParameters; +using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectConnectionRequest; +using ::winrt::Windows::Devices::WiFiDirect:: + WiFiDirectConnectionRequestedEventArgs; +using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectDevice; +using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectDeviceSelectorType; +using ::winrt::Windows::Devices::WiFiDirect::WiFiDirectPairingProcedure; + using ::winrt::Windows::Devices::Enumeration::DeviceInformation; +using ::winrt::Windows::Devices::Enumeration::DeviceInformationCollection; +using ::winrt::Windows::Devices::Enumeration::DeviceInformationCustomPairing; +using ::winrt::Windows::Devices::Enumeration::DeviceInformationKind; +using ::winrt::Windows::Devices::Enumeration::DeviceInformationPairing; using ::winrt::Windows::Devices::Enumeration::DeviceInformationUpdate; +using ::winrt::Windows::Devices::Enumeration::DevicePairingKinds; +using ::winrt::Windows::Devices::Enumeration::DevicePairingProtectionLevel; +using ::winrt::Windows::Devices::Enumeration::DevicePairingRequestedEventArgs; +using ::winrt::Windows::Devices::Enumeration::DevicePairingResult; +using ::winrt::Windows::Devices::Enumeration::DevicePairingResultStatus; +using ::winrt::Windows::Devices::Enumeration::DeviceUnpairingResult; +using ::winrt::Windows::Devices::Enumeration::DeviceUnpairingResultStatus; using ::winrt::Windows::Devices::Enumeration::DeviceWatcher; -using ::winrt::Windows::Devices::WiFiDirect::Services::WiFiDirectService; -using ::winrt::Windows::Devices::WiFiDirect::Services:: - WiFiDirectServiceAdvertisementStatus; -using ::winrt::Windows::Devices::WiFiDirect::Services:: - WiFiDirectServiceAdvertiser; -using ::winrt::Windows::Devices::WiFiDirect::Services:: - WiFiDirectServiceAutoAcceptSessionConnectedEventArgs; -using ::winrt::Windows::Devices::WiFiDirect::Services:: - WiFiDirectServiceConfigurationMethod; -using ::winrt::Windows::Devices::WiFiDirect::Services::WiFiDirectServiceSession; -using ::winrt::Windows::Devices::WiFiDirect::Services:: - WiFiDirectServiceSessionRequestedEventArgs; -using ::winrt::Windows::Devices::WiFiDirect::Services::WiFiDirectServiceStatus; + using ::winrt::Windows::Foundation::AsyncStatus; +using ::winrt::Windows::Foundation::IAsyncOperation; using ::winrt::Windows::Foundation::IInspectable; +using ::winrt::Windows::Foundation::Collections::IVectorView; +using ::winrt::Windows::Networking::EndpointPair; // WifiDirectSocket wraps the socket functions to read and write stream. -// On WiFiDirect GO serverside, a WifiDirectSocket will be passed to +// On WiFiDirect GO server side, a WifiDirectSocket will be passed to // StartAcceptingConnections's callback when Winsock Server Socket receives a // new connection. When client side call API to connect to remote WiFi // WifiDirect GO service, it will return a WifiDirectServiceSocket to caller. @@ -177,6 +199,26 @@ class WifiDirectServerSocket : public api::WifiDirectServerSocket { bool server_socket_accepted_connection_ = false; }; +class WifiDirectDeviceDiscovered { + public: + explicit WifiDirectDeviceDiscovered( + const DeviceInformation& device_info); + + ~WifiDirectDeviceDiscovered() = default; + WifiDirectDeviceDiscovered(WifiDirectDeviceDiscovered&&) = default; + WifiDirectDeviceDiscovered& operator=(WifiDirectDeviceDiscovered&&) = default; + + std::string GetId() { return id_; } + DeviceInformation GetDeviceInformation() { + return windows_wifi_direct_device_; + } + + private: + DeviceInformation windows_wifi_direct_device_; + std::string id_; +}; + +// Container of operations that can be performed over the WifiLan medium. class WifiDirectMedium : public api::WifiDirectMedium { public: WifiDirectMedium(); @@ -217,54 +259,46 @@ class WifiDirectMedium : public api::WifiDirectMedium { const override; private: + // Medium status enum Value : char { kMediumStatusIdle = 0, kMediumStatusAccepting = (1 << 0), - kMediumStatusGOStarted = (1 << 1), + kMediumStatusBeaconing = (1 << 1), kMediumStatusConnecting = (1 << 2), kMediumStatusConnected = (1 << 3), }; // Medium Status int medium_status_ = kMediumStatusIdle; - bool IsWifiDirectServiceSupported(); + bool IsWifiDirectSupported(); bool IsIdle() { return medium_status_ == kMediumStatusIdle; } // Advertiser is accepting connection on server socket bool IsAccepting() { return (medium_status_ & kMediumStatusAccepting) != 0; } - // Advertiser started WifiDirect GO - bool IsGOStarted() { - return (medium_status_ & kMediumStatusGOStarted) != 0; - } - // Discoverer is connecting with the WifiDirect + // GO is started and sending beacon + bool IsBeaconing() { return (medium_status_ & kMediumStatusBeaconing) != 0; } + // GC is connecting to the GO bool IsConnecting() { return (medium_status_ & kMediumStatusConnecting) != 0; } - // Discoverer is connected with the WifiDirect + // GC is connected to the GO bool IsConnected() { return (medium_status_ & kMediumStatusConnected) != 0; } - // Converts WiFiDirectServiceConfigurationMethod enum to a string. - static std::string ConfigMethodToString( - WiFiDirectServiceConfigurationMethod config_method); + // Advertising properties + WiFiDirectAdvertisementPublisher publisher_{nullptr}; + WiFiDirectConnectionListener listener_{nullptr}; + WiFiDirectDevice wifi_direct_device_{nullptr}; - WiFiDirectServiceAdvertiser advertiser_ = nullptr; - WiFiDirectService service_ = nullptr; - WiFiDirectServiceSession session_ = nullptr; - winrt::Windows::System::DispatcherQueueController controller_ = nullptr; - winrt::Windows::System::DispatcherQueue dispatcher_queue_ = nullptr; - DeviceInformation device_info_ = nullptr; + fire_and_forget OnStatusChanged( + WiFiDirectAdvertisementPublisher sender, + WiFiDirectAdvertisementPublisherStatusChangedEventArgs event); + event_token publisher_status_changed_token_; - fire_and_forget OnAdvertisementStatusChanged( - WiFiDirectServiceAdvertiser sender, IInspectable const& event); - fire_and_forget OnAutoAcceptSessionConnected( - WiFiDirectServiceAdvertiser sender, - WiFiDirectServiceAutoAcceptSessionConnectedEventArgs const& args); - fire_and_forget OnSessionRequested( - WiFiDirectServiceAdvertiser const& sender, - WiFiDirectServiceSessionRequestedEventArgs const& args); + fire_and_forget OnConnectionRequested( + WiFiDirectConnectionListener const& sender, + WiFiDirectConnectionRequestedEventArgs const& event); + event_token connection_requested_token_; - event_token advertisement_status_changed_token_; - event_token auto_accept_session_connected_token_; - event_token session_requested_token_; + bool IsAepPaired(winrt::hstring device_id); // Discovery properties DeviceWatcher device_watcher_{nullptr}; @@ -280,24 +314,43 @@ class WifiDirectMedium : public api::WifiDirectMedium { DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate); fire_and_forget Watcher_DeviceRemoved( DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate); - fire_and_forget Watcher_DeviceEnumerationCompleted(DeviceWatcher sender, - IInspectable inspectable); - fire_and_forget Watcher_DeviceStopped(DeviceWatcher sender, - IInspectable inspectable); + fire_and_forget Watcher_DeviceEnumerationCompleted( + DeviceWatcher sender, IInspectable inspectable); + fire_and_forget Watcher_DeviceStopped( + DeviceWatcher sender, IInspectable inspectable); + + fire_and_forget OnPairingRequested( + DeviceInformationCustomPairing const& sender, + DevicePairingRequestedEventArgs const& e); + void OnConnectionStatusChanged( + WiFiDirectDevice const& sender, + winrt::Windows::Foundation::IInspectable const& e); + // IAsyncOperation RequestPairDeviceAsync( + bool RequestPairDeviceAsync(DeviceInformationPairing pairing, + int group_owner_intent, + WiFiDirectConfigurationMethod config_method); + + std::unique_ptr connection_latch_; + absl::Mutex mutex_; + + absl::flat_hash_map> + discovered_devices_by_id_; + + absl::flat_hash_map> + connection_requested_devices_by_id_; bool is_interface_valid_ = false; WifiDirectCredentials* credentials_go_ = nullptr; WifiDirectCredentials credentials_gc_; std::string ip_address_local_; std::string ip_address_remote_; - - absl::Mutex mutex_; absl::CondVar is_ip_address_ready_; - // Keep the server socket listener pointer + WifiDirectServerSocket* server_socket_ptr_ ABSL_GUARDED_BY(mutex_) = nullptr; SubmittableExecutor listener_executor_; }; - } // namespace nearby::windows #endif // PLATFORM_IMPL_WINDOWS_WIFI_DIRECT_H_ diff --git a/internal/platform/implementation/windows/wifi_direct_medium.cc b/internal/platform/implementation/windows/wifi_direct_medium.cc index 1783c3c9..7adc12eb 100644 --- a/internal/platform/implementation/windows/wifi_direct_medium.cc +++ b/internal/platform/implementation/windows/wifi_direct_medium.cc @@ -14,72 +14,60 @@ #include #include +#include #include +#include #include -#include #include #include -#include "absl/strings/str_format.h" +#include "absl/strings/ascii.h" +#include "absl/strings/match.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" +#include "absl/time/clock.h" #include "absl/time/time.h" #include "internal/flags/nearby_flags.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/cancellation_flag_listener.h" +#include "internal/platform/count_down_latch.h" #include "internal/platform/feature_flags.h" #include "internal/platform/flags/nearby_platform_feature_flags.h" #include "internal/platform/implementation/wifi_direct.h" +#include "internal/platform/implementation/windows/device_info.h" +#include "internal/platform/implementation/windows/generated/winrt/base.h" #include "internal/platform/implementation/windows/socket_address.h" -#include "internal/platform/implementation/windows/utils.h" #include "internal/platform/implementation/windows/wifi_direct.h" #include "internal/platform/logging.h" -#include "internal/platform/prng.h" #include "internal/platform/wifi_credential.h" -namespace nearby { -namespace windows { +namespace nearby::windows { + namespace { -constexpr int kWaitingForConnectionTimeoutSeconds = 90; // seconds +constexpr absl::Duration kServiceConnectionTimeout = absl::Seconds(60); +constexpr absl::Duration kWaitingForRePair = absl::Seconds(3); +constexpr absl::Duration kWaitForGOServerStart = absl::Milliseconds(500); +constexpr absl::Duration kConnectTimeout = absl::Seconds(30); } // namespace +WifiDirectDeviceDiscovered::WifiDirectDeviceDiscovered( + const DeviceInformation& device_info) + : windows_wifi_direct_device_(device_info) { + id_ = winrt::to_string(device_info.Id()); +} + +// WifiDirectDeviceDiscovered::~WifiDirectDeviceDiscovered() {} WifiDirectMedium::WifiDirectMedium() { - LOG(INFO) << "WifiDirectMedium::WifiDirectMedium"; - // Create a DispatcherQueue for this thread. - controller_ = winrt::Windows::System::DispatcherQueueController:: - CreateOnDedicatedThread(); - dispatcher_queue_ = controller_.DispatcherQueue(); - if (!dispatcher_queue_) { - LOG(WARNING) << "Failed to get DispatcherQueue for current thread. " - "ConnectAsync might fail if not called from UI thread."; - } - is_interface_valid_ = IsWifiDirectServiceSupported(); + is_interface_valid_ = IsWifiDirectSupported(); } WifiDirectMedium::~WifiDirectMedium() { is_interface_valid_ = false; - listener_executor_.Shutdown(); StopWifiDirect(); DisconnectWifiDirect(); - if (controller_) { - // Asynchronously shut down the dispatcher queue. - winrt::Windows::Foundation::IAsyncAction shutdown_async = - controller_.ShutdownQueueAsync(); - - // Block and wait for the shutdown to complete. This ensures that any - // in-progress event handlers on the dedicated thread are finished - // before this object is fully destroyed. - shutdown_async.get(); - } } -bool WifiDirectMedium::IsWifiDirectServiceSupported() { - if (!IsIntelWifiAdapter()) { - LOG(INFO) << "Intel Wifi adapter is not found, WifiDirectService is not " - "supported."; - return false; - } - +bool WifiDirectMedium::IsWifiDirectSupported() { HANDLE wifi_direct_handle = nullptr; DWORD negotiated_version = 0; DWORD result = 0; @@ -95,9 +83,7 @@ bool WifiDirectMedium::IsWifiDirectServiceSupported() { return true; } -bool WifiDirectMedium::IsInterfaceValid() const { - return is_interface_valid_; -} +bool WifiDirectMedium::IsInterfaceValid() const { return is_interface_valid_; } // Discoverer connects to server socket std::unique_ptr WifiDirectMedium::ConnectToService( @@ -110,27 +96,9 @@ std::unique_ptr WifiDirectMedium::ConnectToService( return nullptr; } - std::string remote_ip_address; - if (ip_address.empty()) { - remote_ip_address = ip_address_remote_; - } else { - remote_ip_address = std::string(ip_address); - } - // when this API is called, GC may not finish connecting to GO, so we need to - // wait the connection is finished and IP address is ready. - if (remote_ip_address.empty()) { - LOG(INFO) << "Waiting for IP address to be ready."; - absl::MutexLock lock(mutex_); - is_ip_address_ready_.WaitWithTimeout( - &mutex_, absl::Seconds(kWaitingForConnectionTimeoutSeconds)); - if (ip_address_remote_.empty()) { - LOG(WARNING) - << "IP address is still empty, probably GC connecting to GO failed."; - return nullptr; - } - LOG(INFO) << "IP address is ready."; - remote_ip_address = ip_address_remote_; - } + LOG(INFO) << "Remote gateway: " << ip_address << ", Port: " << port; + + std::string remote_ip_address = ip_address_remote_; if (remote_ip_address.empty() || port == 0) { LOG(ERROR) << "no valid service address and port to connect: " @@ -139,11 +107,11 @@ std::unique_ptr WifiDirectMedium::ConnectToService( } SocketAddress server_address; - if (!server_address.FromString(server_address, remote_ip_address, port)) { + if (!SocketAddress::FromString(server_address, remote_ip_address, port)) { LOG(ERROR) << "no valid service address and port to connect."; return nullptr; } - VLOG(1) << "ConnectToService server address: " << server_address.ToString(); + LOG(INFO) << "ConnectToService server address: " << server_address.ToString(); // Try connecting to the service up to wifi_direct_max_connection_retries, // because it may fail first time if DHCP procedure is not finished yet. @@ -160,12 +128,16 @@ std::unique_ptr WifiDirectMedium::ConnectToService( platform::config_package_nearby::nearby_platform_feature:: kWifiHotspotConnectionTimeoutMillis); - VLOG(1) << "maximum connection retries=" << wifi_direct_max_connection_retries - << ", connection interval=" << wifi_direct_retry_interval_millis - << "ms, connection timeout=" - << wifi_direct_client_socket_connect_timeout_millis << "ms"; + LOG(INFO) << "maximum connection retries=" + << wifi_direct_max_connection_retries + << ", connection interval=" << wifi_direct_retry_interval_millis + << "ms, connection timeout=" + << wifi_direct_client_socket_connect_timeout_millis << "ms"; LOG(INFO) << "Connect to service "; + // In the test, GO server takes longer to started, so wait for 500ms before + // trying to connect to the service. + absl::SleepFor(kWaitForGOServerStart); for (int i = 0; i < wifi_direct_max_connection_retries; ++i) { auto wifi_direct_socket = std::make_unique(); @@ -187,14 +159,13 @@ std::unique_ptr WifiDirectMedium::ConnectToService( } bool result = wifi_direct_socket->Connect(server_address); - if (!result) { + if (result) { + LOG(INFO) << "connected to remote service "; + return wifi_direct_socket; + } else { LOG(WARNING) << "reconnect to service at " << (i + 1) << "th times"; Sleep(wifi_direct_retry_interval_millis); - continue; } - - LOG(INFO) << "connected to remote service "; - return wifi_direct_socket; } LOG(ERROR) << "Failed to connect to service "; @@ -208,7 +179,7 @@ std::unique_ptr WifiDirectMedium::ListenForService( << " :Start to listen connection from WiFiDirect client."; absl::MutexLock lock(mutex_); - if (!IsGOStarted()) { + if (!IsBeaconing()) { LOG(WARNING) << "WifiDirect GO is not started, skip."; return nullptr; } @@ -231,8 +202,8 @@ std::unique_ptr WifiDirectMedium::ListenForService( if (ip_address_local_.empty()) { if (server_socket_ptr_) { LOG(INFO) << "Waiting for IP address is ready."; - is_ip_address_ready_.WaitWithTimeout( - &mutex_, absl::Seconds(kWaitingForConnectionTimeoutSeconds)); + is_ip_address_ready_.WaitWithTimeout(&mutex_, + kServiceConnectionTimeout); if (!server_socket_ptr_) { LOG(WARNING) << "Server socket was closed before IP address is ready."; @@ -252,8 +223,7 @@ std::unique_ptr WifiDirectMedium::ListenForService( if (port == 0) { port = FeatureFlags::GetInstance().GetFlags().wifi_direct_default_port; } - if (server_socket_ptr_ && - server_socket_ptr_->Listen(port)) { + if (server_socket_ptr_ && server_socket_ptr_->Listen(port)) { medium_status_ |= kMediumStatusAccepting; // Setup close notifier after listen started. @@ -262,6 +232,7 @@ std::unique_ptr WifiDirectMedium::ListenForService( LOG(INFO) << "Server socket was closed."; medium_status_ &= (~kMediumStatusAccepting); server_socket_ptr_ = nullptr; + is_ip_address_ready_.SignalAll(); }); LOG(INFO) << "Started to listen serive on port " << server_socket_ptr_->GetPort(); @@ -275,466 +246,489 @@ std::unique_ptr WifiDirectMedium::ListenForService( }); LOG(INFO) << "Started to listen service on port " << port; - return server_socket; } bool WifiDirectMedium::StartWifiDirect( WifiDirectCredentials* wifi_direct_credentials) { - LOG(INFO) << "WifiDirectMedium::StartWifiDirect"; - absl::MutexLock lock(mutex_); - if (IsGOStarted()) { - LOG(WARNING) << "Already started WifiDirect GO, skip."; + LOG(INFO) << __func__ << ": Start to create WiFiDirect."; + if (IsBeaconing()) { + LOG(WARNING) << "Cannot create WiFiDirect GO again when it is running."; return true; } - - credentials_go_ = wifi_direct_credentials; - Prng prng; - std::string pin = absl::StrFormat("%04x", prng.NextUint32()); - credentials_go_->SetPin(pin); - - std::string service_name = "NC-" + std::to_string(prng.NextUint32()); - credentials_go_->SetServiceName(service_name); - LOG(INFO) << "service_name:pin " << service_name << ":" << pin; - - // Create Advertiser object - advertiser_ = WiFiDirectServiceAdvertiser(winrt::to_hstring(service_name)); - advertisement_status_changed_token_ = advertiser_.AdvertisementStatusChanged( - {this, &WifiDirectMedium::OnAdvertisementStatusChanged}); - auto_accept_session_connected_token_ = advertiser_.AutoAcceptSessionConnected( - {this, &WifiDirectMedium::OnAutoAcceptSessionConnected}); - session_requested_token_ = advertiser_.SessionRequested( - {this, &WifiDirectMedium::OnSessionRequested}); - - advertiser_.AutoAcceptSession(false); - advertiser_.PreferGroupOwnerMode(true); - advertiser_.ServiceStatus(WiFiDirectServiceStatus::Available); - // Config Methods - WiFiDirectServiceConfigurationMethod config_method; - if (pin.empty()) { - config_method = WiFiDirectServiceConfigurationMethod::Default; // NOLINT - } else { - config_method = WiFiDirectServiceConfigurationMethod::PinDisplay; - } - advertiser_.PreferredConfigurationMethods().Clear(); - advertiser_.PreferredConfigurationMethods().Append(config_method); - try { - advertiser_.Start(); - LOG(INFO) << "Start WifiDirect GO Status: " - << (int)advertiser_.AdvertisementStatus(); - if ((advertiser_.AdvertisementStatus() == - WiFiDirectServiceAdvertisementStatus::Created) || - (advertiser_.AdvertisementStatus() == - WiFiDirectServiceAdvertisementStatus::Started)) { - medium_status_ |= kMediumStatusGOStarted; - return true; + publisher_ = WiFiDirectAdvertisementPublisher(); + publisher_status_changed_token_ = + publisher_.StatusChanged({this, &WifiDirectMedium::OnStatusChanged}); + listener_ = WiFiDirectConnectionListener(); + connection_requested_token_ = listener_.ConnectionRequested( + {this, &WifiDirectMedium::OnConnectionRequested}); + // Normal mode: The device is highly discoverable so long as the app is in + // the foreground. + publisher_.Advertisement().ListenStateDiscoverability( + WiFiDirectAdvertisementListenStateDiscoverability::Normal); + // Enable Autonomous GO mode + publisher_.Advertisement().IsAutonomousGroupOwnerEnabled(true); + + publisher_.Start(); + if (publisher_.Status() == + WiFiDirectAdvertisementPublisherStatus::Started) { + LOG(INFO) << "Windows WIFI Direct AutoGO started"; + medium_status_ |= kMediumStatusBeaconing; + + std::optional computer_name = DeviceInfo().GetOsDeviceName(); + if (computer_name.has_value()) { + std::string device_name = absl::AsciiStrToUpper(computer_name.value()); + LOG(INFO) << "GO Device Name(Computer Name) is:" << device_name; + credentials_go_ = wifi_direct_credentials; + // Current pairing scheme uses ConfirmOnly, so pin is empty; + credentials_go_->SetPin(""); + credentials_go_->SetDeviceName(device_name); + return true; + } + LOG(ERROR) << "Windows WIFI Direct AutoGO failed to get computer name"; } - LOG(ERROR) << "Start WifiDirect GO failed."; - return false; + LOG(ERROR) << "Windows WIFI Direct AutoGO fails to start"; } catch (std::exception exception) { - LOG(ERROR) << __func__ << ": Start WifiDirect GO failed. Exception: " + LOG(ERROR) << __func__ << ": Cannot start WifiDirect GO. Exception: " << exception.what(); } catch (const winrt::hresult_error& error) { - LOG(ERROR) << __func__ << ": Start WifiDirect GO failed. WinRT exception: " + LOG(ERROR) << __func__ << ": Cannot start WifiDirect GO. WinRT exception: " << error.code() << ": " << winrt::to_string(error.message()); } catch (...) { - LOG(ERROR) << __func__ << ": Unknown exeption."; + LOG(ERROR) << __func__ << ": Unknown exception."; } - advertiser_.AdvertisementStatusChanged(advertisement_status_changed_token_); - advertiser_.AutoAcceptSessionConnected(auto_accept_session_connected_token_); - advertiser_.SessionRequested(session_requested_token_); - advertiser_.PreferredConfigurationMethods().Clear(); - advertiser_ = nullptr; + + if (listener_) { + listener_.ConnectionRequested(connection_requested_token_); + } + if (publisher_) { + publisher_.StatusChanged(publisher_status_changed_token_); + } + listener_ = nullptr; + publisher_ = nullptr; return false; } bool WifiDirectMedium::StopWifiDirect() { - LOG(INFO) << "WifiDirectMedium::StopWifiDirect"; - absl::MutexLock lock(mutex_); - if (!IsGOStarted()) { - LOG(WARNING) << "Cannot stop Service because no Service is started."; - return true; + std::vector> devices; + { + absl::MutexLock lock(mutex_); + devices.reserve(connection_requested_devices_by_id_.size()); + for (auto& [id, device] : connection_requested_devices_by_id_) { + devices.push_back(std::move(device)); + } + connection_requested_devices_by_id_.clear(); } - try { - if (advertiser_) { - advertiser_.Stop(); - advertiser_.AdvertisementStatusChanged( - advertisement_status_changed_token_); - advertiser_.AutoAcceptSessionConnected( - auto_accept_session_connected_token_); - advertiser_.SessionRequested(session_requested_token_); - advertiser_ = nullptr; - device_info_ = nullptr; - session_ = nullptr; + for (auto& device : devices) { + LOG(INFO) << "Unpair WifiDirect GC: " << device->GetId(); + DeviceInformationPairing pairing = device->GetDeviceInformation().Pairing(); + if (pairing.IsPaired()) { + LOG(INFO) << "GC Paired, unpair it"; + DeviceUnpairingResult unpairing_result = pairing.UnpairAsync().get(); + LOG(INFO) << "GC Unpair result:" + << static_cast(unpairing_result.Status()); + if (unpairing_result.Status() == DeviceUnpairingResultStatus::Unpaired) { + LOG(INFO) << "GC Unpaired successfully"; + } else { + LOG(INFO) << "GC Unpair failed"; + } + } else { + LOG(INFO) << "GC Not Paired, skip"; } - medium_status_ &= (~kMediumStatusGOStarted); + } + + absl::MutexLock lock(mutex_); + is_ip_address_ready_.SignalAll(); + + if (!IsBeaconing()) { + LOG(WARNING) + << "Cannot stop advertising because no advertising is running."; + return true; + } + try { + if (publisher_) { + publisher_.Stop(); + listener_.ConnectionRequested(connection_requested_token_); + publisher_.StatusChanged(publisher_status_changed_token_); + wifi_direct_device_ = nullptr; + listener_ = nullptr; + publisher_ = nullptr; + LOG(INFO) << "succeeded to stop WIFI advertising"; + } + medium_status_ &= (~kMediumStatusBeaconing); medium_status_ &= (~kMediumStatusConnected); medium_status_ &= (~kMediumStatusAccepting); server_socket_ptr_ = nullptr; ip_address_local_.clear(); ip_address_remote_.clear(); return true; - } catch (std::exception exception) { + } catch (const std::exception& exception) { LOG(ERROR) << __func__ << ": Stop WifiDirect GO failed. Exception: " << exception.what(); } catch (const winrt::hresult_error& error) { LOG(ERROR) << __func__ << ": Stop WifiDirect GO failed. WinRT exception: " << error.code() << ": " << winrt::to_string(error.message()); } catch (...) { - LOG(ERROR) << __func__ << ": Unknown exeption."; + LOG(ERROR) << __func__ << ": Unknown exception."; } return false; } -std::string WifiDirectMedium::ConfigMethodToString( - WiFiDirectServiceConfigurationMethod config_method) { - switch (config_method) { - case WiFiDirectServiceConfigurationMethod::Default: - return "Default"; - case WiFiDirectServiceConfigurationMethod::PinDisplay: - return "PinDisplay"; - case WiFiDirectServiceConfigurationMethod::PinEntry: - return "PinEntry"; - default: - return "Unknown"; +fire_and_forget WifiDirectMedium::OnStatusChanged( + WiFiDirectAdvertisementPublisher sender, + WiFiDirectAdvertisementPublisherStatusChangedEventArgs event) { + LOG(INFO) << "WIFI direct PublisherStatusChangedEvent: " + << static_cast(event.Status()); + if (event.Status() == WiFiDirectAdvertisementPublisherStatus::Started) { + LOG(INFO) << "Receive WiFi direct/SoftAP Started event."; + if (sender.Advertisement().LegacySettings().IsEnabled()) { + LOG(INFO) << "WIFI direct Legacy AP ssid: " + << winrt::to_string( + publisher_.Advertisement().LegacySettings().Ssid()); + LOG(INFO) << "WIFI direct Legacy AP pw: " + << winrt::to_string(publisher_.Advertisement() + .LegacySettings() + .Passphrase() + .Password()); + } + return winrt::fire_and_forget(); + } else if (event.Status() == + WiFiDirectAdvertisementPublisherStatus::Created) { + LOG(INFO) << "Receive WiFi direct/SoftAP Created event."; + return winrt::fire_and_forget(); + } else if (event.Status() == + WiFiDirectAdvertisementPublisherStatus::Stopped) { + LOG(INFO) << "Receive WiFi direct/SoftAP Stopped event."; + } else if (event.Status() == + WiFiDirectAdvertisementPublisherStatus::Aborted) { + LOG(INFO) << "Receive WiFi direct/SoftAP Aborted event."; } -} - -fire_and_forget WifiDirectMedium::OnAdvertisementStatusChanged( - WiFiDirectServiceAdvertiser sender, IInspectable const& event) { - LOG(INFO) << "WiFiDirectServiceAdvertiser status changed: " - << (int)sender.AdvertisementStatus(); - auto status = sender.ServiceStatus(); - switch (status) { - case WiFiDirectServiceStatus ::Available: - LOG(INFO) << "WifiDirectAdvertiser service status changed: " - "status: Available"; - break; - case WiFiDirectServiceStatus ::Busy: - LOG(INFO) << "WifiDirectAdvertiser service status changed: " - "status: Busy"; - break; - case WiFiDirectServiceStatus ::Custom: - LOG(INFO) << "WifiDirectAdvertiser service status changed: " - "status: Custom"; - break; - default: - LOG(INFO) << "WifiDirectAdvertiser service status changed: " - "Code: " - << (int)status; - break; + // Publisher is stopped. Need to clean up the publisher. + { + absl::MutexLock lock(mutex_); + if (publisher_ != nullptr) { + LOG(ERROR) << "Windows WiFi Direct cleanup."; + listener_.ConnectionRequested(connection_requested_token_); + publisher_.StatusChanged(publisher_status_changed_token_); + wifi_direct_device_ = nullptr; + listener_ = nullptr; + publisher_ = nullptr; + medium_status_ &= (~kMediumStatusBeaconing); + } } return winrt::fire_and_forget(); } -fire_and_forget WifiDirectMedium::OnAutoAcceptSessionConnected( - WiFiDirectServiceAdvertiser sender, - WiFiDirectServiceAutoAcceptSessionConnectedEventArgs const& args) { - LOG(INFO) << "WifiDirectMedium::OnAutoAcceptSessionConnected"; - try { - auto session = args.Session(); - if (!session) { - LOG(ERROR) << "OnAutoAcceptSessionConnected returned null session"; - co_return; - } - session_ = std::move(session); - LOG(INFO) << "Service Address: " - << winrt::to_string(session_.ServiceAddress()) - << ", Service Name: " << winrt::to_string(session_.ServiceName()) - << ", Advertisement ID: " << session_.AdvertisementId() - << ", Session Address: " - << winrt::to_string(session_.SessionAddress()) - << ", Session ID: " << session_.SessionId(); - // Subscribe to events to prevent early teardown - session_.SessionStatusChanged([](auto const& s, auto const& e) { - LOG(INFO) << "GO: Session status changed"; - }); - co_return; - } catch (std::exception exception) { - LOG(ERROR) << __func__ - << ": Failed to get session. Exception: " << exception.what(); - } catch (const winrt::hresult_error& error) { - LOG(ERROR) << __func__ - << ": Failed to get session. WinRT exception: " << error.code() - << ": " << winrt::to_string(error.message()); - } catch (...) { - LOG(ERROR) << __func__ << ": Unknown exeption."; - } -} +fire_and_forget WifiDirectMedium::OnConnectionRequested( + WiFiDirectConnectionListener const& sender, + WiFiDirectConnectionRequestedEventArgs const& event) { + WiFiDirectConnectionRequest connection_request = event.GetConnectionRequest(); + winrt::hstring device_name = connection_request.DeviceInformation().Name(); + winrt::hstring device_id = connection_request.DeviceInformation().Id(); + LOG(INFO) << "Receive connection request from: " + << winrt::to_string(device_name) + << "; device ID: " << winrt::to_string(device_id); -fire_and_forget WifiDirectMedium::OnSessionRequested( - WiFiDirectServiceAdvertiser const& sender, - WiFiDirectServiceSessionRequestedEventArgs const& args) { - try { - auto request = args.GetSessionRequest(); - if (!request) { - LOG(ERROR) << "OnSessionRequested returned null session request"; - co_return; - } - device_info_ = request.DeviceInformation(); - LOG(INFO) << "GO: OnSessionRequested: " - << winrt::to_string(device_info_.Id()) - << " Is GroupFormationNeeded: " - << request.ProvisioningInfo().IsGroupFormationNeeded() - << ", SelectedConfigurationMethod: " - << ConfigMethodToString( - request.ProvisioningInfo().SelectedConfigurationMethod()); + DeviceInformation windows_device_info(connection_request.DeviceInformation()); + auto deviceInfoP = + std::make_unique(windows_device_info); - LOG(INFO) << "GO: Dispatch to UI thread to call ConnectAsync"; - dispatcher_queue_.TryEnqueue([this]() { - LOG(INFO) << "GO: TryEnqueue: calling ConnectAsync"; - - absl::MutexLock lock(mutex_); - WiFiDirectServiceSession session = nullptr; - auto pin = credentials_go_->GetPin(); - if (pin.empty()) { - session = advertiser_.ConnectAsync(device_info_).get(); // NOLINT - } else { - session = advertiser_.ConnectAsync(device_info_, winrt::to_hstring(pin)) - .get(); - } - LOG(INFO) << "GO: TryEnqueue: Wait for ConnectAsync finish"; - if (!session) { - LOG(ERROR) << "OnSessionRequested returned null session"; - return; - } - LOG(INFO) << "GO: TryEnqueue: OnSessionRequested: ConnectAsync succeeded"; - session_ = std::move(session); - - auto endpoint_pairs = session_.GetConnectionEndpointPairs(); - if (endpoint_pairs.Size() > 0) { - auto const& pair = endpoint_pairs.GetAt(0); - ip_address_local_ = - winrt::to_string(pair.LocalHostName().DisplayName()); - ip_address_remote_ = - winrt::to_string(pair.RemoteHostName().DisplayName()); - LOG(INFO) << "GO: Local IP: " << ip_address_local_ - << ", Remote IP: " << ip_address_remote_; - is_ip_address_ready_.SignalAll(); - } else { - LOG(WARNING) << "GO: No connection endpoint pairs found."; - } - medium_status_ |= kMediumStatusConnected; - - LOG(INFO) << "Service Address: " - << winrt::to_string(session_.ServiceAddress()) - << ", Service Name: " - << winrt::to_string(session_.ServiceName()) - << ", Advertisement ID: " << session_.AdvertisementId() - << ", Session Address: " - << winrt::to_string(session_.SessionAddress()) - << ", Session ID: " << session_.SessionId(); - // Subscribe to events to prevent early teardown - session_.SessionStatusChanged([](auto const& s, auto const& e) { - LOG(INFO) << "GO: TryEnqueue: Session status changed"; - }); - }); - LOG(INFO) << "GO: Dispatch to UI thread to call ConnectAsync finish"; - } catch (std::exception exception) { - LOG(ERROR) << __func__ - << ": Failed to get session. Exception: " << exception.what(); - } catch (const winrt::hresult_error& error) { - LOG(ERROR) << __func__ - << ": Failed to get session. WinRT exception: " << error.code() - << ": " << winrt::to_string(error.message()); - } catch (...) { - LOG(ERROR) << __func__ << ": Unknown exeption."; - } -} - -bool WifiDirectMedium::ConnectWifiDirect( - const WifiDirectCredentials& credentials) { - LOG(INFO) << "WifiDirectMedium::ConnectWifiDirect"; - absl::MutexLock lock(mutex_); - if (IsConnecting()) { - LOG(WARNING) << "Service discovery already running"; - return false; + { + absl::MutexLock lock(&mutex_); + connection_requested_devices_by_id_[device_id] = std::move(deviceInfoP); } - if (device_watcher_) { - LOG(WARNING) - << "Device Watcher has already been set, please investigate! Skip"; - return false; - } + bool is_paired = false; + DeviceInformationPairing pairing = + connection_request.DeviceInformation().Pairing(); + WiFiDirectConfigurationMethod config_method = + WiFiDirectConfigurationMethod::PushButton; - credentials_gc_ = credentials; - if (credentials_gc_.GetServiceName().empty()) { - LOG(ERROR) << "GC: Service name is empty, return false"; - return false; - } - winrt::hstring device_selector = WiFiDirectService::GetSelector( - winrt::to_hstring(credentials_gc_.GetServiceName())); - const winrt::param::iterable requested_properties = - winrt::single_threaded_vector({ - winrt::to_hstring("System.Devices.WiFiDirectServices.ServiceAddress"), - winrt::to_hstring("System.Devices.WiFiDirectServices.ServiceName"), - winrt::to_hstring( - "System.Devices.WiFiDirectServices.ServiceInformation"), - winrt::to_hstring( - "System.Devices.WiFiDirectServices.AdvertisementId"), - winrt::to_hstring( - "System.Devices.WiFiDirectServices.ServiceConfigMethods"), - }); - LOG(INFO) << "Create device watcher"; - device_watcher_ = - DeviceInformation::CreateWatcher(device_selector, requested_properties); - device_watcher_added_event_token_ = - device_watcher_.Added({this, &WifiDirectMedium::Watcher_DeviceAdded}); - device_watcher_updated_event_token_ = - device_watcher_.Updated({this, &WifiDirectMedium::Watcher_DeviceUpdated}); - device_watcher_removed_event_token_ = - device_watcher_.Removed({this, &WifiDirectMedium::Watcher_DeviceRemoved}); - device_watcher_enumeration_completed_event_token_ = - device_watcher_.EnumerationCompleted( - {this, &WifiDirectMedium::Watcher_DeviceEnumerationCompleted}); - device_watcher_stopped_event_token_ = - device_watcher_.Stopped({this, &WifiDirectMedium::Watcher_DeviceStopped}); - device_watcher_.Start(); - medium_status_ |= kMediumStatusConnecting; - LOG(INFO) << "Started to discover WifiDirect service and connect."; - return true; -} - -fire_and_forget WifiDirectMedium::Watcher_DeviceAdded( - DeviceWatcher sender, DeviceInformation device_info) { - LOG(INFO) << "Device Service founded for device ID " - << winrt::to_string(device_info.Id()) - << "; device name: " << winrt::to_string(device_info.Name()); - - auto props = device_info.Properties(); - if (props.HasKey(L"System.Devices.WiFiDirectServices.ServiceName")) { - winrt::hstring svc_name = winrt::unbox_value( - props.Lookup(L"System.Devices.WiFiDirectServices.ServiceName")); - LOG(INFO) << "Discovered service: " << winrt::to_string(svc_name); - } - try { - service_ = co_await WiFiDirectService::FromIdAsync(device_info.Id()); - if (!service_) { - LOG(ERROR) << "FromIdAsync returned null service"; - co_return; - } - LOG(INFO) << "GC: ConnectAsync in Watcher_DeviceAdded"; - service_.PreferGroupOwnerMode(false); - - WiFiDirectServiceSession session = nullptr; - auto pin = credentials_gc_.GetPin(); - if (pin.empty()) { - session = service_.ConnectAsync().get(); // NOLINT + if (pairing.IsPaired() || IsAepPaired(device_id)) { + if (pairing.IsPaired()) { + LOG(INFO) << "GO Paired"; } else { - auto prov_info = co_await service_.GetProvisioningInfoAsync( - WiFiDirectServiceConfigurationMethod::PinEntry); + LOG(INFO) << "GO Not Paired, but AEP is paired"; + } + LOG(INFO) << "GO already paired, unpair it first"; + DeviceUnpairingResult unpairing_result = pairing.UnpairAsync().get(); + LOG(INFO) << "GO Unpair result:" + << static_cast(unpairing_result.Status()); + if (unpairing_result.Status() == DeviceUnpairingResultStatus::Unpaired || + unpairing_result.Status() == + DeviceUnpairingResultStatus::AlreadyUnpaired) { + LOG(INFO) << "GO Unpaired GC, Re-pair"; + // Wait for kWaitingForRePair to allow WiFi driver to stabilize. + absl::SleepFor(kWaitingForRePair); + // Refresh device info after unpairing. + DeviceInformation refreshed_device_info = + DeviceInformation::CreateFromIdAsync(device_id).get(); + is_paired = RequestPairDeviceAsync(refreshed_device_info.Pairing(), 14, + config_method); + } else { + is_paired = true; + LOG(INFO) << "GO Unpair failed, skip pairing"; + } + } else { + LOG(INFO) << "GO trying to pair with GC"; + is_paired = RequestPairDeviceAsync(pairing, 14, config_method); + } - if (prov_info.IsGroupFormationNeeded()) { - LOG(INFO) << "GC: Group formation needed"; - } else { - LOG(INFO) << "GC: Group formation not needed"; - } - LOG(INFO) << "GC: SelectedConfigurationMethod: " - << ConfigMethodToString( - prov_info.SelectedConfigurationMethod()); - - session = service_.ConnectAsync(winrt::to_hstring(pin)).get(); + if (is_paired) { + WiFiDirectDevice device = nullptr; + try { + device = WiFiDirectDevice::FromIdAsync(device_id).get(); + } catch (winrt::hresult_error const& ex) { + LOG(ERROR) << __func__ << ": winrt exception: " << ex.code() << ": " + << winrt::to_string(ex.message()); + return winrt::fire_and_forget(); } - if (!session) { - LOG(ERROR) << "GC: ConnectAsync returned null session"; - co_return; - } - LOG(INFO) << "GC: ConnectAsync succeeded"; - session_ = std::move(session); + device.ConnectionStatusChanged( + {this, &WifiDirectMedium::OnConnectionStatusChanged}); + + IVectorView endpoint_pairs = + device.GetConnectionEndpointPairs(); - auto endpoint_pairs = session_.GetConnectionEndpointPairs(); if (endpoint_pairs.Size() > 0) { auto const& pair = endpoint_pairs.GetAt(0); - ip_address_local_ = winrt::to_string(pair.LocalHostName().DisplayName()); - ip_address_remote_ = + std::string local_ip = + winrt::to_string(pair.LocalHostName().DisplayName()); + std::string remote_ip = winrt::to_string(pair.RemoteHostName().DisplayName()); - LOG(INFO) << "GC: Local IP: " << ip_address_local_ + + absl::MutexLock lock(&mutex_); + wifi_direct_device_ = device; + ip_address_local_ = local_ip; + ip_address_remote_ = remote_ip; + LOG(INFO) << "GO: Local IP: " << ip_address_local_ << ", Remote IP: " << ip_address_remote_; - } else { - LOG(WARNING) << "GC: No connection endpoint pairs found."; - } - - LOG(INFO) << "Service Address: " - << winrt::to_string(session_.ServiceAddress()) - << ", Service Name: " << winrt::to_string(session_.ServiceName()) - << ", Advertisement ID: " << session_.AdvertisementId() - << ", Session Address: " - << winrt::to_string(session_.SessionAddress()) - << ", Session ID: " << session_.SessionId(); - { - absl::MutexLock lock(mutex_); is_ip_address_ready_.SignalAll(); + } else { + LOG(WARNING) << "GO: No connection endpoint pairs found."; } - medium_status_ |= kMediumStatusConnected; - - // Subscribe to events to prevent early teardown - session_.SessionStatusChanged([](auto const& s, auto const& e) { - LOG(INFO) << "GC: TryEnqueue: Session status changed"; - }); - } catch (std::exception exception) { - LOG(ERROR) << __func__ - << ": Failed to resolve WiFiDirectService from Id. Exception: " - << exception.what(); - } catch (const winrt::hresult_error& error) { - LOG(ERROR) - << __func__ - << ": Failed to resolve WiFiDirectService from Id. WinRT exception: " - << error.code() << ": " << winrt::to_string(error.message()); - } catch (...) { - LOG(ERROR) << __func__ << ": Unknown exeption."; } + return winrt::fire_and_forget(); } -fire_and_forget WifiDirectMedium::Watcher_DeviceUpdated( - DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) { - VLOG(1) << "WifiDirectMedium::Watcher_DeviceUpdated"; - return fire_and_forget(); +// In Windows, a single physical device can appear in the system as multiple +// different "objects" (e.g., a WiFi Direct object, a Bluetooth object, etc.). +// When a WiFi Direct connection request comes in, the code checks if Windows +// already has a "Paired" record for that physical MAC address under a different +// category. If it finds one, it considers the device "already known" to the +// system. The goal is to find and remove any stale pairing records that might +// cause the new WiFi Direct pairing to fail or hang. +bool WifiDirectMedium::IsAepPaired(winrt::hstring device_id) { + try { + DeviceInformation device_info = + DeviceInformation::CreateFromIdAsync( + device_id, {L"System.Devices.Aep.DeviceAddress"}) + .get(); + + auto properties = device_info.Properties(); + if (!properties.HasKey(L"System.Devices.Aep.DeviceAddress")) { + return false; + } + + winrt::hstring aep_device_address = winrt::unbox_value( + properties.Lookup(L"System.Devices.Aep.DeviceAddress")); + LOG(INFO) << "aep_device_address: " << winrt::to_string(aep_device_address); + if (aep_device_address.empty()) { + return false; + } + + winrt::hstring device_selector = + L"System.Devices.Aep.DeviceAddress:=\"" + aep_device_address + L"\""; + LOG(INFO) << "Finding devices with selector: " + << winrt::to_string(device_selector); + DeviceInformationCollection device_collection = + DeviceInformation::FindAllAsync(device_selector, + {L"System.Devices.Aep.IsPaired"}, + DeviceInformationKind::Device) + .get(); + + LOG(INFO) << "Found " << device_collection.Size() + << " devices with that MAC address."; + for (auto const& device : device_collection) { + LOG(INFO) << "Checking device: " << winrt::to_string(device.Name()) + << ", Id: " << winrt::to_string(device.Id()); + auto pairing = device.Pairing(); + if (pairing && pairing.IsPaired()) { + LOG(INFO) << "Device is paired."; + return true; + } + LOG(INFO) << "Device is not paired."; + } + return false; + } catch (std::exception exception) { + LOG(ERROR) << __func__ << " failed. Exception: " << exception.what(); + } catch (const winrt::hresult_error& error) { + LOG(ERROR) << __func__ << " failed. WinRT exception: " << error.code() + << ": " << winrt::to_string(error.message()); + } catch (...) { + LOG(ERROR) << __func__ << ": Unknown exception."; + } + return false; } -fire_and_forget WifiDirectMedium::Watcher_DeviceRemoved( - DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) { - LOG(INFO) << "WifiDirectMedium::Watcher_DeviceRemoved"; - return fire_and_forget(); -} +// Returns true once the WifiLan discovery has been initiated. +bool WifiDirectMedium::ConnectWifiDirect( + const WifiDirectCredentials& credentials) { + DisconnectWifiDirect(); + LOG(INFO) << "WifiDirectMedium::ConnectWifiDirect"; + { + absl::MutexLock lock(mutex_); + if (IsConnecting()) { + LOG(WARNING) << "GC discovery already running."; + return false; + } -fire_and_forget WifiDirectMedium::Watcher_DeviceEnumerationCompleted( - DeviceWatcher sender, IInspectable inspectable) { - LOG(INFO) << "WifiDirectMedium::Watcher_DeviceEnumerationCompleted"; - return fire_and_forget(); -} + if (IsBeaconing()) { + LOG(WARNING) << "Already acting as GO, skip discovery."; + return false; + } -fire_and_forget WifiDirectMedium::Watcher_DeviceStopped( - DeviceWatcher sender, IInspectable inspectable) { - medium_status_ &= (~kMediumStatusConnecting); - return fire_and_forget(); + if (device_watcher_) { + LOG(WARNING) + << "Device Watcher has already been set, please investigate! Skip"; + return false; + } + + credentials_gc_ = credentials; + if (credentials_gc_.GetDeviceName().empty()) { + LOG(ERROR) << "GC: Device name is empty, return false"; + return false; + } + + try { + discovered_devices_by_id_.clear(); + connection_requested_devices_by_id_.clear(); + winrt::hstring device_selector = WiFiDirectDevice::GetDeviceSelector( + WiFiDirectDeviceSelectorType::AssociationEndpoint); + const winrt::param::iterable requested_properties = + winrt::single_threaded_vector( + {winrt::to_hstring( + "System.Devices.WiFiDirect.InformationElements"), + winrt::to_hstring("System.Devices.Aep.CanPair"), + winrt::to_hstring("System.Devices.Aep.IsPaired")}); + device_watcher_ = DeviceInformation::CreateWatcher( + device_selector, requested_properties, + DeviceInformationKind::AssociationEndpoint); + device_watcher_added_event_token_ = + device_watcher_.Added({this, &WifiDirectMedium::Watcher_DeviceAdded}); + device_watcher_updated_event_token_ = device_watcher_.Updated( + {this, &WifiDirectMedium::Watcher_DeviceUpdated}); + device_watcher_removed_event_token_ = device_watcher_.Removed( + {this, &WifiDirectMedium::Watcher_DeviceRemoved}); + device_watcher_enumeration_completed_event_token_ = + device_watcher_.EnumerationCompleted( + {this, &WifiDirectMedium::Watcher_DeviceEnumerationCompleted}); + device_watcher_stopped_event_token_ = device_watcher_.Stopped( + {this, &WifiDirectMedium::Watcher_DeviceStopped}); + connection_latch_ = std::make_unique(1); + device_watcher_.Start(); + medium_status_ |= kMediumStatusConnecting; + } catch (const std::exception& exception) { + LOG(ERROR) << __func__ << " failed. Exception: " << exception.what(); + goto error; + } catch (const winrt::hresult_error& error) { + LOG(ERROR) << __func__ << " failed. WinRT exception: " << error.code() + << ": " << winrt::to_string(error.message()); + goto error; + } catch (...) { + LOG(ERROR) << __func__ << ": Unknown exception."; + goto error; + } + } + + LOG(INFO) << "Started to discover and wait " << kConnectTimeout + << " for connection."; + connection_latch_->Await(kConnectTimeout); + { + absl::MutexLock lock(mutex_); + if (IsConnected()) { + LOG(INFO) << "WifiDirectMedium::ConnectWifiDirect succeeded."; + return true; + } else { + LOG(WARNING) << "WifiDirectMedium::ConnectWifiDirect failed."; + } + } + +error: + { + absl::MutexLock lock(mutex_); + LOG(ERROR) << "GC discovery failed or pairing to GO failed."; + if (device_watcher_) { + device_watcher_.Stop(); + device_watcher_.Added(device_watcher_added_event_token_); + device_watcher_.Updated(device_watcher_updated_event_token_); + device_watcher_.Removed(device_watcher_removed_event_token_); + device_watcher_.EnumerationCompleted( + device_watcher_enumeration_completed_event_token_); + device_watcher_.Stopped(device_watcher_stopped_event_token_); + } + + device_watcher_ = nullptr; + medium_status_ &= (~kMediumStatusConnecting); + medium_status_ &= (~kMediumStatusConnected); + } + return false; } bool WifiDirectMedium::DisconnectWifiDirect() { - LOG(WARNING) << "Stop connecting."; - absl::MutexLock lock(mutex_); - if (!IsConnecting()) { - LOG(WARNING) << "no discovering service to stop."; - return false; + LOG(INFO) << "WifiDirectMedium::DisconnectWifiDirect"; + std::vector> devices; + { + absl::MutexLock lock(mutex_); + devices.reserve(discovered_devices_by_id_.size()); + for (auto& [id, device] : discovered_devices_by_id_) { + devices.push_back(std::move(device)); + } + discovered_devices_by_id_.clear(); } + + for (auto& device : devices) { + LOG(INFO) << "Unpair WifiDirect GO: " << device->GetId(); + DeviceInformationPairing pairing = device->GetDeviceInformation().Pairing(); + if (pairing.IsPaired()) { + LOG(INFO) << "GC Paired, unpair it"; + DeviceUnpairingResult unpairing_result = pairing.UnpairAsync().get(); + LOG(INFO) << "GC Unpair result:" + << static_cast(unpairing_result.Status()); + if (unpairing_result.Status() == DeviceUnpairingResultStatus::Unpaired) { + LOG(INFO) << "GC Unpaired successfully"; + } else { + LOG(INFO) << "GC Unpair failed"; + } + } else { + LOG(INFO) << "GC Not Paired, skip"; + } + } + + absl::MutexLock lock(mutex_); + if (!IsConnecting() && !IsConnected()) { + LOG(WARNING) << "WifiDirect GC is not connecting, skip"; + return true; + } + LOG(WARNING) << "Stop connecting."; try { - device_watcher_.Stop(); - device_watcher_.Added(device_watcher_added_event_token_); - device_watcher_.Updated(device_watcher_updated_event_token_); - device_watcher_.EnumerationCompleted( - device_watcher_enumeration_completed_event_token_); - device_watcher_.Removed(device_watcher_removed_event_token_); - device_watcher_.Stopped(device_watcher_stopped_event_token_); + if (device_watcher_) { + device_watcher_.Stop(); + device_watcher_.Added(device_watcher_added_event_token_); + device_watcher_.Updated(device_watcher_updated_event_token_); + device_watcher_.EnumerationCompleted( + device_watcher_enumeration_completed_event_token_); + device_watcher_.Removed(device_watcher_removed_event_token_); + device_watcher_.Stopped(device_watcher_stopped_event_token_); + device_watcher_ = nullptr; + ip_address_local_.clear(); + ip_address_remote_.clear(); + } medium_status_ &= (~kMediumStatusConnecting); medium_status_ &= (~kMediumStatusConnected); - device_watcher_ = nullptr; - service_ = nullptr; - session_ = nullptr; - ip_address_local_.clear(); - ip_address_remote_.clear(); return true; } catch (std::exception exception) { LOG(ERROR) << __func__ << ": Stop WifiDirect GC failed. Exception: " @@ -743,16 +737,199 @@ bool WifiDirectMedium::DisconnectWifiDirect() { LOG(ERROR) << __func__ << ": Stop WifiDirect GC failed. WinRT exception: " << error.code() << ": " << winrt::to_string(error.message()); } catch (...) { - LOG(ERROR) << __func__ << ": Unknown exeption."; + LOG(ERROR) << __func__ << ": Unknown exception."; } return false; } -std::vector -WifiDirectMedium::GetSupportedWifiDirectAuthTypes() const { - // Windows only supports WifiDirect with Service Discovery, which uses a PIN. - return {WifiDirectAuthType::WIFI_DIRECT_WITH_PIN}; +fire_and_forget WifiDirectMedium::Watcher_DeviceAdded( + DeviceWatcher sender, DeviceInformation device_info) { + LOG(INFO) << "Device found for device ID " + << winrt::to_string(device_info.Id()) + << "; device name: " << winrt::to_string(device_info.Name()); + winrt::hstring device_id = device_info.Id(); + { + absl::MutexLock lock(&mutex_); + if (discovered_devices_by_id_.contains(device_id)) { + return winrt::fire_and_forget(); + } + std::string device_name_to_match = credentials_gc_.GetDeviceName(); + if (!absl::EqualsIgnoreCase(device_name_to_match, + winrt::to_string(device_info.Name()))) { + LOG(INFO) << "We are looking for device: " << device_name_to_match + << ", but found: " << winrt::to_string(device_info.Name()) + << ", skip."; + return winrt::fire_and_forget(); + } + discovered_devices_by_id_[device_id] = + std::make_unique(device_info); + } + LOG(INFO) << "Connect to device name: " + << winrt::to_string(device_info.Name()); + DeviceInformationPairing pairing = device_info.Pairing(); + // WiFiDirectConfigurationMethod config_method = + // WiFiDirectConfigurationMethod::ProvidePin; + WiFiDirectConfigurationMethod config_method = + WiFiDirectConfigurationMethod::PushButton; + bool is_paired; + if (pairing.IsPaired()) { + LOG(INFO) << "GC Paired, unpair it first to clean up stale state"; + DeviceUnpairingResult unpairing_result = pairing.UnpairAsync().get(); + LOG(INFO) << "GC Unpair result: " + << static_cast(unpairing_result.Status()); + if (unpairing_result.Status() == DeviceUnpairingResultStatus::Unpaired || + unpairing_result.Status() == + DeviceUnpairingResultStatus::AlreadyUnpaired) { + // Wait kWaitingForRePair for the device stabilize before re-pairing. + // This may avoid the possible contention problems in Intel WiFi driver. + absl::SleepFor(kWaitingForRePair); + DeviceInformation refreshed_device_info = + DeviceInformation::CreateFromIdAsync(device_id).get(); + is_paired = RequestPairDeviceAsync(refreshed_device_info.Pairing(), 1, + config_method); + LOG(INFO) << "GC Re-Paired after unpair: " << is_paired; + } else { + LOG(INFO) << "GC Unpair failed, assume it's still paired."; + is_paired = + true; // Fallback to true if unpair fails, maybe it's still usable. + } + } else { + LOG(INFO) << "GC Not Paired, start to pair"; + is_paired = RequestPairDeviceAsync(device_info.Pairing(), 1, config_method); + } + // Create a WiFiDirectDevice out of this id + if (!is_paired) { + LOG(INFO) << "GC paired failed!"; + absl::MutexLock lock(&mutex_); + if (connection_latch_) { + connection_latch_->CountDown(); + } + return fire_and_forget(); + } + WiFiDirectDevice::FromIdAsync(device_info.Id()) + .Completed( + [this, device_info]( + IAsyncOperation wifidirectDevice, + AsyncStatus status) { + absl::MutexLock lock(mutex_); + WiFiDirectDevice(wifidirectDevice.get()) + .ConnectionStatusChanged( + {this, &WifiDirectMedium::OnConnectionStatusChanged}); + IVectorView endpoint_pairs = + WiFiDirectDevice(wifidirectDevice.get()) + .GetConnectionEndpointPairs(); + if (endpoint_pairs.Size() > 0) { + auto const& pair = endpoint_pairs.GetAt(0); + ip_address_local_ = + winrt::to_string(pair.LocalHostName().DisplayName()); + ip_address_remote_ = + winrt::to_string(pair.RemoteHostName().DisplayName()); + LOG(INFO) << "GC: Local IP: " << ip_address_local_ + << ", Remote IP: " << ip_address_remote_; + medium_status_ |= kMediumStatusConnected; + if (connection_latch_) { + connection_latch_->CountDown(); + } + } else { + LOG(WARNING) << "GC: No connection endpoint pairs found."; + } + }); + return fire_and_forget(); } -} // namespace windows -} // namespace nearby +fire_and_forget WifiDirectMedium::Watcher_DeviceUpdated( + DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) { + LOG(INFO) << "device updated for device ID " + << winrt::to_string(deviceInfoUpdate.Id()); + return fire_and_forget(); +} + +fire_and_forget WifiDirectMedium::Watcher_DeviceRemoved( + DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) { + LOG(INFO) << "device removed for device ID " + << winrt::to_string(deviceInfoUpdate.Id()); + return fire_and_forget(); +} + +fire_and_forget WifiDirectMedium::Watcher_DeviceEnumerationCompleted( + DeviceWatcher sender, IInspectable inspectable) { + LOG(INFO) << "DeviceWatcher enumeration completed!"; + return fire_and_forget(); +} + +fire_and_forget WifiDirectMedium::Watcher_DeviceStopped( + DeviceWatcher sender, IInspectable inspectable) { + LOG(INFO) << "DeviceWatcher stopped!"; + return fire_and_forget(); +} + +fire_and_forget WifiDirectMedium::OnPairingRequested( + DeviceInformationCustomPairing const& sender, + DevicePairingRequestedEventArgs const& event) { + LOG(INFO) << "Handle Pairing Kind"; + switch (event.PairingKind()) { + case DevicePairingKinds::DisplayPin: + LOG(INFO) << "Display pin is: " << winrt::to_string(event.Pin()); + event.Accept(); + break; + case DevicePairingKinds::ConfirmOnly: + LOG(INFO) << "DevicePairingKinds::ConfirmOnly"; + event.Accept(); + break; + case DevicePairingKinds::ProvidePin: { + absl::MutexLock lock(mutex_); + std::string pin; + LOG(INFO) << "Enter pin:"; + std::cin >> pin; + LOG(INFO) << "DevicePairingKinds::ProvidePin:" << pin; + event.Accept(winrt::to_hstring(pin)); + } break; + default: + LOG(INFO) << "DevicePairingKinds::" + << static_cast(event.PairingKind()); + break; + } + return winrt::fire_and_forget(); +} +void WifiDirectMedium::OnConnectionStatusChanged( + WiFiDirectDevice const& sender, + winrt::Windows::Foundation::IInspectable const&) { + LOG(INFO) << "Connection status: " + << static_cast(sender.ConnectionStatus()); +} +bool WifiDirectMedium::RequestPairDeviceAsync( + DeviceInformationPairing pairing, int group_owner_intent, + WiFiDirectConfigurationMethod config_method) { + LOG(INFO) << __func__ << " Group Intent: " << group_owner_intent; + WiFiDirectConnectionParameters connectionParams; + connectionParams.GroupOwnerIntent(group_owner_intent); + connectionParams.PreferenceOrderedConfigurationMethods().Append( + config_method); + DevicePairingKinds devicePairingKinds = + WiFiDirectConnectionParameters::GetDevicePairingKinds(config_method); + LOG(INFO) << "DevicePairingKinds: " << static_cast(devicePairingKinds); + connectionParams.PreferredPairingProcedure( + WiFiDirectPairingProcedure::Invitation); + DeviceInformationCustomPairing customPairing = pairing.Custom(); + customPairing.PairingRequested({this, &WifiDirectMedium::OnPairingRequested}); + DevicePairingResult result = + customPairing + .PairAsync(devicePairingKinds, DevicePairingProtectionLevel::Default, + connectionParams) + .get(); + if (result.Status() != DevicePairingResultStatus::Paired && + result.Status() != DevicePairingResultStatus::AlreadyPaired) { + LOG(INFO) << "Pair result: " << static_cast(result.Status()); + return false; + } + LOG(INFO) << "Pair success "; + return true; +} + +std::vector +WifiDirectMedium::GetSupportedWifiDirectAuthTypes() const { + // Windows only supports WifiDirect with Device Name Discovery. + return {WifiDirectAuthType::WIFI_DIRECT_WITH_DEVICE_NAME}; +} + +} // namespace nearby::windows diff --git a/internal/platform/implementation/windows/wifi_direct_server_socket.cc b/internal/platform/implementation/windows/wifi_direct_server_socket.cc index c32b4b5c..c72a5014 100644 --- a/internal/platform/implementation/windows/wifi_direct_server_socket.cc +++ b/internal/platform/implementation/windows/wifi_direct_server_socket.cc @@ -25,6 +25,7 @@ #include "internal/platform/exception.h" #include "internal/platform/feature_flags.h" #include "internal/platform/implementation/wifi_direct.h" +#include "internal/platform/implementation/windows/network_info.h" #include "internal/platform/implementation/windows/socket_address.h" #include "internal/platform/implementation/windows/wifi_direct.h" #include "internal/platform/logging.h" @@ -32,7 +33,7 @@ namespace nearby::windows { namespace { -constexpr int kWaitingForServerSocketReadyTimeoutSeconds = 90; // seconds +constexpr int kWaitingForServerSocketReadyTimeoutSeconds = 60; // seconds } // namespace WifiDirectServerSocket::~WifiDirectServerSocket() { Close(); } @@ -50,27 +51,32 @@ void WifiDirectServerSocket::SetIPAddress(std::string ip_address) { } std::unique_ptr WifiDirectServerSocket::Accept() { - absl::MutexLock lock(mutex_); - if (server_socket_accepted_connection_) { - LOG(INFO) << "Server socket has already accepted a connection. Return."; - return nullptr; - } - if (!is_listen_started_) { - LOG(INFO) << __func__ - << ": Server socket is not started, wait for server socket is " - "ready."; - is_listen_ready_.WaitWithTimeout( - &mutex_, absl::Seconds(kWaitingForServerSocketReadyTimeoutSeconds)); - if (!is_listen_started_) { - LOG(INFO) << __func__ - << ": Server socket failed to start within timeout."; + { + absl::MutexLock lock(&mutex_); + if (closed_) return nullptr; + if (server_socket_accepted_connection_) { + LOG(INFO) << "Server socket has already accepted a connection. Return."; return nullptr; } + LOG(INFO) << "Check if server socket is ready."; + if (!is_listen_started_) { + LOG(INFO) <<"Server socket is not started, wait for server socket is " + "ready."; + is_listen_ready_.WaitWithTimeout( + &mutex_, absl::Seconds(kWaitingForServerSocketReadyTimeoutSeconds)); + if (closed_ || !is_listen_started_) { + LOG(INFO) << ": Server socket failed to start or was closed."; + return nullptr; + } + } } + LOG(INFO) << "Start to accept connection from WiFiDirect client."; auto client_socket = server_socket_.Accept(); - if (client_socket == nullptr) { - LOG(INFO) << "Accept server socket failed."; + + absl::MutexLock lock(&mutex_); + if (closed_ || client_socket == nullptr) { + LOG(INFO) << "Accept server socket failed or closed."; return nullptr; } @@ -80,9 +86,45 @@ std::unique_ptr WifiDirectServerSocket::Accept() { return std::make_unique(std::move(client_socket)); } +std::string GetWifiDirectGOAddresses() { + for (int i = 0; i < 3; i++) { + // Force refresh network info since assignment of the well known + // static IP address to the hotspot interface does not trigger the IP + // interface change notification in network_monitor.cc. + NetworkInfo::GetNetworkInfo().Refresh(); + for (const auto& net_interface : + NetworkInfo::GetNetworkInfo().GetInterfaces()) { + if (net_interface.type == InterfaceType::kWifiHotspot) { + LOG(INFO) << "Found Wifi Hotspot interface, index: " + << net_interface.index; + for (const SocketAddress& ipaddress : net_interface.ipv6_addresses) { + LOG(INFO) << "Found ipv6 address: " << ipaddress.ToString(); + // IPv6 link-local addresses are allowed and preferred since it skips + // the DHCP wait time. + } + for (const SocketAddress& ipaddress : net_interface.ipv4_addresses) { + LOG(INFO) << "Found ipv4 address: " << ipaddress.ToString(); + // Skip link-local IPv4 addresses. + if (ipaddress.IsV4LinkLocal()) { + LOG(INFO) << "Skip link-local IPv4 address: "; + continue; + } + + return ipaddress.ToString(); + } + } + } + LOG(WARNING) + << "Failed to find Wifi Hotspot interface. Wait 500ms snd try again"; + Sleep(500); + } + return ""; +} + void WifiDirectServerSocket::PopulateWifiDirectCredentials( WifiDirectCredentials& wifi_direct_credentials) { - wifi_direct_credentials.SetGateway(wifi_direct_ipaddr_); + std::string wifi_direct_ipaddr = GetWifiDirectGOAddresses(); + wifi_direct_credentials.SetGateway(wifi_direct_ipaddr); if (GetPort() != 0) { wifi_direct_credentials.SetPort(GetPort()); } else { @@ -93,15 +135,19 @@ void WifiDirectServerSocket::PopulateWifiDirectCredentials( } Exception WifiDirectServerSocket::Close() { - absl::MutexLock lock(mutex_); - if (closed_) { - return {Exception::kSuccess}; + { + absl::MutexLock lock(mutex_); + if (closed_) { + return {Exception::kSuccess}; + } + closed_ = true; + wifi_direct_ipaddr_.clear(); + is_listen_started_ = false; + server_socket_accepted_connection_ = false; + is_listen_ready_.SignalAll(); } - wifi_direct_ipaddr_.clear(); - is_listen_started_ = false; - server_socket_accepted_connection_ = false; + server_socket_.Close(); - closed_ = true; LOG(INFO) << __func__ << ": Close completed succesfully."; return {Exception::kSuccess}; diff --git a/internal/platform/implementation/windows/wifi_direct_test.cc b/internal/platform/implementation/windows/wifi_direct_test.cc index 9be5e148..6eb5c293 100644 --- a/internal/platform/implementation/windows/wifi_direct_test.cc +++ b/internal/platform/implementation/windows/wifi_direct_test.cc @@ -19,6 +19,8 @@ #include #include "gtest/gtest.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "internal/platform/implementation/wifi_direct.h" @@ -28,7 +30,13 @@ namespace nearby { namespace windows { namespace { - +constexpr absl::string_view kServiceNamePrefix = + "com.google.nearby.connection."; +// Tests are prefixed with DISABLED_ for several reasons: 1. They require user +// interaction, 2. They have access to Windows APIs and physical WiFi hardware. +// These tests are intended for validation on actual Windows machines, 3. By +// using the DISABLED_ prefix, we can Keep the code in the repo and prevent CI +// failures. TEST(WifiDirectMedium, DISABLED_StartWifiDirect) { int run_test; LOG(INFO) << "Run StartWifiDirect test case? input 0 or 1:"; @@ -72,7 +80,9 @@ TEST(WifiDirectMedium, DISABLED_ConnectWifiDirect) { LOG(INFO) << "Enter pin: "; std::string pin; std::cin >> pin; - credentials.SetServiceName(service_name); + std::string service_name_with_prefix = + absl::StrCat(kServiceNamePrefix, service_name); + credentials.SetDeviceName(service_name_with_prefix); credentials.SetPin(pin); EXPECT_TRUE(wifi_direct_medium.ConnectWifiDirect(credentials)); @@ -147,7 +157,9 @@ TEST(WifiDirectMedium, DISABLED_WifiDirectConnectToServiceServer) { LOG(INFO) << "Enter pin: "; std::string pin; std::cin >> pin; - credentials.SetServiceName(service_name); + std::string service_name_with_prefix = + absl::StrCat(kServiceNamePrefix, service_name); + credentials.SetDeviceName(service_name_with_prefix); credentials.SetPin(pin); EXPECT_TRUE(wifi_direct_medium.ConnectWifiDirect(credentials)); diff --git a/internal/platform/implementation/windows/wifi_hotspot.h b/internal/platform/implementation/windows/wifi_hotspot.h index c6b4c34a..b68926ec 100644 --- a/internal/platform/implementation/windows/wifi_hotspot.h +++ b/internal/platform/implementation/windows/wifi_hotspot.h @@ -111,7 +111,7 @@ class WifiHotspotMedium : public api::WifiHotspotMedium { WiFiDirectConnectionListener listener_{nullptr}; // The list of WiFiDirectDevice is used to keep hotspot connection alive. - std::list wifi_direct_devices_; + std::list wifi_direct_devices_ ABSL_GUARDED_BY(mutex_); fire_and_forget OnStatusChanged( WiFiDirectAdvertisementPublisher sender, diff --git a/internal/platform/implementation/windows/wifi_hotspot_medium.cc b/internal/platform/implementation/windows/wifi_hotspot_medium.cc index 288efe14..e6ca86bd 100644 --- a/internal/platform/implementation/windows/wifi_hotspot_medium.cc +++ b/internal/platform/implementation/windows/wifi_hotspot_medium.cc @@ -349,7 +349,10 @@ fire_and_forget WifiHotspotMedium::OnConnectionRequested( auto wifi_direct_device = WiFiDirectDevice::FromIdAsync( connection_request.DeviceInformation().Id()) .get(); - wifi_direct_devices_.push_back(wifi_direct_device); + { + absl::MutexLock lock(mutex_); + wifi_direct_devices_.push_back(wifi_direct_device); + } LOG(INFO) << "Registered the device " << winrt::to_string(device_name) << " in WLAN-AutoConfig"; } catch (...) { diff --git a/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc b/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc index 434d7a09..700e5f2f 100644 --- a/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc +++ b/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc @@ -15,28 +15,16 @@ #include #include -#include -#include #include -#include #include #include -// ABSL headers -#include "absl/functional/any_invocable.h" -#include "absl/strings/match.h" - // Nearby connections headers -#include "absl/synchronization/mutex.h" #include "internal/flags/nearby_flags.h" #include "internal/platform/flags/nearby_platform_feature_flags.h" #include "internal/platform/implementation/wifi_hotspot.h" -#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h" -#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Connectivity.h" -#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Sockets.h" #include "internal/platform/implementation/windows/network_info.h" #include "internal/platform/implementation/windows/socket_address.h" -#include "internal/platform/implementation/windows/utils.h" #include "internal/platform/implementation/windows/wifi_hotspot_server_socket.h" #include "internal/platform/implementation/windows/wifi_hotspot_socket.h" #include "internal/platform/logging.h" @@ -44,11 +32,6 @@ #include "internal/platform/wifi_credential.h" namespace nearby::windows { -namespace { -using ::winrt::Windows::Networking::Connectivity::NetworkInformation; -using ::winrt::Windows::Networking::HostNameType; -using ::winrt::Windows::Networking::Sockets::SocketQualityOfService; -} // namespace std::unique_ptr WifiHotspotServerSocket::Accept() { auto client_socket = server_socket_.Accept(); @@ -62,9 +45,6 @@ std::unique_ptr WifiHotspotServerSocket::Accept() { void WifiHotspotServerSocket::PopulateHotspotCredentials( HotspotCredentials& hotspot_credentials) { - bool use_address_candidates = NearbyFlags::GetInstance().GetBoolFlag( - platform::config_package_nearby::nearby_platform_feature:: - kEnableHotspotAddressCandidates); int64_t ip_address_max_retries = NearbyFlags::GetInstance().GetInt64Flag( platform::config_package_nearby::nearby_platform_feature:: kWifiHotspotCheckIpMaxRetries); @@ -72,46 +52,13 @@ void WifiHotspotServerSocket::PopulateHotspotCredentials( NearbyFlags::GetInstance().GetInt64Flag( platform::config_package_nearby::nearby_platform_feature:: kWifiHotspotCheckIpIntervalMillis); - if (!use_address_candidates) { - // Get current IP addresses of the device. - VLOG(1) << "maximum IP check retries=" << ip_address_max_retries - << ", IP check interval=" << ip_address_retry_interval_millis - << "ms"; - std::string hotspot_ipaddr; - for (int i = 0; i < ip_address_max_retries; i++) { - hotspot_ipaddr = GetHotspotIpAddress(); - if (hotspot_ipaddr.empty()) { - LOG(WARNING) << "Failed to find Hotspot's IP addr for the try: " - << i + 1 << ". Wait " << ip_address_retry_interval_millis - << "ms snd try again"; - Sleep(ip_address_retry_interval_millis); - } else { - break; - } - } - if (hotspot_ipaddr.empty()) { - LOG(WARNING) << "Failed to start accepting connection without IP " - "addresses configured on computer."; - return; - } - - std::vector hotspot_ipaddr_bytes; - uint32_t address_int = inet_addr(hotspot_ipaddr.c_str()); - if (address_int != INADDR_NONE) { - hotspot_ipaddr_bytes.resize(4); - std::memcpy(hotspot_ipaddr_bytes.data(), - reinterpret_cast(&address_int), 4); - } - ServiceAddress service_address = { - .address = hotspot_ipaddr_bytes, - .port = static_cast(GetPort()), - }; - hotspot_credentials.SetAddressCandidates({service_address}); - return; - } std::vector service_addresses; bool has_ipv4_address = false; for (int i = 0; i < ip_address_max_retries; i++) { + // Force refresh network info since assignment of the well known + // static IP address to the hotspot interface does not trigger the IP + // interface change notification in network_monitor.cc. + NetworkInfo::GetNetworkInfo().Refresh(); for (const auto& net_interface : NetworkInfo::GetNetworkInfo().GetInterfaces()) { // service_addresses should only have addresses from a single interface. @@ -120,11 +67,13 @@ void WifiHotspotServerSocket::PopulateHotspotCredentials( LOG(INFO) << "Found Wifi Hotspot interface, index: " << net_interface.index; for (const SocketAddress& ipaddress : net_interface.ipv6_addresses) { + VLOG(1) << "Found ipv6 address: " << ipaddress.ToString(); // IPv6 link-local addresses are allowed and preferred since it skips // the DHCP wait time. service_addresses.push_back(ipaddress.ToServiceAddress(GetPort())); } for (const SocketAddress& ipaddress : net_interface.ipv4_addresses) { + VLOG(1) << "Found ipv4 address: " << ipaddress.ToString(); // Skip link-local IPv4 addresses. if (ipaddress.IsV4LinkLocal()) { continue; @@ -164,43 +113,4 @@ bool WifiHotspotServerSocket::Listen(int port) { return true; } -std::string WifiHotspotServerSocket::GetHotspotIpAddress() const { - try { - auto host_names = NetworkInformation::GetHostNames(); - std::vector ip_candidates; - for (auto host_name : host_names) { - if (host_name.IPInformation() != nullptr && - host_name.IPInformation().NetworkAdapter() != nullptr && - host_name.Type() == HostNameType::Ipv4) { - std::string ipv4_s = winrt::to_string(host_name.ToString()); - if (absl::EndsWith(ipv4_s, ".1")) { - ip_candidates.push_back(ipv4_s); - } - } - } - if (ip_candidates.empty()) { - return ""; - } - // Windows always creates Hotspot at address "192.168.137.1". - for (auto &ip_candidate : ip_candidates) { - if (ip_candidate == "192.168.137.1") { - LOG(INFO) << "Found Hotspot IP: " << ip_candidate; - return ip_candidate; - } - } - LOG(INFO) << "Found Hotspot IP: " << ip_candidates.front(); - return ip_candidates.front(); - } catch (std::exception exception) { - LOG(ERROR) << __func__ << ": Exception: " << exception.what(); - return {}; - } catch (const winrt::hresult_error &error) { - LOG(ERROR) << __func__ << ": WinRT exception: " << error.code() << ": " - << winrt::to_string(error.message()); - return ""; - } catch (...) { - LOG(ERROR) << __func__ << ": Unknown exception."; - return ""; - } -} - } // namespace nearby::windows diff --git a/internal/platform/implementation/windows/wifi_hotspot_server_socket.h b/internal/platform/implementation/windows/wifi_hotspot_server_socket.h index 61d4fa0d..8878d2f9 100644 --- a/internal/platform/implementation/windows/wifi_hotspot_server_socket.h +++ b/internal/platform/implementation/windows/wifi_hotspot_server_socket.h @@ -79,8 +79,6 @@ class WifiHotspotServerSocket : public api::WifiHotspotServerSocket { bool Listen(int port); private: - // Retrieves hotspot IP address from local machine - std::string GetHotspotIpAddress() const; NearbyServerSocket server_socket_; }; diff --git a/internal/platform/implementation/windows/wifi_lan_medium.cc b/internal/platform/implementation/windows/wifi_lan_medium.cc index f7ab9504..01200bef 100644 --- a/internal/platform/implementation/windows/wifi_lan_medium.cc +++ b/internal/platform/implementation/windows/wifi_lan_medium.cc @@ -64,6 +64,7 @@ using ::winrt::Windows::Devices::Enumeration::DeviceInformationKind; using ::winrt::Windows::Devices::Enumeration::DeviceInformationUpdate; using ::winrt::Windows::Devices::Enumeration::DeviceWatcher; using ::winrt::Windows::Foundation::Collections::IMapView; +using ::winrt::Windows::Foundation::IInspectable; using ::winrt::Windows::Networking::Connectivity::NetworkInformation; // mDNS text attributes diff --git a/internal/platform/implementation/windows/wifi_lan_server_socket.cc b/internal/platform/implementation/windows/wifi_lan_server_socket.cc index 81a39950..ea09e691 100644 --- a/internal/platform/implementation/windows/wifi_lan_server_socket.cc +++ b/internal/platform/implementation/windows/wifi_lan_server_socket.cc @@ -24,22 +24,35 @@ #include "internal/platform/exception.h" #include "internal/platform/implementation/wifi_lan.h" #include "internal/platform/implementation/windows/nearby_server_socket.h" +#include "internal/platform/implementation/windows/network_info.h" #include "internal/platform/implementation/windows/socket_address.h" -#include "internal/platform/implementation/windows/utils.h" #include "internal/platform/implementation/windows/wifi_lan.h" #include "internal/platform/logging.h" +#include "internal/platform/service_address.h" namespace nearby::windows { -// Returns the first IP address. +// Returns the first IPv4 address. std::string WifiLanServerSocket::GetIPAddress() const { // Just pick an IP address from the list of available addresses. - std::vector ip_addresses = GetIpv4Addresses(); - if (ip_addresses.empty()) { - LOG(ERROR) << "No IP addresses found."; - return ""; + const NetworkInfo& network_info = NetworkInfo::GetNetworkInfo(); + for (const NetworkInfo::InterfaceInfo& net_interface : + network_info.GetInterfaces()) { + if (net_interface.type != InterfaceType::kWifi && + net_interface.type != InterfaceType::kEthernet) { + continue; + } + for (const SocketAddress& v4_address : net_interface.ipv4_addresses) { + // Ignore link local addresses. + if (v4_address.IsV4LinkLocal()) { + continue; + } + ServiceAddress service_address = v4_address.ToServiceAddress(0); + return std::string(service_address.address.begin(), + service_address.address.end()); + } } - return ipaddr_dotdecimal_to_4bytes_string(ip_addresses.front()); + return ""; } // Blocks until either: diff --git a/internal/platform/medium_environment.cc b/internal/platform/medium_environment.cc index f932bfc4..46f8142e 100644 --- a/internal/platform/medium_environment.cc +++ b/internal/platform/medium_environment.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,7 @@ #include "absl/status/status.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" +#include "absl/time/clock.h" #include "absl/time/time.h" #include "internal/platform/borrowable.h" #include "internal/platform/byte_array.h" @@ -46,6 +48,7 @@ #include "internal/platform/nsd_service_info.h" #include "internal/platform/prng.h" #include "internal/platform/runnable.h" +#include "internal/platform/service_address.h" #include "internal/platform/uuid.h" #include "internal/platform/wifi_credential.h" #include "internal/test/fake_clock.h" @@ -69,10 +72,14 @@ MediumEnvironment& MediumEnvironment::Instance() { void MediumEnvironment::Start(EnvironmentConfig config) { if (!enabled_.exchange(true)) { LOG(INFO) << "MediumEnvironment::Start()"; - config_ = std::move(config); - if (config_.use_simulated_clock) { + { MutexLock lock(&mutex_); - simulated_clock_ = std::make_unique(); + config_ = std::move(config); + if (config_.use_simulated_clock) { + simulated_clock_ = std::make_shared(); + } else { + simulated_clock_.reset(); + } } Reset(); } @@ -82,8 +89,8 @@ void MediumEnvironment::Stop() { if (enabled_.exchange(false)) { LOG(INFO) << "MediumEnvironment::Stop()"; Sync(false); + MutexLock lock(&mutex_); if (config_.use_simulated_clock) { - MutexLock lock(&mutex_); simulated_clock_.reset(); } config_ = {}; @@ -96,10 +103,8 @@ void MediumEnvironment::Reset() { bluetooth_adapters_.clear(); bluetooth_mediums_.clear(); ble_mediums_.clear(); -#ifndef NO_WEBRTC webrtc_signaling_message_callback_.clear(); webrtc_signaling_complete_callback_.clear(); -#endif wifi_lan_mediums_.clear(); awdl_mediums_.clear(); { @@ -132,7 +137,8 @@ void MediumEnvironment::Sync(bool enable_notifications) { LOG(INFO) << "MediumEnvironment::Sync(): done [count=" << count << "]"; } -const EnvironmentConfig& MediumEnvironment::GetEnvironmentConfig() { +EnvironmentConfig MediumEnvironment::GetEnvironmentConfig() { + MutexLock lock(&mutex_); return config_; } @@ -667,7 +673,6 @@ MediumEnvironment::GetBleMediumStatus(const api::ble::BleMedium& medium) { return result; } -#ifndef NO_WEBRTC void MediumEnvironment::RegisterWebRtcSignalingMessenger( absl::string_view self_id, OnSignalingMessageCallback message_callback, OnSignalingCompleteCallback complete_callback) { @@ -725,7 +730,7 @@ void MediumEnvironment::SendWebRtcSignalingComplete(absl::string_view peer_id, item->second(success); }); } -#endif + void MediumEnvironment::SetUseValidPeerConnection( bool use_valid_peer_connection) { use_valid_peer_connection_ = use_valid_peer_connection; @@ -978,13 +983,13 @@ void MediumEnvironment::RegisterWifiDirectMedium( } api::WifiDirectMedium* MediumEnvironment::GetWifiDirectMedium( - absl::string_view service_name, absl::string_view ip_address) { + absl::string_view device_name, absl::string_view ip_address) { MutexLock lock(&mutex_); for (auto& medium_info : wifi_direct_mediums_) { auto* medium_found = medium_info.first; auto& info = medium_info.second; if (info.is_go && info.is_active) { - if ((info.wifi_direct_credentials->GetServiceName() == service_name) || + if ((info.wifi_direct_credentials->GetDeviceName() == device_name) || (!ip_address.empty() && (info.wifi_direct_credentials->GetGateway() == ip_address))) { LOG(INFO) << "Found Remote WifiDirect medium=" << medium_found; @@ -1015,8 +1020,8 @@ void MediumEnvironment::UpdateWifiDirectMediumForStartOrConnect( if (wifi_direct_credentials) { LOG(INFO) << "Update WifiDirect medium for GO: this=" << this << "; medium=" << &medium << role_status - << "; service_name=" - << wifi_direct_credentials->GetServiceName() + << "; device_name=" + << wifi_direct_credentials->GetDeviceName() << "; pin=" << wifi_direct_credentials->GetPin(); } else { LOG(INFO) << "Reset WifiDirect medium for GO: this=" << this @@ -1150,12 +1155,43 @@ void MediumEnvironment::SetFeatureFlags(const FeatureFlags::Flags& flags) { FeatureFlags::GetMutableInstanceForTesting().SetFlags(flags); } -std::optional MediumEnvironment::GetSimulatedClock() { +absl::Time MediumEnvironment::Now() { MutexLock lock(&mutex_); if (simulated_clock_) { - return std::optional(simulated_clock_.get()); + return simulated_clock_->Now(); + } + return absl::Now(); +} + +// If simulated_clock_ is valid, it will be advanced by the given duration. +// If simulated_clock_ is not valid, this method will do nothing. +void MediumEnvironment::FastForward(absl::Duration duration) { + std::shared_ptr sim_clock; + { + MutexLock lock(&mutex_); + if (simulated_clock_) { + sim_clock = simulated_clock_; + } + } + if (sim_clock) { + // Mutex is unlocked before calling FastForward to prevent deadlocks. + sim_clock->FastForward(duration); + } +} + +void MediumEnvironment::AddSimulatedClockObserver( + const std::string& name, std::function observer) { + MutexLock lock(&mutex_); + if (simulated_clock_) { + simulated_clock_->AddObserver(name, std::move(observer)); + } +} + +void MediumEnvironment::RemoveSimulatedClockObserver(const std::string& name) { + MutexLock lock(&mutex_); + if (simulated_clock_) { + simulated_clock_->RemoveObserver(name); } - return std::nullopt; } void MediumEnvironment::RegisterGattServer( diff --git a/internal/platform/medium_environment.h b/internal/platform/medium_environment.h index a5bf530d..4ffce3ad 100644 --- a/internal/platform/medium_environment.h +++ b/internal/platform/medium_environment.h @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -36,9 +37,7 @@ #include "internal/platform/runnable.h" #include "internal/platform/uuid.h" #include "internal/test/fake_clock.h" -#ifndef NO_WEBRTC #include "internal/platform/implementation/webrtc.h" -#endif #include "internal/platform/byte_array.h" #include "internal/platform/feature_flags.h" #include "internal/platform/implementation/wifi_direct.h" @@ -79,12 +78,10 @@ class MediumEnvironment { using BluetoothDiscoveryCallback = api::BluetoothClassicMedium::DiscoveryCallback; using BleScanCallback = api::ble::BleMedium::ScanningCallback; -#ifndef NO_WEBRTC using OnSignalingMessageCallback = api::WebRtcSignalingMessenger::OnSignalingMessageCallback; using OnSignalingCompleteCallback = api::WebRtcSignalingMessenger::OnSignalingCompleteCallback; -#endif using WifiLanDiscoveredServiceCallback = api::WifiLanMedium::DiscoveredServiceCallback; using AwdlDiscoveredServiceCallback = @@ -162,8 +159,7 @@ class MediumEnvironment { // Returns a Bluetooth Device object matching given mac address to nullptr. api::BluetoothDevice* FindBluetoothDevice(MacAddress mac_address); - const EnvironmentConfig& GetEnvironmentConfig(); -#ifndef NO_WEBRTC + EnvironmentConfig GetEnvironmentConfig(); // Registers |message_callback| to receive messages sent to device with id // |self_id|, and |complete_callback| to notify when signaling is complete. void RegisterWebRtcSignalingMessenger( @@ -180,7 +176,6 @@ class MediumEnvironment { // Simulates sending an "signaling complete" signal to the WebRTC medium. void SendWebRtcSignalingComplete(absl::string_view peer_id, bool success); -#endif // Used to set if WebRtcMedium should use a valid peer connection or nullptr // in tests. void SetUseValidPeerConnection(bool use_valid_peer_connection); @@ -295,7 +290,7 @@ class MediumEnvironment { // Returns WifiDirect medium that matches ssid or IP address with the role of // the Medium. Returns nullptr if not found. - api::WifiDirectMedium* GetWifiDirectMedium(absl::string_view service_name, + api::WifiDirectMedium* GetWifiDirectMedium(absl::string_view device_name, absl::string_view ip_address); // Updates credential and Medium role(GO or GC) to indicate the current @@ -334,7 +329,11 @@ class MediumEnvironment { void SetFeatureFlags(const FeatureFlags::Flags& flags); - std::optional GetSimulatedClock(); + absl::Time Now(); + void FastForward(absl::Duration duration); + void AddSimulatedClockObserver(const std::string& name, + std::function observer); + void RemoveSimulatedClockObserver(const std::string& name); api::ble::BleMedium* FindBleMedium(api::ble::BlePeripheral::UniqueId id); @@ -492,7 +491,6 @@ class MediumEnvironment { absl::flat_hash_map ble_mediums_; absl::flat_hash_map devices_pairing_contexts_; -#ifndef NO_WEBRTC // Maps peer id to callback for receiving signaling messages. absl::flat_hash_map webrtc_signaling_message_callback_; @@ -500,7 +498,6 @@ class MediumEnvironment { // Maps peer id to callback for signaling complete events. absl::flat_hash_map webrtc_signaling_complete_callback_; -#endif absl::flat_hash_map wifi_lan_mediums_; @@ -516,7 +513,7 @@ class MediumEnvironment { bool use_valid_peer_connection_ = true; absl::Duration peer_connection_latency_ = absl::ZeroDuration(); - std::unique_ptr simulated_clock_ ABSL_GUARDED_BY(mutex_); + std::shared_ptr simulated_clock_ ABSL_GUARDED_BY(mutex_); ObserverList observers_; bool ble_extended_advertisements_available_ = false; }; diff --git a/internal/platform/scheduled_executor_test.cc b/internal/platform/scheduled_executor_test.cc index efc9f77a..80d79c25 100644 --- a/internal/platform/scheduled_executor_test.cc +++ b/internal/platform/scheduled_executor_test.cc @@ -25,13 +25,12 @@ #include "internal/platform/cancelable.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/medium_environment.h" -#include "internal/test/fake_clock.h" namespace nearby { // kShortDelay must be significant enough to guarantee that OS under heavy load // should be able to execute the non-blocking test paths within this time. -absl::Duration kShortDelay = absl::Milliseconds(100); +absl::Duration kShortDelay = absl::Milliseconds(200); // kLongDelay must be long enough to make sure that under OS under heavy load // will let kShortDelay fire and jobs scheduled before the kLongDelay fires. @@ -212,8 +211,6 @@ TEST(ScheduledExecutorTest, ExecuteDuringShutdownFails) { TEST(ScheduledExecutorTest, SimulatedClockCanSchedule) { MediumEnvironment::Instance().Start({.use_simulated_clock = true}); - FakeClock* fake_clock = - MediumEnvironment::Instance().GetSimulatedClock().value(); ScheduledExecutor executor; std::atomic_int value = 0; CountDownLatch first_task_latch(1); @@ -235,24 +232,23 @@ TEST(ScheduledExecutorTest, SimulatedClockCanSchedule) { }, kShortDelay); EXPECT_EQ(value, 0); - fake_clock->FastForward(kShortDelay - absl::Milliseconds(1)); + MediumEnvironment::Instance().FastForward(kShortDelay - + absl::Milliseconds(1)); EXPECT_EQ(value, 0); - fake_clock->FastForward(absl::Milliseconds(1)); + MediumEnvironment::Instance().FastForward(absl::Milliseconds(1)); second_task_latch.Await(); EXPECT_EQ(value, 1); - fake_clock->FastForward(kLongDelay - kShortDelay); + MediumEnvironment::Instance().FastForward(kLongDelay - kShortDelay); first_task_latch.Await(); EXPECT_EQ(value, 5); // Very long sleep to make sure that the sleep is truly simulated. - fake_clock->FastForward(absl::Minutes(30)); + MediumEnvironment::Instance().FastForward(absl::Minutes(30)); MediumEnvironment::Instance().Stop(); } TEST(ScheduledExecutorTest, DestroyExecutorWithSimulatedClockIgnoresPendingTasks) { MediumEnvironment::Instance().Start({.use_simulated_clock = true}); - FakeClock* fake_clock = - MediumEnvironment::Instance().GetSimulatedClock().value(); { ScheduledExecutor executor; executor.Schedule( @@ -262,7 +258,7 @@ TEST(ScheduledExecutorTest, }, kShortDelay); } - fake_clock->FastForward(absl::Minutes(30)); + MediumEnvironment::Instance().FastForward(absl::Minutes(30)); MediumEnvironment::Instance().Stop(); } @@ -403,8 +399,6 @@ TEST(ScheduledExecutorTest, CanCancelOneOfTwoRepeatedTasks) { TEST(ScheduledExecutorTest, SimulatedClockCanScheduleRepeatedly) { MediumEnvironment::Instance().Start({.use_simulated_clock = true}); - FakeClock* fake_clock = - MediumEnvironment::Instance().GetSimulatedClock().value(); ScheduledExecutor executor; std::atomic_int value = 0; std::atomic_int i = 0; @@ -419,11 +413,12 @@ TEST(ScheduledExecutorTest, SimulatedClockCanScheduleRepeatedly) { EXPECT_EQ(value, 0); // Advance to just before the first execution. - fake_clock->FastForward(kShortDelay - absl::Milliseconds(1)); + MediumEnvironment::Instance().FastForward(kShortDelay - + absl::Milliseconds(1)); EXPECT_EQ(value, 0); // Advance past the first execution. - fake_clock->FastForward(absl::Milliseconds(1)); + MediumEnvironment::Instance().FastForward(absl::Milliseconds(1)); latch[0].Await(absl::Seconds(1)); EXPECT_EQ(value, 1); @@ -431,11 +426,12 @@ TEST(ScheduledExecutorTest, SimulatedClockCanScheduleRepeatedly) { absl::SleepFor(kShortDelay); // Advance to just before the second execution. - fake_clock->FastForward(kShortDelay - absl::Milliseconds(1)); + MediumEnvironment::Instance().FastForward(kShortDelay - + absl::Milliseconds(1)); EXPECT_EQ(value, 1); // Advance past the second execution. - fake_clock->FastForward(absl::Milliseconds(1)); + MediumEnvironment::Instance().FastForward(absl::Milliseconds(1)); latch[1].Await(absl::Seconds(1)); EXPECT_EQ(value, 2); @@ -443,7 +439,7 @@ TEST(ScheduledExecutorTest, SimulatedClockCanScheduleRepeatedly) { cancelable.Cancel(); // Advance a long time and make sure it doesn't run again. - fake_clock->FastForward(kLongDelay * 5); + MediumEnvironment::Instance().FastForward(kLongDelay * 5); EXPECT_EQ(value, 2); MediumEnvironment::Instance().Stop(); diff --git a/internal/platform/service_address.h b/internal/platform/service_address.h index dfc6d83b..887e0b18 100644 --- a/internal/platform/service_address.h +++ b/internal/platform/service_address.h @@ -16,6 +16,7 @@ #define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_SERVICE_ADDRESS_H_ #include +#include #include #include @@ -44,6 +45,18 @@ void AbslStringify(Sink& sink, const ServiceAddress& service_address) { service_address.port); } +#ifdef NEARBY_CHROMIUM +// Support logging of ServiceAddress (Chromium does not use absl log). +inline std::ostream& operator<<(std::ostream& os, + const ServiceAddress& service_address) { + return os << "[" + << WifiUtils::GetHumanReadableIpAddress( + std::string(service_address.address.begin(), + service_address.address.end())) + << "]:" << service_address.port; +} +#endif + void ServiceAddressToProto( const ServiceAddress& service_address, location::nearby::connections::ServiceAddress& proto); diff --git a/internal/platform/tachyon_express_signaling_messenger.cc b/internal/platform/tachyon_express_signaling_messenger.cc deleted file mode 100644 index fc93df70..00000000 --- a/internal/platform/tachyon_express_signaling_messenger.cc +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright 2025 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 NO_WEBRTC - -#include "internal/platform/tachyon_express_signaling_messenger.h" - -#include -#include -#include -#include - -#include "absl/functional/any_invocable.h" -#include "absl/strings/string_view.h" -#include "absl/synchronization/mutex.h" -#include "absl/time/time.h" -#include "third_party/grpc/include/grpc/support/time.h" -#include "third_party/grpc/include/grpcpp/channel.h" -#include "third_party/grpc/include/grpcpp/client_context.h" -#include "third_party/grpc/include/grpcpp/create_channel.h" -#include "third_party/grpc/include/grpcpp/security/credentials.h" -#include "third_party/grpc/include/grpcpp/support/client_callback.h" -#include "third_party/grpc/include/grpcpp/support/status.h" -#include "internal/account/account_manager_impl.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/implementation/account_manager.h" -#include "internal/platform/implementation/webrtc.h" -#include "internal/platform/logging.h" -#include "internal/proto/messaging.grpc.pb.h" -#include "internal/proto/tachyon.proto.h" -#include "internal/proto/tachyon_common.proto.h" -#include "internal/proto/tachyon_enums.proto.h" -#include "internal/rpc/utils.h" -#include "util/random/mt_random.h" -#include "util/random/util.h" - -namespace nearby { - -namespace { -using ::google::internal::communications::instantmessaging::v1::ClientInfo; -using ::google::internal::communications::instantmessaging::v1::Id; -using ::google::internal::communications::instantmessaging::v1:: - LocationStandard; -using ::google::internal::communications::instantmessaging::v1:: - ReceiveMessagesResponse; -using ::google::internal::communications::instantmessaging::v1::RequestHeader; -using ::google::internal::communications::instantmessaging::v1:: - SendMessageExpressRequest; -using ::google::internal::communications::instantmessaging::v1:: - SendMessageExpressResponse; - -constexpr absl::string_view kApp = "Nearby"; -constexpr absl::string_view kTachyonAddress = - "instantmessaging-pa.googleapis.com:443"; - -// It is unclear to me where these magic numbers are from but they are used -// across both the Android and CrOS implementations. -// See: -// https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/nearby_sharing/tachyon_ice_config_fetcher.cc;l=53 -constexpr int kMajorVersion = 1; -constexpr int kMinorVersion = 24; -constexpr int kPointVersion = 0; - -void InitId(Id& id, absl::string_view id_str, - const location::nearby::connections::LocationHint& location_hint) { - id.set_id(id_str); - id.set_app(kApp); - id.set_type(google::internal::communications::instantmessaging::v1::IdType:: - NEARBY_ID); - auto* request_location_hint = id.mutable_location_hint(); - request_location_hint->set_location(location_hint.location()); - if (location_hint.format() == - location::nearby::connections::LocationStandard::E164_CALLING) { - request_location_hint->set_format(LocationStandard::E164_CALLING); - } else if (location_hint.format() == - location::nearby::connections::LocationStandard:: - ISO_3166_1_ALPHA_2) { - request_location_hint->set_format(LocationStandard::ISO_3166_1_ALPHA_2); - } else { - request_location_hint->set_format(LocationStandard::UNKNOWN); - } -} - -void InitRequestHeader( - RequestHeader& header, absl::string_view self_id, - const location::nearby::connections::LocationHint& location_hint) { - ClientInfo* client_info = header.mutable_client_info(); - client_info->set_platform_type(google::internal::communications:: - instantmessaging::v1::Platform::DESKTOP); - - client_info->set_major(kMajorVersion); - client_info->set_minor(kMinorVersion); - client_info->set_point(kPointVersion); - - client_info->set_api_version( - google::internal::communications::instantmessaging::v1::ApiVersion::V4); - - // Generate a random message identifier. - MTRandom rand; - header.set_request_id(util_random::RandomString(&rand, /*length=*/13, - util::random::kWebsafe64)); - header.set_app(kApp); - - InitId(*header.mutable_requester_id(), self_id, location_hint); -} - -} // namespace - -bool TachyonExpressSignalingMessenger::ReceiveMessagesReader::Start( - google::internal::communications::instantmessaging::v1::grpc::Messaging:: - StubInterface* stub, - absl::string_view self_id, - const location::nearby::connections::LocationHint& location_hint, - absl::string_view access_token, - absl::AnyInvocable on_fast_path_ready_callback, - absl::AnyInvocable on_inbox_message_callback, - absl::AnyInvocable on_complete_callback) { - { - absl::MutexLock lock(mutex_); - if (is_receiving_messages_) { - return false; - } - is_receiving_messages_ = true; - } - - on_fast_path_ready_callback_ = std::move(on_fast_path_ready_callback); - on_inbox_message_callback_ = std::move(on_inbox_message_callback); - on_complete_callback_ = std::move(on_complete_callback); - const std::shared_ptr call_creds = - grpc::AccessTokenCredentials(std::string(access_token)); - context_.set_credentials(call_creds); - gpr_timespec deadline = gpr_now(GPR_CLOCK_MONOTONIC); - timespec timespec = absl::ToTimespec(absl::Seconds(30)); - deadline.tv_sec += timespec.tv_sec; - deadline.tv_nsec += timespec.tv_nsec; - context_.set_deadline(deadline); - - InitRequestHeader(*request_.mutable_header(), self_id, location_hint); - stub->async()->ReceiveMessagesExpress(&context_, &request_, this); - StartRead(&response_); - StartCall(); - return true; -} - -void TachyonExpressSignalingMessenger::ReceiveMessagesReader::OnReadDone( - bool ok) { - { - absl::MutexLock lock(mutex_); - if (!is_receiving_messages_) { - return; - } - } - if (ok) { - switch (response_.body_case()) { - case ReceiveMessagesResponse::kFastPathReady: - on_fast_path_ready_callback_(); - break; - case ReceiveMessagesResponse::kInboxMessage: - on_inbox_message_callback_( - ByteArray(response_.inbox_message().message())); - break; - default: - break; - } - StartRead(&response_); - } -} - -void TachyonExpressSignalingMessenger::ReceiveMessagesReader::OnDone( - const grpc::Status& s) { - { - absl::MutexLock lock(mutex_); - if (!is_receiving_messages_) { - return; - } - } - if (!s.ok()) { - LOG(ERROR) << "ReceiveMessagesExpress failed: " - << rpc::GrpcStatusToAbslStatus(s); - } - on_complete_callback_(s.ok()); -} - -void TachyonExpressSignalingMessenger::ReceiveMessagesReader::TryCancel() { - { - absl::MutexLock lock(mutex_); - if (!is_receiving_messages_) { - return; - } - is_receiving_messages_ = false; - } - context_.TryCancel(); -} - -TachyonExpressSignalingMessenger::TachyonExpressSignalingMessenger( - absl::string_view self_id, - const location::nearby::connections::LocationHint& location_hint) - : self_id_(self_id), - location_hint_(location_hint), - account_manager_(AccountManagerImpl::Factory::instance()) { - std::shared_ptr channel = - grpc::CreateChannel(std::string(kTachyonAddress), - grpc::SslCredentials(grpc::SslCredentialsOptions())); - messaging_stub_ = google::internal::communications::instantmessaging::v1:: - grpc::Messaging::NewStub(channel); -} - -struct StartState { - CountDownLatch latch{1}; - bool success = false; -}; - -bool TachyonExpressSignalingMessenger::StartReceivingMessages( - OnSignalingMessageCallback on_message_callback, - OnSignalingCompleteCallback on_complete_callback) { - auto state = std::make_shared(); - - account_manager_->GetAccessToken( - [this, state, on_message_callback = std::move(on_message_callback), - on_complete_callback = std::move(on_complete_callback)]( - absl::StatusOr token) mutable { - if (!token.ok()) { - state->success = false; - state->latch.CountDown(); - return; - } - auto reader = std::make_shared(); - - reader_ = reader; - - std::weak_ptr weak_state = state; - - bool started = reader->Start( - messaging_stub_.get(), self_id_, location_hint_, token.value(), - /*on_fast_path_ready_callback=*/ - [state] { - LOG(INFO) << "Received fast path ready message from tachyon."; - state->success = true; - state->latch.CountDown(); - }, - std::move(on_message_callback), - [reader, weak_state, - cb = std::move(on_complete_callback)](bool s) mutable { - LOG(INFO) << "Finished receiving messages from tachyon."; - cb(s); - if (auto state = weak_state.lock()) { - state->success = false; - state->latch.CountDown(); - } - }); - - if (!started) { - state->success = false; - state->latch.CountDown(); - } - }); - state->latch.Await(); - if (state->success) { - LOG(INFO) << "Receiving messages from tachyon."; - } else { - LOG(ERROR) << "Failed to start receiving messages from tachyon."; - reader_.reset(); - } - return state->success; -} - -void TachyonExpressSignalingMessenger::StopReceivingMessages() { - if (reader_) { - reader_->TryCancel(); - reader_.reset(); - } -} - -bool TachyonExpressSignalingMessenger::SendMessage(absl::string_view peer_id, - const ByteArray& message) { - auto rpc_state = - std::make_shared>(); - - InitRequestHeader(*rpc_state->request.mutable_header(), self_id_, - location_hint_); - InitId(*rpc_state->request.mutable_dest_id(), peer_id, location_hint_); - - auto* request_message = rpc_state->request.mutable_message(); - request_message->set_message(message.string_data()); - request_message->set_message_type( - google::internal::communications::instantmessaging::v1::InboxMessage:: - BASIC); - request_message->set_message_class( - google::internal::communications::instantmessaging::v1::InboxMessage:: - EPHEMERAL); - MTRandom rand; - request_message->set_message_id(util_random::RandomString( - &rand, /*length=*/13, util::random::kWebsafe64)); - - CountDownLatch latch(1); - bool success = false; - account_manager_->GetAccessToken( - [this, &latch, &success, rpc_state](absl::StatusOr token) { - if (!token.ok()) { - success = false; - latch.CountDown(); - return; - } - - const std::shared_ptr call_creds = - grpc::AccessTokenCredentials(token.value()); - rpc_state->context.set_credentials(call_creds); - gpr_timespec deadline = gpr_now(GPR_CLOCK_MONOTONIC); - timespec timespec = absl::ToTimespec(absl::Seconds(30)); - deadline.tv_sec += timespec.tv_sec; - deadline.tv_nsec += timespec.tv_nsec; - rpc_state->context.set_deadline(deadline); - - // `rpc_state` is captured to ensure its lifetime is valid until the - // callback is executed. - messaging_stub_->async()->SendMessageExpress( - &rpc_state->context, &rpc_state->request, &rpc_state->response, - [&success, &latch, rpc_state](grpc::Status status) { - if (!status.ok()) { - LOG(ERROR) << "SendMessageExpress failed: " - << rpc::GrpcStatusToAbslStatus(status); - } - success = status.ok(); - latch.CountDown(); - }); - }); - latch.Await(); - return success; -} - -} // namespace nearby - -#endif // #ifndef NO_WEBRTC diff --git a/internal/platform/tachyon_express_signaling_messenger.h b/internal/platform/tachyon_express_signaling_messenger.h deleted file mode 100644 index 872f95b9..00000000 --- a/internal/platform/tachyon_express_signaling_messenger.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2025 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 THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_TACHYON_MESSAGING_CLIENT_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_TACHYON_MESSAGING_CLIENT_H_ - -#ifndef NO_WEBRTC - -#include - -#include -#include - -#include "absl/base/thread_annotations.h" -#include "absl/functional/any_invocable.h" -#include "absl/strings/string_view.h" -#include "absl/synchronization/mutex.h" -#include "third_party/grpc/include/grpcpp/client_context.h" -#include "third_party/grpc/include/grpcpp/support/client_callback.h" -#include "third_party/grpc/include/grpcpp/support/status.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/implementation/account_manager.h" -#include "internal/platform/implementation/webrtc.h" -#include "internal/proto/messaging.grpc.pb.h" - -namespace nearby { - -// Interface for the messaging Tachyon service. See -// third_party/nearby/internal/proto/messaging.proto -class TachyonExpressSignalingMessenger : public api::WebRtcSignalingMessenger { - public: - explicit TachyonExpressSignalingMessenger( - absl::string_view self_id, - const location::nearby::connections::LocationHint& location_hint); - - class ReceiveMessagesReader - : public grpc::ClientReadReactor< - google::internal::communications::instantmessaging::v1:: - ReceiveMessagesResponse> { - public: - ReceiveMessagesReader() = default; - - void OnReadDone(bool ok) override; - void OnDone(const grpc::Status& s) override; - - bool Start( - google::internal::communications::instantmessaging::v1::grpc:: - Messaging::StubInterface* stub, - absl::string_view self_id, - const location::nearby::connections::LocationHint& location_hint, - absl::string_view access_token, - absl::AnyInvocable on_fast_path_ready_callback, - absl::AnyInvocable on_inbox_message_callback, - absl::AnyInvocable on_complete_callback); - void TryCancel(); - - private: - grpc::ClientContext context_; - google::internal::communications::instantmessaging::v1:: - ReceiveMessagesExpressRequest request_; - google::internal::communications::instantmessaging::v1:: - ReceiveMessagesResponse response_; - absl::AnyInvocable on_fast_path_ready_callback_; - absl::AnyInvocable on_inbox_message_callback_; - absl::AnyInvocable on_complete_callback_; - - absl::Mutex mutex_; - bool is_receiving_messages_ ABSL_GUARDED_BY(mutex_) = false; - }; - - bool SendMessage(absl::string_view peer_id, - const ByteArray& message) override; - - bool StartReceivingMessages( - OnSignalingMessageCallback on_message_callback, - OnSignalingCompleteCallback on_complete_callback) override; - - void StopReceivingMessages() override; - - private: - std::string self_id_; - location::nearby::connections::LocationHint location_hint_; - std::unique_ptr - messaging_stub_; - AccountManager* const account_manager_; - std::shared_ptr reader_ = nullptr; -}; - -} // namespace nearby - -#endif // #ifndef NO_WEBRTC - -#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_TACHYON_MESSAGING_CLIENT_H_ diff --git a/internal/platform/webrtc.h b/internal/platform/webrtc.h deleted file mode 100644 index 1eef3d26..00000000 --- a/internal/platform/webrtc.h +++ /dev/null @@ -1,119 +0,0 @@ -// 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. - -#ifndef PLATFORM_PUBLIC_WEBRTC_H_ -#define PLATFORM_PUBLIC_WEBRTC_H_ - -#ifndef NO_WEBRTC - -#include -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/feature_flags.h" -#include "internal/platform/implementation/platform.h" -#include "internal/platform/implementation/webrtc.h" -#include "webrtc/api/peer_connection_interface.h" - -namespace nearby { - -class WebRtcSignalingMessenger { - public: - using OnSignalingMessageCallback = - api::WebRtcSignalingMessenger::OnSignalingMessageCallback; - using OnSignalingCompleteCallback = - api::WebRtcSignalingMessenger::OnSignalingCompleteCallback; - - explicit WebRtcSignalingMessenger( - std::unique_ptr messenger) - : impl_(std::move(messenger)) {} - virtual ~WebRtcSignalingMessenger() = default; - WebRtcSignalingMessenger(WebRtcSignalingMessenger&&) = default; - WebRtcSignalingMessenger operator=(WebRtcSignalingMessenger&&) = delete; - - virtual bool SendMessage(absl::string_view peer_id, - const ByteArray& message) { - return impl_->SendMessage(peer_id, message); - } - - virtual bool StartReceivingMessages( - OnSignalingMessageCallback on_message_callback, - OnSignalingCompleteCallback on_complete_callback) { - return impl_->StartReceivingMessages(std::move(on_message_callback), - std::move(on_complete_callback)); - } - - virtual void StopReceivingMessages() { impl_->StopReceivingMessages(); } - - virtual bool IsValid() const { return impl_ != nullptr; } - - private: - std::unique_ptr impl_; -}; - -class WebRtcMedium { - public: - using PeerConnectionCallback = api::WebRtcMedium::PeerConnectionCallback; - - WebRtcMedium() : impl_(api::ImplementationPlatform::CreateWebRtcMedium()) {} - virtual ~WebRtcMedium() = default; - WebRtcMedium(WebRtcMedium&&) = delete; - WebRtcMedium& operator=(WebRtcMedium&&) = delete; - - // Gets the default two-letter country code associated with current locale. - // For example, en_US locale resolves to "US". - std::string GetDefaultCountryCode() { return impl_->GetDefaultCountryCode(); } - - void SetNonCellular(bool non_cellular) { - non_cellular_ = non_cellular; - } - - // Creates and returns a new webrtc::PeerConnectionInterface object via - // |callback|. - void CreatePeerConnection(webrtc::PeerConnectionObserver* observer, - PeerConnectionCallback callback) { - if (FeatureFlags::GetInstance() - .GetFlags() - .support_web_rtc_non_cellular_medium && non_cellular_) { - std::optional options; - options->network_ignore_mask |= webrtc::ADAPTER_TYPE_CELLULAR; - impl_->CreatePeerConnection(options, observer, std::move(callback)); - } else { - impl_->CreatePeerConnection(observer, std::move(callback)); - } - } - - // Returns a signaling messenger for sending WebRTC signaling messages. - virtual std::unique_ptr GetSignalingMessenger( - absl::string_view self_id, - const location::nearby::connections::LocationHint& location_hint) { - return std::make_unique( - impl_->GetSignalingMessenger(self_id, location_hint)); - } - - virtual bool IsValid() const { return impl_ != nullptr; } - - private: - std::unique_ptr impl_; - bool non_cellular_ = false; -}; - -} // namespace nearby - -#endif - -#endif // PLATFORM_PUBLIC_WEBRTC_H_ diff --git a/internal/platform/wifi_credential.h b/internal/platform/wifi_credential.h index 7618d4af..cc6c78a3 100644 --- a/internal/platform/wifi_credential.h +++ b/internal/platform/wifi_credential.h @@ -83,10 +83,10 @@ class WifiDirectCredentials { std::string GetPassword() const { return password_; } void SetPassword(const std::string& password) { password_ = password; } - // Get/Set Service Name. - std::string GetServiceName() const { return service_name_; } - void SetServiceName(const std::string& service_name) { - service_name_ = service_name; + // Get/Set Device Name. + std::string GetDeviceName() const { return device_name_; } + void SetDeviceName(const std::string& device_name) { + device_name_ = device_name; } // Get/Set Pin. @@ -126,12 +126,12 @@ class WifiDirectCredentials { private: // There are 2 types of WifiDirectAuthType. // 1. Without Service Discovery: the credentials are ssid/password. - // 2. With Service Discovery: the credentials are service_name/pin. + // 2. With Service Discovery: the credentials are device_name/pin. // Android supports type 1 and 2 in the future, but Windows only supports the // second type. std::string ssid_; std::string password_; - std::string service_name_; + std::string device_name_; std::string pin_; std::string ip_address_; std::string gateway_ = "0.0.0.0"; diff --git a/internal/platform/wifi_direct.h b/internal/platform/wifi_direct.h index f78339e4..a8296da0 100644 --- a/internal/platform/wifi_direct.h +++ b/internal/platform/wifi_direct.h @@ -17,13 +17,12 @@ #include #include -#include +#include #include #include #include "absl/base/thread_annotations.h" #include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "internal/flags/nearby_flags.h" #include "internal/platform/cancellation_flag.h" @@ -178,7 +177,7 @@ class WifiDirectMedium { } // Returns the port range as a pair of min and max port. - absl::optional> GetDynamicPortRange() { + std::optional> GetDynamicPortRange() { return impl_->GetDynamicPortRange(); } diff --git a/internal/platform/wifi_direct_test.cc b/internal/platform/wifi_direct_test.cc index fc14c90e..fd6f54cf 100644 --- a/internal/platform/wifi_direct_test.cc +++ b/internal/platform/wifi_direct_test.cc @@ -47,19 +47,19 @@ constexpr FeatureFlags kTestCases[] = { }, }; -constexpr absl::string_view kServiceName = "NC-WifiDirectTest"; +constexpr absl::string_view kDeviceName = "NC-WifiDirectTest"; constexpr absl::string_view kPin = "b592f7d3"; constexpr absl::string_view kIp = "123.234.23.1"; constexpr const size_t kPort = 20; constexpr absl::string_view kData = "ABCD"; constexpr const size_t kChunkSize = 10; -TEST(WifiDirectCredentialsTest, SetGetServiceName) { - std::string service_name(kServiceName); +TEST(WifiDirectCredentialsTest, SetGetDeviceName) { + std::string device_name(kDeviceName); WifiDirectCredentials wifi_direct_credentials; - wifi_direct_credentials.SetServiceName(service_name); + wifi_direct_credentials.SetDeviceName(device_name); - EXPECT_EQ(wifi_direct_credentials.GetServiceName(), kServiceName); + EXPECT_EQ(wifi_direct_credentials.GetDeviceName(), kDeviceName); } TEST(WifiDirectCredentialsTest, SetGetPin) { @@ -116,7 +116,7 @@ TEST_F(WifiDirectMediumTest, CanStartStopWifiDirect) { TEST_F(WifiDirectMediumTest, CanConnectDisconnectWifiDirect) { WifiDirectMedium wifi_direct_a; WifiDirectCredentials credentials; - credentials.SetServiceName(std::string(kServiceName)); + credentials.SetDeviceName(std::string(kDeviceName)); credentials.SetPin(std::string(kPin)); ASSERT_TRUE(wifi_direct_a.IsInterfaceValid()); @@ -136,7 +136,7 @@ TEST_P(WifiDirectMediumTest, CanStartDirectGOThatOtherCanConnect) { WifiDirectCredentials* wifi_direct_credentials = wifi_direct_a.GetCredential(); auto* medium_a = - env_.GetWifiDirectMedium(wifi_direct_credentials->GetServiceName(), {}); + env_.GetWifiDirectMedium(wifi_direct_credentials->GetDeviceName(), {}); EXPECT_NE(medium_a, nullptr); EXPECT_TRUE(wifi_direct_b.ConnectWifiDirect(*wifi_direct_credentials)); @@ -198,7 +198,7 @@ TEST_P(WifiDirectMediumTest, CanStartDirectGOThatOtherCanConnect) { EXPECT_TRUE(wifi_direct_b.DisconnectWifiDirect()); EXPECT_TRUE(wifi_direct_a.StopWifiDirect()); auto* medium_b = - env_.GetWifiDirectMedium(wifi_direct_credentials->GetServiceName(), {}); + env_.GetWifiDirectMedium(wifi_direct_credentials->GetDeviceName(), {}); EXPECT_EQ(medium_b, nullptr); } @@ -278,7 +278,7 @@ TEST_F(WifiDirectMediumTest, CanStartDirectGOThatOtherFailConnect) { ASSERT_TRUE(wifi_direct_b.IsInterfaceValid()); EXPECT_TRUE(wifi_direct_a.StartWifiDirect()); WifiDirectCredentials wifi_direct_credentials; - wifi_direct_credentials.SetServiceName(std::string(kServiceName)); + wifi_direct_credentials.SetDeviceName(std::string(kDeviceName)); wifi_direct_credentials.SetPin(std::string(kPin)); EXPECT_FALSE(wifi_direct_b.ConnectWifiDirect(wifi_direct_credentials)); @@ -289,12 +289,12 @@ TEST_F(WifiDirectMediumTest, CanStartDirectGOThatOtherFailConnect) { TEST_F(WifiDirectMediumTest, GetSupportedWifiDirectAuthTypes) { WifiDirectMedium wifi_direct_a; - // g3 only supports WifiDirect with auth type of PIN. + // g3 only supports WifiDirect with auth type of Device Name. auto supported_types = wifi_direct_a.GetSupportedWifiDirectAuthTypes(); EXPECT_EQ(supported_types.size(), 1); EXPECT_EQ(supported_types[0], - location::nearby::proto::connections:: - WifiDirectAuthType::WIFI_DIRECT_WITH_PIN); + location::nearby::proto::connections::WifiDirectAuthType:: + WIFI_DIRECT_WITH_DEVICE_NAME); } } // namespace diff --git a/internal/platform/wifi_lan.h b/internal/platform/wifi_lan.h index e46a39f6..2f9fc2ed 100644 --- a/internal/platform/wifi_lan.h +++ b/internal/platform/wifi_lan.h @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -24,7 +25,6 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/functional/any_invocable.h" -#include "absl/types/optional.h" #include "internal/platform/blocking_queue_stream.h" #include "internal/platform/byte_array.h" #include "internal/platform/cancellation_flag.h" @@ -254,7 +254,7 @@ class WifiLanMedium { } // Returns the port range as a pair of min and max port. - absl::optional> GetDynamicPortRange() { + std::optional> GetDynamicPortRange() { return impl_->GetDynamicPortRange(); } diff --git a/internal/proto/analytics/BUILD b/internal/proto/analytics/BUILD deleted file mode 100644 index 352db000..00000000 --- a/internal/proto/analytics/BUILD +++ /dev/null @@ -1,60 +0,0 @@ -# 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. - -load("@com_google_protobuf//bazel:cc_proto_library.bzl", "cc_proto_library") -load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library") -load("@rules_cc//cc:cc_test.bzl", "cc_test") - -licenses(["notice"]) - -package(default_visibility = ["//visibility:public"]) - -proto_library( - name = "connections_log_proto", - srcs = [ - "connections_log.proto", - ], - deps = [ - "//proto:connections_enums_proto", - "//proto/errorcode:error_code_enums_proto", - ], -) - -cc_proto_library( - name = "connections_log_cc_proto", - visibility = [ - "//connections:__subpackages__", - "//internal/analytics:__pkg__", - "//location/nearby/analytics/cpp:__subpackages__", - ], - deps = [":connections_log_proto"], -) - -cc_test( - name = "proto_analytics_test", - size = "small", - srcs = [ - "connections_log_test.cc", - ], - shard_count = 16, - deps = [ - ":connections_log_cc_proto", - "//internal/platform:logging", - "//internal/platform/implementation/g3", # build_cleaner: keep - "//proto:connections_enums_cc_proto", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_googletest//:gtest_main", - "@com_google_protobuf//:protobuf", - ], -) diff --git a/internal/proto/analytics/connections_log.proto b/internal/proto/analytics/connections_log.proto deleted file mode 100644 index 6dea3e48..00000000 --- a/internal/proto/analytics/connections_log.proto +++ /dev/null @@ -1,733 +0,0 @@ -// 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. - -syntax = "proto2"; - -package location.nearby.analytics.proto; - -// import "storage/datapol/annotations/proto/semantic_annotations.proto"; -import "proto/connections_enums.proto"; -import "proto/errorcode/error_code_enums.proto"; - -option optimize_for = LITE_RUNTIME; -option java_package = "com.google.location.nearby.analytics.proto"; -option java_outer_classname = "ConnectionsLogProto"; -option objc_class_prefix = "GNCP"; - -// Top-level log proto for Nearby Connections. -// LINT.IfChange(ConnectionsLog) -// Next Tag: 7 -message ConnectionsLog { - // The type of this log. - optional location.nearby.proto.connections.EventType event_type = 1; - - // Non-null for EventType.CLIENT_SESSION. - // Encapsulates all client activity between connecting to and disconnecting - // from the Nearby Connections API via Client. - optional ClientSession client_session = 2; - - // The version of Nearby Connections. E.g. "v1.0.4". - optional string version = 3 /* type = ST_SOFTWARE_ID */; - - // For EventType.ERROR_CODE - optional ErrorCode error_code = 4; - - // Indicates the source of the log. - optional location.nearby.proto.connections.LogSource log_source = 5; - - // This is a temporary logging field for FilesGo migration phase based on - // device geolocation. Example values are "P1", "P2", "PA" etc. - // In files migration, we have different phases to rollout Files-> Nearby - // migration on different list of countries. For example, at phase A (PA), - // the migration happens in Asian countries; P1 for Europe countries; P2 - // for North and South Americas. - // Reference: http://shortn/_9smZZ8CTD6, http://shortn/_Y08gVwZRKc - optional string files_migration_phase = 6; - - // Encapsulates one session of a client connected to Nearby Connections API. - message ClientSession { - // Elapsed time in milliseconds between Client connect and - // disconnect. - optional int64 duration_millis = 1; - - // Zero or more StrategySessions. - repeated StrategySession strategy_session = 2; - - // The client session flow id. - optional int64 client_flow_id = 3 /* type = ST_SESSION_ID */; - - // All the connection tokens used in this client session. - optional string connection_token = 4 - /* type = ST_SESSION_ID */; - - reserved 5; // device type isdeprecated and moved to StrategySession - } - - message OperationResult { - // The category of the operation result - optional location.nearby.proto.connections.OperationResultCategory - result_category = 1; - - // The result code of the operation result - optional location.nearby.proto.connections.OperationResultCode result_code = - 2; - } - - message OperationResultWithMedium { - optional location.nearby.proto.connections.Medium medium = 1; - - // Indicate which mediums belong to the same update API Call. - optional int32 update_index = 2; - - // The category of the operation result - optional location.nearby.proto.connections.OperationResultCategory - result_category = 3; - - // The result code of the operation result - optional location.nearby.proto.connections.OperationResultCode result_code = - 4; - - // The connection mode. - optional location.nearby.proto.connections.ConnectionMode connection_mode = - 5; - } - - // One round of a particular Strategy done by a client. - message StrategySession { - // Elapsed time in milliseconds between a call to startAdvertising/Discovery - // and the end of this particular Strategy. A StrategySession may end due to - // - the client disconnecting from Client; - // - a call to stopAllEndpoints, which disconnects all endpoints and - // stops any advertising/discovery; - // - a new call to startAdvertising/Discovery. - optional int64 duration_millis = 1; - - // The Strategy used for this session. - optional location.nearby.proto.connections.ConnectionsStrategy strategy = 2; - - // The role(s) played by this device during this StrategySession. - repeated location.nearby.proto.connections.SessionRole role = 3; - - // One or more of the following *Phase is present, depending on the role(s). - - // Encapsulates discovery information. - repeated DiscoveryPhase discovery_phase = 4; - // Encapsulates advertising information. - repeated AdvertisingPhase advertising_phase = 5; - - // Attempts at establishing a connection to another device. - repeated ConnectionAttempt connection_attempt = 6; - - // Successful and accepted connections to another device. - repeated EstablishedConnection established_connection = 7; - - // Attempts to upgrade a connection from one medium to another. - repeated BandwidthUpgradeAttempt upgrade_attempt = 9; - - // The build version of the user's device (Same value as the Build number in - // Settings -> about phone). - optional string build_version = 10 - /* type = ST_SOFTWARE_ID */; - } - - // Encapsulates activity during a period of discovery. - message DiscoveryPhase { - // Elapsed time in milliseconds between startDiscovery and stopDiscovery. - optional int64 duration_millis = 1; - - // The Medium(s) used for discovery. - repeated location.nearby.proto.connections.Medium medium = 2; - - // Discovered endpoints during this round of discovery. - repeated DiscoveredEndpoint discovered_endpoint = 3; - - // Attempted ConnectionRequests (requested by the client). They may or - // may not reach the other endpoint. - repeated ConnectionRequest sent_connection_request = 4; - - // UWB ranging related data during discovery (May range with multiple - // endpoints) - repeated UwbRangingProcess uwb_ranging = 5; - - // The SendingEvent flow id. - optional int64 client_flow_id = 6 /* type = ST_SESSION_ID */; - - // Encapsulates additional discovery information. - optional DiscoveryMetadata discovery_metadata = 7; - - // Collect the discovery results of the mediums - repeated OperationResultWithMedium adv_dis_result = 8; - - // The readon of stopping discoverying - optional location.nearby.proto.connections.StopDiscoveringReason - stop_reason = 9; - - // The type of the device. - optional location.nearby.proto.connections.DeviceType device_type = 10; - - // The supported service. - optional location.nearby.proto.connections.SupportedService - supported_service = 11; - } - - // An endpoint discovered on a particular medium during discovery. - message DiscoveredEndpoint { - // The medium on which this endpoint was discovered. - optional location.nearby.proto.connections.Medium medium = 1; - - // Elapsed time between the call to startDiscovery() and the time at which - // this endpoint was discovered. - optional int64 latency_millis = 2; - } - - // Encapsulates activity during UWB ranging. - message UwbRangingProcess { - // Elapsed time in milliseconds between startRanging and stopRanging. - optional int64 duration_millis = 1; - - // UWB raw ranging data received during discovery. This is optional. Only - // certain devices (Debug/Testing etc.) will log the raw data. - repeated RawUwbRangingEvent uwb_ranging_data = 2; - - // Number of ranging data received - optional int32 number_of_ranging_data = 3; - - // The minimum distance during a UWB ranging session - optional int32 distance_min = 4; - - // The maximum distance during a UWB ranging session - optional int32 distance_max = 5; - - // The average distance during a UWB ranging session - optional int32 distance_ave = 6; - - // The distance variance during a UWB ranging session - optional int32 distance_variance = 7; - - // The minimum AoA during a UWB ranging session - optional int32 azimuth_min = 8; - - // The maximum AoA during a UWB ranging session - optional int32 azimuth_max = 9; - - // The average AoA during a UWB ranging session - optional int32 azimuth_ave = 10; - - // The AoA variance during a UWB ranging session - optional int32 azimuth_variance = 11; - } - - // Ranging data received during discovery phase. - message RawUwbRangingEvent { - // Distance in cm - optional int32 distance = 1; - - // Azimuth angle in degree - optional int32 azimuth_angle = 2; - - // Polar angle in degree (0 if the device doesn't support it) - optional int32 polar_angle = 3; - } - - // Encapsulates activity during a period of advertising. - message AdvertisingPhase { - // Elapsed time in milliseconds between startAdvertising and - // stopAdvertising. - optional int64 duration_millis = 1; - - // The Medium(s) used for advertising. - repeated location.nearby.proto.connections.Medium medium = 2; - - // Received ConnectionRequests from remote endpoints. - repeated ConnectionRequest received_connection_request = 3; - - // The ReceivingEvent flow id. - optional int64 client_flow_id = 4 /* type = ST_SESSION_ID */; - - // Encapsulates additional advertising information. - optional AdvertisingMetadata advertising_metadata = 5; - - // Collect the discovery results of the mediums - repeated OperationResultWithMedium adv_dis_result = 6; - - // The readon of stopping advertising - optional location.nearby.proto.connections.StopAdvertisingReason - stop_reason = 7; - - // The type of the device. - optional location.nearby.proto.connections.DeviceType device_type = 8; - - // The supported service. - optional location.nearby.proto.connections.SupportedService - supported_service = 9; - } - - // A request to connect, corresponding to the API's concept of - // request/accept/rejectConnection(). - message ConnectionRequest { - // Elapsed time in milliseconds between the connection request being - // initiated and the responses being received. - optional int64 duration_millis = 1; - - // Elapsed time in milliseconds between the start of the containing - // Advertising/DiscoveryPhase and the start of this ConnectionRequest, i.e. - // the time at which the request is sent (on the discoverer, at the request - // of the client) or received (on the advertiser, over the wire from the - // remote endpoint). - optional int64 request_delay_millis = 2; - - // The local endpoint's response to this connection request. - optional location.nearby.proto.connections.ConnectionRequestResponse - local_response = 3; - - // The remote endpoint's response to this connection request. - optional location.nearby.proto.connections.ConnectionRequestResponse - remote_response = 4; - - // The SendingEvent flow id. - optional int64 client_flow_id = 5 /* type = ST_SESSION_ID */; - } - - // An attempt to connect to an endpoint over a particular medium. - message ConnectionAttempt { - // Elapsed time in milliseconds between starting the connection attempt - // and succeeding/failing. - optional int64 duration_millis = 1; - - // The type of connection attempt. - optional location.nearby.proto.connections.ConnectionAttemptType type = 2; - - // The direction (incoming vs outgoing) of this attempt. - optional location.nearby.proto.connections.ConnectionAttemptDirection - direction = 3; - - // The Medium of this connection attempt. - optional location.nearby.proto.connections.Medium medium = 4; - - // The result of the connection attempt. - optional location.nearby.proto.connections.ConnectionAttemptResult - attempt_result = 5; - - // The ReceivingEvent flow id. - optional int64 client_flow_id = 6 /* type = ST_SESSION_ID */; - - // The token used to identify this connection pair. - optional string connection_token = 7 - /* type = ST_SESSION_ID */; - - // Encapsulates additional connection information. - optional ConnectionAttemptMetadata connection_attempt_metadata = 8; - - // The result code of this connection attempt - optional OperationResult operation_result = 9; - - // The connection mode. - optional location.nearby.proto.connections.ConnectionMode connection_mode = - 10; - - // The type of the device. - optional location.nearby.proto.connections.DeviceType device_type = 11; - - // The supported service. - optional location.nearby.proto.connections.SupportedService - supported_service = 12; - - // The latency of the wifi connection in milliseconds, starting from when - // the wifi credentials are received from the remote device, to the moment - // wifi connection is established with internet access. - optional int64 wifi_connection_latency_millis = 13; - - // The latency of the device attestation in milliseconds, starting from when - // the device attestation is initiated, to the moment the device attestation - // is finished. - optional int64 device_attestation_latency_millis = 14; - - // The error code returned by Play Integrity API during device attestation. - optional int64 play_integrity_error_code = 15; - } - - message DeviceInfo { - enum Platform { - UNKNOWN = 0; - ANDROID = 1; - IOS = 2; - CROS = 3; - WINDOWS = 4; - } - optional string device_model = 1; - optional Platform device_platform = 2; - optional string country_code = 3; - optional string manufacturer = 4; - } - - message DisconnectionReasonDetail { - enum DisconnectionReason { - UNKNOWN_DISCONNECTION_REASON = 0; - DCT_ERROR_MDNS_DISCOVERY_TIMEOUT = 1; - DCT_ERROR_MDNS_REGISTER_SERVICE = 2; - DCT_ERROR_SUBSEQUENT_TLS_SPAKE = 3; - DCT_ERROR_REQUEST_FAILED = 4; - DCT_ERROR_RESPONSE_FAILED = 5; - DCT_ERROR_CONTROL_MESSAGE_EXCHANGE = 6; - DCT_ERROR_CAPABILITY_MISMATCH = 7; - DCT_ERROR_HIGH_SPEED_MEDIUM_UNAVAILABLE = 8; - DCT_ERROR_WIFI_DISABLED = 9; - DCT_ERROR_WIFI_DISCONNECTED = 10; - DCT_ERROR_WIFI_CREDENTIAL_TRANSFER = 11; - DCT_ERROR_WIFI_INTERNET_CONNECTION = 12; - DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED = 13; - DCT_ERROR_USER_CANCELLED = 14; - DCT_ERROR_SERVICE_CANCELLED = 15; - DCT_ERROR_UNVERIFIED_INTEGRITY = 16; - SESSION_SUCCESS = 17; - } - optional bool is_local_disconnection = 1; - optional DisconnectionReason disconnection_reason = 2; - } - - // A successfully-established connection over a particular medium. - message EstablishedConnection { - enum SafeDisconnectionResult { - UNKNOWN_SAFE_DISCONNECTION_RESULT = 0; - SAFE_DISCONNECTION = 1; - UNSAFE_DISCONNECTION = 2; - } - - // Elapsed time in milliseconds that the connection is active. - optional int64 duration_millis = 1; - - // The Medium of this connection. - optional location.nearby.proto.connections.Medium medium = 2; - - // Payloads sent over this connection. - repeated Payload sent_payload = 3; - - // Payloads received over this connection. - repeated Payload received_payload = 4; - - // The reason this connection was disconnected. - optional location.nearby.proto.connections.DisconnectionReason - disconnection_reason = 5; - - // The SendingEvent flow id. - optional int64 client_flow_id = 6 /* type = ST_SESSION_ID */; - - // The token use to identify this established connection. - optional string connection_token = 7 - /* type = ST_SESSION_ID */; - - // The type of established connection. - optional location.nearby.proto.connections.ConnectionAttemptType type = 8; - - // If this is a safe disconnection. - optional SafeDisconnectionResult safe_disconnection_result = 9; - - // The result code of this established connection - optional OperationResult operation_result = 10; - - // The remote device info - optional DeviceInfo remote_device_info = 11; - - // The disconnection reason details - optional DisconnectionReasonDetail disconnection_reason_detail = 12; - - // The type of the device. - optional location.nearby.proto.connections.DeviceType device_type = 13; - - // The supported service. - optional location.nearby.proto.connections.SupportedService - supported_service = 14; - - // The speed test report. - optional SpeedTestReport speed_test_report = 15; - } - - message SpeedTestReport { - // The throughput in kbytes per second. - optional int32 throughput_kbytes_per_sec = 1; - - // Whether the throughput is incoming or outgoing. - optional bool is_incoming = 2; - } - - // Contains the transfer statistics for a DCT payload. - message DctPayloadTransferStats { - // The type of the DCT payload. - // Note: For legacy payloads, the type is logged in the Payload message - // instead. - optional location.nearby.proto.connections.DctPayloadType type = 1; - - // Indicates whether the payload was sent using the multipart protocol. - // True if multipart was used, false otherwise. - optional bool is_multipart = 2; - - // The number of parts that were successfully transferred. - // This field is only meaningful when is_multipart is true. - // The counting method differs for outgoing and incoming multipart payloads: - // - OUTGOING: All parts associated with the same request are treated as one - // payload. This field increments by 1 if the payload transfers - // successfully. - // - INCOMING: Each part of the incoming multipart payload is logged - // individually. This field increments for each successfully received - // part. - optional int32 num_parts_success = 3; - - // The number of parts that failed to transfer. - // This field is only meaningful when is_multipart is true. - // The counting method differs for outgoing and incoming multipart payloads: - // - OUTGOING: All parts associated with the same request are treated as one - // payload. This field increments by 1 if the payload fails to transfer. - // - INCOMING: Each part of the incoming multipart payload is logged - // individually. This field increments for each part that fails to be - // received. - optional int32 num_parts_failure = 4; - - // True if this payload transfer is an attempt to resume an interrupted - // payload after a reconnection. False if it's a new payload transfer. - optional bool is_resumption = 5; - - // The data speed report in kbyte per second using global bytes counter - optional int32 data_speed_report_kbyte_per_sec = 6; - } - - // A Payload transferred (or attempted to be transferred) between devices. - message Payload { - // Elapsed time in milliseconds that num_bytes_transferred took to transfer. - optional int64 duration_millis = 1; - - // The type of this payload. - optional location.nearby.proto.connections.PayloadType type = 2; - - // Total size of the payload in bytes. - optional int64 total_size_bytes = 3; - - // Total number of bytes transferred successfully. - optional int64 num_bytes_transferred = 4; - - // The number of chunks used to transfer num_bytes_transferred. - optional int32 num_chunks = 5; - - // The end status of the payload transfer. - optional location.nearby.proto.connections.PayloadStatus status = 6; - - // The number of successful auto resume. - optional int32 num_successful_auto_resume = 7; - - // The result code of this sent payload - optional OperationResult operation_result = 8; - - // The number of failed auto resume attempts. - optional int32 num_failed_auto_resume = 9; - - // Statistics for DCT payloads, e.g., type, multipart details. Populated - // only for DCT payloads. - optional DctPayloadTransferStats dct_payload_transfer_stats = 10; - } - - // An attempt to upgrade an existing connection from one medium to another. - message BandwidthUpgradeAttempt { - // The direction (incoming vs outgoing) of the upgrade attempt. - optional location.nearby.proto.connections.ConnectionAttemptDirection - direction = 1; - - // Elapsed time in milliseconds of the upgrade attempt. - optional int64 duration_millis = 2; - - // The original medium (e.g. bluetooth). - optional location.nearby.proto.connections.Medium from_medium = 3; - - // The new medium that we're hoping to upgrade to (e.g. wifi). - optional location.nearby.proto.connections.Medium to_medium = 4; - - // The result of the upgrade attempt. - optional location.nearby.proto.connections.BandwidthUpgradeResult - upgrade_result = 5; - - // If upgrade_result is not success, the stage at which the error occurred. - optional location.nearby.proto.connections.BandwidthUpgradeErrorStage - error_stage = 6; - - // The SendingEvent flow id. - optional int64 client_flow_id = 7 /* type = ST_SESSION_ID */; - - // The token used to identify this upgrade pair. - optional string connection_token = 8 - /* type = ST_SESSION_ID */; - - // The result code of this upgrade attempt - optional OperationResult operation_result = 9; - - optional location.nearby.proto.connections.DeviceType device_type = 10; - - // The supported service. - optional location.nearby.proto.connections.SupportedService - supported_service = 11; - - // The number of network interfaces on the device for the upgrade medium - // that can be used for bandwidth upgrade. - optional int32 num_interfaces = 12; - // The number of network interfaces on the device for the upgrade medium - // that can be used for bandwidth upgrade and are IPv6 only. - optional int32 num_ipv6_only_interfaces = 13; - } - - // Next Id: 22 - message ErrorCode { - // The direction (incoming vs outgoing) of this error. - optional location.nearby.proto.connections.ConnectionAttemptDirection - direction = 1; - optional string service_id = 2; - // The error medium (e.g. bluetooth). - optional location.nearby.proto.connections.Medium medium = 3; - // The event which the error occurs on. - optional location.nearby.errorcode.proto.Event event = 4; - // The error description. - optional location.nearby.errorcode.proto.Description description = 5; - // The flow id which the error occurs on. - optional int64 flow_id = 6 /* type = ST_SESSION_ID */; - - // Error code value - oneof ErrorCodeDetail { - location.nearby.errorcode.proto.CommonError common_error = 7; - location.nearby.errorcode.proto.StartAdvertisingError - start_advertising_error = 8; - location.nearby.errorcode.proto.StartDiscoveringError - start_discovering_error = 9; - location.nearby.errorcode.proto.StopAdvertisingError - stop_advertising_error = 10; - location.nearby.errorcode.proto.StopDiscoveringError - stop_discovering_error = 11; - location.nearby.errorcode.proto.StartListeningIncomingConnectionError - start_listening_incoming_connection_error = 12; - location.nearby.errorcode.proto.StopListeningIncomingConnectionError - stop_listening_incoming_connection_error = 13; - location.nearby.errorcode.proto.ConnectError connect_error = 14; - location.nearby.errorcode.proto.DisconnectError disconnect_error = 15; - location.nearby.errorcode.proto.SendPayloadError send_payload_error = 17; - location.nearby.errorcode.proto.ReceivePayloadError - receive_payload_error = 18; - location.nearby.errorcode.proto.UpgradeError upgrade_error = 19; - location.nearby.errorcode.proto.AcceptConnectionError - accept_connection_error = 20; - location.nearby.errorcode.proto.RejectConnectionError - reject_connection_error = 21; - } - - // The token use to identify this established connection. - optional string connection_token = 16 - /* type = ST_SESSION_ID */; - } - - // Some additional information to keep with the advertising phase. - message AdvertisingMetadata { - // The bluetooth low energy extended advertisement support status. - optional bool supports_extended_ble_advertisements = 1; - - // The frequency of the connected WiFi AP. - optional int32 connected_ap_frequency = 2; - - // The NFC (Near Field Communication) support status - optional bool supports_nfc_technology = 3; - - // The Bluetooth multiple advertisement support status. - optional bool multiple_advertisement_supported = 4; - - // The power level of this advertising - optional location.nearby.proto.connections.PowerLevel power_level = 5; - - // The dual band support status - optional bool supports_dual_band = 6; - - // The wifi aware support status - optional bool supports_wifi_aware = 7; - - // The endpoint info size - optional int32 endpoint_info_size = 8; - } - - // Some additional information to keep with the discovery phase. - message DiscoveryMetadata { - // The bluetooth low energy extended advertisement support status. - optional bool supports_extended_ble_advertisements = 1; - - // The frequency of the connected WiFi AP. - optional int32 connected_ap_frequency = 2; - - // The NFC (Near Field Communication) support status - optional bool supports_nfc_technology = 3; - - // The power level of this discovering - optional location.nearby.proto.connections.PowerLevel power_level = 4; - } - - // Some additional information to keep with the connection attempt. - message ConnectionAttemptMetadata { - // The technology used by the mediums. - optional location.nearby.proto.connections.ConnectionTechnology technology = - 1; - - // The wifi band used by the wifi mediums. - optional location.nearby.proto.connections.ConnectionBand band = 2; - - // The frequency used by the wifi mediums. - optional int32 frequency = 3; - - // The MCC (Mobile country code) MNC (Mobile network code) of the network - // operator. - optional string network_operator = 4 - /* type = ST_LOCATION */; - - // The upper-case ISO 3166-1 alpha-2 country code of: - // 1. the current connected WiFi network - // 2. or the current registered operator's MCC (Mobile Country Code) - // 3. or empty string. - optional string country_code = 5 /* type = ST_LOCATION */; - - // The TDLS status used by the wifi lan medium, - // TDLS, shortened from Tunneled Direct Link Setup, is "a seamless way to - // stream media and other data faster between devices already on the same - // Wi-Fi network." Devices using it communicate directly with one another, - // without involving the wireless network's router. - optional bool is_tdls_used = 6; - - // The try times for this hosted group or connection operation. - optional int32 try_counts = 7; - - // The enabled status of the wifi hotspot(tethering) when doing this - // connection attempt. - optional bool wifi_hotspot_status = 8; - - // The MAX supported TX link speed (Mbps). - optional int32 max_tx_speed = 9; - - // The MAX supported RX link speed (Mbps). - optional int32 max_rx_speed = 10; - - // The connected wifi channel width. - optional int32 wifi_channel_width = 11; - - // The send buffer size of the created socket - optional int32 send_buffer_size = 12; - - // The receive buffer size of the created socket - optional int32 receive_buffer_size = 13; - - // The frequency of the connected WiFi AP. - optional int32 connected_ap_frequency = 14; - - // The connectivity MCC mode - optional bool is_mcc_mode = 15; - } -} -// LINT.ThenChange() diff --git a/internal/proto/analytics/connections_log_test.cc b/internal/proto/analytics/connections_log_test.cc deleted file mode 100644 index 53048875..00000000 --- a/internal/proto/analytics/connections_log_test.cc +++ /dev/null @@ -1,124 +0,0 @@ -// 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 "logs/proto/location/nearby/nearby_client_log.proto.h" -#include "google/protobuf/descriptor.h" -#include "gtest/gtest.h" -#include "internal/platform/logging.h" -#include "internal/proto/analytics/connections_log.pb.h" -#include "proto/connections_enums.pb.h" - -namespace nearby { -namespace analytics { -namespace proto { - -namespace { - -using G3ConnectionsLog = ::location::nearby::analytics::proto::ConnectionsLog; -using P3ConnectionsLog = ::location::nearby::analytics::proto::ConnectionsLog; - -using ::proto2::Descriptor; -using ::proto2::FieldDescriptor; - -// Forward declaration. -bool Compare(const Descriptor* desc1, const Descriptor* desc2); - -// Compares the two field descriptors and return false if name, number, label, -// or type is different. -bool Compare(const FieldDescriptor* field1, const FieldDescriptor* field2) { - if (field1->name() != field2->name()) { - LOG(WARNING) << "Field name diff: " << field1->name() << " <=> " - << field2->name(); - return false; - } - if (field1->number() != field2->number()) { - LOG(WARNING) << "Field " << field1->name() - << " number diff: " << field1->number() << " <=> " - << field2->number(); - return false; - } - if (field1->label() != field2->label()) { - LOG(WARNING) << "Field " << field1->name() - << " label diff: " << field1->label() << " <=> " - << field2->label(); - return false; - } - bool bRet = false; - if (field1->type() != field2->type()) { - LOG(WARNING) << "Field " << field1->name() - << " type diff: " << field1->type() << " <=> " - << field2->type(); - return bRet; - } else if (field1->type() == FieldDescriptor::TYPE_MESSAGE) { - const Descriptor* msg1 = field1->message_type(); - const Descriptor* msg2 = field2->message_type(); - - bRet = Compare(msg1, msg2); - } else { - bRet = true; - } - - return bRet; -} - -// Compares the two descriptors and return false immediately if different. -bool Compare(const Descriptor* desc1, const Descriptor* desc2) { - LOG(INFO) << "Descriptor1 full name: " << desc1->full_name() << " <=> " - << desc2->full_name(); - for (int i = 0; i < desc1->field_count(); ++i) { - const FieldDescriptor* field1 = desc1->field(i); - const FieldDescriptor* field2 = desc2->FindFieldByName(field1->name()); - - bool bRet = false; - if (field2) { - bRet = Compare(field1, field2); - } else { - LOG(ERROR) << "Descriptor1 full name: " << desc1->full_name() - << "=> Extra field1 name=" << field1->name() - << ", number=" << field1->number() - << ", label=" << field1->label() - << ", type=" << field1->type(); - } - if (!bRet) { - return false; - } - } - for (int i = 0; i < desc2->field_count(); ++i) { - const FieldDescriptor* field2 = desc2->field(i); - const FieldDescriptor* field1 = desc1->FindFieldByName(field2->name()); - if (!field1) { - LOG(ERROR) << "Descriptor2 full name: " << desc2->full_name() - << "=> Extra field2 name=" << field2->name() - << ", number=" << field2->number() - << ", label=" << field2->label() - << ", type=" << field2->type(); - return false; - } - } - - return true; -} - -TEST(ConnectionsLogTest, TwoMessagesAreIdentical) { - const proto2::Descriptor* descriptor1 = G3ConnectionsLog::descriptor(); - const proto2::Descriptor* descriptor2 = P3ConnectionsLog::descriptor(); - - EXPECT_TRUE(Compare(descriptor1, descriptor2)); -} - -} // namespace - -} // namespace proto -} // namespace analytics -} // namespace nearby diff --git a/internal/test/BUILD b/internal/test/BUILD index da7d9bcf..7046cd8d 100644 --- a/internal/test/BUILD +++ b/internal/test/BUILD @@ -17,36 +17,15 @@ load("@rules_cc//cc:cc_test.bzl", "cc_test") licenses(["notice"]) -cc_library( - name = "mocks", - testonly = 1, - hdrs = [ - "mock_account_manager.h", - "mock_account_observer.h", - ], - visibility = ["//visibility:public"], - deps = [ - "//internal/platform/implementation:account_manager", - "//internal/platform/implementation:signin_attempt", - "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings:string_view", - "@com_google_googletest//:gtest_for_library_testonly", - ], -) - cc_library( name = "test", srcs = [ - "fake_account_manager.cc", "fake_clock.cc", "fake_single_thread_executor.cc", "fake_task_runner.cc", "fake_timer.cc", ], hdrs = [ - "fake_account_manager.h", "fake_clock.h", "fake_device_info.h", "fake_http_client.h", @@ -60,20 +39,15 @@ cc_library( ], visibility = ["//visibility:public"], deps = [ - "//internal/base", "//internal/base:file_path", "//internal/base:files", "//internal/network:types", - "//internal/platform:comm", "//internal/platform:logging", "//internal/platform:types", - "//internal/platform/implementation:account_manager", - "//internal/platform/implementation:signin_attempt", "//internal/platform/implementation:types", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/log", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", diff --git a/internal/test/fake_account_manager.cc b/internal/test/fake_account_manager.cc deleted file mode 100644 index c6e919f8..00000000 --- a/internal/test/fake_account_manager.cc +++ /dev/null @@ -1,101 +0,0 @@ -// 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/test/fake_account_manager.h" - -#include -#include -#include -#include - -#include "absl/functional/any_invocable.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/account_manager.h" -#include "internal/platform/implementation/signin_attempt.h" - -namespace nearby { - -std::optional FakeAccountManager::GetCurrentAccount() { - return account_; -} - -std::unique_ptr FakeAccountManager::Login( - absl::string_view client_id, absl::string_view client_secret) { - return nullptr; -} - -void FakeAccountManager::Logout( - absl::AnyInvocable logout_callback) { - if (is_logout_success_) { - std::string account_id = account_->id; - SetAccount(std::nullopt); - NotifyLogout(account_id, /*credential_error=*/false); - // Invoke callback after all operations have been performed since test cases - // may rely on the callback for synchronization. - logout_callback(absl::OkStatus()); - return; - } - - logout_callback(absl::NotFoundError("No account login.")); -} - -bool FakeAccountManager::GetAccessToken( - absl::AnyInvocable)> callback) { - if (!callback) { - return false; - } - if (!account_.has_value()) { - callback(absl::UnavailableError("No current user.")); - return true; - } - callback("FAKE_ACCESS_TOKEN"); - return true; -} - -std::pair -FakeAccountManager::GetOAuthClientCredential() { - return {"", ""}; -} - -void FakeAccountManager::SetAccount(std::optional account) { - account_ = account; -} - -void FakeAccountManager::AddObserver(Observer* observer) { - observers_.AddObserver(observer); -} - -void FakeAccountManager::RemoveObserver(Observer* observer) { - if (!observers_.HasObserver(observer)) { - return; - } - observers_.RemoveObserver(observer); -} - -void FakeAccountManager::NotifyLogin(absl::string_view account_id) { - for (const auto& observer : observers_.GetObservers()) { - observer->OnLoginSucceeded(account_id); - } -} - -void FakeAccountManager::NotifyLogout(absl::string_view account_id, - bool credential_error) { - for (const auto& observer : observers_.GetObservers()) { - observer->OnLogoutSucceeded(account_id, credential_error); - } -} - -} // namespace nearby diff --git a/internal/test/fake_account_manager.h b/internal/test/fake_account_manager.h deleted file mode 100644 index 346663cf..00000000 --- a/internal/test/fake_account_manager.h +++ /dev/null @@ -1,81 +0,0 @@ -// 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 THIRD_PARTY_NEARBY_INTERNAL_TEST_FAKE_ACCOUNT_MANAGER_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_TEST_FAKE_ACCOUNT_MANAGER_H_ - -#include -#include -#include -#include - -#include "absl/functional/any_invocable.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "internal/base/observer_list.h" -#include "internal/platform/implementation/account_manager.h" -#include "internal/platform/implementation/signin_attempt.h" - -namespace nearby { - -// A fake implementation of FakeAccountManager, along with a fake -// factory, to be used in tests. -class FakeAccountManager : public AccountManager { - public: - FakeAccountManager() = default; - ~FakeAccountManager() override = default; - - std::optional GetCurrentAccount() override; - - std::unique_ptr Login( - absl::string_view client_id, absl::string_view client_secret) override; - - void Logout(absl::AnyInvocable logout_callback) override; - - bool GetAccessToken( - absl::AnyInvocable)> callback) override; - std::pair GetOAuthClientCredential() override; - void AddObserver(Observer* observer) override; - void RemoveObserver(Observer* observer) override; - - void SaveAccountPrefs(absl::string_view user_id, absl::string_view client_id, - absl::string_view client_secret) override {} - - // Methods to set API response. - void SetAccount(std::optional account); - - void SetLogoutSuccess(bool is_logout_success) { - is_logout_success_ = is_logout_success; - } - - void NotifyCredentialError() { - NotifyLogout(account_->id, /*credential_error=*/true); - } - - void NotifyLogin(absl::string_view account_id); - void NotifyLogout(absl::string_view account_id, bool credential_error); - - private: - // Login will fail when account_ is empty. - std::optional account_; - - // Logout will fail when is_logout_success_ is false; - bool is_logout_success_ = true; - nearby::ObserverList observers_; -}; - -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_INTERNAL_TEST_FAKE_ACCOUNT_MANAGER_H_ diff --git a/internal/test/fake_clock.cc b/internal/test/fake_clock.cc index edb26a7f..8df56432 100644 --- a/internal/test/fake_clock.cc +++ b/internal/test/fake_clock.cc @@ -19,6 +19,10 @@ #include #include +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/time.h" + namespace nearby { FakeClock::~FakeClock() { diff --git a/internal/test/fake_clock.h b/internal/test/fake_clock.h index 5b7a9af8..b5fbeea6 100644 --- a/internal/test/fake_clock.h +++ b/internal/test/fake_clock.h @@ -53,6 +53,7 @@ class FakeClock : public Clock { absl::flat_hash_map> observers_ ABSL_GUARDED_BY(mutex_); }; + } // namespace nearby #endif // THIRD_PARTY_NEARBY_INTERNAL_TEST_FAKE_CLOCK_H_ diff --git a/internal/test/fake_clock_test.cc b/internal/test/fake_clock_test.cc index 0f57ce56..770f78ed 100644 --- a/internal/test/fake_clock_test.cc +++ b/internal/test/fake_clock_test.cc @@ -14,9 +14,8 @@ #include "internal/test/fake_clock.h" -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/time/time.h" namespace nearby { namespace { diff --git a/internal/test/fake_device_info.h b/internal/test/fake_device_info.h index 22d491da..f79476f1 100644 --- a/internal/test/fake_device_info.h +++ b/internal/test/fake_device_info.h @@ -27,14 +27,15 @@ #include "absl/strings/string_view.h" #include "internal/base/file_path.h" #include "internal/base/files.h" -#include "internal/platform/device_info.h" #include "internal/platform/implementation/device_info.h" namespace nearby { -class FakeDeviceInfo : public DeviceInfo { +class FakeDeviceInfo : public api::DeviceInfo { public: - std::string GetOsDeviceName() const override { return device_name_; } + std::optional GetOsDeviceName() const override { + return device_name_; + } api::DeviceInfo::DeviceType GetDeviceType() const override { return device_type_; @@ -46,8 +47,10 @@ class FakeDeviceInfo : public DeviceInfo { return download_path_; } - FilePath GetAppDataPath() const override { - return app_data_path_; + FilePath GetLocalAppDataPath(FilePath sub_path) const override { + FilePath path = app_data_path_; + path.append(sub_path); + return path; } FilePath GetTemporaryPath() const override { return temp_path_; } diff --git a/internal/test/fake_device_info_test.cc b/internal/test/fake_device_info_test.cc index 4e7c936e..5c2e8fb2 100644 --- a/internal/test/fake_device_info_test.cc +++ b/internal/test/fake_device_info_test.cc @@ -51,13 +51,16 @@ TEST(FakeDeviceInfo, GetDownloadPath) { Files::GetTemporaryDirectory().append(FilePath("test"))); } -TEST(FakeDeviceInfo, GetAppDataPath) { +TEST(FakeDeviceInfo, GetLocalAppDataPath) { FakeDeviceInfo device_info; - EXPECT_EQ(device_info.GetAppDataPath(), Files::GetTemporaryDirectory()); + EXPECT_EQ(device_info.GetLocalAppDataPath(FilePath("abc")), + Files::GetTemporaryDirectory().append(FilePath("abc"))); device_info.SetAppDataPath( Files::GetTemporaryDirectory().append(FilePath("test"))); - EXPECT_EQ(device_info.GetAppDataPath(), - Files::GetTemporaryDirectory().append(FilePath("test"))); + EXPECT_EQ(device_info.GetLocalAppDataPath(FilePath("def")), + Files::GetTemporaryDirectory() + .append(FilePath("test")) + .append(FilePath("def"))); } TEST(FakeDeviceInfo, GetTemporaryPath) { @@ -76,7 +79,8 @@ TEST(FakeDeviceInfo, GetAvailableDiskSpaceInBytes) { device_info.SetTemporaryPath(FilePath("temp")); device_info.SetAvailableDiskSpaceInBytes(device_info.GetDownloadPath(), 10); - device_info.SetAvailableDiskSpaceInBytes(device_info.GetAppDataPath(), 100); + device_info.SetAvailableDiskSpaceInBytes( + device_info.GetLocalAppDataPath(FilePath()), 100); device_info.SetAvailableDiskSpaceInBytes(device_info.GetTemporaryPath(), 1000); @@ -84,7 +88,8 @@ TEST(FakeDeviceInfo, GetAvailableDiskSpaceInBytes) { device_info.GetAvailableDiskSpaceInBytes(device_info.GetDownloadPath()), 10); EXPECT_EQ( - device_info.GetAvailableDiskSpaceInBytes(device_info.GetAppDataPath()), + device_info.GetAvailableDiskSpaceInBytes( + device_info.GetLocalAppDataPath(FilePath())), 100); EXPECT_EQ( device_info.GetAvailableDiskSpaceInBytes(device_info.GetTemporaryPath()), diff --git a/internal/test/fake_webrtc.cc b/internal/test/fake_webrtc.cc deleted file mode 100644 index e3850c86..00000000 --- a/internal/test/fake_webrtc.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2023 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/test/fake_webrtc.h" - -#include - -namespace nearby { - -FakeWebRtcMedium::FakeWebRtcMedium(CancellationFlag* flag) - : WebRtcMedium(), flag_(flag) {} - -FakeWebRtcMedium::~FakeWebRtcMedium() = default; - -std::unique_ptr -FakeWebRtcMedium::GetSignalingMessenger( - absl::string_view self_id, - const location::nearby::connections::LocationHint& location_hint) { - if (cancel_during_get_signaling_messenger_) { - flag_->Cancel(); - } - - return WebRtcMedium::GetSignalingMessenger(self_id, location_hint); -} - -} // namespace nearby diff --git a/internal/test/fake_webrtc.h b/internal/test/fake_webrtc.h deleted file mode 100644 index b1f54c5c..00000000 --- a/internal/test/fake_webrtc.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2023 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 THIRD_PARTY_NEARBY_INTERNAL_TEST_FAKE_WEBRTC_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_TEST_FAKE_WEBRTC_H_ - -#include - -#include "internal/platform/webrtc.h" - -namespace nearby { - -class FakeWebRtcMedium : public WebRtcMedium { - public: - explicit FakeWebRtcMedium(CancellationFlag* flag); - FakeWebRtcMedium(FakeWebRtcMedium&&) = delete; - FakeWebRtcMedium& operator=(FakeWebRtcMedium&&) = delete; - ~FakeWebRtcMedium() override; - - // WebRtcMedium: - bool IsValid() const override { return is_valid_; } - - std::unique_ptr GetSignalingMessenger( - absl::string_view self_id, - const location::nearby::connections::LocationHint& location_hint) - override; - - void TriggerCancellationDuringGetSignalingMessenger() { - cancel_during_get_signaling_messenger_ = true; - } - - void SetIsValid(bool is_valid) { is_valid_ = is_valid; } - - private: - CancellationFlag* flag_ = nullptr; - bool is_valid_ = true; - bool cancel_during_get_signaling_messenger_ = false; -}; - -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_INTERNAL_TEST_FAKE_WEBRTC_H_ diff --git a/internal/test/mock_account_manager.h b/internal/test/mock_account_manager.h deleted file mode 100644 index 778aa7aa..00000000 --- a/internal/test/mock_account_manager.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2024 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 THIRD_PARTY_NEARBY_INTERNAL_TEST_MOCK_ACCOUNT_MANAGER_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_TEST_MOCK_ACCOUNT_MANAGER_H_ - -#include -#include -#include -#include - -#include "gmock/gmock.h" -#include "absl/functional/any_invocable.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/account_manager.h" -#include "internal/platform/implementation/signin_attempt.h" - -namespace nearby { - -class MockAccountManager : public AccountManager { - public: - MOCK_METHOD(std::optional, GetCurrentAccount, (), (override)); - MOCK_METHOD(std::unique_ptr, Login, - (absl::string_view client_id, absl::string_view client_secret), - (override)); - MOCK_METHOD(void, Logout, - (absl::AnyInvocable logout_callback), - (override)); - MOCK_METHOD(bool, GetAccessToken, - (absl::AnyInvocable)> callback), - (override)); - MOCK_METHOD((std::pair), GetOAuthClientCredential, - (), (override)); - MOCK_METHOD(void, AddObserver, (Observer * observer), (override)); - MOCK_METHOD(void, RemoveObserver, (Observer * observer), (override)); - MOCK_METHOD(void, SaveAccountPrefs, - (absl::string_view user_id, absl::string_view client_id, - absl::string_view client_secret), - (override)); -}; - -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_INTERNAL_TEST_MOCK_ACCOUNT_MANAGER_H_ diff --git a/internal/test/mock_account_observer.h b/internal/test/mock_account_observer.h deleted file mode 100644 index bf4da0e6..00000000 --- a/internal/test/mock_account_observer.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2024 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 THIRD_PARTY_NEARBY_INTERNAL_TEST_MOCK_ACCOUNT_OBSERVER_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_TEST_MOCK_ACCOUNT_OBSERVER_H_ - -#include "gmock/gmock.h" -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/account_manager.h" - -namespace nearby { - -class MockAccountObserver : public AccountManager::Observer { - public: - ~MockAccountObserver() override = default; - - MOCK_METHOD(void, OnLoginSucceeded, (absl::string_view account_id), - (override)); - - MOCK_METHOD(void, OnLogoutSucceeded, - (absl::string_view account_id, bool credential_error), - (override)); -}; - -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_INTERNAL_TEST_MOCK_ACCOUNT_OBSERVER_H_ diff --git a/location/nearby/analytics/cpp/logging/BUILD b/location/nearby/analytics/cpp/logging/BUILD new file mode 100644 index 00000000..f9fa2f06 --- /dev/null +++ b/location/nearby/analytics/cpp/logging/BUILD @@ -0,0 +1,10 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +licenses(["notice"]) + +cc_library( + name = "event_logger", + hdrs = ["event_logger.h"], + visibility = ["//visibility:public"], +) + diff --git a/internal/analytics/event_logger.h b/location/nearby/analytics/cpp/logging/event_logger.h similarity index 50% rename from internal/analytics/event_logger.h rename to location/nearby/analytics/cpp/logging/event_logger.h index b360c659..018fafb4 100644 --- a/internal/analytics/event_logger.h +++ b/location/nearby/analytics/cpp/logging/event_logger.h @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2026 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,30 +12,30 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef NEARBY_ANALYTICS_EVENT_LOGGER_H_ -#define NEARBY_ANALYTICS_EVENT_LOGGER_H_ +#ifndef LOCATION_NEARBY_ANALYTICS_CPP_LOGGING_EVENT_LOGGER_H_ +#define LOCATION_NEARBY_ANALYTICS_CPP_LOGGING_EVENT_LOGGER_H_ -#include "internal/proto/analytics/connections_log.pb.h" -#include "sharing/proto/analytics/nearby_sharing_log.pb.h" +namespace location::nearby::analytics::proto { +class ConnectionsLog; +} // namespace location::nearby::analytics::proto -namespace nearby { -namespace analytics { +namespace nearby::sharing::analytics::proto { +class SharingLog; +} // namespace nearby::sharing::analytics::proto + +namespace nearby::analytics { -// Allows callers to log the proto collected at the client (e.g. Nearby -// Connections, Nearby Sharing, etc). Callers need to implement the API -// if they want to collect this log. class EventLogger { public: virtual ~EventLogger() = default; - // Logs the proto details. Might block to do I/O, e.g. upload - // synchronously to some metrics server. virtual void Log( const location::nearby::analytics::proto::ConnectionsLog& message) = 0; - virtual void Log(const sharing::analytics::proto::SharingLog& message) = 0; + virtual void Log( + const nearby::sharing::analytics::proto::SharingLog& message) = 0; }; -} // namespace analytics -} // namespace nearby +} // namespace nearby::analytics + +#endif // LOCATION_NEARBY_ANALYTICS_CPP_LOGGING_EVENT_LOGGER_H_ -#endif // NEARBY_ANALYTICS_EVENT_LOGGER_H_ diff --git a/location/nearby/analytics/cpp/proto/BUILD b/location/nearby/analytics/cpp/proto/BUILD new file mode 100644 index 00000000..fe716469 --- /dev/null +++ b/location/nearby/analytics/cpp/proto/BUILD @@ -0,0 +1,16 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +licenses(["notice"]) + +cc_library( + name = "connections_log_cc_proto", + hdrs = ["connections_log.pb.h"], + visibility = ["//visibility:public"], +) + +cc_library( + name = "sharing_log_cc_proto", + hdrs = ["sharing_log.pb.h"], + visibility = ["//visibility:public"], +) + diff --git a/presence/presence_action.cc b/location/nearby/analytics/cpp/proto/connections_log.pb.h similarity index 53% rename from presence/presence_action.cc rename to location/nearby/analytics/cpp/proto/connections_log.pb.h index 1265eada..b17fb8a4 100644 --- a/presence/presence_action.cc +++ b/location/nearby/analytics/cpp/proto/connections_log.pb.h @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2026 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,20 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "presence/presence_action.h" +#ifndef LOCATION_NEARBY_ANALYTICS_CPP_PROTO_CONNECTIONS_LOG_PB_H_ +#define LOCATION_NEARBY_ANALYTICS_CPP_PROTO_CONNECTIONS_LOG_PB_H_ -#include "internal/platform/logging.h" +#include -namespace nearby { -namespace presence { +namespace location::nearby::analytics::proto { -PresenceAction::PresenceAction(int action_identifier) - : action_identifier_(action_identifier) { - CHECK(kMinActionIdentifierValue <= action_identifier_ && - action_identifier_ <= kMaxActionIdentifierValue); -} +class ConnectionsLog { + public: + std::string SerializeAsString() const { return {}; } +}; -int PresenceAction::GetActionIdentifier() const { return action_identifier_; } +} // namespace location::nearby::analytics::proto + +#endif // LOCATION_NEARBY_ANALYTICS_CPP_PROTO_CONNECTIONS_LOG_PB_H_ -} // namespace presence -} // namespace nearby diff --git a/presence/fpp/fpp/src/lib.rs b/location/nearby/analytics/cpp/proto/sharing_log.pb.h similarity index 50% rename from presence/fpp/fpp/src/lib.rs rename to location/nearby/analytics/cpp/proto/sharing_log.pb.h index ef30da9e..e25e2dad 100644 --- a/presence/fpp/fpp/src/lib.rs +++ b/location/nearby/analytics/cpp/proto/sharing_log.pb.h @@ -1,10 +1,10 @@ -// Copyright 2023 Google LLC +// Copyright 2026 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 // -// http://www.apache.org/licenses/LICENSE-2.0 +// 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, @@ -12,26 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![deny( - missing_docs, - clippy::indexing_slicing, - clippy::unwrap_used, - clippy::panic, - clippy::expect_used -)] +#ifndef LOCATION_NEARBY_ANALYTICS_CPP_PROTO_SHARING_LOG_PB_H_ +#define LOCATION_NEARBY_ANALYTICS_CPP_PROTO_SHARING_LOG_PB_H_ -//! Processes raw scan results from BLE, UWB and NAN and outputs proximity estimates/zones +#include -mod fspl_converter; +namespace nearby::sharing::analytics::proto { -/// Fused presence Utils -pub mod fused_presence_utils; +class SharingLog { + public: + std::string SerializeAsString() const { return {}; } +}; -/// Presence detector module -pub mod presence_detector; +} // namespace nearby::sharing::analytics::proto -#[cfg(test)] -mod fspl_converter_test; +#endif // LOCATION_NEARBY_ANALYTICS_CPP_PROTO_SHARING_LOG_PB_H_ -#[cfg(test)] -mod presence_detector_test; diff --git a/location/nearby/sharing/lib/account/BUILD b/location/nearby/sharing/lib/account/BUILD new file mode 100644 index 00000000..53237a05 --- /dev/null +++ b/location/nearby/sharing/lib/account/BUILD @@ -0,0 +1,16 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +licenses(["notice"]) + +cc_library( + name = "account_manager", + hdrs = ["account_manager.h"], + visibility = ["//visibility:public"], + deps = [ + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings:string_view", + ], +) + diff --git a/internal/platform/implementation/account_manager.h b/location/nearby/sharing/lib/account/account_manager.h similarity index 50% rename from internal/platform/implementation/account_manager.h rename to location/nearby/sharing/lib/account/account_manager.h index 3ab62731..33b8a5e7 100644 --- a/internal/platform/implementation/account_manager.h +++ b/location/nearby/sharing/lib/account/account_manager.h @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2026 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef PLATFORM_API_ACCOUNT_MANAGER_H_ -#define PLATFORM_API_ACCOUNT_MANAGER_H_ +#ifndef LOCATION_NEARBY_SHARING_LIB_ACCOUNT_ACCOUNT_MANAGER_H_ +#define LOCATION_NEARBY_SHARING_LIB_ACCOUNT_ACCOUNT_MANAGER_H_ #include #include @@ -24,66 +24,62 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" -#include "internal/platform/implementation/account_info.h" -#include "internal/platform/implementation/signin_attempt.h" -namespace nearby { +namespace nearby::sharing { + +struct AccountInfo { + std::string id; + std::string email; + std::string display_name; + std::string given_name; + std::string picture_url; +}; + +enum AuthStatus { + SUCCESS = 0, + ERROR = 1, + UNSUPPORTED = 2, +}; + +class SigninAttempt { + public: + virtual ~SigninAttempt() = default; + + virtual std::string Start( + absl::AnyInvocable + callback) = 0; + virtual void Close() = 0; +}; -// AccountManager manages the accounts are used to access Nearby backend. -// In current design, AccountManager only support one active account. class AccountManager { public: using Account = AccountInfo; - // Observes the activity of the account manager. class Observer { public: virtual ~Observer() = default; - - virtual void OnLoginSucceeded(absl::string_view account_id) = 0; - // |credential_error| is true if the logout is due to critical auth error. + virtual void OnLoginSucceeded(absl::string_view account_id) {} virtual void OnLogoutSucceeded(absl::string_view account_id, - bool credential_error) = 0; + bool credential_error) {} }; virtual ~AccountManager() = default; - // Gets current active account. If no login user, return std::nullopt. virtual std::optional GetCurrentAccount() = 0; - - // Initializes the login process for a Google account from an oauth client. - // |client_id| GCP client_id of the client - // |client_secret| GCP client_secret of the client - // Returns a SigninAttempt object that can be used to complete the login - // process. - virtual std::unique_ptr Login( - absl::string_view client_id, absl::string_view client_secret) = 0; - - // Logs out current active account. |logout_callback| is called when logout is - // completed. - virtual void Logout( - absl::AnyInvocable logout_callback) = 0; - - // Gets access token for the active account. - // |callback| is called with the access token or error status. - // - // Returns false if callback is null. + virtual std::unique_ptr Login(absl::string_view client_id, + absl::string_view client_secret) = 0; + virtual void Logout(absl::AnyInvocable logout_callback) = 0; virtual bool GetAccessToken( absl::AnyInvocable)> callback) = 0; - - // Returns a pair containing the client id and client secret used in the most - // recent Login request. - // If no current user is logged in, returns empty string for both. virtual std::pair GetOAuthClientCredential() = 0; - virtual void AddObserver(Observer* observer) = 0; virtual void RemoveObserver(Observer* observer) = 0; - virtual void SaveAccountPrefs(absl::string_view user_id, absl::string_view client_id, absl::string_view client_secret) = 0; }; -} // namespace nearby +} // namespace nearby::sharing -#endif // PLATFORM_API_ACCOUNT_MANAGER_H_ +#endif // LOCATION_NEARBY_SHARING_LIB_ACCOUNT_ACCOUNT_MANAGER_H_ diff --git a/sharing/linux/stubs/BUILD b/location/nearby/sharing/lib/rpc/BUILD similarity index 50% rename from sharing/linux/stubs/BUILD rename to location/nearby/sharing/lib/rpc/BUILD index 0937b185..65e3cd62 100644 --- a/sharing/linux/stubs/BUILD +++ b/location/nearby/sharing/lib/rpc/BUILD @@ -3,39 +3,29 @@ load("@rules_cc//cc:cc_library.bzl", "cc_library") licenses(["notice"]) cc_library( - name = "rpc", + name = "sharing_rpc_client", hdrs = [ - "grpc_async_client_factory.h", "identity_rpc_types.h", "sharing_rpc_client.h", ], visibility = ["//visibility:public"], deps = [ - "//internal/platform/implementation:account_manager", - "//sharing/analytics", "//sharing/proto:share_cc_proto", - "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/time", "@com_google_protobuf//:protobuf", ], ) cc_library( - name = "sync", - hdrs = ["sync_manager.h"], + name = "grpc_async_client_factory", + hdrs = ["grpc_async_client_factory.h"], visibility = ["//visibility:public"], deps = [ - "//sharing/internal/api:platform", - "@com_google_absl//absl/strings:string_view", + ":sharing_rpc_client", + "//location/nearby/sharing/lib/account:account_manager", + "//sharing/analytics", + "@com_google_absl//absl/status", ], ) -cc_library( - name = "highway_fingerprint", - hdrs = ["highway_fingerprint.h"], - visibility = ["//visibility:public"], - deps = [ - "@com_google_absl//absl/hash", - "@com_google_absl//absl/strings:string_view", - ], -) diff --git a/sharing/linux/stubs/grpc_async_client_factory.h b/location/nearby/sharing/lib/rpc/grpc_async_client_factory.h similarity index 68% rename from sharing/linux/stubs/grpc_async_client_factory.h rename to location/nearby/sharing/lib/rpc/grpc_async_client_factory.h index 3e685e69..996cffc0 100644 --- a/sharing/linux/stubs/grpc_async_client_factory.h +++ b/location/nearby/sharing/lib/rpc/grpc_async_client_factory.h @@ -12,16 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_GRPC_ASYNC_CLIENT_FACTORY_H_ -#define THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_GRPC_ASYNC_CLIENT_FACTORY_H_ +#ifndef LOCATION_NEARBY_SHARING_LIB_RPC_GRPC_ASYNC_CLIENT_FACTORY_H_ +#define LOCATION_NEARBY_SHARING_LIB_RPC_GRPC_ASYNC_CLIENT_FACTORY_H_ #include -#include +#include "absl/status/status.h" #include "internal/platform/clock.h" -#include "internal/platform/implementation/account_manager.h" +#include "location/nearby/sharing/lib/account/account_manager.h" +#include "location/nearby/sharing/lib/rpc/sharing_rpc_client.h" #include "sharing/analytics/analytics_recorder.h" -#include "sharing/linux/stubs/sharing_rpc_client.h" namespace nearby::sharing::platform::common { namespace internal { @@ -40,22 +40,36 @@ class NoOpIdentityRpcClient : public nearby::sharing::api::IdentityRpcClient { public: void QuerySharedCredentials( google::nearby::identity::v1::QuerySharedCredentialsRequest request, - QuerySharedCredentialsCallback callback) override { + absl::Duration timeout, QuerySharedCredentialsCallback callback) override { static_cast(request); + static_cast(timeout); callback(google::nearby::identity::v1::QuerySharedCredentialsResponse()); } + void QuerySharedCredentialsWithBindingIds( + google::nearby::identity::v1::QuerySharedCredentialsWithBindingIdsRequest + request, + absl::Duration timeout, + QuerySharedCredentialsWithBindingIdsCallback callback) override { + static_cast(request); + static_cast(timeout); + callback(google::nearby::identity::v1:: + QuerySharedCredentialsWithBindingIdsResponse()); + } + void PublishDevice( google::nearby::identity::v1::PublishDeviceRequest request, - PublishDeviceCallback callback) override { + absl::Duration timeout, PublishDeviceCallback callback) override { static_cast(request); + static_cast(timeout); callback(google::nearby::identity::v1::PublishDeviceResponse()); } void GetAccountInfo( google::nearby::identity::v1::GetAccountInfoRequest request, - GetAccountInfoCallback callback) override { + absl::Duration timeout, GetAccountInfoCallback callback) override { static_cast(request); + static_cast(timeout); callback(google::nearby::identity::v1::GetAccountInfoResponse()); } }; @@ -83,4 +97,5 @@ class GrpcAsyncClientFactory { } // namespace nearby::sharing::platform::common -#endif // THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_GRPC_ASYNC_CLIENT_FACTORY_H_ +#endif // LOCATION_NEARBY_SHARING_LIB_RPC_GRPC_ASYNC_CLIENT_FACTORY_H_ + diff --git a/sharing/linux/stubs/identity_rpc_types.h b/location/nearby/sharing/lib/rpc/identity_rpc_types.h similarity index 92% rename from sharing/linux/stubs/identity_rpc_types.h rename to location/nearby/sharing/lib/rpc/identity_rpc_types.h index 7ce12065..93392219 100644 --- a/sharing/linux/stubs/identity_rpc_types.h +++ b/location/nearby/sharing/lib/rpc/identity_rpc_types.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_IDENTITY_RPC_TYPES_H_ -#define THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_IDENTITY_RPC_TYPES_H_ +#ifndef LOCATION_NEARBY_SHARING_LIB_RPC_IDENTITY_RPC_TYPES_H_ +#define LOCATION_NEARBY_SHARING_LIB_RPC_IDENTITY_RPC_TYPES_H_ #include @@ -34,13 +34,10 @@ class SharedCredential { void set_id(uint64_t id) { id_ = id; } uint64_t id() const { return id_; } - void set_data(std::string data) { data_ = std::move(data); } const std::string& data() const { return data_; } - void set_data_type(DataType data_type) { data_type_ = data_type; } DataType data_type() const { return data_type_; } - google::protobuf::Timestamp* mutable_expiration_time() { return &expiration_time_; } @@ -65,7 +62,6 @@ class PerVisibilitySharedCredentials { void set_visibility(Visibility visibility) { visibility_ = visibility; } Visibility visibility() const { return visibility_; } - SharedCredential* add_shared_credentials() { shared_credentials_.emplace_back(); return &shared_credentials_.back(); @@ -89,15 +85,12 @@ class Device { void set_name(std::string name) { name_ = std::move(name); } const std::string& name() const { return name_; } - void set_display_name(std::string display_name) { display_name_ = std::move(display_name); } const std::string& display_name() const { return display_name_; } - void set_contact(Contact contact) { contact_ = contact; } Contact contact() const { return contact_; } - PerVisibilitySharedCredentials* add_per_visibility_shared_credentials() { per_visibility_shared_credentials_.emplace_back(); return &per_visibility_shared_credentials_.back(); @@ -118,7 +111,6 @@ class QuerySharedCredentialsRequest { public: void set_name(std::string name) { name_ = std::move(name); } const std::string& name() const { return name_; } - void set_page_token(std::string page_token) { page_token_ = std::move(page_token); } @@ -138,7 +130,6 @@ class QuerySharedCredentialsResponse { const std::vector& shared_credentials() const { return shared_credentials_; } - void set_next_page_token(std::string next_page_token) { next_page_token_ = std::move(next_page_token); } @@ -149,6 +140,12 @@ class QuerySharedCredentialsResponse { std::string next_page_token_; }; +class QuerySharedCredentialsWithBindingIdsRequest + : public QuerySharedCredentialsRequest {}; + +class QuerySharedCredentialsWithBindingIdsResponse + : public QuerySharedCredentialsResponse {}; + class PublishDeviceRequest { public: Device* mutable_device() { return &device_; } @@ -163,6 +160,7 @@ class PublishDeviceResponse { enum ContactUpdate { CONTACT_UPDATE_UNKNOWN = 0, CONTACT_UPDATE_REMOVED = 1, + CONTACT_UPDATE_ADDED = 2, }; void add_contact_updates(ContactUpdate contact_update) { @@ -181,6 +179,7 @@ class GetAccountInfoRequest {}; class AccountInfo { public: enum Capability { + CAPABILITY_UNSPECIFIED = 0, CAPABILITY_UNKNOWN = 0, CAPABILITY_TITANIUM = 1, }; @@ -205,4 +204,5 @@ class GetAccountInfoResponse { } // namespace google::nearby::identity::v1 -#endif // THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_IDENTITY_RPC_TYPES_H_ +#endif // LOCATION_NEARBY_SHARING_LIB_RPC_IDENTITY_RPC_TYPES_H_ + diff --git a/sharing/linux/stubs/sharing_rpc_client.h b/location/nearby/sharing/lib/rpc/sharing_rpc_client.h similarity index 65% rename from sharing/linux/stubs/sharing_rpc_client.h rename to location/nearby/sharing/lib/rpc/sharing_rpc_client.h index eec3a7ab..0509cd89 100644 --- a/sharing/linux/stubs/sharing_rpc_client.h +++ b/location/nearby/sharing/lib/rpc/sharing_rpc_client.h @@ -12,13 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_SHARING_RPC_CLIENT_H_ -#define THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_SHARING_RPC_CLIENT_H_ +#ifndef LOCATION_NEARBY_SHARING_LIB_RPC_SHARING_RPC_CLIENT_H_ +#define LOCATION_NEARBY_SHARING_LIB_RPC_SHARING_RPC_CLIENT_H_ #include #include "absl/status/statusor.h" -#include "sharing/linux/stubs/identity_rpc_types.h" +#include "absl/time/time.h" +#include "location/nearby/sharing/lib/rpc/identity_rpc_types.h" #include "sharing/proto/contact_rpc.pb.h" namespace nearby::sharing::api { @@ -37,9 +38,14 @@ class SharingRpcClient { class IdentityRpcClient { public: + static constexpr absl::Duration kTimeout = absl::Seconds(30); + using QuerySharedCredentialsCallback = std::function&)>; + using QuerySharedCredentialsWithBindingIdsCallback = std::function&)>; using PublishDeviceCallback = std::function&)>; using GetAccountInfoCallback = std::function +#include +#include +#include + +namespace nearby::sharing::sync { + +class SyncBinding { + public: + enum SourceDeviceType { + SOURCE_DEVICE_TYPE_UNKNOWN = 0, + SOURCE_DEVICE_TYPE_PHONE = 1, + SOURCE_DEVICE_TYPE_TABLET = 2, + SOURCE_DEVICE_TYPE_LAPTOP = 3, + SOURCE_DEVICE_TYPE_CAR = 4, + SOURCE_DEVICE_TYPE_FOLDABLE = 5, + SOURCE_DEVICE_TYPE_XR = 6, + }; + + void set_binding_id(std::string binding_id) { + binding_id_ = std::move(binding_id); + } + void set_binding_id(std::string_view binding_id) { + binding_id_ = std::string(binding_id); + } + const std::string& binding_id() const { return binding_id_; } + void set_source_name(std::string source_name) { + source_name_ = std::move(source_name); + } + void set_source_name(std::string_view source_name) { + source_name_ = std::string(source_name); + } + const std::string& source_name() const { return source_name_; } + void set_destination_directory(std::string destination_directory) { + destination_directory_ = std::move(destination_directory); + } + void set_destination_directory(std::string_view destination_directory) { + destination_directory_ = std::string(destination_directory); + } + const std::string& destination_directory() const { + return destination_directory_; + } + void set_source_device_type(SourceDeviceType source_device_type) { + source_device_type_ = source_device_type; + } + SourceDeviceType source_device_type() const { return source_device_type_; } + + private: + std::string binding_id_; + std::string source_name_; + std::string destination_directory_; + SourceDeviceType source_device_type_ = SOURCE_DEVICE_TYPE_UNKNOWN; +}; + +class SyncBindingPrefs { + public: + static const SyncBindingPrefs& default_instance() { + static const SyncBindingPrefs* prefs = new SyncBindingPrefs(); + return *prefs; + } + + SyncBinding* add_sync_bindings() { + sync_bindings_.emplace_back(); + return &sync_bindings_.back(); + } + const std::vector& sync_bindings() const { + return sync_bindings_; + } + const SyncBinding& sync_bindings(int index) const { + return sync_bindings_.at(index); + } + int sync_bindings_size() const { + return static_cast(sync_bindings_.size()); + } + + bool SerializeToString(std::string* output) const { + if (output == nullptr) { + return false; + } + output->clear(); + for (const SyncBinding& binding : sync_bindings_) { + output->append(Escape(binding.binding_id())); + output->push_back('\t'); + output->append(Escape(binding.source_name())); + output->push_back('\t'); + output->append(Escape(binding.destination_directory())); + output->push_back('\t'); + output->append(std::to_string(static_cast(binding.source_device_type()))); + output->push_back('\n'); + } + return true; + } + + bool ParseFromString(const std::string& input) { + sync_bindings_.clear(); + size_t start = 0; + while (start < input.size()) { + size_t end = input.find('\n', start); + std::string line = input.substr(start, end == std::string::npos + ? std::string::npos + : end - start); + if (!line.empty()) { + std::vector fields; + size_t field_start = 0; + while (fields.size() < 4) { + size_t field_end = line.find('\t', field_start); + fields.push_back(Unescape(line.substr( + field_start, field_end == std::string::npos + ? std::string::npos + : field_end - field_start))); + if (field_end == std::string::npos) { + break; + } + field_start = field_end + 1; + } + if (fields.size() != 4) { + return false; + } + SyncBinding binding; + binding.set_binding_id(fields[0]); + binding.set_source_name(fields[1]); + binding.set_destination_directory(fields[2]); + binding.set_source_device_type( + static_cast(std::stoi(fields[3]))); + sync_bindings_.push_back(std::move(binding)); + } + if (end == std::string::npos) { + break; + } + start = end + 1; + } + return true; + } + + private: + static std::string Escape(const std::string& value) { + std::string escaped; + for (char c : value) { + if (c == '\\' || c == '\t' || c == '\n') { + escaped.push_back('\\'); + } + if (c == '\t') { + escaped.push_back('t'); + } else if (c == '\n') { + escaped.push_back('n'); + } else { + escaped.push_back(c); + } + } + return escaped; + } + + static std::string Unescape(const std::string& value) { + std::string unescaped; + bool escaping = false; + for (char c : value) { + if (escaping) { + if (c == 't') { + unescaped.push_back('\t'); + } else if (c == 'n') { + unescaped.push_back('\n'); + } else { + unescaped.push_back(c); + } + escaping = false; + } else if (c == '\\') { + escaping = true; + } else { + unescaped.push_back(c); + } + } + return unescaped; + } + + std::vector sync_bindings_; +}; + +class SyncConfigPrefs { + public: + bool SerializeToString(std::string* output) const { + if (output == nullptr) { + return false; + } + output->clear(); + return true; + } + + bool ParseFromString(const std::string& input) { + return input.empty(); + } +}; + +} // namespace nearby::sharing::sync + +#endif // LOCATION_NEARBY_SHARING_LIB_SYNC_SYNC_BINDING_PREFS_PB_H_ diff --git a/location/nearby/sharing/lib/sync/sync_manager.h b/location/nearby/sharing/lib/sync/sync_manager.h new file mode 100644 index 00000000..118f10df --- /dev/null +++ b/location/nearby/sharing/lib/sync/sync_manager.h @@ -0,0 +1,126 @@ +// Copyright 2026 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 LOCATION_NEARBY_SHARING_LIB_SYNC_SYNC_MANAGER_H_ +#define LOCATION_NEARBY_SHARING_LIB_SYNC_SYNC_MANAGER_H_ + +#include +#include +#include + +#include "absl/functional/any_invocable.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "internal/base/file_path.h" +#include "location/nearby/sharing/lib/rpc/sharing_rpc_client.h" +#include "location/nearby/sharing/lib/sync/sync_binding_prefs.pb.h" +#include "sharing/internal/api/preference_manager.h" + +namespace nearby::sharing { + +class SyncManager { + public: + SyncManager(api::IdentityRpcClient* identity_client, + api::PreferenceManager* preference_manager) + : identity_client_(identity_client), preference_manager_(preference_manager) {} + + explicit SyncManager(api::PreferenceManager* preference_manager) + : SyncManager(nullptr, preference_manager) {} + + void AsyncInitiateSyncBinding( + absl::AnyInvocable)> callback) { + static_cast(identity_client_); + if (callback) { + std::move(callback)(absl::UnavailableError( + "Sync binding RPC is not available in this build")); + } + } + + void AddSyncBinding(const sync::SyncBinding& binding) { + if (preference_manager_ == nullptr) { + return; + } + sync::SyncBindingPrefs prefs = + preference_manager_->GetSyncBindingValue().value_or( + sync::SyncBindingPrefs()); + sync::SyncBinding* stored = prefs.add_sync_bindings(); + *stored = binding; + preference_manager_->SetSyncBindingValue(prefs); + } + + std::optional GetSyncBinding( + absl::string_view binding_id) const { + if (preference_manager_ == nullptr) { + return std::nullopt; + } + std::optional prefs = + preference_manager_->GetSyncBindingValue(); + if (!prefs.has_value()) { + return std::nullopt; + } + for (const sync::SyncBinding& binding : prefs->sync_bindings()) { + if (binding.binding_id() == binding_id) { + return binding; + } + } + return std::nullopt; + } + + bool IsFileSyncBinding(absl::string_view binding_id) const { + return GetSyncBinding(binding_id).has_value(); + } + + std::optional GetSyncConfig( + absl::string_view binding_id) const { + static_cast(binding_id); + return std::nullopt; + } + + absl::StatusOr UpdateSyncBindingDestinationDirectory( + absl::string_view binding_id, FilePath save_path) { + if (preference_manager_ == nullptr) { + return absl::FailedPreconditionError("Preference manager is unavailable"); + } + sync::SyncBindingPrefs prefs = + preference_manager_->GetSyncBindingValue().value_or( + sync::SyncBindingPrefs()); + std::optional original_path; + sync::SyncBindingPrefs updated_prefs; + for (const sync::SyncBinding& binding : prefs.sync_bindings()) { + sync::SyncBinding* updated_binding = updated_prefs.add_sync_bindings(); + *updated_binding = binding; + if (binding.binding_id() == binding_id) { + original_path = FilePath(binding.destination_directory()); + updated_binding->set_destination_directory(save_path.ToString()); + } + } + if (!original_path.has_value()) { + return absl::NotFoundError( + absl::StrCat("Sync binding not found: ", binding_id)); + } + preference_manager_->SetSyncBindingValue(updated_prefs); + return *original_path; + } + + private: + api::IdentityRpcClient* identity_client_; + api::PreferenceManager* preference_manager_; +}; + +} // namespace nearby::sharing + +#endif // LOCATION_NEARBY_SHARING_LIB_SYNC_SYNC_MANAGER_H_ + diff --git a/presence/BUILD b/presence/BUILD deleted file mode 100644 index 5aaf4501..00000000 --- a/presence/BUILD +++ /dev/null @@ -1,264 +0,0 @@ -# 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. - -load("@rules_cc//cc:cc_library.bzl", "cc_library") -load("@rules_cc//cc:cc_binary.bzl", "cc_binary") -load("@rules_cc//cc:cc_test.bzl", "cc_test") - -package(default_visibility = ["//:__subpackages__"]) - -licenses(["notice"]) - -cc_library( - name = "presence", - srcs = [ - "presence_client_impl.cc", - "presence_device_provider.cc", - "presence_service_impl.cc", - ], - hdrs = [ - "presence_client.h", - "presence_client_impl.h", - "presence_device_provider.h", - "presence_service.h", - "presence_service_impl.h", - ], - deps = [ - ":types", - "//internal/interop:authentication_status", - "//internal/interop:authentication_transport_interface", - "//internal/interop:device", - "//internal/platform:base", - "//internal/platform:logging", - "//internal/platform:types", - "//internal/platform/implementation:comm", - "//internal/platform/implementation:types", - "//internal/proto:local_credential_cc_proto", - "//internal/proto:metadata_cc_proto", - "//presence/implementation:internal", # build_cleaner: keep - "//presence/implementation/mediums", - "//presence/proto:presence_frame_cc_proto", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings:string_view", - "@com_google_absl//absl/time", - "@com_google_absl//absl/types:variant", - ], -) - -cc_library( - name = "test_support", - testonly = 1, - srcs = [ - "fake_presence_client.cc", - "fake_presence_service.cc", - ], - hdrs = [ - "fake_presence_client.h", - "fake_presence_service.h", - ], - deps = [ - ":presence", - ":types", - "//internal/interop:device", - "//internal/interop:test_support", - "//internal/platform:types", - "//internal/proto:metadata_cc_proto", - "//presence/implementation:internal", # build_cleaner: keep - "@com_google_absl//absl/status:statusor", - ], -) - -cc_library( - name = "types", - srcs = [ - "device_motion.cc", - "discovery_filter.cc", - "presence_action.cc", - "presence_device.cc", - "presence_zone.cc", - "scan_request_builder.cc", - ], - hdrs = [ - "broadcast_options.h", - "broadcast_request.h", - "data_element.h", - "data_types.h", - "device_motion.h", - "discovery_filter.h", - "discovery_options.h", - "power_mode.h", - "presence_action.h", - "presence_device.h", - "presence_zone.h", - "scan_request.h", - "scan_request_builder.h", - ], - deps = [ - "//connections/implementation/proto:offline_wire_formats_cc_proto", - "//internal/interop:device", - "//internal/platform:base", - "//internal/platform:connection_info", - "//internal/platform:logging", - "//internal/platform/implementation:types", - "//internal/proto:credential_cc_proto", - "//internal/proto:metadata_cc_proto", - "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - "@com_google_absl//absl/types:variant", - ], -) - -cc_test( - name = "types_test", - size = "small", - srcs = [ - "broadcast_options_test.cc", - "device_motion_test.cc", - "discovery_filter_test.cc", - "discovery_options_test.cc", - "presence_action_test.cc", - "presence_device_test.cc", - "presence_identity_test.cc", - "presence_zone_test.cc", - "scan_request_builder_test.cc", - ], - shard_count = 6, - deps = [ - ":types", - "//connections/implementation/proto:offline_wire_formats_cc_proto", - "//internal/platform:connection_info", - "//internal/platform:types", - "//internal/proto:credential_cc_proto", - "//internal/proto:metadata_cc_proto", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/types:variant", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_binary( - name = "presence_advertiser_demo", - srcs = ["presence_advertiser_demo.cc"], - deps = [ - ":types", - ":presence_advertiser_demo_platform_impl", - "//internal/platform:types", - "//presence/implementation:internal", - "@com_google_absl//absl/status", - "@com_google_absl//absl/synchronization", - ], -) - -cc_binary( - name = "presence_scanner_demo", - srcs = ["presence_scanner_demo.cc"], - deps = [ - ":presence", - ":presence_advertiser_demo_platform_impl", - "//internal/platform:types", - "@com_google_absl//absl/synchronization", - ], -) - -cc_library( - name = "presence_advertiser_demo_platform_impl", - alwayslink = 1, - deps = select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "@platforms//os:linux": [ - "//internal/platform/implementation/linux:linux", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "credential_test", - size = "small", - srcs = [ - "credential_test.cc", - "presence_identity_test.cc", - ], - shard_count = 6, - deps = [ - "//internal/platform:uuid", - "//internal/proto:credential_cc_proto", - "//internal/proto:local_credential_cc_proto", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "presence_test", - size = "small", - srcs = [ - "presence_client_test.cc", - "presence_device_provider_test.cc", - "presence_service_test.cc", - ], - shard_count = 6, - deps = [ - ":presence", - ":types", - "//internal/crypto", - "//internal/interop:authentication_status", - "//internal/interop:authentication_transport_interface", - "//internal/interop:device", - "//internal/platform:test_util", - "//internal/platform:types", - "//internal/platform/implementation:comm", - "//internal/platform/implementation:types", - "//internal/proto:credential_cc_proto", - "//internal/proto:local_credential_cc_proto", - "//internal/proto:metadata_cc_proto", - "//presence/implementation:internal", - "//presence/implementation:internal_test", - "//presence/proto:presence_frame_cc_proto", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) diff --git a/presence/broadcast_options.h b/presence/broadcast_options.h deleted file mode 100644 index 18d11440..00000000 --- a/presence/broadcast_options.h +++ /dev/null @@ -1,36 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_BROADCAST_OPTIONS_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_BROADCAST_OPTIONS_H_ - -#include -namespace nearby { -namespace presence { -struct BroadcastOptions { - const std::int64_t reporting_interval_millis; -}; - -inline bool operator==(const BroadcastOptions& o1, const BroadcastOptions& o2) { - return o1.reporting_interval_millis == o2.reporting_interval_millis; -} - -inline bool operator!=(const BroadcastOptions& o1, const BroadcastOptions& o2) { - return !(o1 == o2); -} - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_BROADCAST_OPTIONS_H_ diff --git a/presence/broadcast_options_test.cc b/presence/broadcast_options_test.cc deleted file mode 100644 index e2e5c381..00000000 --- a/presence/broadcast_options_test.cc +++ /dev/null @@ -1,53 +0,0 @@ -// 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 "presence/broadcast_options.h" - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" - -namespace nearby { -namespace presence { -namespace { - -constexpr std::int64_t kReportingIntervalMillis1 = 1000; -constexpr std::int64_t kReportingIntervalMillis2 = 2000; -TEST(BroadcastOptionsTest, NoDefaultConstructor) { - EXPECT_FALSE(std::is_trivially_constructible::value); -} - -TEST(BroadcastOptionsTest, ExplicitInitEquals) { - BroadcastOptions option1 = {kReportingIntervalMillis1}; - BroadcastOptions option2 = {kReportingIntervalMillis1}; - EXPECT_EQ(option1, option2); - EXPECT_EQ(option1.reporting_interval_millis, kReportingIntervalMillis1); -} - -TEST(BroadcastOptionsTest, ExplicitInitNotEquals) { - BroadcastOptions option1 = {kReportingIntervalMillis1}; - BroadcastOptions option2 = {kReportingIntervalMillis2}; - EXPECT_NE(option1, option2); -} - -TEST(BroadcastOptionsTest, CopyInitEquals) { - BroadcastOptions option1 = {kReportingIntervalMillis1}; - BroadcastOptions option2 = {option1}; - - EXPECT_EQ(option1, option2); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/broadcast_request.h b/presence/broadcast_request.h deleted file mode 100644 index 598ffdf3..00000000 --- a/presence/broadcast_request.h +++ /dev/null @@ -1,77 +0,0 @@ -// 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 THIRD_PARTY_NEARBY_PRESENCE_BROADCAST_REQUEST_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_BROADCAST_REQUEST_H_ - -#include -#include - -#include "absl/types/variant.h" -#include "internal/proto/credential.pb.h" -#include "presence/data_element.h" -#include "presence/power_mode.h" - -namespace nearby { -namespace presence { - -// Broadcast parameter for presence features. -struct PresenceBroadcast { - struct BroadcastSection { - // Presence identity type. - ::nearby::internal::IdentityType identity = - ::nearby::internal::IdentityType::IDENTITY_TYPE_UNSPECIFIED; - - // Additional Data Elements. - // The Presence SDK generates: - // - Salt, - // - (Private/Trusted/Public/Provisioned) Identity, - // - TX power, - // - Advertisement signature - // Data Elements when they are required in the advertisement. Other Data - // Elements are provided by the client application. - // Nearby SDK encrypts Data ELements before broadcasting if a non-public - // `PresenceIdentity` is provided. - std::vector extended_properties; - - // Account name used to select private credentials. - std::string account_name; - - // Manager app id, used to select private credentials. - std::string manager_app_id; - }; - - std::vector sections; -}; - -// Broadcast request for legacy Android T, which needs to provide credential -// and salt in the broadcast parameters. -// TODO(b/243443813) - Support Legacy Broadcast Request -struct LegacyPresenceBroadcast {}; - -// Nearby Presence advertisement request options. -struct BroadcastRequest { - // Calibrated TX power. The broadcast recipient uses it to calculate the - // distance between both devices. - int tx_power; - - // The broadcast frequency hint. - PowerMode power_mode; - - absl::variant variant; -}; - -} // namespace presence -} // namespace nearby -#endif // THIRD_PARTY_NEARBY_PRESENCE_BROADCAST_REQUEST_H_ diff --git a/presence/credential_test.cc b/presence/credential_test.cc deleted file mode 100644 index 8b1ba211..00000000 --- a/presence/credential_test.cc +++ /dev/null @@ -1,79 +0,0 @@ -// 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 -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "internal/platform/uuid.h" -#include "internal/proto/credential.pb.h" -#include "internal/proto/local_credential.pb.h" - -namespace nearby { -namespace presence { -namespace { -using ::nearby::internal::LocalCredential; -using ::nearby::internal::SharedCredential; -using ::nearby::internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP; - -using ::protobuf_matchers::EqualsProto; - -TEST(CredentialsTest, NoDefaultConstructor) { - EXPECT_FALSE(std::is_trivially_constructible::value); - EXPECT_FALSE(std::is_trivially_constructible::value); -} - -TEST(CredentialsTest, InitSharedCredential) { - SharedCredential pc1 = {}; - SharedCredential pc2 = {}; - EXPECT_THAT(pc1, EqualsProto(pc2)); - pc1.set_identity_type(IDENTITY_TYPE_PRIVATE_GROUP); - EXPECT_THAT(pc1, ::testing::Not(EqualsProto(pc2))); - pc2.set_identity_type(IDENTITY_TYPE_PRIVATE_GROUP); - EXPECT_THAT(pc1, EqualsProto(pc2)); -} - -TEST(CredentialsTest, InitLocalCredential) { - LocalCredential pc1 = {}; - LocalCredential pc2 = {}; - EXPECT_THAT(pc1, EqualsProto(pc2)); - pc1.set_identity_type(IDENTITY_TYPE_PRIVATE_GROUP); - EXPECT_THAT(pc1, ::testing::Not(EqualsProto(pc2))); - pc2.set_identity_type(IDENTITY_TYPE_PRIVATE_GROUP); - EXPECT_THAT(pc1, EqualsProto(pc2)); -} - -TEST(CredentialsTest, CopyLocalCredential) { - LocalCredential pc1 = {}; - pc1.set_identity_type(IDENTITY_TYPE_PRIVATE_GROUP); - auto salts = pc1.mutable_consumed_salts(); - salts->insert(std::pair(15, true)); - LocalCredential pc1_copy = {pc1}; - EXPECT_THAT(pc1, EqualsProto(pc1_copy)); -} - -TEST(CredentialsTest, CopySharedCredential) { - SharedCredential pc1 = {}; - pc1.set_identity_type(IDENTITY_TYPE_PRIVATE_GROUP); - for (const uint8_t byte : nearby::Uuid().data()) { - pc1.mutable_secret_id()->push_back(byte); - } - SharedCredential pc1_copy = {pc1}; - EXPECT_THAT(pc1, EqualsProto(pc1_copy)); -} -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/data_element.h b/presence/data_element.h deleted file mode 100644 index eb2ffebb..00000000 --- a/presence/data_element.h +++ /dev/null @@ -1,111 +0,0 @@ -// 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 THIRD_PARTY_NEARBY_PRESENCE_DATA_ELEMENT_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_DATA_ELEMENT_H_ - -#include - -#include -#include -#include - -#include "absl/strings/escaping.h" -#include "absl/strings/string_view.h" -namespace nearby { -namespace presence { - -// Reserved Action types when the field type is kActionFieldType. -// The values are bit numbers in BE ordering. -// TODO(b/338107166): these are out of date, need to be updated to latest spec -enum class ActionBit { - kCallTransferAction = 4, - kActiveUnlockAction = 8, - kNearbyShareAction = 9, - kInstantTetheringAction = 10, - kPhoneHubAction = 11, - kPresenceManagerAction = 12, - kFinderAction = 13, - kFastPairSassAction = 14, - kTapToTransferAction = 15, - kLastAction -}; - -// helpful for enumerating overall all possible action bit types, this must be -// kept in sync with the above enum -constexpr std::initializer_list kAllActionBits = { - ActionBit::kCallTransferAction, ActionBit::kActiveUnlockAction, - ActionBit::kNearbyShareAction, ActionBit::kInstantTetheringAction, - ActionBit::kPhoneHubAction, ActionBit::kPresenceManagerAction, - ActionBit::kFinderAction, ActionBit::kFastPairSassAction, - ActionBit::kTapToTransferAction}; - -/** Describes a custom Data element in NP advertisement. */ -class DataElement { - public: - // The field types listed below require special processing when generating and - // parsing NP advertisements. - static constexpr int kSaltFieldType = 0; - static constexpr int kPrivateGroupIdentityFieldType = 1; - static constexpr int kContactsGroupIdentityFieldType = 2; - static constexpr int kPublicIdentityFieldType = 3; - static constexpr int kTxPowerFieldType = 5; - static constexpr int kActionFieldType = 6; - static constexpr int kModelIdFieldType = 7; - static constexpr int kEddystoneIdFieldType = 8; - static constexpr int kAccountKeyDataFieldType = 9; - static constexpr int kConnectionStatusFieldType = 10; - static constexpr int kBatteryFieldType = 11; - static constexpr int kAdvertisementSignature = 12; - static constexpr int kContextTimestampFieldType = 13; - // Maximum allowed Data Element's value length - static constexpr int kMaxDataElementLength = 15; - // Maximum allowed Data Element's type - static constexpr int kMaxDataElementType = 15; - // The DE header is (length << kDataElementLengthShift | type) - static constexpr int kDataElementLengthShift = 4; - - DataElement(uint16_t type, absl::string_view value) - : type_(type), value_(value) {} - - DataElement(uint16_t type, uint8_t value) - : type_(type), - value_(reinterpret_cast(&value), sizeof(value)) {} - - explicit DataElement(ActionBit action) - : DataElement(kActionFieldType, static_cast(action)) {} - - ~DataElement() = default; - - uint16_t GetType() const { return type_; } - absl::string_view GetValue() const { return value_; } - - private: - uint16_t type_; - std::string value_; -}; - -inline bool operator==(const DataElement& i1, const DataElement& i2) { - return i1.GetType() == i2.GetType() && i1.GetValue() == i2.GetValue(); -} - -inline std::ostream& operator<<(std::ostream& os, const DataElement& elem) { - return os << "DataElement(" << elem.GetType() << ", " - << absl::BytesToHexString(elem.GetValue()) << ")"; -} - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_DATA_ELEMENT_H_ diff --git a/presence/data_types.h b/presence/data_types.h deleted file mode 100644 index 48fdde74..00000000 --- a/presence/data_types.h +++ /dev/null @@ -1,64 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_SCAN_CALLBACK_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_SCAN_CALLBACK_H_ - -#include - -#include "absl/functional/any_invocable.h" -#include "internal/platform/logging.h" -#include "presence/presence_device.h" - -namespace nearby { -namespace presence { - -// Unique Scan Session Identifier. -using ScanSessionId = uint64_t; - -// Callers would provide the implementation of these callbacks. If callers -// don't need these signal updates, they can skip with the provided default -// empty functions. -struct ScanCallback { - // Updates client with the result of start scanning. - absl::AnyInvocable start_scan_cb = [](absl::Status) {}; - - // Reports a {@link PresenceDevice} being discovered. - absl::AnyInvocable on_discovered_cb = - [](PresenceDevice) {}; - - // Reports a {@link PresenceDevice} information(distance, and etc) - // changed. - absl::AnyInvocable on_updated_cb = [](PresenceDevice) { - }; - - // Reports a {@link PresenceDevice} is no longer within range. - absl::AnyInvocable on_lost_cb = [](PresenceDevice) {}; -}; - -// Unique Broadcast Session Identifier. -using BroadcastSessionId = uint64_t; - -// Callers would provide the implementation of these callbacks. If callers -// don't need these signal updates, they can skip with the provided default -// empty functions. -struct BroadcastCallback { - absl::AnyInvocable start_broadcast_cb = [](absl::Status) { - }; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_SCAN_CALLBACK_H_ diff --git a/presence/device_motion.h b/presence/device_motion.h deleted file mode 100644 index 439b5349..00000000 --- a/presence/device_motion.h +++ /dev/null @@ -1,47 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_DEVICE_MOTION_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_DEVICE_MOTION_H_ - -namespace nearby { -namespace presence { -class DeviceMotion { - public: - enum class MotionType { - kPointAndHold = 0, - kStationaryAndHold = 1, - }; - DeviceMotion(MotionType motion_type = MotionType::kPointAndHold, - float confidence = 0) noexcept; - MotionType GetMotionType() const; - float GetConfidence() const; - - private: - const MotionType motion_type_; - const float confidence_; -}; - -inline bool operator==(const DeviceMotion& m1, const DeviceMotion& m2) { - return m1.GetMotionType() == m2.GetMotionType() && - m1.GetConfidence() == m2.GetConfidence(); -} -inline bool operator!=(const DeviceMotion& m1, const DeviceMotion& m2) { - return !(m1 == m2); -} - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_DEVICE_MOTION_H_ diff --git a/presence/device_motion_test.cc b/presence/device_motion_test.cc deleted file mode 100644 index 1155903a..00000000 --- a/presence/device_motion_test.cc +++ /dev/null @@ -1,61 +0,0 @@ -// 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 "presence/device_motion.h" - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" - -namespace nearby { -namespace presence { -namespace { -static const DeviceMotion::MotionType kDefaultMotionType = - DeviceMotion::MotionType::kPointAndHold; -static const float kDefaultConfidence = 0; -static const float kConfidenceForTest = 0.1; -TEST(DeviceMotionTest, DefaultConstructorWorks) { - DeviceMotion motion; - EXPECT_EQ(motion.GetMotionType(), kDefaultMotionType); - EXPECT_EQ(motion.GetConfidence(), kDefaultConfidence); -} - -TEST(DeviceMotionTest, DefaultEquals) { - DeviceMotion motion1; - DeviceMotion motion2; - EXPECT_EQ(motion1, motion2); -} - -TEST(DeviceMotionTest, ExplicitInitEquals) { - DeviceMotion motion1 = {kDefaultMotionType, kConfidenceForTest}; - DeviceMotion motion2 = {kDefaultMotionType, kConfidenceForTest}; - EXPECT_EQ(motion1, motion2); - EXPECT_EQ(motion1.GetConfidence(), kConfidenceForTest); -} - -TEST(DeviceMotionTest, ExplicitInitNotEquals) { - DeviceMotion motion1 = {kDefaultMotionType, kConfidenceForTest}; - DeviceMotion motion2 = {kDefaultMotionType}; - EXPECT_NE(motion1, motion2); -} - -TEST(DeviceMotionTest, CopyInitEquals) { - DeviceMotion motion1; - DeviceMotion motion2 = {motion1}; - EXPECT_EQ(motion1, motion2); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/discovery_filter.cc b/presence/discovery_filter.cc deleted file mode 100644 index 453309a5..00000000 --- a/presence/discovery_filter.cc +++ /dev/null @@ -1,36 +0,0 @@ -// 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 "presence/discovery_filter.h" - -namespace nearby { -namespace presence { - -using ::nearby::internal::IdentityType; - -DiscoveryFilter::DiscoveryFilter( - const std::vector& actions, - const std::vector& identities, - const std::vector& zones) noexcept - : actions_(actions), identities_(identities), zones_(zones) {} -std::vector DiscoveryFilter::GetActions() const { - return actions_; -} -std::vector DiscoveryFilter::GetIdentities() const { - return identities_; -} -std::vector DiscoveryFilter::GetZones() const { return zones_; } - -} // namespace presence -} // namespace nearby diff --git a/presence/discovery_filter.h b/presence/discovery_filter.h deleted file mode 100644 index 06b72413..00000000 --- a/presence/discovery_filter.h +++ /dev/null @@ -1,43 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_DISCOVERY_FILTER_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_DISCOVERY_FILTER_H_ - -#include - -#include "internal/proto/credential.pb.h" -#include "presence/presence_action.h" -#include "presence/presence_zone.h" -namespace nearby { -namespace presence { -class DiscoveryFilter { - public: - DiscoveryFilter(const std::vector& = {}, - const std::vector<::nearby::internal::IdentityType>& = {}, - const std::vector& = {}) noexcept; - std::vector GetActions() const; - std::vector<::nearby::internal::IdentityType> GetIdentities() const; - std::vector GetZones() const; - - private: - const std::vector actions_; - const std::vector<::nearby::internal::IdentityType> identities_; - const std::vector zones_; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_DISCOVERY_FILTER_H_ diff --git a/presence/discovery_filter_test.cc b/presence/discovery_filter_test.cc deleted file mode 100644 index c85f8f83..00000000 --- a/presence/discovery_filter_test.cc +++ /dev/null @@ -1,51 +0,0 @@ -// 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 "presence/discovery_filter.h" - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" - -namespace nearby { -namespace presence { -namespace { - -using ::nearby::internal::IdentityType; - -const PresenceAction kTestAction = {1}; -const IdentityType kTestIdentity = {IdentityType::IDENTITY_TYPE_CONTACTS_GROUP}; - -TEST(DiscoveryFilterTest, DefaultConstructorWorks) { - DiscoveryFilter filter; - EXPECT_EQ(filter.GetActions().size(), 0); - EXPECT_EQ(filter.GetIdentities().size(), 0); - EXPECT_EQ(filter.GetZones().size(), 0); -} - -TEST(DiscoveryFilterTest, PartiallyInitializationWorks) { - DiscoveryFilter filter1{{kTestAction}, {kTestIdentity}}; - DiscoveryFilter filter2{{kTestAction}}; - EXPECT_EQ(filter1.GetActions(), filter2.GetActions()); - EXPECT_NE(filter1.GetIdentities(), filter2.GetIdentities()); - EXPECT_EQ(filter1.GetZones(), filter2.GetZones()); - - EXPECT_EQ(filter1.GetActions()[0], kTestAction); - EXPECT_EQ(filter1.GetIdentities()[0], kTestIdentity); - EXPECT_EQ(filter1.GetZones().capacity(), 0); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/discovery_options.h b/presence/discovery_options.h deleted file mode 100644 index 44dcfc0b..00000000 --- a/presence/discovery_options.h +++ /dev/null @@ -1,34 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_DISCOVERY_OPTIONS_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_DISCOVERY_OPTIONS_H_ - -namespace nearby { -namespace presence { -struct DiscoveryOptions { - const bool local_wifi_only_; -}; - -inline bool operator==(const DiscoveryOptions& o1, const DiscoveryOptions& o2) { - return o1.local_wifi_only_ == o2.local_wifi_only_; -} - -inline bool operator!=(const DiscoveryOptions& o1, const DiscoveryOptions& o2) { - return !(o1 == o2); -} - -} // namespace presence -} // namespace nearby -#endif // THIRD_PARTY_NEARBY_PRESENCE_DISCOVERY_OPTIONS_H_ diff --git a/presence/discovery_options_test.cc b/presence/discovery_options_test.cc deleted file mode 100644 index 631ed12f..00000000 --- a/presence/discovery_options_test.cc +++ /dev/null @@ -1,51 +0,0 @@ -// 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 "presence/discovery_options.h" - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" - -namespace nearby { -namespace presence { -namespace { - -constexpr bool kTestLocalWifiOnly = false; -TEST(DiscoveryOptionsTest, NoDefaultConstructor) { - EXPECT_FALSE(std::is_trivially_constructible::value); -} - -TEST(DiscoveryOptionsTest, ExplicitInitEquals) { - DiscoveryOptions option1 = {kTestLocalWifiOnly}; - DiscoveryOptions option2 = {kTestLocalWifiOnly}; - EXPECT_EQ(option1, option2); - EXPECT_EQ(option1.local_wifi_only_, kTestLocalWifiOnly); -} - -TEST(DiscoveryOptionsTest, ExplicitInitNotEquals) { - DiscoveryOptions option1 = {kTestLocalWifiOnly}; - DiscoveryOptions option2 = {!kTestLocalWifiOnly}; - EXPECT_NE(option1, option2); -} - -TEST(DiscoveryOptionsTest, CopyInitEquals) { - DiscoveryOptions option1 = {kTestLocalWifiOnly}; - DiscoveryOptions option2 = {option1}; - EXPECT_EQ(option1, option2); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/fake_presence_client.cc b/presence/fake_presence_client.cc deleted file mode 100644 index 44e73440..00000000 --- a/presence/fake_presence_client.cc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2023 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/fake_presence_client.h" - - -#include -#include -#include - -#include "presence/data_types.h" -#include "presence/presence_device.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { - -absl::StatusOr FakePresenceClient::StartScan( - ScanRequest scan_request, ScanCallback callback) { - current_scan_session_id_++; - active_scan_sessions_.push_back(current_scan_session_id_); - absl::StatusOr scan_session_id(current_scan_session_id_); - callback_ = std::move(callback); - return scan_session_id; -} - -void FakePresenceClient::StopScan(ScanSessionId id) { - auto position = - std::find(active_scan_sessions_.begin(), active_scan_sessions_.end(), id); - if (position != active_scan_sessions_.end()) { - active_scan_sessions_.erase(position); - } -} - -std::vector FakePresenceClient::GetActiveScanSessions() { - return active_scan_sessions_; -} - -void FakePresenceClient::CallStartScanCallback(absl::Status status) { - callback_.start_scan_cb(status); -} - -void FakePresenceClient::CallOnDiscovered(PresenceDevice device) { - callback_.on_discovered_cb(device); -} - -void FakePresenceClient::CallOnUpdated(PresenceDevice device) { - callback_.on_updated_cb(device); -} - -void FakePresenceClient::CallOnLost(PresenceDevice device) { - callback_.on_lost_cb(device); -} - -} // namespace presence -} // namespace nearby diff --git a/presence/fake_presence_client.h b/presence/fake_presence_client.h deleted file mode 100644 index b2a6d816..00000000 --- a/presence/fake_presence_client.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2023 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 THIRD_PARTY_NEARBY_PRESENCE_FAKE_PRESENCE_CLIENT_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_FAKE_PRESENCE_CLIENT_H_ - -#include -#include - -#include "absl/status/statusor.h" -#include "presence/broadcast_request.h" -#include "presence/presence_client.h" -#include "presence/presence_device.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { - -class FakePresenceClient : public PresenceClient { - public: - FakePresenceClient() = default; - FakePresenceClient(const FakePresenceClient&) = delete; - FakePresenceClient(FakePresenceClient&&) = default; - FakePresenceClient& operator=(const FakePresenceClient&) = delete; - ~FakePresenceClient() = default; - - absl::StatusOr StartScan(ScanRequest scan_request, - ScanCallback callback) override; - - void StopScan(ScanSessionId session_id) override; - - // Not Implemented. - absl::StatusOr StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback) override { - return 0; - } - - // Not Implemented. - void StopBroadcast(BroadcastSessionId session_id) override {} - - // Not Implemented. - std::optional GetLocalDevice() override { - return std::nullopt; - } - - - std::vector GetActiveScanSessions(); - void CallStartScanCallback(absl::Status status); - void CallOnDiscovered(PresenceDevice device); - void CallOnUpdated(PresenceDevice device); - void CallOnLost(PresenceDevice device); - - private: - uint64_t current_scan_session_id_; - ScanCallback callback_; - std::vector active_scan_sessions_; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_FAKE_PRESENCE_CLIENT_H_ diff --git a/presence/fake_presence_service.cc b/presence/fake_presence_service.cc deleted file mode 100644 index ff06221a..00000000 --- a/presence/fake_presence_service.cc +++ /dev/null @@ -1,107 +0,0 @@ -// 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 "presence/fake_presence_service.h" - -#include -#include -#include - -#include "internal/interop/device_provider.h" -#include "internal/platform/borrowable.h" -#include "presence/fake_presence_client.h" - -namespace nearby { -namespace presence { - -FakePresenceService::FakePresenceService() = default; - -std::unique_ptr FakePresenceService::CreatePresenceClient() { - auto fake = std::make_unique(); - most_recent_fake_presence_client_ = fake.get(); - return std::move(fake); -} - -// Not implemented. -absl::StatusOr FakePresenceService::StartScan( - ScanRequest scan_request, ScanCallback callback) { - return absl::Status(absl::StatusCode::kCancelled, - "StartScan not implemented yet"); -} - -// Not implemented. -void FakePresenceService::StopScan(ScanSessionId session_id) {} - -// Not implemented. -absl::StatusOr FakePresenceService::StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback) { - return absl::Status(absl::StatusCode::kCancelled, - "StartBroadcast not implemented yet"); -} - -// Not implemented. -void FakePresenceService::StopBroadcast(BroadcastSessionId session_id) {} - -void FakePresenceService::UpdateDeviceIdentityMetaData( - const ::nearby::internal::DeviceIdentityMetaData& metadata, - bool regen_credentials, - absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) { - metadata_ = metadata; - - if (!regen_credentials) { - // No need to call back on credentials_generated_cb. - return; - } - - if (gen_credentials_status_.ok()) { - std::move(credentials_generated_cb.credentials_generated_cb)( - shared_credentials_); - } else { - std::move(credentials_generated_cb.credentials_generated_cb)( - gen_credentials_status_); - } -} - -NearbyDeviceProvider* FakePresenceService::GetLocalDeviceProvider() { - return provider_; -} - -void FakePresenceService::GetLocalPublicCredentials( - const CredentialSelector& credential_selector, - GetPublicCredentialsResultCallback callback) { - if (get_public_credentials_status_.ok()) { - std::move(callback.credentials_fetched_cb)(shared_credentials_); - return; - } - - std::move(callback.credentials_fetched_cb)(get_public_credentials_status_); -} - -void FakePresenceService::UpdateRemotePublicCredentials( - absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& remote_public_creds, - UpdateRemotePublicCredentialsCallback credentials_updated_cb) { - if (update_remote_public_credentials_status_.ok()) { - remote_shared_credentials_ = remote_public_creds; - } - - std::move(credentials_updated_cb.credentials_updated_cb)( - update_remote_public_credentials_status_); -} - -} // namespace presence -} // namespace nearby diff --git a/presence/fake_presence_service.h b/presence/fake_presence_service.h deleted file mode 100644 index 81dca042..00000000 --- a/presence/fake_presence_service.h +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2023 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 THIRD_PARTY_NEARBY_PRESENCE_FAKE_PRESENCE_SERVICE_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_FAKE_PRESENCE_SERVICE_H_ - -#include "internal/interop/device_provider.h" -#include "internal/interop/fake_device_provider.h" -#include "internal/platform/borrowable.h" -#include "internal/proto/metadata.pb.h" -#include "presence/broadcast_request.h" -#include "presence/data_types.h" -#include "presence/presence_client.h" -#include "presence/presence_service.h" - -namespace nearby { -namespace presence { - -class FakePresenceClient; - -class FakePresenceService : public PresenceService { - public: - FakePresenceService(); - ~FakePresenceService() override { lender_.Release(); } - - // PresenceService: - std::unique_ptr CreatePresenceClient() override; - - absl::StatusOr StartScan(ScanRequest scan_request, - ScanCallback callback) override; - - void StopScan(ScanSessionId session_id) override; - - absl::StatusOr StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback) override; - - void StopBroadcast(BroadcastSessionId session_id) override; - - void UpdateDeviceIdentityMetaData( - const ::nearby::internal::DeviceIdentityMetaData& metadata, - bool regen_credentials, - absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) override; - - NearbyDeviceProvider* GetLocalDeviceProvider() override; - - ::nearby::internal::DeviceIdentityMetaData GetDeviceIdentityMetaData() - override { - return metadata_; - } - - void GetLocalPublicCredentials( - const CredentialSelector& credential_selector, - GetPublicCredentialsResultCallback callback) override; - void UpdateRemotePublicCredentials( - absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& - remote_public_creds, - UpdateRemotePublicCredentialsCallback credentials_updated_cb) override; - - // Use for testing. Call this to set the response to - // `UpdateLocalDeviceMetadata`. - void SetUpdateLocalDeviceMetadataResponse( - absl::Status status, - std::vector shared_credentials) { - shared_credentials_ = shared_credentials; - gen_credentials_status_ = status; - } - - FakePresenceClient* GetMostRecentFakePresenceClient() { - return most_recent_fake_presence_client_; - } - - // Used for testing to verify the remote credentials set. - std::vector GetRemoteSharedCredentials() { - return remote_shared_credentials_; - } - - void SetUpdateRemoteSharedCredentialsResult(absl::Status status) { - update_remote_public_credentials_status_ = status; - } - - void SetLocalPublicCredentialsResult( - absl::Status status, - std::vector shared_credentials) { - get_public_credentials_status_ = status; - shared_credentials_ = shared_credentials; - } - - void SetDeviceProvider(NearbyDeviceProvider* provider) { - provider_ = provider; - } - - private: - FakePresenceClient* most_recent_fake_presence_client_ = nullptr; - std::vector shared_credentials_; - std::vector remote_shared_credentials_; - absl::Status gen_credentials_status_; - absl::Status update_remote_public_credentials_status_; - absl::Status get_public_credentials_status_; - ::nearby::internal::DeviceIdentityMetaData metadata_; - NearbyDeviceProvider* provider_; - ::nearby::Lender lender_{this}; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_FAKE_PRESENCE_SERVICE_H_ diff --git a/presence/fpp/BUILD b/presence/fpp/BUILD deleted file mode 100644 index 6e5d738d..00000000 --- a/presence/fpp/BUILD +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright 2023 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 -# -# http://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. - -load("@rules_cc//cc:cc_library.bzl", "cc_library") -load("@rules_cc//cc:cc_test.bzl", "cc_test") - -licenses(["notice"]) - -cc_library( - name = "fpp_manager", - srcs = [ - "fpp_manager.cc", - ], - hdrs = ["fpp_manager.h"], - visibility = [ - "//presence:__subpackages__", - ], - deps = [ - "//internal/platform:logging", - "//presence:types", - "//presence/fpp/fpp_c_ffi", - "//presence/implementation:sensor_fusion", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/status", - ], -) - -cc_library( - name = "sensor_fusion_impl", - srcs = [ - "sensor_fusion_impl.cc", - ], - hdrs = ["sensor_fusion_impl.h"], - visibility = [ - "//presence:__subpackages__", - ], - deps = [ - ":fpp_manager", - "//presence/implementation:sensor_fusion", - "@com_google_absl//absl/status", - ], -) - -cc_test( - name = "fpp_manager_test", - size = "small", - srcs = ["fpp_manager_test.cc"], - deps = [ - ":fpp_manager", - "//presence/implementation:sensor_fusion", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/status", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "sensor_fusion_test", - size = "small", - srcs = ["sensor_fusion_test.cc"], - deps = [ - ":sensor_fusion_impl", - "//presence/implementation:sensor_fusion", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/status", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) diff --git a/presence/fpp/fpp/Cargo.lock b/presence/fpp/fpp/Cargo.lock deleted file mode 100644 index be2b9180..00000000 --- a/presence/fpp/fpp/Cargo.lock +++ /dev/null @@ -1,25 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "fpp" -version = "0.1.0" -dependencies = [ - "itertools", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] diff --git a/presence/fpp/fpp/Cargo.toml b/presence/fpp/fpp/Cargo.toml deleted file mode 100644 index b3de1c90..00000000 --- a/presence/fpp/fpp/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "fpp" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -itertools = "0.10.5" diff --git a/presence/fpp/fpp/src/fspl_converter.rs b/presence/fpp/fpp/src/fspl_converter.rs deleted file mode 100644 index 6b03f6b8..00000000 --- a/presence/fpp/fpp/src/fspl_converter.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2023 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 -// -// http://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. - -const ADVERTISE_TX_POWER_HIGH_DB: i32 = 1; - -const FSPL_AT_1_METER_DB: i32 = 40; - -const MEASURED_POWER_AT_1_METER_DB_AT_HIGH_TX_POWER: i32 = -60; - -pub fn compute_distance_meters_at_high_tx_power(rssi: i32) -> f64 { - let nominal_tx_power = ADVERTISE_TX_POWER_HIGH_DB; - let antenna_gain = - (nominal_tx_power - FSPL_AT_1_METER_DB) - MEASURED_POWER_AT_1_METER_DB_AT_HIGH_TX_POWER; - let tx_power_at_0_meters = nominal_tx_power - antenna_gain; - compute_distance_meters(tx_power_at_0_meters, rssi) -} - -pub fn compute_distance_meters(tx_power_at_0_meters: i32, rssi: i32) -> f64 { - let fspl = tx_power_at_0_meters - rssi; - ble_fspl_to_meters(fspl) -} - -fn ble_fspl_to_meters(fspl: i32) -> f64 { - let base: f64 = 10.0; - base.powi((fspl - FSPL_AT_1_METER_DB) / 20) -} diff --git a/presence/fpp/fpp/src/fused_presence_utils.rs b/presence/fpp/fpp/src/fused_presence_utils.rs deleted file mode 100644 index 48b2dd0e..00000000 --- a/presence/fpp/fpp/src/fused_presence_utils.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2023 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 -// -// http://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. - -pub(crate) const DEFAULT_TAP_DISTANCE_THRESHOLD_METERS: f64 = AMBIGUITY_METERS + 0.02; -pub(crate) const DEFAULT_REACH_DISTANCE_THRESHOLD_METERS: f64 = AMBIGUITY_METERS + 0.5; -pub(crate) const DEFAULT_SHORT_RANGE_DISTANCE_THRESHOLD_METERS: f64 = AMBIGUITY_METERS + 1.2; -pub(crate) const DEFAULT_LONG_RANGE_DISTANCE_THRESHOLD_METERS: f64 = AMBIGUITY_METERS + 3.0; -pub(crate) const DEFAULT_CONSECUTIVE_SCANS_REQUIRED: u8 = 2; -const AMBIGUITY_METERS: f64 = 0.06; - -/// Proximity state from device to another in terms of actionability -#[derive(Eq, Hash, Copy, Clone, PartialEq, Debug)] -#[repr(C)] -pub enum ProximityState { - /// Unknown proximity state - Unknown, - /// The device is within a tap zone (<0.02m) - Tap, - /// The device is within a reach zone (<0.5m) - Reach, - /// The device is within a short range zone (<1.2m) - ShortRange, - /// The device is within a long range zone (<3.0m) - LongRange, - /// The device is at a far range - Far, -} - -/// Represents the confidence levels for a given measurement -#[derive(Copy, Clone, PartialEq, Debug)] -#[repr(C)] -pub enum MeasurementConfidence { - /// Measurement confidence is low, the default for BLE medium - Low, - /// Measurement confidence is medium - Medium, - /// Measurement confidence is High - High, - /// Measurement confidence is unknown - Unknown, -} - -/// Data sources that are used to track presence -#[derive(Copy, Clone, PartialEq, Debug)] -#[repr(C)] -pub enum PresenceDataSource { - /// Data source for proximity estimate is BLE - Ble, - /// Data source for proximity estimate is UWB - Uwb, - /// Data source for proximity estimate is NAN - Nan, - /// Data source for proximity estimate is unknown - Unknown, -} - -/// A PII-stripped subset of Bluetooth scan result -#[repr(C)] -pub struct BleScanResult { - /// Device ID of the nearby device - pub device_id: u64, - /// Transmitting power of signal - pub tx_power: MaybeTxPower, - /// RSSI value - pub rssi: i32, - /// Time scan result was obtained - pub elapsed_real_time_millis: u64, -} - -/// Enum representing an optional tx power value -#[repr(C)] -pub enum MaybeTxPower { - /// Valid TX power with associated data value - Valid(i32), - /// Absent Tx Power - Invalid, -} - -/// Describes the most accurate and recent measurement for a given device -#[derive(Copy, Clone, PartialEq, Debug)] -#[repr(C)] -pub struct ProximityEstimate { - /// Device ID of the nearby device - pub device_id: u64, - /// Distance to the nearby device in meters - pub distance_meters: f64, - /// Measurement confidence of the estimate - pub distance_confidence: MeasurementConfidence, - /// The time the proximity estimate was obtained (milliseconds since the - /// program start time) - pub elapsed_real_time_millis: u64, - /// Proximity state zone of the nearby device - pub proximity_state: ProximityState, - /// Medium through which the proximity estimate was computed - pub source: PresenceDataSource, -} diff --git a/presence/fpp/fpp/src/presence_detector.rs b/presence/fpp/fpp/src/presence_detector.rs deleted file mode 100644 index 7f613ab5..00000000 --- a/presence/fpp/fpp/src/presence_detector.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2023 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 -// -// http://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. - -use std::collections::{HashMap, VecDeque}; -use std::time::{Instant, SystemTime}; - -use itertools::Itertools; - -use crate::fspl_converter::compute_distance_meters_at_high_tx_power; -use crate::fused_presence_utils::{ - BleScanResult, MaybeTxPower, MeasurementConfidence, PresenceDataSource, ProximityEstimate, - ProximityState, DEFAULT_CONSECUTIVE_SCANS_REQUIRED, - DEFAULT_LONG_RANGE_DISTANCE_THRESHOLD_METERS, DEFAULT_REACH_DISTANCE_THRESHOLD_METERS, - DEFAULT_SHORT_RANGE_DISTANCE_THRESHOLD_METERS, DEFAULT_TAP_DISTANCE_THRESHOLD_METERS, -}; - -const MAX_RSSI_FILTER_VALUE: i32 = 10; -const DEFAULT_ESTIMATED_DISTANCE_DATA_TTL_MILLIS: u128 = 4000; - -/// Static function for getting proximity state from threshold -fn get_proximity_state_from_threshold(distance_meters: f64) -> ProximityState { - if distance_meters <= DEFAULT_TAP_DISTANCE_THRESHOLD_METERS { - return ProximityState::Tap; - } - if distance_meters <= DEFAULT_REACH_DISTANCE_THRESHOLD_METERS { - return ProximityState::Reach; - } - if distance_meters <= DEFAULT_SHORT_RANGE_DISTANCE_THRESHOLD_METERS { - return ProximityState::ShortRange; - } - if distance_meters <= DEFAULT_LONG_RANGE_DISTANCE_THRESHOLD_METERS { - return ProximityState::LongRange; - } - ProximityState::Far -} - -/// Tracks and computes proximity/presence state events. -pub struct PresenceDetector { - start_time: Instant, - last_range_update_time: RangingUpdateTime, - best_proximity_estimate_per_device: HashMap, - transition_history: VecDeque, -} - -struct RangingUpdateTime(u128); - -impl RangingUpdateTime { - pub fn is_expired(&self) -> bool { - let elapsed_real_time_millis = Instant::now().elapsed().as_millis(); - elapsed_real_time_millis - self.0 > DEFAULT_ESTIMATED_DISTANCE_DATA_TTL_MILLIS - } - - pub fn update(&mut self, start_time: Instant) { - self.0 = Instant::now().duration_since(start_time).as_millis(); - } -} - -impl PresenceDetector { - /// Creates a new instance of presence detector - pub fn new() -> Self { - PresenceDetector { - start_time: Instant::now(), - last_range_update_time: RangingUpdateTime(0), - best_proximity_estimate_per_device: HashMap::new(), - transition_history: VecDeque::with_capacity( - (DEFAULT_CONSECUTIVE_SCANS_REQUIRED + 1).into(), - ), - } - } - - /// Updates the presence detector with a new scan result and returns the - /// current proximity estimate - pub fn on_ble_scan_result( - &mut self, - ble_scan_result: BleScanResult, - ) -> Option { - let device_id = ble_scan_result.device_id; - if ble_scan_result.rssi > MAX_RSSI_FILTER_VALUE { - return self.best_proximity_estimate_per_device.get(&device_id).copied(); - } - if self.last_range_update_time.is_expired() { - self.transition_history.clear(); - } - let mut tx_power: i32 = 0; - if let MaybeTxPower::Valid(some_tx_power) = ble_scan_result.tx_power { - tx_power = some_tx_power; - } - let rssi = ble_scan_result.rssi + tx_power; - let distance_meters = compute_distance_meters_at_high_tx_power(rssi); - let new_proximity_estimate = ProximityEstimate { - device_id, - distance_confidence: MeasurementConfidence::Low, - distance_meters, - proximity_state: get_proximity_state_from_threshold(distance_meters), - elapsed_real_time_millis: Instant::now().duration_since(self.start_time).as_millis() - as u64, - source: PresenceDataSource::Ble, - }; - self.transition_history.push_front(new_proximity_estimate.proximity_state); - self.transition_history.truncate(DEFAULT_CONSECUTIVE_SCANS_REQUIRED.into()); - if self.transition_history.iter().unique().count() == 1 - && self.transition_history.len() == DEFAULT_CONSECUTIVE_SCANS_REQUIRED.into() - { - self.best_proximity_estimate_per_device.insert(device_id, new_proximity_estimate); - self.last_range_update_time.update(self.start_time); - } - self.best_proximity_estimate_per_device.get(&device_id).copied() - } - - /// Returns the current proximity estimate for a given device - pub fn get_proximity_estimate(&self, device_id: u64) -> Option { - self.best_proximity_estimate_per_device.get(&device_id).copied() - } -} - -impl Default for PresenceDetector { - fn default() -> Self { - Self::new() - } -} diff --git a/presence/fpp/fpp/src/presence_detector_test.rs b/presence/fpp/fpp/src/presence_detector_test.rs deleted file mode 100644 index 1d35f187..00000000 --- a/presence/fpp/fpp/src/presence_detector_test.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2023 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 -// -// http://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. - -use crate::fused_presence_utils::*; -use crate::presence_detector::*; - -const BLE_SCAN_RESULT_REACH_ZONE: BleScanResult = BleScanResult { - device_id: 1234, - tx_power: { MaybeTxPower::Invalid }, - rssi: -40, - elapsed_real_time_millis: 123456, -}; - -const BLE_SCAN_RESULT_BAD_RSSI: BleScanResult = BleScanResult { - rssi: 127, - ..BLE_SCAN_RESULT_REACH_ZONE -}; - -const BLE_SCAN_RESULT_SHORT_RANGE_ZONE: BleScanResult = BleScanResult { - rssi: -60, - ..BLE_SCAN_RESULT_REACH_ZONE -}; - -const REACH_PROXIMITY_ESTIMATE: ProximityEstimate = ProximityEstimate { - device_id: 1234, - distance_meters: 0.1, - distance_confidence: MeasurementConfidence::Low, - elapsed_real_time_millis: 0, - proximity_state: ProximityState::Reach, - source: PresenceDataSource::Ble, -}; - -const SHORT_RANGE_PROXIMITY_ESTIMATE: ProximityEstimate = ProximityEstimate { - distance_meters: 1.0, - proximity_state: ProximityState::ShortRange, - ..REACH_PROXIMITY_ESTIMATE -}; - -#[test] -fn test_on_ble_scan_result_success() { - // Tests that the proximity state stored for each device is the accurate one after two - // consecutive scan results - let mut presence_detector = PresenceDetector::new(); - assert_eq!( - presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_REACH_ZONE), - None - ); - assert_eq!( - presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_REACH_ZONE), - Some(ProximityEstimate { - device_id: 1234, - distance_meters: 0.1, - distance_confidence: MeasurementConfidence::Low, - elapsed_real_time_millis: 0, - proximity_state: ProximityState::Reach, - source: PresenceDataSource::Ble - }) - ); -} - -#[test] -fn test_on_ble_scan_result_bad_rssi() { - // Tests that scan results with bad RSSIs are ignored - let mut presence_detector = PresenceDetector::new(); - assert_eq!( - presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_REACH_ZONE), - None - ); - - assert_eq!( - presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_BAD_RSSI), - None - ); -} -#[test] -fn test_on_ble_scan_result_transition_to_new_zone() { - let mut presence_detector = PresenceDetector::new(); - assert_eq!( - presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_REACH_ZONE), - None - ); - assert_eq!( - presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_REACH_ZONE), - Some(REACH_PROXIMITY_ESTIMATE) - ); - assert_eq!( - presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_SHORT_RANGE_ZONE), - Some(REACH_PROXIMITY_ESTIMATE) - ); - assert_eq!( - presence_detector.on_ble_scan_result(BLE_SCAN_RESULT_SHORT_RANGE_ZONE), - Some(SHORT_RANGE_PROXIMITY_ESTIMATE) - ); -} diff --git a/presence/fpp/fpp_c_ffi/Cargo.lock b/presence/fpp/fpp_c_ffi/Cargo.lock deleted file mode 100644 index 99bf6078..00000000 --- a/presence/fpp/fpp_c_ffi/Cargo.lock +++ /dev/null @@ -1,105 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "fpp" -version = "0.1.0" -dependencies = [ - "itertools", -] - -[[package]] -name = "fpp_c_ffi" -version = "0.1.0" -dependencies = [ - "fpp", - "lazy_static", - "rand", -] - -[[package]] -name = "getrandom" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.144" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/presence/fpp/fpp_c_ffi/Cargo.toml b/presence/fpp/fpp_c_ffi/Cargo.toml deleted file mode 100644 index 301a85c3..00000000 --- a/presence/fpp/fpp_c_ffi/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "fpp_c_ffi" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -fpp = {path = "../fpp"} -lazy_static = "1.4.0" -rand = "0.8.5" diff --git a/presence/fpp/fpp_c_ffi/include/presence_detector.h b/presence/fpp/fpp_c_ffi/include/presence_detector.h deleted file mode 100644 index 7bc03aa5..00000000 --- a/presence/fpp/fpp_c_ffi/include/presence_detector.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2023 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 -// -// http://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 PRESENCE_DETECTOR_H_ -#define PRESENCE_DETECTOR_H_ - -#include -#include -#include -#include -#include - -// Represents the confidence levels for a given measurement -enum class MeasurementConfidence { - /// Measurement confidence is low, the default for BLE medium - Low, - /// Measurement confidence is medium - Medium, - /// Measurement confidence is High - High, - /// Measurement confidence is unknown - Unknown, -}; - -/// Data sources that are used to track presence -enum class PresenceDataSource { - /// Data source for proximity estimate is BLE - Ble, - /// Data source for proximity estimate is UWB - Uwb, - /// Data source for proximity estimate is NAN - Nan, - /// Data source for proximity estimate is unknown - Unknown, -}; - -/// Proximity state from device to another in terms of actionability -enum class ProximityState { - /// Unknown proximity state - Unknown, - /// The device is within a tap zone (<0.02m) - Tap, - /// The device is within a reach zone (<0.5m) - Reach, - /// The device is within a short range zone (<1.2m) - ShortRange, - /// The device is within a long range zone (<3.0m) - LongRange, - /// The device is at a far range - Far, -}; - -/// Wraps the handle ID to an underlying PresenceDetector object -struct PresenceDetectorHandle { - uint64_t handle; -}; - -/// Enum representing an optional tx power value -struct MaybeTxPower { - enum class Tag { - /// Valid TX power with associated data value - Valid, - /// Absent Tx Power - Invalid, - }; - - struct Valid_Body { - int32_t _0; - }; - - Tag tag; - union { - Valid_Body valid; - }; -}; - -/// A PII-stripped subset of Bluetooth scan result -struct BleScanResult { - /// Device ID of the nearby device - uint64_t device_id; - /// Transmitting power of signal - MaybeTxPower tx_power; - /// RSSI value - int32_t rssi; - /// Time scan result was obtained - uint64_t elapsed_real_time_millis; -}; - -/// Describes the most accurate and recent measurement for a given device -struct ProximityEstimate { - /// Device ID of the nearby device - uint64_t device_id; - /// Distance to the nearby device in meters - double distance_meters; - /// Measurement confidence of the estimate - MeasurementConfidence distance_confidence; - /// The time the proximity estimate was obtained - uint64_t elapsed_real_time_millis; - /// Proximity state zone of the nearby device - ProximityState proximity_state; - /// Medium through which the proximity estimate was computed - PresenceDataSource source; -}; - -extern "C" { - -/// Creates a new presence detector object and returns the handle for the new -/// object -PresenceDetectorHandle presence_detector_create(); - -/// Updates PresenceDetector with a new scan result and returns an error code -/// if unsuccessful -/// -/// # Safety -/// -/// Ensure that the output parameter refers to an initialized instance -int32_t update_ble_scan_result(PresenceDetectorHandle presence_detector_handle, - BleScanResult ble_scan_result, - ProximityEstimate *proximity_estimate); - -/// Gets the current proximity estimate for a given device ID -/// -/// # Safety -/// -/// Ensure that the output parameter refers to an initialized instance -int32_t get_proximity_estimate(PresenceDetectorHandle presence_detector_handle, - uint64_t device_id, - ProximityEstimate *proximity_estimate); - -/// De-allocates memory for a presence detector object -int presence_detector_free(PresenceDetectorHandle presence_detector_handle); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // PRESENCE_DETECTOR_H_ diff --git a/presence/fpp/fpp_c_ffi/src/handle_map.rs b/presence/fpp/fpp_c_ffi/src/handle_map.rs deleted file mode 100644 index 0f9566a9..00000000 --- a/presence/fpp/fpp_c_ffi/src/handle_map.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2023 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 -// -// http://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. - -use core::marker::PhantomData; -use fpp::presence_detector::PresenceDetector; -use lazy_static::lazy_static; -use rand::Rng; -use std::collections::HashMap; -use std::sync::{Mutex, MutexGuard}; - -pub(crate) struct HandleMap { - _marker: PhantomData, - map: HashMap, -} - -impl HandleMap { - pub(crate) fn init() -> Self { - Self { - _marker: Default::default(), - map: HashMap::new(), - } - } - - /// inserts an entry into the map and returns the randomly generated handle to the entry - pub(crate) fn insert(&mut self, data: T) -> u64 { - let mut rng = rand::thread_rng(); - let mut handle: u64 = rng.gen(); - - while self.map.contains_key(&handle) { - handle = rng.gen(); - } - - assert!(self.map.insert(handle, data).is_none()); - handle - } - - /// Removes an entry at a given handle returning an Option of the owned value - pub(crate) fn remove(&mut self, handle: &u64) -> Option { - self.map.remove(handle) - } - - /// Gets a reference to the entry stored at the specified handle - pub(crate) fn get(&mut self, handle: &u64) -> Option<&mut T> { - self.map.get_mut(handle) - } -} - -// Returns a threadsafe instance of the global static hashmap tracking the PresenceDetector handles -pub(crate) fn get_presence_detector_handle_map( -) -> MutexGuard<'static, HandleMap>> { - PRESENCE_DETECTOR_HANDLE_MAP - .lock() - .unwrap_or_else(|err_guard| err_guard.into_inner()) -} - -// Global hashmap to track valid pointers, this is a safety precaution to make sure we are not -// reading from unsafe memory address's passed in by the caller -lazy_static! { - static ref PRESENCE_DETECTOR_HANDLE_MAP: Mutex>> = - Mutex::new(HandleMap::init()); -} diff --git a/presence/fpp/fpp_c_ffi/src/lib.rs b/presence/fpp/fpp_c_ffi/src/lib.rs deleted file mode 100644 index b0c536f7..00000000 --- a/presence/fpp/fpp_c_ffi/src/lib.rs +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2023 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 -// -// http://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. - -#![deny( - missing_docs, - clippy::indexing_slicing, - clippy::unwrap_used, - clippy::panic, - clippy::expect_used -)] - -//! Rust FFI wrapper for PresenceDetector. Can be called from C/C++ clients - -use fpp::fused_presence_utils::*; -use fpp::presence_detector::*; - -use crate::handle_map::get_presence_detector_handle_map; - -mod handle_map; - -/// Wraps the handle ID to an underlying PresenceDetector object -#[repr(C)] -pub struct PresenceDetectorHandle { - handle: u64, -} - -/// Enum class representing possible outputs of proximity data processing call -#[repr(C)] -pub enum ComputationStatus { - /// Returned if the proximity estimate calculation was successful - Success, - /// Returned if there is no computed proximity estimate - NoComputedProximityEstimate, - /// Returned if the handle is invalid - InvalidPresenceDetectorHandleError, - /// Returned if the output parameter is null - NullOutputParameterError, -} - -impl ComputationStatus { - fn to_status_code(&self) -> i32 { - match self { - /// Status codes 100+ are considered errors - Self::Success => 1, - Self::NoComputedProximityEstimate => 2, - Self::InvalidPresenceDetectorHandleError => 101, - Self::NullOutputParameterError => 102, - } - } -} - -/// Creates a new presence detector object and returns the handle for the new -/// object -#[no_mangle] -pub extern "C" fn presence_detector_create() -> PresenceDetectorHandle { - let handle = get_presence_detector_handle_map().insert(Box::new(PresenceDetector::new())); - PresenceDetectorHandle { handle } -} - -/// Updates PresenceDetector with a new scan result and returns an error code if -/// unsuccessful -/// -/// # Safety -/// -/// Ensure that the output parameter refers to an initialized instance -#[no_mangle] -pub unsafe extern "C" fn update_ble_scan_result( - presence_detector_handle: PresenceDetectorHandle, - ble_scan_result: BleScanResult, - proximity_estimate: *mut ProximityEstimate, -) -> i32 { - if let Some(presence_detector) = - get_presence_detector_handle_map().get(&presence_detector_handle.handle) - { - if let Some(current_proximity_estimate) = - presence_detector.on_ble_scan_result(ble_scan_result) - { - if let Some(proximity_estimate) = proximity_estimate.as_mut() { - *proximity_estimate = current_proximity_estimate; - ComputationStatus::Success.to_status_code() - } else { - ComputationStatus::NullOutputParameterError.to_status_code() - } - } else { - ComputationStatus::NoComputedProximityEstimate.to_status_code() - } - } else { - ComputationStatus::InvalidPresenceDetectorHandleError.to_status_code() - } -} - -/// Gets the current proximity estimate for a given device ID -/// -/// # Safety -/// -/// Ensure that the output parameter refers to an initialized instance -#[no_mangle] -pub unsafe extern "C" fn get_proximity_estimate( - presence_detector_handle: PresenceDetectorHandle, - device_id: u64, - proximity_estimate: *mut ProximityEstimate, -) -> i32 { - if let Some(presence_detector) = - get_presence_detector_handle_map().get(&presence_detector_handle.handle) - { - presence_detector.get_proximity_estimate(device_id).map(|current_proximity_estimate| { - if let Some(proximity_estimate) = proximity_estimate.as_mut() { - *proximity_estimate = current_proximity_estimate; - return ComputationStatus::Success.to_status_code(); - } - ComputationStatus::NullOutputParameterError.to_status_code() - }); - } - - ComputationStatus::InvalidPresenceDetectorHandleError.to_status_code() -} - -/// De-allocates memory for a presence detector object -#[no_mangle] -pub extern "C" fn presence_detector_free( - presence_detector_handle: PresenceDetectorHandle, -) -> std::os::raw::c_int { - if let Some(presence_detector) = - get_presence_detector_handle_map().remove(&presence_detector_handle.handle) - { - let _ = *presence_detector; - return ComputationStatus::Success.to_status_code(); - } - ComputationStatus::InvalidPresenceDetectorHandleError.to_status_code() -} diff --git a/presence/fpp/fpp_manager.cc b/presence/fpp/fpp_manager.cc deleted file mode 100644 index e7d9efc8..00000000 --- a/presence/fpp/fpp_manager.cc +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2023 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 -// -// http://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/fpp/fpp_manager.h" - -#include -#include -#include -#include - -#include "absl/status/status.h" -#include "internal/platform/logging.h" -#include "presence/fpp/fpp_c_ffi/include/presence_detector.h" -#include "presence/implementation/sensor_fusion.h" -#include "presence/presence_zone.h" - -namespace nearby { -namespace presence { - -namespace { -// See -// https://source.corp.google.com/piper///depot/google3/third_party/nearby/presence/fpp/fpp_c_ffi/src/lib.rs;l=49 -// for constants definition -constexpr int kSuccess = 1; -constexpr int kNoComputedProximityEstimate = 2; -constexpr int kInvalidPresenceDetectorHandleError = 101; -constexpr int kNullOutputParameterError = 102; - -// Converts optional tx power to the rust api compatible equivalent -MaybeTxPower ConvertTxPower(std::optional txPower) { - if (txPower.has_value()) { - return {MaybeTxPower::Tag::Valid, {txPower.value()}}; - } - return {MaybeTxPower::Tag::Invalid, {}}; -} - -// Converts FPP ProximityState struct to NP RangeType struct -PresenceZone::DistanceBoundary::RangeType ConvertProximityStateToRangeType( - ProximityState proximity_state) { - switch (proximity_state) { - case ProximityState::Tap: - return PresenceZone::DistanceBoundary::RangeType::kWithinTap; - case ProximityState::Reach: - return PresenceZone::DistanceBoundary::RangeType::kWithinReach; - case ProximityState::ShortRange: - case ProximityState::LongRange: - case ProximityState::Far: - return PresenceZone::DistanceBoundary::RangeType::kFar; - case ProximityState::Unknown: - default: - LOG(WARNING) << "Proximity state is unknown"; - return PresenceZone::DistanceBoundary::RangeType::kRangeUnknown; - } -} -} // namespace - -absl::Status FppManager::UpdateBleScanResult(uint64_t device_id, - std::optional txPower, - int rssi, - uint64_t elapsed_realtime_millis) { - if (zone_transition_callbacks_.empty()) { - return absl::InternalError("No callback registered"); - } - BleScanResult ble_scan_result = {device_id, ConvertTxPower(txPower), rssi, - elapsed_realtime_millis}; - ProximityEstimate default_proximity_estimate = - ProximityEstimate{device_id, - /*distanceMeters=*/0.0, - MeasurementConfidence::Unknown, - /*elapsedRealtime=*/0, - ProximityState::Unknown, - PresenceDataSource::Ble}; - ProximityEstimate old_proximity_estimate = - current_proximity_estimates_.contains(device_id) - ? current_proximity_estimates_[device_id] - : default_proximity_estimate; - ProximityEstimate new_proximity_estimate = default_proximity_estimate; - int status_code = update_ble_scan_result( - presence_detector_handle_, ble_scan_result, &new_proximity_estimate); - if (status_code == kNoComputedProximityEstimate) { - LOG(INFO) << "Insufficient number of scan results available to " - "compute proximity state"; - return absl::OkStatus(); - } - if (status_code == kSuccess) { - current_proximity_estimates_[device_id] = new_proximity_estimate; - CheckPresenceZoneChanged(device_id, old_proximity_estimate, - new_proximity_estimate); - return absl::OkStatus(); - } - LOG(WARNING) - << "Could not successfully update FPP with new scan result: Error code=" - << status_code; - return absl::InternalError(GetStatusStringFromCode(status_code)); -} - -void FppManager::RegisterZoneTransitionListener( - uint64_t callback_id, ZoneTransitionCallback callback) { - zone_transition_callbacks_[callback_id] = std::move(callback); -} - -void FppManager::UnregisterZoneTransitionListener(uint64_t callback_id) { - zone_transition_callbacks_.erase(callback_id); -} - -void FppManager::ResetProximityStateData() { - current_proximity_estimates_.clear(); -} - -std::optional FppManager::GetRangingData(uint64_t device_id) { - return ConvertProximityEstimateToRangingData( - current_proximity_estimates_[device_id]); -} - -// Converts FPP ProximityEstimate struct to NP RangingData struct -RangingData FppManager::ConvertProximityEstimateToRangingData( - ProximityEstimate estimate) { - RangingMeasurement ranging_measurement = { - /*confidenceLevel=*/0.0, static_cast(estimate.distance_meters)}; - RangingPosition ranging_position = { - ranging_measurement, /*azimuth=*/std::nullopt, - /*elevation=*/std::nullopt, estimate.elapsed_real_time_millis}; - ZoneTransition zone_transition = { - ConvertProximityStateToRangeType(estimate.proximity_state), - /*confidenceLevel=*/0.0}; - return {DataSource::kBle, ranging_position, zone_transition, - std::vector()}; -} - -void FppManager::CheckPresenceZoneChanged(uint64_t device_id, - ProximityEstimate old_estimate, - ProximityEstimate new_estimate) { - if (old_estimate.proximity_state != new_estimate.proximity_state) { - LOG(WARNING) << "Updating zone transition callbacks with new zone. Zone=" - << static_cast(new_estimate.proximity_state); - for (auto& pair : zone_transition_callbacks_) { - pair.second.on_proximity_zone_changed( - device_id, - ConvertProximityStateToRangeType(new_estimate.proximity_state)); - } - } -} - -std::string FppManager::GetStatusStringFromCode(int status_code) { - switch (status_code) { - case kInvalidPresenceDetectorHandleError: - return "INVALID_PRESENCE_DETECTOR_HANDLE"; - case kNullOutputParameterError: - return "NULL_OUTPUT_PARAMETER"; - default: - LOG(WARNING) << "Error code is unknown"; - return "UNKNOWN_ERROR"; - } -} - -} // namespace presence -} // namespace nearby diff --git a/presence/fpp/fpp_manager.h b/presence/fpp/fpp_manager.h deleted file mode 100644 index cecdb21e..00000000 --- a/presence/fpp/fpp_manager.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2023 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 -// -// http://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 THIRD_PARTY_NEARBY_PRESENCE_FPP_FPP_MANAGER_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_FPP_FPP_MANAGER_H_ - -#include -#include - -#include "absl/container/flat_hash_map.h" -#include "absl/status/status.h" -#include "presence/fpp/fpp_c_ffi/include/presence_detector.h" -#include "presence/implementation/sensor_fusion.h" -#include "presence/presence_zone.h" - -namespace nearby { -namespace presence { - -// Manages fused presence updates and serves as a sync -> async converter class -// between fpp and NP sensor fusion -class FppManager { - public: - using RangeType = PresenceZone::DistanceBoundary::RangeType; - - FppManager() { presence_detector_handle_ = presence_detector_create(); } - ~FppManager() { presence_detector_free(presence_detector_handle_); } - - /** Updates FPP with new BLE scan results. Returns status code */ - absl::Status UpdateBleScanResult(uint64_t device_id, - std::optional txPower, int rssi, - uint64_t elapsed_realtime_millis); - /** - * Adds callback for updates of proximity zone transitions. - */ - void RegisterZoneTransitionListener(uint64_t callback_id, - ZoneTransitionCallback callback); - - /** - * Unregister callback for updates of proximity zone transitions. - */ - void UnregisterZoneTransitionListener(uint64_t callback_id); - - /** - * Clears all proximity state data - */ - void ResetProximityStateData(); - - /* - * Converts ProximityEstimate to a NP compatible struct - */ - RangingData ConvertProximityEstimateToRangingData(ProximityEstimate estimate); - - /** - * Gets the most recent ranging data for a given device - */ - std::optional GetRangingData(uint64_t device_id); - - /* - * Converts a status code to a string representation - */ - std::string GetStatusStringFromCode(int status_code); - - private: - void CheckPresenceZoneChanged(uint64_t device_id, - ProximityEstimate old_estimate, - ProximityEstimate new_estimate); - absl::flat_hash_map current_proximity_estimates_; - absl::flat_hash_map - zone_transition_callbacks_; - PresenceDetectorHandle presence_detector_handle_; -}; -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_FPP_FPP_MANAGER_H_ diff --git a/presence/fpp/fpp_manager_test.cc b/presence/fpp/fpp_manager_test.cc deleted file mode 100644 index 6911e06f..00000000 --- a/presence/fpp/fpp_manager_test.cc +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2023 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 -// -// http://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/fpp/fpp_manager.h" - -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/status/status.h" -#include "presence/implementation/sensor_fusion.h" - -namespace nearby { -namespace presence { -namespace { -constexpr uint64_t kDeviceId = 1234; -constexpr int kReachRssi = -40; -constexpr int kShortRangeRssi = -60; -constexpr int kCallbackId = 12345; - -TEST(FppManager, UpdateBleScanResultSuccess) { - FppManager manager; - bool callback_called = false; - manager.RegisterZoneTransitionListener( - kCallbackId, - {.on_proximity_zone_changed = - [&callback_called]( - uint64_t device_id, - PresenceZone::DistanceBoundary::RangeType range_type) { - callback_called = true; - }}); - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, - kReachRssi, - /*elapsed_real_time_millis=*/0)); - // State is only computed after second consecutive scan is fulfilled - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, - kReachRssi, - /*elapsed_real_time_millis=*/2000)); - EXPECT_EQ(manager.GetRangingData(kDeviceId) - ->zone_transition.value() - .distance_range_type, - PresenceZone::DistanceBoundary::RangeType::kWithinReach); - EXPECT_TRUE(callback_called); -} - -TEST(FppManager, ZoneTransitionDetected) { - FppManager manager; - bool callback_called = false; - manager.RegisterZoneTransitionListener( - kCallbackId, - {.on_proximity_zone_changed = - [&callback_called]( - uint64_t device_id, - PresenceZone::DistanceBoundary::RangeType range_type) { - callback_called = true; - }}); - // ProximityEstimate is only computed after consecutive scans is fulfilled - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, - kReachRssi, - /*elapsed_real_time_millis=*/0)); - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, - kReachRssi, - /*elapsed_real_time_millis=*/2000)); - EXPECT_EQ(manager.GetRangingData(kDeviceId) - ->zone_transition.value() - .distance_range_type, - PresenceZone::DistanceBoundary::RangeType::kWithinReach); - EXPECT_TRUE(callback_called); - callback_called = false; - - // Update with new zone - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, - kShortRangeRssi, - /*elapsed_real_time_millis=*/0)); - EXPECT_EQ(manager.GetRangingData(kDeviceId) - ->zone_transition.value() - .distance_range_type, - PresenceZone::DistanceBoundary::RangeType::kWithinReach); - EXPECT_FALSE(callback_called); - // Update with consecutive scan of new zone - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, - kShortRangeRssi, - /*elapsed_real_time_millis=*/0)); - EXPECT_EQ(manager.GetRangingData(kDeviceId) - ->zone_transition.value() - .distance_range_type, - PresenceZone::DistanceBoundary::RangeType::kFar); - EXPECT_TRUE(callback_called); -} - -TEST(FppManager, ConvertProximityEstimateToRangingData) { - FppManager manager; - ProximityEstimate proximity_estimate = - ProximityEstimate{kDeviceId, - 0.1, - MeasurementConfidence::Low, - 0, - ProximityState::Reach, - PresenceDataSource::Ble}; - RangingData rangingData = - manager.ConvertProximityEstimateToRangingData(proximity_estimate); - EXPECT_EQ(rangingData.data_source, DataSource::kBle); - EXPECT_EQ(rangingData.position.distance.value, 0.1f); - EXPECT_EQ(rangingData.zone_transition->confidence_level, 0.0f); - EXPECT_EQ(rangingData.zone_transition->distance_range_type, - PresenceZone::DistanceBoundary::RangeType::kWithinReach); - ProximityEstimate unknown_proximity_estimate = - ProximityEstimate{kDeviceId, - 0.0, - MeasurementConfidence::Low, - 0, - ProximityState::Unknown, - PresenceDataSource::Ble}; - RangingData unknown_rangingData = - manager.ConvertProximityEstimateToRangingData(unknown_proximity_estimate); - EXPECT_EQ(unknown_rangingData.zone_transition->distance_range_type, - PresenceZone::DistanceBoundary::RangeType::kRangeUnknown); - ProximityEstimate tap_proximity_estimate = - ProximityEstimate{kDeviceId, - 0.03, - MeasurementConfidence::Low, - 0, - ProximityState::Tap, - PresenceDataSource::Ble}; - RangingData tap_rangingData = - manager.ConvertProximityEstimateToRangingData(tap_proximity_estimate); - EXPECT_EQ(tap_rangingData.zone_transition->distance_range_type, - PresenceZone::DistanceBoundary::RangeType::kWithinTap); -} - -TEST(FppManager, UpdateBleScanResultWithTxPowerSuccess) { - FppManager manager; - bool callback_called = false; - manager.RegisterZoneTransitionListener( - kCallbackId, - {.on_proximity_zone_changed = - [&callback_called]( - uint64_t device_id, - PresenceZone::DistanceBoundary::RangeType range_type) { - callback_called = true; - }}); - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/20, kReachRssi, - /*elapsed_real_time_millis=*/0)); - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/20, kReachRssi, - /*elapsed_real_time_millis=*/2000)); - EXPECT_EQ(manager.GetRangingData(kDeviceId) - ->zone_transition.value() - .distance_range_type, - PresenceZone::DistanceBoundary::RangeType::kWithinTap); - EXPECT_TRUE(callback_called); -} - -TEST(FppManager, UnregisterZoneTransitionListener) { - FppManager manager; - bool callback_called = false; - manager.RegisterZoneTransitionListener( - kCallbackId, - {.on_proximity_zone_changed = - [&callback_called]( - uint64_t device_id, - PresenceZone::DistanceBoundary::RangeType range_type) { - callback_called = true; - }}); - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, - kReachRssi, - /*elapsed_real_time_millis=*/0)); - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, - kReachRssi, - /*elapsed_real_time_millis=*/2000)); - EXPECT_TRUE(callback_called); - callback_called = false; - - // Unregister listener and update with new zone - manager.UnregisterZoneTransitionListener(kCallbackId); - EXPECT_EQ(manager - .UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, - kShortRangeRssi, - /*elapsed_real_time_millis=*/0) - .code(), - absl::StatusCode::kInternal); - EXPECT_EQ(manager - .UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, - kShortRangeRssi, - /*elapsed_real_time_millis=*/0) - .code(), - absl::StatusCode::kInternal); - EXPECT_FALSE(callback_called); -} - -TEST(FppManager, ResetProximityStateData) { - FppManager manager; - bool callback_called = false; - manager.RegisterZoneTransitionListener( - kCallbackId, - {.on_proximity_zone_changed = - [&callback_called]( - uint64_t device_id, - PresenceZone::DistanceBoundary::RangeType range_type) { - callback_called = true; - }}); - ASSERT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, - kReachRssi, - /*elapsed_real_time_millis=*/0)); - // State is only computed after second consecutive scan is fulfilled - ASSERT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, - kReachRssi, - /*elapsed_real_time_millis=*/2000)); - EXPECT_EQ(manager.GetRangingData(kDeviceId) - ->zone_transition.value() - .distance_range_type, - PresenceZone::DistanceBoundary::RangeType::kWithinReach); - EXPECT_TRUE(callback_called); - - // Reset proximity state data - manager.ResetProximityStateData(); - EXPECT_EQ(manager.GetRangingData(kDeviceId) - ->zone_transition.value() - .distance_range_type, - PresenceZone::DistanceBoundary::RangeType::kRangeUnknown); -} - -TEST(FppManager, GetStatusStringFromCode) { - FppManager manager; - EXPECT_EQ(manager.GetStatusStringFromCode(101), - "INVALID_PRESENCE_DETECTOR_HANDLE"); - EXPECT_EQ(manager.GetStatusStringFromCode(102), "NULL_OUTPUT_PARAMETER"); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/fpp/sensor_fusion_impl.cc b/presence/fpp/sensor_fusion_impl.cc deleted file mode 100644 index 2bda18b5..00000000 --- a/presence/fpp/sensor_fusion_impl.cc +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2023 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 -// -// http://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/fpp/sensor_fusion_impl.h" - -#include -#include -#include - -#include "absl/status/status.h" -#include "presence/fpp/fpp_manager.h" - -namespace nearby { -namespace presence { -std::vector SensorFusionImpl::GetDataSources( - uint64_t elapsed_realtime_millis, - const std::vector& available_sources) { - // TODO(b/264547688) - Implement - return std::vector(); -} -absl::Status SensorFusionImpl::UpdateBleScanResult( - uint64_t device_id, std::optional txPower, int rssi, - uint64_t elapsed_realtime_millis) { - return fpp_manager_.UpdateBleScanResult(device_id, txPower, rssi, - elapsed_realtime_millis); -} -void SensorFusionImpl::UpdateUwbRangingResult(uint64_t device_id, - RangingPosition position) { - // TODO(b/264547688) - Implement -} - -void SensorFusionImpl::RequestZoneTransitionUpdates( - ZoneTransitionCallback callback) { - int callback_id = ++id_generator_; - callback.on_callback_id_generated(callback_id); - fpp_manager_.RegisterZoneTransitionListener(callback_id, std::move(callback)); -} - -void SensorFusionImpl::RequestDeviceMotionUpdates( - SensorFusion::DeviceMotionCallback callback) { - // TODO(b/264547688) - Implement -} -void SensorFusionImpl::RemoveDeviceMotionUpdates( - SensorFusion::DeviceMotionCallback callback) { - // TODO(b/264547688) - Implement -} - -void SensorFusionImpl::RemoveZoneTransitionUpdates(uint64_t callback_id) { - fpp_manager_.UnregisterZoneTransitionListener(callback_id); -} - -std::optional SensorFusionImpl::GetRangingData( - uint64_t device_id) { - return fpp_manager_.GetRangingData(device_id); -} -} // namespace presence -} // namespace nearby diff --git a/presence/fpp/sensor_fusion_impl.h b/presence/fpp/sensor_fusion_impl.h deleted file mode 100644 index ac37b6a8..00000000 --- a/presence/fpp/sensor_fusion_impl.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2023 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 -// -// http://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 THIRD_PARTY_NEARBY_PRESENCE_FPP_SENSOR_FUSION_IMPL_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_FPP_SENSOR_FUSION_IMPL_H_ - -#include -#include - -#include "presence/fpp/fpp_manager.h" -#include "presence/implementation/sensor_fusion.h" - -namespace nearby { -namespace presence { - -class SensorFusionImpl : public SensorFusion { - public: - ~SensorFusionImpl() = default; - std::vector GetDataSources( - uint64_t elapsed_realtime_millis, - const std::vector& available_sources) override; - absl::Status UpdateBleScanResult(uint64_t device_id, - std::optional txPower, int rssi, - uint64_t elapsed_realtime_millis) override; - void UpdateUwbRangingResult(uint64_t device_id, - RangingPosition position) override; - void RequestZoneTransitionUpdates(ZoneTransitionCallback callback) override; - void RemoveZoneTransitionUpdates(uint64_t callback_id) override; - void RequestDeviceMotionUpdates(DeviceMotionCallback callback) override; - void RemoveDeviceMotionUpdates(DeviceMotionCallback callback) override; - std::optional GetRangingData(uint64_t device_id) override; - - private: - FppManager fpp_manager_; - int id_generator_ = 0; -}; -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_FPP_SENSOR_FUSION_IMPL_H_ diff --git a/presence/fpp/sensor_fusion_test.cc b/presence/fpp/sensor_fusion_test.cc deleted file mode 100644 index aee34d7b..00000000 --- a/presence/fpp/sensor_fusion_test.cc +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2023 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 -// -// http://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 -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/status/status.h" -#include "presence/fpp/sensor_fusion_impl.h" - -namespace nearby { -namespace presence { -namespace { -constexpr uint64_t kDeviceId = 1234; -constexpr int kReachRssi = -40; - -TEST(SensorFusion, RequestZoneTransitionUpdatesSuccess) { - SensorFusionImpl sensor_fusion_impl; - bool callback_called = false; - bool callback2_called = false; - sensor_fusion_impl.RequestZoneTransitionUpdates( - {.on_callback_id_generated = [&callback_called](uint64_t callback_id) { - callback_called = true; - EXPECT_EQ(callback_id, 1); - }}); - sensor_fusion_impl.RequestZoneTransitionUpdates( - {.on_callback_id_generated = [&callback2_called](uint64_t callback_id2) { - callback2_called = true; - EXPECT_EQ(callback_id2, 2); - }}); - EXPECT_TRUE(callback2_called); -} - -TEST(SensorFusion, RemoveZoneTransitionUpdates) { - SensorFusionImpl sensor_fusion_impl; - bool callback_called = false; - sensor_fusion_impl.RequestZoneTransitionUpdates( - {.on_callback_id_generated = [&callback_called](uint64_t callback_id) { - callback_called = true; - EXPECT_EQ(callback_id, 1); - }}); - sensor_fusion_impl.RemoveZoneTransitionUpdates(1); - EXPECT_EQ( - sensor_fusion_impl - .UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, kReachRssi, - /*elapsed_real_time_millis=*/0) - .code(), - absl::StatusCode::kInternal); -} - -TEST(SensorFusion, UpdateBleScanResult) { - SensorFusionImpl sensor_fusion_impl; - bool proximity_zone_changed_called = false; - sensor_fusion_impl.RequestZoneTransitionUpdates( - {.on_proximity_zone_changed = - [&proximity_zone_changed_called]( - uint64_t device_id, - PresenceZone::DistanceBoundary::RangeType range_type) { - proximity_zone_changed_called = true; - }}); - EXPECT_OK(sensor_fusion_impl.UpdateBleScanResult( - kDeviceId, /*txPower=*/std::nullopt, kReachRssi, - /*elapsed_real_time_millis=*/0)); - EXPECT_OK(sensor_fusion_impl.UpdateBleScanResult( - kDeviceId, /*txPower=*/std::nullopt, kReachRssi, - /*elapsed_real_time_millis=*/0)); - - EXPECT_TRUE(proximity_zone_changed_called); -} - -TEST(SensorFusion, GetRangingData) { - SensorFusionImpl sensor_fusion_impl; - bool proximity_zone_changed_called = false; - sensor_fusion_impl.RequestZoneTransitionUpdates( - {.on_proximity_zone_changed = - [&proximity_zone_changed_called]( - uint64_t device_id, - PresenceZone::DistanceBoundary::RangeType range_type) { - proximity_zone_changed_called = true; - }}); - EXPECT_OK(sensor_fusion_impl.UpdateBleScanResult( - kDeviceId, /*txPower=*/std::nullopt, kReachRssi, - /*elapsed_real_time_millis=*/0)); - EXPECT_OK(sensor_fusion_impl.UpdateBleScanResult( - kDeviceId, /*txPower=*/std::nullopt, kReachRssi, - /*elapsed_real_time_millis=*/0)); - - EXPECT_TRUE(proximity_zone_changed_called); - - EXPECT_EQ(sensor_fusion_impl.GetRangingData(kDeviceId) - ->zone_transition.value() - .distance_range_type, - PresenceZone::DistanceBoundary::RangeType::kWithinReach); -} -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/BUILD b/presence/implementation/BUILD deleted file mode 100644 index a819906f..00000000 --- a/presence/implementation/BUILD +++ /dev/null @@ -1,494 +0,0 @@ -# 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. - -load("@rules_cc//cc:cc_library.bzl", "cc_library") -load("@rules_cc//cc:cc_test.bzl", "cc_test") - -licenses(["notice"]) - -filegroup( - name = "presence_internal_common_srcs", - srcs = [ - "action_factory.cc", - "advertisement_factory.cc", - "advertisement_filter.cc", - "base_broadcast_request.cc", - "broadcast_manager.cc", - "connection_authenticator_impl.cc", - "credential_manager_impl.cc", - "ldt.cc", - "scan_manager.cc", - "service_controller_impl.cc", - ], -) - -filegroup( - name = "presence_internal_common_hdrs", - srcs = [ - "action_factory.h", - "advertisement_decoder.h", - "advertisement_decoder_impl.h", - "advertisement_factory.h", - "advertisement_filter.h", - "base_broadcast_request.h", - "broadcast_manager.h", - "connection_authenticator.h", - "connection_authenticator_impl.h", - "credential_manager.h", - "credential_manager_impl.h", - "ldt.h", - "np_ldt.h", - "scan_manager.h", - "service_controller.h", - "service_controller_impl.h", - ], -) - -cc_library( - name = "internal", - srcs = [ - "advertisement_decoder_rust_impl.cc", - ":presence_internal_common_srcs", - ], - hdrs = [ - "advertisement_decoder_rust_impl.h", - ":presence_internal_common_hdrs", - ], - defines = ["USE_RUST_DECODER=1"], - visibility = [ - "//presence:__subpackages__", - ], - deps = [ - "//internal/crypto", - "//internal/crypto_cros", - "//internal/platform:base", - "//internal/platform:comm", - "//internal/platform:logging", - "//internal/platform:types", - "//internal/platform:uuid", - "//internal/platform/implementation:comm", - "//internal/platform/implementation:types", - "//internal/proto:credential_cc_proto", - "//internal/proto:local_credential_cc_proto", - "//internal/proto:metadata_cc_proto", - "//presence:types", - "//presence/implementation/mediums", - "@beto-core//:ldt_np_adv_ffi", - "@beto-core//:np_c_ffi_types", - "@beto-core//:np_cpp_ffi", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/container:flat_hash_set", - "@com_google_absl//absl/hash", - "@com_google_absl//absl/log:check", - "@com_google_absl//absl/log:die_if_null", - "@com_google_absl//absl/random", - "@com_google_absl//absl/random:distributions", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/time", - "@com_google_absl//absl/types:optional", - "@com_google_absl//absl/types:span", - "@com_google_absl//absl/types:variant", - ], -) - -cc_library( - name = "internal_deprecated", - srcs = [ - "advertisement_decoder_impl.cc", - ":presence_internal_common_srcs", - ], - hdrs = [ - "advertisement_decoder_impl.h", - ":presence_internal_common_hdrs", - ], - visibility = [ - "//presence:__subpackages__", - ], - deps = [ - "//devtools/rust:rust_okay_here", - "//internal/crypto", - "//internal/crypto_cros", - "//internal/platform:base", - "//internal/platform:comm", - "//internal/platform:logging", - "//internal/platform:types", - "//internal/platform:uuid", - "//internal/platform/implementation:comm", - "//internal/platform/implementation:types", - "//internal/proto:credential_cc_proto", - "//internal/proto:local_credential_cc_proto", - "//internal/proto:metadata_cc_proto", - "//presence:types", - "//presence/implementation/mediums", - "@beto-core//:ldt_np_adv_ffi", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/container:flat_hash_set", - "@com_google_absl//absl/hash", - "@com_google_absl//absl/log:check", - "@com_google_absl//absl/log:die_if_null", - "@com_google_absl//absl/random", - "@com_google_absl//absl/random:distributions", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/time", - "@com_google_absl//absl/types:optional", - "@com_google_absl//absl/types:span", - "@com_google_absl//absl/types:variant", - ], -) - -cc_library( - name = "sensor_fusion", - hdrs = ["sensor_fusion.h"], - visibility = [ - "//presence:__subpackages__", - ], - deps = [ - "//presence:types", - "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/status", - ], -) - -cc_library( - name = "internal_test", - testonly = True, - srcs = [ - ], - hdrs = [ - "mock_connection_authenticator.h", - "mock_credential_manager.h", - "mock_service_controller.h", - ], - visibility = [ - "//presence:__subpackages__", - ], - deps = [ - ":internal", - "//internal/platform/implementation:comm", - "//internal/proto:credential_cc_proto", - "//internal/proto:local_credential_cc_proto", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings:string_view", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "advertisement_decoder_test", - size = "small", - srcs = ["advertisement_decoder_test.cc"], - deps = [ - ":internal_deprecated", - "//internal/platform:base", - "//internal/proto:credential_cc_proto", - "//presence:types", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "advertisement_decoder_new_format_test", - size = "small", - srcs = ["advertisement_decoder_new_format_test.cc"], - deps = [ - ":internal", - "//internal/platform:base", - "//internal/proto:credential_cc_proto", - "//presence:types", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "advertisement_filter_test", - size = "small", - srcs = ["advertisement_filter_test.cc"], - deps = [ - ":internal", - "//internal/platform:base", - "//internal/proto:credential_cc_proto", - "//presence:types", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/strings", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "advertisement_factory_test", - size = "small", - srcs = ["advertisement_factory_test.cc"], - deps = [ - ":internal", - "//internal/platform:base", - "//internal/proto:credential_cc_proto", - "//presence:types", - "//presence/implementation/mediums", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/status", - "@com_google_absl//absl/strings", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "broadcast_manager_test", - size = "small", - srcs = ["broadcast_manager_test.cc"], - deps = [ - ":internal", - "//internal/platform:base", - "//internal/platform:test_util", - "//internal/platform:types", - "//internal/proto:credential_cc_proto", - "//presence/implementation/mediums", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "ldt_test", - size = "small", - srcs = ["ldt_test.cc"], - deps = [ - ":internal", - "//internal/platform:base", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "base_broadcast_request_test", - srcs = ["base_broadcast_request_test.cc"], - deps = [ - ":internal", - "//internal/proto:credential_cc_proto", - "//presence:types", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/types:variant", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "action_factory_test", - size = "small", - srcs = ["action_factory_test.cc"], - deps = [ - ":internal", - "//presence:types", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/strings", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "connection_authenticator_impl_test", - size = "small", - srcs = ["connection_authenticator_impl_test.cc"], - deps = [ - ":internal", - "//internal/crypto", - "//internal/crypto_cros", - "//internal/proto:credential_cc_proto", - "//internal/proto:local_credential_cc_proto", - "//presence/proto:presence_frame_cc_proto", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "credential_manager_impl_test", - size = "small", - srcs = ["credential_manager_impl_test.cc"], - deps = [ - ":internal", - "//internal/platform:comm", - "//internal/platform:logging", - "//internal/platform:test_util", - "//internal/platform:types", - "//internal/platform/implementation:comm", - "//internal/platform/implementation:types", - "//internal/proto:credential_cc_proto", - "//net/proto2/contrib/parse_proto:testing", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "scan_manager_test", - size = "small", - srcs = ["scan_manager_test.cc"], - deps = [ - ":internal", - ":internal_test", - "//internal/platform:base", - "//internal/platform:comm", - "//internal/platform:logging", - "//internal/platform:mac_address", - "//internal/platform:test_util", - "//internal/platform:types", - "//internal/platform/implementation:comm", - "//internal/platform/implementation:types", - "//internal/proto:credential_cc_proto", - "//presence:types", - "//presence/implementation/mediums", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - "@com_google_absl//absl/types:variant", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) - -cc_test( - name = "service_controller_impl_test", - size = "small", - srcs = ["service_controller_impl_test.cc"], - deps = [ - ":internal", - ":internal_test", - "//internal/platform:comm", - "//internal/platform:test_util", - "//internal/platform:types", - "//internal/platform/implementation:comm", - "//internal/platform/implementation:types", - "//internal/proto:credential_cc_proto", - "//net/proto2/contrib/parse_proto:testing", - "//presence/implementation/mediums", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) diff --git a/presence/implementation/action_factory.cc b/presence/implementation/action_factory.cc deleted file mode 100644 index fe4a6e45..00000000 --- a/presence/implementation/action_factory.cc +++ /dev/null @@ -1,106 +0,0 @@ -// 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/action_factory.h" - -#include -#include -#include - -#include "internal/platform/logging.h" -#include "presence/data_element.h" -#include "presence/implementation/base_broadcast_request.h" - -namespace nearby { -namespace presence { - -constexpr int kContentTimestampMask = 0x0F; -constexpr int kContentTimestampShift = 28; -constexpr int kEmptyMask = 0; -constexpr int kActionSizeInBits = 32; - -namespace { - -int GetActionMask(ActionBit action) { - int bit = static_cast(action); - if (bit < 0 || bit >= kActionSizeInBits) { - LOG(WARNING) << "Unsupported action " << static_cast(action); - return kEmptyMask; - } - return 1 << (kActionSizeInBits - 1 - bit); -} - -// The reverse of `GetActionMask()` -ActionBit GetActionFromBit(int bit) { - return ActionBit(kActionSizeInBits - 1 - bit); -} - -int GetMask(const DataElement& element) { - int type = element.GetType(); - switch (type) { - case DataElement::kContextTimestampFieldType: { - auto value = element.GetValue(); - if (!value.empty()) { - return (value[0] & kContentTimestampMask) << kContentTimestampShift; - } else { - LOG(WARNING) << "Context timestamp Data Element without value"; - return kEmptyMask; - } - } - case DataElement::kActionFieldType: { - if (element.GetValue().empty()) { - LOG(WARNING) << "Action Data Element without value"; - return kEmptyMask; - } - return GetActionMask(ActionBit(element.GetValue()[0])); - } - } - LOG(WARNING) << "Data Element " << type - << " not supported in base advertisement"; - return kEmptyMask; -} - -} // namespace - -Action ActionFactory::CreateAction( - const std::vector& data_elements) { - Action action = {.action = 0}; - std::for_each(data_elements.begin(), data_elements.end(), - [&](const auto& element) { - int mask = GetMask(element); - action.action |= mask; - }); - return action; -} - -void ActionFactory::DecodeAction(const Action& action, - std::vector& output) { - uint8_t context_timestamp = - (action.action >> kContentTimestampShift) & kContentTimestampMask; - if (context_timestamp) { - output.emplace_back(DataElement::kContextTimestampFieldType, - context_timestamp); - } - constexpr int kFirstUsedBit = - kActionSizeInBits - static_cast(ActionBit::kLastAction); - for (int i = kFirstUsedBit; i < kContentTimestampShift; i++) { - int bit_mask = 1 << i; - if (action.action & bit_mask) { - output.emplace_back(DataElement(GetActionFromBit(i))); - } - } -} - -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/action_factory.h b/presence/implementation/action_factory.h deleted file mode 100644 index b2a43880..00000000 --- a/presence/implementation/action_factory.h +++ /dev/null @@ -1,45 +0,0 @@ -// 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 THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ACTION_FACTORY_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ACTION_FACTORY_H_ - -#include - -#include "presence/data_element.h" -#include "presence/implementation/base_broadcast_request.h" - -namespace nearby { -namespace presence { - -// Defines the mapping between Data Elements and Actions in the Base NP -// advertisement. -class ActionFactory { - public: - // Returns an Action for Base NP advertisement from a collection of Data - // Elements. Data Elements unsupported in the Base NP advertisement are - // ignored. - static Action CreateAction(const std::vector& data_elements); - - // Decodes a Base NP Action into a list of Data Elements. The Data Elements - // are appended to the `output` list. - // - // DecodeAction is effectively a reverse operation of CreateAction. - static void DecodeAction(const Action& action, - std::vector& output); -}; -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ACTION_FACTORY_H_ diff --git a/presence/implementation/action_factory_test.cc b/presence/implementation/action_factory_test.cc deleted file mode 100644 index 63db1990..00000000 --- a/presence/implementation/action_factory_test.cc +++ /dev/null @@ -1,107 +0,0 @@ -// 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/action_factory.h" - -#include -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/strings/escaping.h" -#include "presence/data_element.h" -#include "presence/implementation/base_broadcast_request.h" - -namespace nearby { -namespace presence { -namespace { - -using ::testing::ElementsAre; - -constexpr uint32_t kActiveUnlockBitMask = 1 << 23; -constexpr uint32_t kFastPairBitMask = 1 << 17; - -TEST(ActionFactory, CreateActiveUnlockAction) { - std::vector data_elements; - data_elements.emplace_back(ActionBit::kActiveUnlockAction); - - Action action = ActionFactory::CreateAction(data_elements); - - EXPECT_EQ(action.action, kActiveUnlockBitMask); -} - -TEST(ActionFactory, CreateActiveIgnoresUnsupportedActions) { - std::vector data_elements; - data_elements.emplace_back(ActionBit::kActiveUnlockAction); - // The action is 32 bit, so the valid range is [0-31] - data_elements.emplace_back(ActionBit(-1)); - data_elements.emplace_back(ActionBit(32)); - Action action = ActionFactory::CreateAction(data_elements); - - EXPECT_EQ(action.action, kActiveUnlockBitMask); -} - -TEST(ActionFactory, CreateContextTimestamp) { - const std::string kTimestamp = absl::HexStringToBytes("0B"); - - std::vector data_elements; - data_elements.emplace_back(DataElement::kContextTimestampFieldType, - kTimestamp); - - Action action = ActionFactory::CreateAction(data_elements); - - EXPECT_EQ(action.action, 0x0BU << 28); -} - -TEST(ActionFactory, CreateContextTimestampAndFastPair) { - const std::string kTimestamp = absl::HexStringToBytes("0B"); - - std::vector data_elements; - data_elements.emplace_back(DataElement::kContextTimestampFieldType, - kTimestamp); - data_elements.emplace_back(ActionBit::kFastPairSassAction); - - Action action = ActionFactory::CreateAction(data_elements); - - EXPECT_EQ(action.action, (0x0BU << 28) | kFastPairBitMask); -} - -TEST(ActionFactory, DecodeActiveUnlockAction) { - constexpr Action kAction = {.action = kActiveUnlockBitMask}; - std::vector data_elements; - - ActionFactory::DecodeAction(kAction, data_elements); - - EXPECT_THAT( - data_elements, - ElementsAre(DataElement(DataElement(ActionBit::kActiveUnlockAction)))); -} - -TEST(ActionFactory, DecodeContextTimestampAndFastPair) { - constexpr Action kAction = {.action = (0x0BU << 28) | kFastPairBitMask}; - std::vector data_elements; - - ActionFactory::DecodeAction(kAction, data_elements); - - EXPECT_THAT( - data_elements, - ElementsAre(DataElement(DataElement::kContextTimestampFieldType, - absl::HexStringToBytes("0B")), - DataElement(DataElement(ActionBit::kFastPairSassAction)))); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/advertisement_decoder.h b/presence/implementation/advertisement_decoder.h deleted file mode 100644 index 3c5d444b..00000000 --- a/presence/implementation/advertisement_decoder.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2024 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 THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ADVERTISEMENT_DECODER_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ADVERTISEMENT_DECODER_H_ - -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "internal/proto/credential.pb.h" -#include "presence/data_element.h" - -namespace nearby { -namespace presence { - -// The structured decoded form of a detected Nearby Presence advertisement -struct Advertisement { - uint8_t version = 0; - std::vector data_elements; - absl::StatusOr public_credential = - absl::NotFoundError(""); - internal::IdentityType identity_type = internal::IDENTITY_TYPE_UNSPECIFIED; - std::string metadata_key; -}; - -// Interface for decoding Nearby Presence advertisements from a payload of raw -// bytes into a structured, decrypted, and decoded format -class AdvertisementDecoder { - public: - // Is needed otherwise deleting an instance via a pointer to a base class - // results in undefined behavior - virtual ~AdvertisementDecoder() = default; - - // Returns the structured and decoded contents of an advertisement given a - // payload of bytes as a string. Returns an error if the advertisement is - // misformatted or if it couldn't be decrypted. - virtual absl::StatusOr DecodeAdvertisement( - absl::string_view advertisement) = 0; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ADVERTISEMENT_DECODER_H_ diff --git a/presence/implementation/advertisement_decoder_impl.cc b/presence/implementation/advertisement_decoder_impl.cc deleted file mode 100644 index fec04c85..00000000 --- a/presence/implementation/advertisement_decoder_impl.cc +++ /dev/null @@ -1,287 +0,0 @@ -// 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/advertisement_decoder_impl.h" - -#include -#include -#include -#include -#include - -#include "absl/container/flat_hash_map.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/escaping.h" -#include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" -#include "internal/platform/logging.h" -#include "presence/data_element.h" -#include "presence/implementation/action_factory.h" -#include "presence/implementation/advertisement_decoder.h" -#include "presence/implementation/base_broadcast_request.h" -#include "presence/implementation/ldt.h" - -namespace nearby { -namespace presence { - -namespace { - -constexpr uint8_t kDataTypeMask = - (1 << DataElement::kDataElementLengthShift) - 1; - -constexpr int kAdvertisementVersion = 0; - -constexpr int kEncryptedIdentityAdditionalLength = - kSaltSize + kBaseMetadataSize; -constexpr int kEddystoneAdditionalLength = 20; - -uint8_t GetDataElementType(uint8_t header) { return header & kDataTypeMask; } - -size_t GetDataElementLength(uint8_t header) { - return header >> DataElement::kDataElementLengthShift; -} - -// Verifies if the DE header describes a valid DE in v0 advertisement. -bool IsDataElementAllowed(uint8_t header) { - uint8_t data_type = GetDataElementType(header); - size_t length = GetDataElementLength(header); - switch (data_type) { - case DataElement::kSaltFieldType: - return length == 2; - case DataElement::kPublicIdentityFieldType: - return length == 0; - case DataElement::kPrivateGroupIdentityFieldType: - case DataElement::kContactsGroupIdentityFieldType: - return length >= 2 && length <= 6; - case DataElement::kTxPowerFieldType: - return length == 1; - case DataElement::kActionFieldType: - return length >= 1 && length <= 3; - case DataElement::kModelIdFieldType: - return length == 3; - case DataElement::kEddystoneIdFieldType: - return length == 0; - case DataElement::kAccountKeyDataFieldType: - return length <= 12; - case DataElement::kConnectionStatusFieldType: - return length <= 3; - case DataElement::kBatteryFieldType: - return length <= 3; - default: - return false; - } -} - -bool IsEncryptedIdentity(int data_type) { - return data_type == DataElement::kPrivateGroupIdentityFieldType || - data_type == DataElement::kContactsGroupIdentityFieldType; -} - -bool IsIdentity(int data_type) { - return data_type == DataElement::kPublicIdentityFieldType || - IsEncryptedIdentity(data_type); -} - -internal::IdentityType GetIdentityType(int data_type) { - switch (data_type) { - case DataElement::kPrivateGroupIdentityFieldType: - return internal::IDENTITY_TYPE_PRIVATE_GROUP; - case DataElement::kContactsGroupIdentityFieldType: - return internal::IDENTITY_TYPE_CONTACTS_GROUP; - case DataElement::kPublicIdentityFieldType: - return internal::IDENTITY_TYPE_PUBLIC; - } - return internal::IDENTITY_TYPE_UNSPECIFIED; -} - -// Returns the real length of a DE in v0 advertisement, which may be larger than -// the value in the header. -size_t GetDataElementTrueLength(uint8_t header) { - uint8_t data_type = GetDataElementType(header); - size_t length = GetDataElementLength(header); - if (IsEncryptedIdentity(data_type)) { - // The length of an encrypted DE is 16 bytes of overhead (salt + metadata - // key) + the actual payload, which is too long to fit in the 4-bit DE - // length field. - length += kEncryptedIdentityAdditionalLength; - } else if (data_type == DataElement::kEddystoneIdFieldType) { - // Length in the header is set to EID length minus 20, because EID is longer - // than 15 (the maximum length that can be stored in 4 bits. - length += kEddystoneAdditionalLength; - } - return length; -} - -absl::StatusOr ParseDataElement(const absl::string_view input, - size_t& index) { - if (index >= input.size()) { - return absl::OutOfRangeError(absl::StrFormat( - "Data element (%s) is %d bytes long. Expected more than %d", - absl::BytesToHexString(input), input.size(), index)); - } - uint8_t header = input[index]; - if (!IsDataElementAllowed(header)) { - return absl::InvalidArgumentError( - absl::StrFormat("Unsupported Data Element 0x%x", header)); - } - uint8_t data_type = GetDataElementType(header); - size_t length = GetDataElementTrueLength(header); - ++index; - size_t start = index; - index += length; - if (index > input.size()) { - return absl::OutOfRangeError(absl::StrFormat( - "Data element (%s) is %d bytes long. Expected at least %d", - absl::BytesToHexString(input), input.size(), index)); - } - VLOG(1) << "Type: " << static_cast(data_type) - << " length: " << static_cast(length) - << " DE: " << absl::BytesToHexString(input.substr(start, length)); - return DataElement(data_type, input.substr(start, length)); -} -} // namespace - -void DecodeBaseAction(absl::string_view serialized_action, - Advertisement& decoded_advertisement) { - if (serialized_action.empty() || serialized_action.size() > 3) { - LOG(WARNING) << "Base NP action \'" - << absl::BytesToHexString(serialized_action) - << "\' has wrong length " << serialized_action.size() - << " , expected size in range [1 - 3]"; - return; - } - // Action, 0-2 bytes in Big Endian order. - Action action = {.action = 0}; - for (int i = 0; i < serialized_action.size(); ++i) { - int offset = (sizeof(uint32_t) - 1 - i) * 8; - action.action |= serialized_action[i] << offset; - } - - ActionFactory::DecodeAction(action, decoded_advertisement.data_elements); -} - -absl::StatusOr DecryptLdt( - const std::vector& credentials, - absl::string_view salt, absl::string_view encrypted_contents, - Advertisement& decoded_advertisement) { - if (credentials.empty()) { - return absl::UnavailableError("No credentials"); - } - for (const auto& credential : credentials) { - absl::StatusOr encryptor = LdtEncryptor::Create( - credential.key_seed(), credential.metadata_encryption_key_tag_v0()); - if (encryptor.ok()) { - absl::StatusOr result = - encryptor->DecryptAndVerify(encrypted_contents, salt); - if (result.ok() && result->size() > kBaseMetadataSize) { - decoded_advertisement.public_credential = credential; - decoded_advertisement.metadata_key = - result->substr(0, kBaseMetadataSize); - return result->substr(kBaseMetadataSize); - } - } - } - return absl::UnavailableError( - "Couldn't decrypt the message with any credentials"); -} - -absl::Status DecryptDataElements( - const std::vector& credentials, - const DataElement& elem, Advertisement& decoded_advertisement) { - if (elem.GetValue().size() <= kEncryptedIdentityAdditionalLength) { - return absl::OutOfRangeError(absl::StrFormat( - "Encrypted identity data element is too short - %d bytes", - elem.GetValue().size())); - } - absl::string_view salt = elem.GetValue().substr(0, kSaltSize); - decoded_advertisement.data_elements.emplace_back(DataElement::kSaltFieldType, - salt); - absl::string_view encrypted = elem.GetValue().substr(kSaltSize); - absl::StatusOr decrypted = - DecryptLdt(credentials, salt, encrypted, decoded_advertisement); - if (!decrypted.ok()) { - LOG(WARNING) << "Failed to decrypt advertisement, status: " - << decrypted.status(); - return decrypted.status(); - } - size_t index = 0; - while (index < decrypted->size()) { - absl::StatusOr internal_elem = - ParseDataElement(*decrypted, index); - if (!internal_elem.ok()) { - LOG(WARNING) << "Failed to read data element, status: " - << internal_elem.status(); - return internal_elem.status(); - } - if (internal_elem->GetType() == DataElement::kActionFieldType) { - DecodeBaseAction(internal_elem->GetValue(), decoded_advertisement); - } else { - decoded_advertisement.data_elements.push_back(*std::move(internal_elem)); - } - } - return absl::OkStatus(); -} - -absl::StatusOr AdvertisementDecoderImpl::DecodeAdvertisement( - absl::string_view advertisement) { - Advertisement decoded_advertisement = Advertisement{}; - std::vector result; - LOG(INFO) << "Advertisement: " << absl::BytesToHexString(advertisement); - if (advertisement.empty()) { - return absl::OutOfRangeError("Empty advertisement"); - } - uint8_t version = advertisement[0]; - VLOG(1) << "Version: " << version; - if (version != kAdvertisementVersion) { - return absl::UnimplementedError(absl::StrFormat( - "Advertisement version (%d) is not supported", version)); - } - decoded_advertisement.version = version; - size_t index = 1; - absl::StatusOr decrypted; - while (index < advertisement.size()) { - absl::StatusOr elem = ParseDataElement(advertisement, index); - if (!elem.ok()) { - LOG(WARNING) << "Failed to read data element, status: " << elem.status(); - return elem.status(); - } - if (IsIdentity(elem->GetType())) { - decoded_advertisement.identity_type = GetIdentityType(elem->GetType()); - } - if (IsEncryptedIdentity(elem->GetType())) { - if (credentials_map_ == nullptr) { - return absl::FailedPreconditionError("Missing credentials"); - } - auto identity_type_specific_creds = - (*credentials_map_)[decoded_advertisement.identity_type]; - absl::Status status = DecryptDataElements(identity_type_specific_creds, - *elem, decoded_advertisement); - if (!status.ok()) { - return status; - } - } else { - if (elem->GetType() == DataElement::kActionFieldType) { - DecodeBaseAction(elem->GetValue(), decoded_advertisement); - } else { - decoded_advertisement.data_elements.push_back(*std::move(elem)); - } - } - } - - return std::move(decoded_advertisement); -} - -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/advertisement_decoder_impl.h b/presence/implementation/advertisement_decoder_impl.h deleted file mode 100644 index c3e7e592..00000000 --- a/presence/implementation/advertisement_decoder_impl.h +++ /dev/null @@ -1,51 +0,0 @@ -// 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 THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_DECODER_IMPL_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_DECODER_IMPL_H_ - -#include - -#include "absl/container/flat_hash_map.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "internal/proto/credential.pb.h" -#include "presence/implementation/advertisement_decoder.h" - -namespace nearby { -namespace presence { - -// Implements the C++ backed parsing and decrypting of advertisement bytes -class AdvertisementDecoderImpl : public AdvertisementDecoder { - public: - AdvertisementDecoderImpl() = default; - explicit AdvertisementDecoderImpl( - absl::flat_hash_map>* - credentials_map) - : credentials_map_(credentials_map) {} - - absl::StatusOr DecodeAdvertisement( - absl::string_view advertisement) override; - - private: - absl::flat_hash_map>* - credentials_map_ = nullptr; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_DECODER_IMPL_H_ diff --git a/presence/implementation/advertisement_decoder_new_format_test.cc b/presence/implementation/advertisement_decoder_new_format_test.cc deleted file mode 100644 index ef78c828..00000000 --- a/presence/implementation/advertisement_decoder_new_format_test.cc +++ /dev/null @@ -1,156 +0,0 @@ -// 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 -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/container/flat_hash_map.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/escaping.h" -#include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" -#include "internal/proto/credential.pb.h" -#include "presence/data_element.h" -#include "presence/implementation/advertisement_decoder.h" -#include "presence/implementation/advertisement_decoder_rust_impl.h" - -namespace nearby { -namespace presence { -namespace { - -using ::nearby::ByteArray; // NOLINT -using ::nearby::internal::IdentityType; // NOLINT -using ::nearby::internal::SharedCredential; // NOLINT -using ::testing::ElementsAre; -using ::testing::status::StatusIs; - -TEST(AdvertisementDecoderImpl, DecodePublicAdvertisement) { - std::string V0AdvPlaintextBytes = - "00" // Adv Header V0 unencrypted - "1503"; // length 1 Tx Power DE value 3 - AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl(); - - absl::StatusOr result = - decoder.DecodeAdvertisement(absl::HexStringToBytes(V0AdvPlaintextBytes)); - ASSERT_OK(result); - EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_PUBLIC); - EXPECT_EQ(result->version, 0); - EXPECT_THAT(result->data_elements, - ElementsAre(DataElement(DataElement::kTxPowerFieldType, - absl::HexStringToBytes("03")))); -} - -TEST(AdvertisementDecoderImpl, DecodePublicAdvertisementMultiDe) { - std::string V0AdvPlaintextMultiDeBytes = - "00" // Adv Header V0 unencrypted - "1505" // length 1 Tx Power DE value 5 - "260040"; // length 2 actions de with NearbyShare bit set - - AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl(); - absl::StatusOr result = decoder.DecodeAdvertisement( - absl::HexStringToBytes(V0AdvPlaintextMultiDeBytes)); - ASSERT_OK(result); - EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_PUBLIC); - EXPECT_EQ(result->version, 0); - EXPECT_THAT(result->data_elements, - ElementsAre(DataElement(DataElement::kTxPowerFieldType, - absl::HexStringToBytes("05")), - DataElement(ActionBit::kNearbyShareAction))); -} - -// V0 encrypted advertisement data - ripped out of np_adv/tests/examples_v0.rs -TEST(AdvertisementDecoderImpl, DecodeEncryptedAdvertisement) { - std::string V0AdvEncryptedBytes = "042222D82212EF16DBF872F2A3A7C0FA5248EC"; - ByteArray seed({ - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - }); - ByteArray known_mac({0x09, 0xFE, 0x9E, 0x81, 0xB7, 0x3E, 0x5E, 0xCC, - 0x76, 0x59, 0x57, 0x71, 0xE0, 0x1F, 0xFB, 0x34, - 0x38, 0xE7, 0x5F, 0x24, 0xA7, 0x69, 0x56, 0xA0, - 0xB8, 0xEA, 0x67, 0xD1, 0x1C, 0x3E, 0x36, 0xFD}); - SharedCredential public_credential; - public_credential.set_key_seed(seed.AsStringView()); - public_credential.set_metadata_encryption_key_tag_v0( - known_mac.AsStringView()); - public_credential.set_id(12345678); - absl::flat_hash_map> - credentials; - credentials[IdentityType::IDENTITY_TYPE_PRIVATE_GROUP].push_back( - public_credential); - AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl(&credentials); - - absl::StatusOr result = - decoder.DecodeAdvertisement(absl::HexStringToBytes(V0AdvEncryptedBytes)); - ASSERT_OK(result); - EXPECT_EQ(result->public_credential.value().id(), public_credential.id()); - EXPECT_EQ(result->public_credential.value().key_seed(), - public_credential.key_seed()); - EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_PRIVATE_GROUP); - EXPECT_EQ(result->version, 0); - EXPECT_THAT(result->data_elements, - ElementsAre(DataElement(DataElement::kSaltFieldType, - absl::HexStringToBytes("2222")), - DataElement(DataElement::kTxPowerFieldType, - absl::HexStringToBytes("03")))); -} - -TEST(AdvertisementDecoderImpl, DecodeEncryptedAdvertisementNoCreds) { - std::string V0AdvEncryptedBytes = "042222D82212EF16DBF872F2A3A7C0FA5248EC"; - AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl(); - - absl::StatusOr result = - decoder.DecodeAdvertisement(absl::HexStringToBytes(V0AdvEncryptedBytes)); - EXPECT_THAT(result, StatusIs(absl::StatusCode::kUnavailable)); -} - -TEST(AdvertisementDecoderImpl, V1AdvCurrentlyUnsupported) { - std::string V1Adv = - "20" // Version header V1 - "00" // format - "02" // section len - "1503"; // Tx power value 3 - - AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl(); - absl::StatusOr result = - decoder.DecodeAdvertisement(absl::HexStringToBytes(V1Adv)); - EXPECT_THAT(result, StatusIs(absl::StatusCode::kUnimplemented)); -} - -TEST(AdvertisementDecoderImpl, V0InvalidEmptyAdv) { - std::string V1Adv = "00"; - AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl(); - absl::StatusOr result = - decoder.DecodeAdvertisement(absl::HexStringToBytes(V1Adv)); - EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); -} - -TEST(AdvertisementDecoderImpl, V0InvalidAdvContents) { - std::string invalid_v0_adv = - "00" // Adv Header V0 unencrypted - "3503"; // length 3 Tx Power DE with only 1 byte - AdvertisementDecoderImpl decoder = AdvertisementDecoderImpl(); - absl::StatusOr result = - decoder.DecodeAdvertisement(absl::HexStringToBytes(invalid_v0_adv)); - EXPECT_THAT(result, StatusIs(absl::StatusCode::kInvalidArgument)); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/advertisement_decoder_rust_impl.cc b/presence/implementation/advertisement_decoder_rust_impl.cc deleted file mode 100644 index 9ff10320..00000000 --- a/presence/implementation/advertisement_decoder_rust_impl.cc +++ /dev/null @@ -1,241 +0,0 @@ -// 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/advertisement_decoder_rust_impl.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "absl/container/flat_hash_map.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" -#include "np_cpp_ffi_types.h" -#include "nearby_protocol.h" -#include "internal/platform/logging.h" -#include "presence/data_element.h" -#include "presence/implementation/advertisement_decoder.h" - -namespace nearby { -namespace presence { -namespace { - -absl::StatusOr<::nearby_protocol::ActionType> MapAction( - const ActionBit action) { - return ::nearby_protocol::ActionType::TryBuildFromU8( - static_cast(action)); -} - -void AddActionsToAdvertisement(const nearby_protocol::V0Actions& parsed_actions, - Advertisement& advertisement) { - for (const auto action : kAllActionBits) { - auto action_type = MapAction(action); - if (!action_type.ok()) { - LOG(WARNING) << "Advertisement contains an unsupported action bit: " - << (int)action; - continue; - } - if (parsed_actions.HasAction(*action_type)) { - advertisement.data_elements.push_back(DataElement(action)); - } - } -} - -void ProcessDataElement(const nearby_protocol::V0DataElement& data_element, - Advertisement& advertisement) { - switch (data_element.GetKind()) { - case nearby_protocol::V0DataElementKind::TxPower: { - advertisement.data_elements.push_back(DataElement( - DataElement::kTxPowerFieldType, data_element.AsTxPower().GetAsI8())); - return; - } - case nearby_protocol::V0DataElementKind::Actions: { - AddActionsToAdvertisement(data_element.AsActions(), advertisement); - return; - } - default: { - LOG(WARNING) << "Unsupported data element type: " - << (int)data_element.GetKind(); - } - } -} - -internal::IdentityType GetIdentityType( - nearby_protocol::DeserializedV0IdentityKind identity) { - switch (identity) { - case np_ffi::internal::DeserializedV0IdentityKind::Plaintext: - return internal::IdentityType::IDENTITY_TYPE_PUBLIC; - case np_ffi::internal::DeserializedV0IdentityKind::Decrypted: - return internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP; - } -} - -absl::StatusOr<::nearby::internal::SharedCredential> FindById( - std::vector<::nearby::internal::SharedCredential> private_credentials, - uint64_t id) { - auto cred = - std::find_if(private_credentials.begin(), private_credentials.end(), - [&id](const auto& x) { return x.id() == id; }); - if (cred == private_credentials.end()) { - return absl::NotFoundError("No credential found with id: " + - std::to_string(id)); - } - return *cred; -} - -absl::Status ProcessLegibleV0Adv( - nearby_protocol::LegibleDeserializedV0Advertisement legible_adv, - std::vector<::nearby::internal::SharedCredential> private_credentials, - Advertisement& advertisement) { - advertisement.identity_type = GetIdentityType(legible_adv.GetIdentityKind()); - - auto num_des = legible_adv.GetNumberOfDataElements(); - auto payload = legible_adv.IntoPayload(); - - // TODO(b/333126765): salt isn't a DE, we should restructure the - // Advertisement struct to reflect this - if (advertisement.identity_type == - internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP) { - auto cred_details = payload.TryGetIdentityDetails(); - if (!cred_details.ok()) { - return cred_details.status(); - } - - advertisement.public_credential = - FindById(private_credentials, cred_details->cred_id); - - // TODO(b/333126765): update salt to use unsigned char * to remove cast - std::string salt(reinterpret_cast(cred_details->salt), 2); - advertisement.data_elements.push_back(DataElement(0x00, salt)); - - std::string metadata_key( - reinterpret_cast(cred_details->identity_token), 14); - advertisement.metadata_key = std::move(metadata_key); - } - - for (int i = 0; i < num_des; i++) { - auto de_result = payload.TryGetDataElement(i); - if (!de_result.ok()) { - return de_result.status(); - } - ProcessDataElement(*de_result, advertisement); - } - return absl::OkStatus(); -} - -absl::Status ProcessV0Advertisement( - nearby_protocol::DeserializedV0Advertisement result, - std::vector<::nearby::internal::SharedCredential> private_credentials, - Advertisement& adv) { - switch (result.GetKind()) { - case nearby_protocol::DeserializedV0AdvertisementKind::Legible: - return ProcessLegibleV0Adv(result.IntoLegible(), private_credentials, - adv); - break; - case nearby_protocol::DeserializedV0AdvertisementKind:: - NoMatchingCredentials: { - return absl::UnavailableError( - "Couldn't decrypt the message with any credentials"); - } - } -} - -} // namespace - -absl::StatusOr AdvertisementDecoderImpl::DecodeAdvertisement( - absl::string_view advertisement) { - auto byte_buffer = nearby_protocol::ByteBuffer< - nearby_protocol::MAX_ADV_PAYLOAD_SIZE>::TryFromString(advertisement); - if (!byte_buffer.ok()) { - return absl::InvalidArgumentError("Invalid length advertisement"); - } - - Advertisement decoded_advertisement; - const nearby_protocol::RawAdvertisementPayload payload(byte_buffer.value()); - auto deserialize_result = - nearby_protocol::Deserializer::DeserializeAdvertisement(payload, - cred_book_); - - switch (deserialize_result.GetKind()) { - case np_ffi::internal::DeserializeAdvertisementResultKind::Error: { - return absl::InvalidArgumentError("Invalid advertisement format"); - } - case np_ffi::internal::DeserializeAdvertisementResultKind::V1: { - return absl::UnimplementedError( - absl::StrFormat("V1 Advertisement format is not supported")); - } - case np_ffi::internal::DeserializeAdvertisementResultKind::V0: { - decoded_advertisement.version = 0; - auto result = - ProcessV0Advertisement(deserialize_result.IntoV0(), - private_credentials_, decoded_advertisement); - if (!result.ok()) { - return result; - } - break; - } - } - - return decoded_advertisement; -} - -nearby_protocol::CredentialBook -AdvertisementDecoderImpl::InitializeCredentialBook( - absl::flat_hash_map>* - credentials_map) { - if (credentials_map == nullptr) { - nearby_protocol::CredentialSlab slab; - nearby_protocol::CredentialBook cred_book(slab); - return cred_book; - } - - nearby_protocol::CredentialSlab slab; - for (const auto& credential : (*credentials_map) - [internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP]) { - // Make sure the vector is not empty, as this is a prerequisite of the Rust - // code we call into. - std::vector metadata_bytes(1); - if (!credential.encrypted_metadata_bytes_v0().empty()) { - metadata_bytes = - std::vector(credential.encrypted_metadata_bytes_v0().begin(), - credential.encrypted_metadata_bytes_v0().end()); - } - nearby_protocol::MatchedCredentialData matched_cred(credential.id(), - metadata_bytes); - - auto key_seed = credential.key_seed(); - std::array key_seed_array; - std::copy(key_seed.begin(), key_seed.end(), key_seed_array.data()); - - auto tag = credential.metadata_encryption_key_tag_v0(); - std::array tag_array; - std::copy(tag.begin(), tag.end(), tag_array.data()); - - auto matchable_credential = nearby_protocol::V0MatchableCredential( - key_seed_array, tag_array, matched_cred); - slab.AddV0Credential(matchable_credential); - } - nearby_protocol::CredentialBook cred_book(slab); - return cred_book; -} - -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/advertisement_decoder_rust_impl.h b/presence/implementation/advertisement_decoder_rust_impl.h deleted file mode 100644 index b2444e7d..00000000 --- a/presence/implementation/advertisement_decoder_rust_impl.h +++ /dev/null @@ -1,61 +0,0 @@ -// 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 THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_DECODER_RUST_IMPL_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_DECODER_RUST_IMPL_H_ - -#include - -#include "absl/container/flat_hash_map.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "nearby_protocol.h" -#include "presence/implementation/advertisement_decoder.h" - -namespace nearby { -namespace presence { - -// Implements the Rust backed parsing and decrypting of advertisement bytes -class AdvertisementDecoderImpl : public AdvertisementDecoder { - public: - AdvertisementDecoderImpl() - : cred_book_(InitializeCredentialBook(nullptr)), - private_credentials_( - std::vector<::nearby::internal::SharedCredential>()) {} - - explicit AdvertisementDecoderImpl( - absl::flat_hash_map>* - credentials_map) - : cred_book_(InitializeCredentialBook(credentials_map)), - private_credentials_( - (*credentials_map) - [internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP]) {} - - absl::StatusOr DecodeAdvertisement( - absl::string_view advertisement) override; - - private: - nearby_protocol::CredentialBook InitializeCredentialBook( - absl::flat_hash_map>* - credentials_map); - nearby_protocol::CredentialBook cred_book_; - std::vector<::nearby::internal::SharedCredential> private_credentials_; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_DECODER_IMPL_H_ diff --git a/presence/implementation/advertisement_decoder_test.cc b/presence/implementation/advertisement_decoder_test.cc deleted file mode 100644 index 5b2a70d4..00000000 --- a/presence/implementation/advertisement_decoder_test.cc +++ /dev/null @@ -1,226 +0,0 @@ -// 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/advertisement_decoder.h" - -#include -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/container/flat_hash_map.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/escaping.h" -#include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" -#include "internal/proto/credential.pb.h" -#include "presence/data_element.h" -#include "presence/implementation/advertisement_decoder_impl.h" -#include "presence/scan_request.h" -#include "presence/scan_request_builder.h" - -namespace nearby { -namespace presence { - -namespace { -using ::nearby::ByteArray; // NOLINT -using ::nearby::internal::IdentityType; // NOLINT -using ::nearby::internal::SharedCredential; // NOLINT -using ::testing::ElementsAre; -using ::testing::UnorderedElementsAre; -using ::testing::status::StatusIs; - -constexpr absl::string_view kAccountName = "test account"; - -ScanRequest GetScanRequest() { - return {.account_name = std::string(kAccountName), - .identity_types = { - IdentityType::IDENTITY_TYPE_PRIVATE_GROUP, - IdentityType::IDENTITY_TYPE_CONTACTS_GROUP, - IdentityType::IDENTITY_TYPE_PUBLIC, - }}; -} - -ScanRequest GetScanRequest(std::vector credentials) { - LegacyPresenceScanFilter scan_filter = {.remote_public_credentials = - credentials}; - return ScanRequestBuilder() - .SetAccountName(kAccountName) - .AddIdentityType(IdentityType::IDENTITY_TYPE_PRIVATE_GROUP) - .AddIdentityType(IdentityType::IDENTITY_TYPE_CONTACTS_GROUP) - .AddIdentityType(IdentityType::IDENTITY_TYPE_PUBLIC) - .Build(); -} - -SharedCredential GetPublicCredential() { - // Values copied from LDT tests - ByteArray seed({204, 219, 36, 137, 233, 252, 172, 66, 179, 147, 72, - 184, 148, 30, 209, 154, 29, 54, 14, 117, 224, 152, - 200, 193, 94, 107, 28, 194, 182, 32, 205, 57}); - ByteArray known_mac({0xB4, 0xC5, 0x9F, 0xA5, 0x99, 0x24, 0x1B, 0x81, - 0x75, 0x8D, 0x97, 0x6B, 0x5A, 0x62, 0x1C, 0x05, - 0x23, 0x2F, 0xE1, 0xBF, 0x89, 0xAE, 0x59, 0x87, - 0xCA, 0x25, 0x4C, 0x35, 0x54, 0xDC, 0xE5, 0x0E}); - SharedCredential public_credential; - public_credential.set_key_seed(seed.AsStringView()); - public_credential.set_metadata_encryption_key_tag_v0( - known_mac.AsStringView()); - return public_credential; -} - -TEST(AdvertisementDecoderImpl, - DecodeBaseNpV0PublicIdentityWithTxAndActionFields) { - AdvertisementDecoderImpl decoder; - // v0 public identity, power and action, action value 8 for active unlock. - // These values all come from - // //third_party/nearby/presence/implementation/advertisement_factory_test.cc - auto result = - decoder.DecodeAdvertisement(absl::HexStringToBytes("000315FF260080")); - - ASSERT_OK(result); - EXPECT_THAT(result->data_elements, - UnorderedElementsAre( - DataElement(DataElement::kPublicIdentityFieldType, ""), - DataElement(DataElement::kTxPowerFieldType, - absl::HexStringToBytes("ff")), - DataElement(DataElement(ActionBit::kActiveUnlockAction)))); -} - -TEST(AdvertisementDecoderImpl, DecodeBaseNpPublicAdvertisement) { - const std::string salt = "AB"; - AdvertisementDecoderImpl decoder; - - const absl::StatusOr result = decoder.DecodeAdvertisement( - absl::HexStringToBytes("002041420337C1C2C31BEE")); - - ASSERT_OK(result); - EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_PUBLIC); - EXPECT_EQ(result->version, 0); - EXPECT_THAT( - result->data_elements, - ElementsAre(DataElement(DataElement::kSaltFieldType, salt), - DataElement(DataElement::kPublicIdentityFieldType, ""), - DataElement(DataElement::kModelIdFieldType, - absl::HexStringToBytes("C1C2C3")), - DataElement(DataElement::kBatteryFieldType, - absl::HexStringToBytes("EE")))); -} - -TEST(AdvertisementDecoderImpl, DecodeBaseNpWithTxAndActionFields) { - std::string salt = "AB"; - AdvertisementDecoderImpl decoder; - - auto result = decoder.DecodeAdvertisement( - absl::HexStringToBytes("0020414203155036B04180")); - - ASSERT_OK(result); - EXPECT_THAT(result->data_elements, - UnorderedElementsAre( - DataElement(DataElement::kSaltFieldType, salt), - DataElement(DataElement::kPublicIdentityFieldType, ""), - DataElement(DataElement::kTxPowerFieldType, - absl::HexStringToBytes("50")), - DataElement(DataElement::kContextTimestampFieldType, - absl::HexStringToBytes("0B")), - DataElement(DataElement(ActionBit::kTapToTransferAction)), - DataElement(DataElement(ActionBit::kNearbyShareAction)))); -} - -TEST(AdvertisementDecoderImpl, DecodeBaseNpPrivateAdvertisement) { - std::string salt = "AB"; - ByteArray metadata_key( - {205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174}); - absl::flat_hash_map> - credentials; - credentials[IdentityType::IDENTITY_TYPE_PRIVATE_GROUP].push_back( - GetPublicCredential()); - AdvertisementDecoderImpl decoder(&credentials); - - absl::StatusOr result = decoder.DecodeAdvertisement( - absl::HexStringToBytes("00514142b8412efb0bc657ba514baf4d1b50ddc842cd1c")); - ASSERT_OK(result); - EXPECT_EQ(result->metadata_key, metadata_key.AsStringView()); - EXPECT_EQ(result->identity_type, IdentityType::IDENTITY_TYPE_PRIVATE_GROUP); - EXPECT_THAT(result->data_elements, - ElementsAre(DataElement(DataElement::kSaltFieldType, salt), - DataElement(DataElement::kTxPowerFieldType, - absl::HexStringToBytes("05")), - DataElement(DataElement::kActionFieldType, - absl::HexStringToBytes("08")))); -} - -TEST(AdvertisementDecoderImpl, InvalidEncryptedContent) { - std::string salt = "AB"; - ByteArray metadata_key( - {205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174}); - absl::flat_hash_map> - credentials; - credentials[IdentityType::IDENTITY_TYPE_PRIVATE_GROUP].push_back( - GetPublicCredential()); - AdvertisementDecoderImpl decoder(&credentials); - - EXPECT_THAT(decoder.DecodeAdvertisement(absl::HexStringToBytes( - "00414142f085d661ac8cb110e792e7faeb736294")), - StatusIs(absl::StatusCode::kOutOfRange)); -} - -TEST(AdvertisementDecoderImpl, UnsupportedDataElement) { - std::string valid_header_and_salt = absl::HexStringToBytes("00204142"); - AdvertisementDecoderImpl decoder; - - EXPECT_THAT(decoder.DecodeAdvertisement(valid_header_and_salt + - absl::HexStringToBytes("0D")), - StatusIs(absl::StatusCode::kInvalidArgument)); -} - -TEST(AdvertisementDecoderImpl, InvalidAdvertisementFieldTooShort) { - AdvertisementDecoderImpl decoder; - - // 0x59 header means 5 bytes long Account Key Data but only 4 bytes follow. - EXPECT_THAT( - decoder.DecodeAdvertisement(absl::HexStringToBytes("0059A0A1A2A3")), - StatusIs(absl::StatusCode::kOutOfRange)); -} - -TEST(AdvertisementDecoderImpl, ZeroLengthPayload) { - AdvertisementDecoderImpl decoder; - - // A action with type 0xA and no payload - const absl::StatusOr result = - decoder.DecodeAdvertisement(absl::HexStringToBytes("000A")); - - ASSERT_OK(result); - EXPECT_THAT(result->data_elements, ElementsAre(DataElement(0xA, ""))); -} - -TEST(AdvertisementDecoderImpl, EmptyAdvertisement) { - AdvertisementDecoderImpl decoder; - - EXPECT_THAT(decoder.DecodeAdvertisement(""), - StatusIs(absl::StatusCode::kOutOfRange)); -} - -TEST(AdvertisementDecoderImpl, UnsupportedAdvertisementVersion) { - AdvertisementDecoderImpl decoder; - - EXPECT_THAT(decoder.DecodeAdvertisement( - absl::HexStringToBytes("012041420318CD29EEFF")), - StatusIs(absl::StatusCode::kUnimplemented)); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/advertisement_factory.cc b/presence/implementation/advertisement_factory.cc deleted file mode 100644 index 87c74183..00000000 --- a/presence/implementation/advertisement_factory.cc +++ /dev/null @@ -1,236 +0,0 @@ -// 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/advertisement_factory.h" - -#include -#include -#include -#include -#include - -#include "absl/base/attributes.h" -#include "absl/status/status.h" -#include "absl/strings/escaping.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" -#include "absl/types/optional.h" -#include "absl/types/variant.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "internal/platform/logging.h" -#include "internal/platform/uuid.h" -#include "internal/proto/credential.pb.h" -#include "presence/data_element.h" -#include "presence/implementation/base_broadcast_request.h" -#include "presence/implementation/ldt.h" -#include "presence/implementation/mediums/advertisement_data.h" - -namespace nearby { -namespace presence { - -namespace { -using ::nearby::internal::IdentityType; -constexpr uint8_t kBaseVersion = 0; -constexpr size_t kMaxBaseNpAdvSize = 26; - -absl::StatusOr CreateDataElementHeader(size_t length, - unsigned data_type) { - if (length > DataElement::kMaxDataElementLength) { - return absl::InvalidArgumentError( - absl::StrFormat("Unsupported Data Element length: %d", length)); - } - if (data_type > DataElement::kMaxDataElementType) { - return absl::InvalidArgumentError( - absl::StrFormat("Unsupported Data Element type: %d", data_type)); - } - return (length << DataElement::kDataElementLengthShift) | data_type; -} - -absl::Status AppendDataElement(unsigned data_type, - absl::string_view data_element, - std::string& output) { - auto header = CreateDataElementHeader(data_element.size(), data_type); - if (!header.ok()) { - LOG(WARNING) << "Can't add Data element type: " << data_type - << ", length: " << data_element.size(); - return header.status(); - } - output.push_back(*header); - output.insert(output.end(), data_element.begin(), data_element.end()); - return absl::OkStatus(); -} - -uint8_t GetIdentityFieldType(IdentityType type) { - switch (type) { - case IdentityType::IDENTITY_TYPE_PRIVATE_GROUP: - return DataElement::kPrivateGroupIdentityFieldType; - case IdentityType::IDENTITY_TYPE_CONTACTS_GROUP: - return DataElement::kContactsGroupIdentityFieldType; - case IdentityType::IDENTITY_TYPE_PUBLIC: - ABSL_FALLTHROUGH_INTENDED; - default: - return DataElement::kPublicIdentityFieldType; - } -} - -std::string SerializeAction(const Action& action) { - std::string output; - uint32_t input = action.action; - for (int i = 3; i >= 0; --i) { - if (input == 0) { - return output; - } - int shift = 8 * i; - output.push_back(static_cast((input >> shift) & 0xFF)); - input &= (1 << shift) - 1; - } - return output; -} - -bool RequiresCredentials(IdentityType identity_type) { - return identity_type == IdentityType::IDENTITY_TYPE_PRIVATE_GROUP || - identity_type == IdentityType::IDENTITY_TYPE_CONTACTS_GROUP; -} -} // namespace - -absl::StatusOr AdvertisementFactory::CreateAdvertisement( - const BaseBroadcastRequest& request, - absl::optional credential) const { - AdvertisementData advert = {}; - if (absl::holds_alternative( - request.variant)) { - return CreateBaseNpAdvertisement(request, std::move(credential)); - } - return advert; -} - -absl::StatusOr -AdvertisementFactory::CreateBaseNpAdvertisement( - const BaseBroadcastRequest& request, - absl::optional credential) const { - const auto& presence = - absl::get(request.variant); - std::string payload; - payload.reserve(kMaxBaseNpAdvSize); - payload.push_back(kBaseVersion); - absl::Status result; - std::string tx_power = {static_cast(request.tx_power)}; - std::string action = SerializeAction(presence.action); - uint8_t identity_type = - GetIdentityFieldType(presence.credential_selector.identity_type); - bool needs_encryption = - identity_type != DataElement::kPublicIdentityFieldType; - if (needs_encryption) { - if (request.salt.size() != kSaltSize) { - return absl::InvalidArgumentError( - absl::StrFormat("Unsupported salt size %d", request.salt.size())); - } - if (!credential) { - return absl::FailedPreconditionError("Missing credentials"); - } - std::string unencrypted; - result = AppendDataElement(DataElement::kTxPowerFieldType, tx_power, - unencrypted); - if (!result.ok()) { - return result; - } - result = - AppendDataElement(DataElement::kActionFieldType, action, unencrypted); - if (!result.ok()) { - return result; - } - VLOG(1) << "Unencrypted advertisement payload " - << absl::BytesToHexString(unencrypted); - absl::StatusOr encrypted = - EncryptDataElements(*credential, request.salt, unencrypted); - if (!encrypted.ok()) { - return encrypted.status(); - } - if (encrypted->size() <= kBaseMetadataSize) { - return absl::OutOfRangeError( - absl::StrFormat("Encrypted identity DE is too short - %d bytes. " - "Expected more than %d", - encrypted->size(), kBaseMetadataSize)); - } - - // The Identity DE header does not include the length of salt nor metadata. - absl::StatusOr identity_header = CreateDataElementHeader( - encrypted->size() - kBaseMetadataSize, identity_type); - if (!identity_header.ok()) { - return identity_header.status(); - } - payload.push_back(*identity_header); - // In the encrypted format, salt is not a DE (thus no header) - payload.append(request.salt); - payload.append(*encrypted); - } else { - result = AppendDataElement(identity_type, "", payload); - if (!result.ok()) { - return result; - } - if (!request.salt.empty()) { - result = - AppendDataElement(DataElement::kSaltFieldType, request.salt, payload); - if (!result.ok()) { - return result; - } - } - result = - AppendDataElement(DataElement::kTxPowerFieldType, tx_power, payload); - if (!result.ok()) { - return result; - } - result = AppendDataElement(DataElement::kActionFieldType, action, payload); - if (!result.ok()) { - return result; - } - } - return AdvertisementData{.is_extended_advertisement = false, - .content = payload}; -} -absl::StatusOr AdvertisementFactory::EncryptDataElements( - const LocalCredential& credential, absl::string_view salt, - absl::string_view data_elements) const { - if (credential.metadata_encryption_key_v0().size() != kBaseMetadataSize) { - return absl::FailedPreconditionError(absl::StrFormat( - "Metadata key size %d, expected %d", - credential.metadata_encryption_key_v0().size(), kBaseMetadataSize)); - } - - // HMAC is not used during encryption, so we can pass an empty value. - absl::StatusOr encryptor = - LdtEncryptor::Create(credential.key_seed(), /*known_hmac=*/""); - if (!encryptor.ok()) { - return encryptor.status(); - } - std::string plaintext = - absl::StrCat(credential.metadata_encryption_key_v0(), data_elements); - return encryptor->Encrypt(plaintext, salt); -} - -absl::StatusOr AdvertisementFactory::GetCredentialSelector( - const BaseBroadcastRequest& request) { - if (absl::holds_alternative( - request.variant)) { - const auto& presence = - absl::get(request.variant); - if (RequiresCredentials(presence.credential_selector.identity_type)) { - return presence.credential_selector; - } - } - return absl::NotFoundError("credentials not required"); -} -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/advertisement_factory.h b/presence/implementation/advertisement_factory.h deleted file mode 100644 index 54211944..00000000 --- a/presence/implementation/advertisement_factory.h +++ /dev/null @@ -1,63 +0,0 @@ -// 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 THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_FACTORY_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_FACTORY_H_ - -#include - -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "absl/types/optional.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "presence/implementation/base_broadcast_request.h" -#include "presence/implementation/mediums/advertisement_data.h" - -namespace nearby { -namespace presence { - -// Builds BLE advertisements from broadcast requests. -class AdvertisementFactory { - public: - using LocalCredential = internal::LocalCredential; - - // Returns a `CredentialSelector` if credentials are required to create an - // advertisement from the `request`. - static absl::StatusOr GetCredentialSelector( - const BaseBroadcastRequest& request); - - // Returns a BLE advertisement for given `request. - absl::StatusOr CreateAdvertisement( - const BaseBroadcastRequest& request, - absl::optional credential) const; // NOLINT - - absl::StatusOr CreateAdvertisement( - const BaseBroadcastRequest& request) const { - return CreateAdvertisement(request, - absl::optional()); // NOLINT - } - - private: - absl::StatusOr CreateBaseNpAdvertisement( - const BaseBroadcastRequest& request, - absl::optional credential) const; // NOLINT - absl::StatusOr EncryptDataElements( - const LocalCredential& credential, absl::string_view salt, - absl::string_view data_elements) const; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_ADVERTISEMENT_FACTORY_H_ diff --git a/presence/implementation/advertisement_factory_test.cc b/presence/implementation/advertisement_factory_test.cc deleted file mode 100644 index 8c2897f8..00000000 --- a/presence/implementation/advertisement_factory_test.cc +++ /dev/null @@ -1,148 +0,0 @@ -// 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/advertisement_factory.h" - -#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/escaping.h" -#include "internal/platform/byte_array.h" -#include "internal/proto/credential.pb.h" -#include "presence/data_element.h" -#include "presence/implementation/action_factory.h" -#include "presence/implementation/mediums/advertisement_data.h" - -namespace nearby { -namespace presence { - -namespace { - -using ::nearby::ByteArray; // NOLINT -using ::nearby::internal::IdentityType; -using ::nearby::internal::LocalCredential; // NOLINT -using ::testing::NiceMock; -using ::testing::Return; -using ::testing::status::StatusIs; - -LocalCredential CreateLocalCredential(IdentityType identity_type) { - // Values copied from LDT tests - ByteArray seed({204, 219, 36, 137, 233, 252, 172, 66, 179, 147, 72, - 184, 148, 30, 209, 154, 29, 54, 14, 117, 224, 152, - 200, 193, 94, 107, 28, 194, 182, 32, 205, 57}); - ByteArray metadata_key( - {205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174}); - - LocalCredential private_credential; - private_credential.set_identity_type(identity_type); - private_credential.set_key_seed(seed.AsStringView()); - private_credential.set_metadata_encryption_key_v0( - metadata_key.AsStringView()); - return private_credential; -} - -TEST(AdvertisementFactory, CreateAdvertisementFromPrivateIdentity) { - std::string account_name = "Test account"; - std::string salt = "AB"; - constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_PRIVATE_GROUP; - std::vector data_elements; - data_elements.emplace_back(ActionBit::kActiveUnlockAction); - Action action = ActionFactory::CreateAction(data_elements); - BaseBroadcastRequest request = - BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity) - .SetAccountName(account_name) - .SetSalt(salt) - .SetTxPower(5) - .SetAction(action)); - - absl::StatusOr result = - AdvertisementFactory().CreateAdvertisement( - request, CreateLocalCredential(kIdentity)); - - ASSERT_OK(result); - EXPECT_FALSE(result->is_extended_advertisement); - EXPECT_EQ(absl::BytesToHexString(result->content), - "00514142b8412efb0bc657ba514baf4d1b50ddc842cd1c"); -} - -TEST(AdvertisementFactory, CreateAdvertisementFromTrustedIdentity) { - std::string account_name = "Test account"; - std::string salt = "AB"; - constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_CONTACTS_GROUP; - std::vector data_elements; - data_elements.emplace_back(ActionBit::kActiveUnlockAction); - data_elements.emplace_back(ActionBit::kPresenceManagerAction); - Action action = ActionFactory::CreateAction(data_elements); - BaseBroadcastRequest request = - BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity) - .SetAccountName(account_name) - .SetSalt(salt) - .SetTxPower(5) - .SetAction(action)); - - absl::StatusOr result = - AdvertisementFactory().CreateAdvertisement( - request, CreateLocalCredential(kIdentity)); - - ASSERT_OK(result); - EXPECT_FALSE(result->is_extended_advertisement); - EXPECT_EQ(absl::BytesToHexString(result->content), - "0052414257a35c020f1c547d7e169303196d75da7118ba"); -} - -TEST(AdvertisementFactory, CreateAdvertisementFromPublicIdentity) { - std::string salt = "AB"; - constexpr IdentityType kIdentity = IdentityType::IDENTITY_TYPE_PUBLIC; - std::vector data_elements; - data_elements.emplace_back(ActionBit::kActiveUnlockAction); - Action action = ActionFactory::CreateAction(data_elements); - BaseBroadcastRequest request = - BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity) - .SetSalt(salt) - .SetTxPower(5) - .SetAction(action)); - - absl::StatusOr result = - AdvertisementFactory().CreateAdvertisement(request); - - ASSERT_OK(result); - EXPECT_FALSE(result->is_extended_advertisement); - EXPECT_EQ(absl::BytesToHexString(result->content), "00032041421505260080"); -} - -TEST(AdvertisementFactory, CreateAdvertisementFailsWhenSaltIsTooShort) { - std::string salt = "AB"; - constexpr IdentityType kIdentity = internal::IDENTITY_TYPE_PRIVATE_GROUP; - std::vector data_elements; - data_elements.emplace_back(ActionBit::kActiveUnlockAction); - Action action = ActionFactory::CreateAction(data_elements); - BaseBroadcastRequest request = - BaseBroadcastRequest(BasePresenceRequestBuilder(kIdentity) - .SetSalt(salt) - .SetTxPower(5) - .SetAction(action)); - // Override the salt with invalid value - request.salt = "C"; - - EXPECT_THAT(AdvertisementFactory().CreateAdvertisement(request), - StatusIs(absl::StatusCode::kInvalidArgument)); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/advertisement_filter.cc b/presence/implementation/advertisement_filter.cc deleted file mode 100644 index e3b5bb37..00000000 --- a/presence/implementation/advertisement_filter.cc +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2024 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/advertisement_filter.h" - -#include -#include - -#include "absl/types/variant.h" -#include "internal/platform/logging.h" -#include "presence/data_element.h" -#include "presence/implementation/advertisement_decoder.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { - -bool Contains(const std::vector& data_elements, - const DataElement& data_element) { - return std::find(data_elements.begin(), data_elements.end(), data_element) != - data_elements.end(); -} - -bool ContainsAll(const std::vector& data_elements, - const std::vector& extended_properties) { - for (const auto& filter_element : extended_properties) { - if (!Contains(data_elements, filter_element)) { - return false; - } - } - return true; -} - -bool ContainsAny(const std::vector& data_elements, - const std::vector& actions) { - if (actions.empty()) { - return true; - } - for (int action : actions) { - if (Contains(data_elements, DataElement(ActionBit(action)))) { - return true; - } - } - return false; -} - -bool AdvertisementFilter::MatchesScanFilter( - const Advertisement& advertisement) { - // Verify the identity is one requested in the scan_request. - // Per the Public API of scan_request, if identity_types provided in the - // scan_request is empty then decode advertisements of every identity type - auto requested_identity_types = scan_request_.identity_types; - if (!requested_identity_types.empty() && - !(std::find( - requested_identity_types.begin(), requested_identity_types.end(), - advertisement.identity_type) != requested_identity_types.end())) { - LOG(INFO) << "Skipping advertisement with identity type: " - << advertisement.identity_type - << " because that identity type was not requested in the scan " - "request"; - return false; - } - - // The advertisement matches the scan request when it matches at least - // one of the filters in the request. - if (scan_request_.scan_filters.empty()) { - return true; - } - - // NOLINT is used to suppress google3-legacy-absl-backport lints because the - // the suggestion is not compatible with Chrome - for (const auto& filter : scan_request_.scan_filters) { - if (absl::holds_alternative(filter)) { // NOLINT - if (MatchesScanFilter(advertisement.data_elements, - absl::get(filter))) { // NOLINT - return true; - } - } else if (absl::holds_alternative( // NOLINT - filter)) { - if (MatchesScanFilter( - advertisement.data_elements, - absl::get(filter))) { // NOLINT - return true; - } - } - } - return false; -} - -bool AdvertisementFilter::MatchesScanFilter( - const std::vector& data_elements, - const PresenceScanFilter& filter) { - // The advertisement must contain all Data Elements in scan request. - return ContainsAll(data_elements, filter.extended_properties); -} - -bool AdvertisementFilter::MatchesScanFilter( - const std::vector& data_elements, - const LegacyPresenceScanFilter& filter) { - // The advertisement must: - // * contain any Action from scan request, - // * contain all Data Elements in scan request. - return ContainsAny(data_elements, filter.actions) && - ContainsAll(data_elements, filter.extended_properties); -} - -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/advertisement_filter.h b/presence/implementation/advertisement_filter.h deleted file mode 100644 index 2e2db1e4..00000000 --- a/presence/implementation/advertisement_filter.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2024 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 THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ADVERTISEMENT_FILTER_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ADVERTISEMENT_FILTER_H_ - -#include - -#include "presence/data_element.h" -#include "presence/implementation/advertisement_decoder.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { -class AdvertisementFilter { - public: - explicit AdvertisementFilter(ScanRequest scan_request) - : scan_request_(scan_request) {} - - // Returns true if the decoded advertisement in `data_elements` matches the - // filters in `scan_request`. - bool MatchesScanFilter(const Advertisement& adv); - - private: - bool MatchesScanFilter(const std::vector& data_elements, - const PresenceScanFilter& filter); - bool MatchesScanFilter(const std::vector& data_elements, - const LegacyPresenceScanFilter& filter); - ScanRequest scan_request_; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_ADVERTISEMENT_FILTER_H_ diff --git a/presence/implementation/advertisement_filter_test.cc b/presence/implementation/advertisement_filter_test.cc deleted file mode 100644 index ff3a1dfd..00000000 --- a/presence/implementation/advertisement_filter_test.cc +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2024 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/advertisement_filter.h" - -#include -#include -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/strings/escaping.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" -#include "internal/proto/credential.pb.h" -#include "presence/data_element.h" -#include "presence/implementation/advertisement_decoder.h" -#include "presence/scan_request.h" -#include "presence/scan_request_builder.h" - -namespace nearby { -namespace presence { -namespace { - -TEST(AdvertisementFilter, MatchesScanFilterNoFilterPasses) { - std::vector adv = { - DataElement(DataElement::kPrivateGroupIdentityFieldType, "payload")}; - ScanRequest empty_scan_request = {}; - AdvertisementFilter adv_filter(empty_scan_request); - - // A scan request without scan filters matches any advertisement - EXPECT_TRUE(adv_filter.MatchesScanFilter( - {.data_elements = {DataElement( - DataElement::kPrivateGroupIdentityFieldType, "payload")}})); - EXPECT_TRUE(adv_filter.MatchesScanFilter({})); -} - -TEST(AdvertisementFilter, MatchesPresenceScanFilter) { - std::vector adv = { - DataElement(DataElement::kPrivateGroupIdentityFieldType, "payload")}; - DataElement model_id = - DataElement(DataElement::kModelIdFieldType, "model id"); - DataElement salt = DataElement(DataElement::kSaltFieldType, "salt"); - DataElement salt2 = DataElement(DataElement::kSaltFieldType, "salt 2"); - PresenceScanFilter filter = {.extended_properties = {model_id, salt}}; - - AdvertisementFilter adv_filter( - ScanRequestBuilder().AddScanFilter(filter).Build()); - - EXPECT_FALSE(adv_filter.MatchesScanFilter({})); - EXPECT_FALSE(adv_filter.MatchesScanFilter({.data_elements = {salt}})); - EXPECT_TRUE( - adv_filter.MatchesScanFilter({.data_elements = {salt, model_id}})); - EXPECT_TRUE( - adv_filter.MatchesScanFilter({.data_elements = {salt, salt2, model_id}})); - EXPECT_FALSE( - adv_filter.MatchesScanFilter({.data_elements = {salt2, model_id}})); -} - -TEST(AdvertisementFilter, MatchesLegacyPresenceScanFilter) { - std::vector adv = { - DataElement(DataElement::kPrivateGroupIdentityFieldType, "payload")}; - DataElement model_id = - DataElement(DataElement::kModelIdFieldType, "model id"); - DataElement salt = DataElement(DataElement::kSaltFieldType, "salt"); - DataElement salt2 = DataElement(DataElement::kSaltFieldType, "salt 2"); - LegacyPresenceScanFilter filter = {.extended_properties = {model_id, salt}}; - - AdvertisementFilter adv_filter( - ScanRequestBuilder().AddScanFilter(filter).Build()); - - EXPECT_FALSE(adv_filter.MatchesScanFilter(Advertisement{})); - EXPECT_FALSE(adv_filter.MatchesScanFilter({.data_elements = {salt}})); - EXPECT_TRUE( - adv_filter.MatchesScanFilter({.data_elements = {salt, model_id}})); - EXPECT_TRUE( - adv_filter.MatchesScanFilter({.data_elements = {salt, salt2, model_id}})); - EXPECT_FALSE(adv_filter.MatchesScanFilter( - Advertisement{.data_elements = {salt2, model_id}})); -} - -TEST(AdvertisementFilter, - EncryptedIdentityFilterIgnoresPublicIdentityAdvertisement) { - AdvertisementFilter adv_filter( - {.identity_types = { - internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP, - internal::IdentityType::IDENTITY_TYPE_CONTACTS_GROUP}}); - - EXPECT_FALSE(adv_filter.MatchesScanFilter( - {.identity_type = internal::IdentityType::IDENTITY_TYPE_PUBLIC})); - EXPECT_TRUE(adv_filter.MatchesScanFilter( - {.identity_type = internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP})); -} - -TEST(AdvertisementFilter, PublicIdentityFilterMatchesPublicIdentityAdv) { - AdvertisementFilter adv_filter( - {.identity_types = {internal::IdentityType::IDENTITY_TYPE_PUBLIC}}); - - EXPECT_TRUE(adv_filter.MatchesScanFilter( - {.identity_type = internal::IdentityType::IDENTITY_TYPE_PUBLIC})); - EXPECT_FALSE(adv_filter.MatchesScanFilter( - {.identity_type = internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP})); -} - -TEST(AdvertisementFilter, EmptyIdentityFilterMatchesAllAdvIdentityTypes) { - AdvertisementFilter adv_filter({}); - - EXPECT_TRUE(adv_filter.MatchesScanFilter( - {.identity_type = internal::IdentityType::IDENTITY_TYPE_PUBLIC})); - EXPECT_TRUE(adv_filter.MatchesScanFilter( - {.identity_type = internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP})); -} - -TEST(AdvertisementFilter, MatchesLegacyPresenceScanFilterWithActions) { - std::vector adv = { - DataElement(DataElement::kPrivateGroupIdentityFieldType, "payload")}; - DataElement model_id = - DataElement(DataElement::kModelIdFieldType, "model id"); - DataElement salt = DataElement(DataElement::kSaltFieldType, "salt"); - DataElement ttt_action = DataElement(ActionBit::kTapToTransferAction); - LegacyPresenceScanFilter filter = { - .actions = {static_cast(ActionBit::kActiveUnlockAction), - static_cast(ActionBit::kTapToTransferAction)}, - .extended_properties = {model_id, salt}}; - - AdvertisementFilter adv_filter( - ScanRequestBuilder().AddScanFilter(filter).Build()); - - EXPECT_FALSE( - adv_filter.MatchesScanFilter({.data_elements = {salt, model_id}})); - EXPECT_TRUE(adv_filter.MatchesScanFilter( - {.data_elements = {salt, ttt_action, model_id}})); -} - -TEST(AdvertisementFilter, MatchesMultipleFilters) { - std::vector adv = { - DataElement(DataElement::kPrivateGroupIdentityFieldType, "payload")}; - DataElement model_id = - DataElement(DataElement::kModelIdFieldType, "model id"); - DataElement salt = DataElement(DataElement::kSaltFieldType, "salt"); - DataElement ttt_action = DataElement(ActionBit::kTapToTransferAction); - PresenceScanFilter presence_filter = {.extended_properties = {model_id}}; - LegacyPresenceScanFilter legacy_filter = { - .actions = {static_cast(ActionBit::kActiveUnlockAction), - static_cast(ActionBit::kTapToTransferAction)}, - .extended_properties = {salt}}; - - AdvertisementFilter adv_filter(ScanRequestBuilder() - .AddScanFilter(presence_filter) - .AddScanFilter(legacy_filter) - .Build()); - - EXPECT_TRUE(adv_filter.MatchesScanFilter({.data_elements = {model_id}})); - EXPECT_TRUE( - adv_filter.MatchesScanFilter({.data_elements = {salt, ttt_action}})); - EXPECT_FALSE(adv_filter.MatchesScanFilter({.data_elements = {ttt_action}})); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/base_broadcast_request.cc b/presence/implementation/base_broadcast_request.cc deleted file mode 100644 index 30dda102..00000000 --- a/presence/implementation/base_broadcast_request.cc +++ /dev/null @@ -1,112 +0,0 @@ -// 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/base_broadcast_request.h" - -#include -#include - -#include "absl/status/status.h" -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/crypto.h" -#include "internal/platform/logging.h" -#include "presence/broadcast_request.h" -#include "presence/implementation/action_factory.h" - -namespace nearby { -namespace presence { - -BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetSalt( - absl::string_view salt) { - if (salt.size() != kSaltSize) { - LOG(WARNING) << "Unsupported salt length: " << salt.size(); - } else { - salt_ = std::string(salt); - } - return *this; -} -BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetTxPower( - int8_t tx_power) { - tx_power_ = tx_power; - return *this; -} - -BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetAction( - const Action& action) { - action_ = action; - return *this; -} - -BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetPowerMode( - PowerMode power_mode) { - power_mode_ = power_mode; - return *this; -} - -BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetAccountName( - absl::string_view account_name) { - account_name_ = std::string(account_name); - return *this; -} - -BasePresenceRequestBuilder& BasePresenceRequestBuilder::SetManagerAppId( - absl::string_view manager_app_id) { - manager_app_id_ = std::string(manager_app_id); - return *this; -} - -BasePresenceRequestBuilder::operator BaseBroadcastRequest() const { - BaseBroadcastRequest::BasePresence presence{ - .credential_selector = {.manager_app_id = manager_app_id_, - .account_name = account_name_, - .identity_type = identity_}, - .action = action_}; - - std::string bytes(kSaltSize, 0); - RandBytes(const_cast(bytes.data()), bytes.size()); - - BaseBroadcastRequest broadcast_request{ - .variant = presence, - .salt = salt_.size() == kSaltSize ? salt_ : bytes, - .tx_power = tx_power_, - .power_mode = power_mode_}; - return broadcast_request; -} - -absl::StatusOr BaseBroadcastRequest::Create( - const BroadcastRequest& request) { - if (absl::holds_alternative(request.variant)) { - const auto& presence_request = - absl::get(request.variant); - if (presence_request.sections.empty()) { - return absl::InvalidArgumentError("Missing broadcast sections"); - } - if (presence_request.sections.size() > 1) { - LOG(WARNING) << "Only first section is used in BLE 4.2 advertisement"; - } - const PresenceBroadcast::BroadcastSection& section = - presence_request.sections.front(); - return BaseBroadcastRequest( - BasePresenceRequestBuilder(section.identity) - .SetTxPower(request.tx_power) - .SetAction(ActionFactory::CreateAction(section.extended_properties)) - .SetPowerMode(request.power_mode) - .SetManagerAppId(section.manager_app_id) - .SetAccountName(section.account_name)); - } - return absl::UnimplementedError("Request not supported"); -} - -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/base_broadcast_request.h b/presence/implementation/base_broadcast_request.h deleted file mode 100644 index 36821ccc..00000000 --- a/presence/implementation/base_broadcast_request.h +++ /dev/null @@ -1,102 +0,0 @@ -// 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 THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_BASE_BROADCAST_REQUEST_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_BASE_BROADCAST_REQUEST_H_ - -#include - -#include -#include - -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "absl/types/variant.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "presence/broadcast_request.h" -#include "presence/power_mode.h" - -namespace nearby { -namespace presence { - -constexpr int8_t kUnspecifiedTxPower = -128; -constexpr size_t kSaltSize = 2; -// The identity metadata size in the base advertisement -constexpr size_t kBaseMetadataSize = 14; - -/** Defines the action (intended actions) of base NP advertisement */ -struct Action { - uint32_t action; -}; - -/** Defines a Nearby Presence broadcast request */ -struct BaseBroadcastRequest { - // Creates `BaseBroadcastRequest` from the public API request in - // `BroadcastRequest`. - static absl::StatusOr Create( - const BroadcastRequest& request); - - struct BasePresence { - CredentialSelector credential_selector; - Action action; - }; - struct BaseFastPair { - struct Discoverable { - std::string model_id; - }; - struct Nondiscoverable { - std::string account_key_data; - std::string battery_info; - }; - absl::variant advertisement; - }; - struct BaseEddystone { - std::string ephemeral_id; - }; - absl::variant variant; - std::string salt; - int8_t tx_power; - unsigned int interval_ms; - PowerMode power_mode; -}; - -/** Builds a brodacast request variant with NP identity for BLE 4.2 */ -class BasePresenceRequestBuilder { - public: - explicit BasePresenceRequestBuilder( - const nearby::internal::IdentityType& identity) - : identity_(identity) {} - BasePresenceRequestBuilder& SetSalt(absl::string_view salt); - BasePresenceRequestBuilder& SetTxPower(int8_t tx_power); - BasePresenceRequestBuilder& SetAction(const Action& action); - BasePresenceRequestBuilder& SetPowerMode(PowerMode power_mode); - BasePresenceRequestBuilder& SetAccountName(absl::string_view account_name); - BasePresenceRequestBuilder& SetManagerAppId(absl::string_view manager_app_id); - - explicit operator BaseBroadcastRequest() const; - - private: - nearby::internal::IdentityType identity_; - std::string salt_; - int8_t tx_power_ = kUnspecifiedTxPower; - Action action_; - PowerMode power_mode_ = PowerMode::kNoPower; - std::string account_name_; - std::string manager_app_id_; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_BASE_BROADCAST_REQUEST_H_ diff --git a/presence/implementation/base_broadcast_request_test.cc b/presence/implementation/base_broadcast_request_test.cc deleted file mode 100644 index b27040b3..00000000 --- a/presence/implementation/base_broadcast_request_test.cc +++ /dev/null @@ -1,94 +0,0 @@ -// 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/base_broadcast_request.h" - -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/types/variant.h" -#include "internal/proto/credential.pb.h" -#include "presence/broadcast_request.h" -#include "presence/data_element.h" - -namespace nearby { -namespace presence { -namespace { - -using ::nearby::internal::IdentityType; -using ::testing::status::StatusIs; - -TEST(BroadcastRequestTest, CreateBasePresenceRequest) { - nearby::internal::IdentityType identity; - constexpr int8_t kTxPower = -13; - - BaseBroadcastRequest request = BaseBroadcastRequest( - BasePresenceRequestBuilder(identity).SetTxPower(kTxPower).SetPowerMode( - PowerMode::kBalanced)); - - EXPECT_TRUE(absl::holds_alternative( - request.variant)); - EXPECT_EQ(request.salt.size(), 2); - EXPECT_EQ(request.tx_power, kTxPower); - EXPECT_EQ(request.power_mode, PowerMode::kBalanced); -} - -TEST(BroadcastRequestTest, CreateFromPresenceRequest) { - constexpr int8_t kTxPower = 30; - constexpr uint32_t kExpectedAction = - (1 << 23); // encoded kActiveUnlockAction - std::string account_name = "Test account"; - std::string manager_app_id = "Manager app id"; - PresenceBroadcast::BroadcastSection section = { - .identity = internal::IDENTITY_TYPE_PUBLIC, - .extended_properties = {DataElement( - DataElement(ActionBit::kActiveUnlockAction))}, - .account_name = account_name, - .manager_app_id = manager_app_id}; - PresenceBroadcast presence_request = {.sections = {section}}; - BroadcastRequest input = {.tx_power = kTxPower, .variant = presence_request}; - - absl::StatusOr request = - BaseBroadcastRequest::Create(input); - - ASSERT_OK(request); - EXPECT_THAT(request->tx_power, kTxPower); - EXPECT_THAT(absl::get(request->variant) - .credential_selector.identity_type, - IdentityType::IDENTITY_TYPE_PUBLIC); - EXPECT_THAT(absl::get(request->variant) - .action.action, - kExpectedAction); - EXPECT_THAT(absl::get(request->variant) - .credential_selector.account_name, - account_name); - EXPECT_THAT(absl::get(request->variant) - .credential_selector.manager_app_id, - manager_app_id); -} - -TEST(BroadcastRequestTest, CreateFromEmptyPresenceRequestFails) { - BroadcastRequest empty = { - .variant = PresenceBroadcast(), - }; - - EXPECT_THAT(BaseBroadcastRequest::Create(empty), - StatusIs(absl::StatusCode::kInvalidArgument)); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/broadcast_manager.cc b/presence/implementation/broadcast_manager.cc deleted file mode 100644 index 088083b3..00000000 --- a/presence/implementation/broadcast_manager.cc +++ /dev/null @@ -1,269 +0,0 @@ -// 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/broadcast_manager.h" - -#include -#include -#include -#include -#include -#include - -#include "absl/base/thread_annotations.h" -#include "absl/status/status.h" -#include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" -#include "absl/types/optional.h" -#include "internal/platform/implementation/ble.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "internal/platform/implementation/crypto.h" -#include "internal/platform/logging.h" -#include "presence/broadcast_request.h" -#include "presence/data_types.h" -#include "presence/implementation/advertisement_factory.h" -#include "presence/implementation/base_broadcast_request.h" -#include "presence/implementation/mediums/advertisement_data.h" - -namespace nearby { -namespace presence { -namespace { - -using AdvertisingCallback = ::nearby::api::ble::BleMedium::AdvertisingCallback; -using AdvertisingSession = ::nearby::api::ble::BleMedium::AdvertisingSession; -using LocalCredential = internal::LocalCredential; - -uint16_t SaltToInt(absl::string_view salt) { - if (salt.length() < 2) return 0; - uint16_t b0 = salt[0]; - uint16_t b1 = salt[1]; - return b0 << 8 | b1; -} -std::string SaltFromInt(uint16_t x) { - std::string salt; - salt.resize(2); - salt[0] = x >> 8 & 0xFF; - salt[1] = x & 0xFF; - return salt; -} - -// Selects a salt that has not been used yet. The salt is added to -// `credential.consumed_salts`. -// We may fail to find an unused salt. In this unlikely event, an already -// consumed salt is returned. -std::string SelectSalt(LocalCredential& credential, - absl::string_view preferred_salt) { - // NP certificate guidelines say that we should try to get an unused salt 128 - // times. - constexpr int kMaxSaltSelectRetries = 128; - - uint16_t s = SaltToInt(preferred_salt); - for (int i = 0; i < kMaxSaltSelectRetries; i++) { - if (!credential.consumed_salts().contains(s)) { - break; - } - s = nearby::RandData(); - } - credential.mutable_consumed_salts()->insert({s, true}); - return SaltFromInt(s); -} - -} // namespace - -absl::StatusOr BroadcastManager::StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback) { - absl::StatusOr request = - BaseBroadcastRequest::Create(broadcast_request); - if (!request.ok()) { - LOG(WARNING) << "Invalid broadcast request, reason: " << request.status(); - callback.start_broadcast_cb(request.status()); - return request.status(); - } - BroadcastSessionId id = GenerateBroadcastSessionId(); - RunOnServiceControllerThread( - "start-broadcast", - [this, id, power_mode = broadcast_request.power_mode, request = *request, - broadcast_callback = std::move( - callback)]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) mutable { - sessions_.insert({id, BroadcastSessionState( - std::move(broadcast_callback), power_mode)}); - FetchCredentials(id, std::move(request)); - }); - return id; -} - -void BroadcastManager::FetchCredentials( - BroadcastSessionId id, BaseBroadcastRequest broadcast_request) { - absl::StatusOr credential_selector = - AdvertisementFactory::GetCredentialSelector(broadcast_request); - if (!credential_selector.ok()) { - // Public advertisement, we don't need credential to advertise. - Advertise(id, broadcast_request, /*credentials=*/{}); - return; - } - credential_manager_->GetLocalCredentials( - *credential_selector, - GetLocalCredentialsResultCallback{ - .credentials_fetched_cb = - [this, id, broadcast_request = std::move(broadcast_request), - selector = *credential_selector]( - absl::StatusOr< - std::vector<::nearby::internal::LocalCredential>> - credentials) { - if (!credentials.ok()) { - LOG(WARNING) << "Failed to fetch credentials, status: " - << credentials.status(); - NotifyStartCallbackStatus(id, credentials.status()); - return; - } - RunOnServiceControllerThread( - "advertise-non-public", - [this, id, broadcast_request = std::move(broadcast_request), - credentials = std::move(*credentials), - selector = std::move(selector)]() - ABSL_EXCLUSIVE_LOCKS_REQUIRED(executor_) mutable { - absl::optional credential = - Advertise(id, broadcast_request, credentials); - if (credential) { - credential_manager_->UpdateLocalCredential( - selector, std::move(*credential), - {[](absl::Status status) { - if (!status.ok()) { - LOG(WARNING) << "Failed to update private " - "credential, status: " - << status; - } - }}); - } - }); - }}); -} - -absl::optional BroadcastManager::SelectCredential( // NOLINT - BaseBroadcastRequest& broadcast_request, - std::vector credentials) { - if (credentials.empty()) { - return absl::optional(); // NOLINT - } - auto credential = - std::min_element(credentials.begin(), credentials.end(), - [](const LocalCredential& a, const LocalCredential& b) { - return a.start_time_millis() < b.start_time_millis(); - }); - if (credential == credentials.end()) { - LOG(WARNING) << "No active credentials"; - return absl::optional(); // NOLINT - } - std::string salt = SelectSalt(*credential, broadcast_request.salt); - if (salt != broadcast_request.salt) { - VLOG(1) << "Changed salt"; - broadcast_request.salt = salt; - } - return *credential; -} - -absl::optional BroadcastManager::Advertise( // NOLINT - BroadcastSessionId id, BaseBroadcastRequest broadcast_request, - std::vector credentials) { - auto it = sessions_.find(id); - if (it == sessions_.end()) { - LOG(INFO) << "Broadcast session terminated, id: " << id; - return absl::optional(); // NOLINT - } - absl::optional credential = // NOLINT - SelectCredential(broadcast_request, std::move(credentials)); - absl::StatusOr advertisement = - AdvertisementFactory().CreateAdvertisement(broadcast_request, credential); - if (!advertisement.ok()) { - LOG(WARNING) << "Can't create advertisement, reason: " - << advertisement.status(); - NotifyStartCallbackStatus(id, advertisement.status()); - return absl::optional(); // NOLINT - } - std::unique_ptr session = - mediums_->GetBle().StartAdvertising( - *advertisement, it->second.GetPowerMode(), - AdvertisingCallback{ - .start_advertising_result = [this, id](absl::Status status) { - NotifyStartCallbackStatus(id, status); - }}); - if (!session) { - NotifyStartCallbackStatus(id, - absl::InternalError("Can't start advertising")); - return absl::optional(); // NOLINT - } - it->second.SetAdvertisingSession(std::move(session)); - return credential; -} - -void BroadcastManager::NotifyStartCallbackStatus(BroadcastSessionId id, - absl::Status status) { - RunOnServiceControllerThread("started-broadcast-cb", - [this, id, status]() - ABSL_EXCLUSIVE_LOCKS_REQUIRED(executor_) { - auto it = sessions_.find(id); - if (it == sessions_.end()) { - return; - } - it->second.CallStartedCallback(status); - if (!status.ok()) { - // Delete failed session. - sessions_.erase(it); - } - }); -} - -void BroadcastManager::StopBroadcast(BroadcastSessionId id) { - RunOnServiceControllerThread( - "stop-broadcast", [this, id]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(executor_) { - auto it = sessions_.find(id); - if (it == sessions_.end()) { - VLOG(1) << absl::StrFormat("BroadcastSession(0x%x) not found", id); - return; - } - it->second.StopAdvertising(); - sessions_.erase(it); - }); -} - -BroadcastSessionId BroadcastManager::GenerateBroadcastSessionId() { - return nearby::RandData(); -} - -void BroadcastManager::BroadcastSessionState::SetAdvertisingSession( - std::unique_ptr session) { - advertising_session_ = std::move(session); -} - -void BroadcastManager::BroadcastSessionState::CallStartedCallback( - absl::Status status) { - BroadcastCallback callback = std::move(broadcast_callback_); - if (callback.start_broadcast_cb) { - callback.start_broadcast_cb(status); - } -} - -void BroadcastManager::BroadcastSessionState::StopAdvertising() { - std::unique_ptr advertising_session = - std::move(advertising_session_); - if (advertising_session) { - absl::Status status = advertising_session->stop_advertising(); - if (!status.ok()) { - LOG(WARNING) << "StopAdvertising error: " << status; - } - } -} - -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/broadcast_manager.h b/presence/implementation/broadcast_manager.h deleted file mode 100644 index aedf2fec..00000000 --- a/presence/implementation/broadcast_manager.h +++ /dev/null @@ -1,109 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_BROADCAST_MANAGER_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_BROADCAST_MANAGER_H_ - -#include -#include -#include -#include - -#include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "absl/types/optional.h" -#include "internal/platform/implementation/ble.h" -#include "internal/platform/runnable.h" -#include "internal/platform/single_thread_executor.h" -#include "presence/broadcast_request.h" -#include "presence/data_types.h" -#include "presence/implementation/base_broadcast_request.h" -#include "presence/implementation/credential_manager.h" -#include "presence/implementation/mediums/mediums.h" -#include "presence/power_mode.h" - -namespace nearby { -namespace presence { - -// The instance of BroadcastManager is owned by {@code ServiceControllerImpl}. -// Helping service controller to manage broadcast requests and callbacks. - -class BroadcastManager { - public: - using SingleThreadExecutor = ::nearby::SingleThreadExecutor; - using AdvertisingSession = ::nearby::api::ble::BleMedium::AdvertisingSession; - using Runnable = ::nearby::Runnable; - using LocalCredential = internal::LocalCredential; - BroadcastManager(Mediums& mediums, CredentialManager& credential_manager, - SingleThreadExecutor& executor) { - mediums_ = &mediums, credential_manager_ = &credential_manager, - executor_ = &executor; - } - ~BroadcastManager() = default; - absl::StatusOr StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback); - void StopBroadcast(BroadcastSessionId); - - private: - Mediums* mediums_; - CredentialManager* credential_manager_; - SingleThreadExecutor* executor_; - class BroadcastSessionState { - public: - explicit BroadcastSessionState(BroadcastCallback broadcast_callback, - PowerMode power_mode) - : broadcast_callback_(std::move(broadcast_callback)), - power_mode_(power_mode) {} - - void SetAdvertisingSession(std::unique_ptr session); - void CallStartedCallback(absl::Status status); - void StopAdvertising(); - - PowerMode GetPowerMode() { return power_mode_; } - - private: - BroadcastCallback broadcast_callback_; - PowerMode power_mode_; - std::unique_ptr advertising_session_; - }; - BroadcastSessionId GenerateBroadcastSessionId(); - void NotifyStartCallbackStatus(BroadcastSessionId id, absl::Status status); - void RunOnServiceControllerThread(absl::string_view name, Runnable runnable) { - executor_->Execute(std::string(name), std::move(runnable)); - } - void FetchCredentials(BroadcastSessionId id, - BaseBroadcastRequest broadcast_request) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); - absl::optional SelectCredential( // NOLINT - BaseBroadcastRequest& broadcast_request, - std::vector credentials); - - // Returns the private credential, if any, selected to generate the - // advertisement. A salt used in the advertisement is added to the returned - // private credential. The caller must save it in the storage. - absl::optional Advertise( // NOLINT - BroadcastSessionId id, BaseBroadcastRequest broadcast_request, - std::vector credentials) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); - absl::flat_hash_map sessions_ - ABSL_GUARDED_BY(*executor_); -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_BROADCAST_MANAGER_H_ diff --git a/presence/implementation/broadcast_manager_test.cc b/presence/implementation/broadcast_manager_test.cc deleted file mode 100644 index 32effaf9..00000000 --- a/presence/implementation/broadcast_manager_test.cc +++ /dev/null @@ -1,177 +0,0 @@ -// 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/broadcast_manager.h" - -#include -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/feature_flags.h" -#include "internal/platform/future.h" -#include "internal/platform/medium_environment.h" -#include "internal/proto/credential.pb.h" -#include "presence/implementation/credential_manager_impl.h" -#include "presence/implementation/mediums/mediums.h" - -namespace nearby { -namespace presence { -namespace { - -using FeatureFlags = ::nearby::FeatureFlags::Flags; -using internal::IdentityType; -using ::nearby::CountDownLatch; -using ::nearby::MediumEnvironment; -using ::testing::status::StatusIs; - -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 BroadcastManagerTest : public testing::TestWithParam { - protected: - void TearDown() override { - MediumEnvironment::Instance().Sync(); - // Finish pending tasks before destroying BroadcastManager - executor_.Shutdown(); - } - bool IsAdvertising() { - WaitForServiceControllerTasks(); - MediumEnvironment::Instance().Sync(); - return MediumEnvironment::Instance() - .GetBleMediumStatus(*mediums_.GetBle().GetImpl()) - ->is_advertising; - } - BroadcastCallback CreateBroadcastCallback() { - return BroadcastCallback{.start_broadcast_cb = [this](absl::Status status) { - start_broadcast_status_.Set(status); - }}; - } - - void WaitForServiceControllerTasks() { - CountDownLatch latch(1); - executor_.Execute([&]() { latch.CountDown(); }); - latch.Await(); - } - - // The medium environment must be initialized (started) before the service - // controller. - MediumEnvironmentStarter env_; - nearby::Future start_broadcast_status_; - BroadcastCallback broadcast_callback_{ - .start_broadcast_cb = [this](absl::Status status) { - start_broadcast_status_.Set(status); - }}; - Mediums mediums_; - SingleThreadExecutor executor_; - CredentialManagerImpl credential_manager_{&executor_}; - BroadcastManager broadcast_manager_{mediums_, credential_manager_, executor_}; -}; - -INSTANTIATE_TEST_SUITE_P(ParametrisedBroadcastManagerTest, BroadcastManagerTest, - testing::ValuesIn(kTestCases)); - -TEST_P(BroadcastManagerTest, StartBroadcastPublicIdentity) { - absl::StatusOr session = - broadcast_manager_.StartBroadcast( - CreateBroadcastRequest(internal::IDENTITY_TYPE_PUBLIC), - CreateBroadcastCallback()); - - EXPECT_OK(session); - EXPECT_TRUE(start_broadcast_status_.Get().ok()); - EXPECT_OK(start_broadcast_status_.Get().GetResult()); - EXPECT_TRUE(IsAdvertising()); -} - -TEST_P(BroadcastManagerTest, StartAndStopBroadcast) { - absl::StatusOr session = - broadcast_manager_.StartBroadcast( - CreateBroadcastRequest(internal::IDENTITY_TYPE_PUBLIC), - CreateBroadcastCallback()); - ASSERT_OK(session); - EXPECT_TRUE(IsAdvertising()); - - broadcast_manager_.StopBroadcast(*session); - EXPECT_FALSE(IsAdvertising()); -} - -TEST_P(BroadcastManagerTest, StopBroadcastTwiceNoSideEffects) { - absl::StatusOr session = - broadcast_manager_.StartBroadcast( - CreateBroadcastRequest(internal::IDENTITY_TYPE_PUBLIC), - CreateBroadcastCallback()); - ASSERT_OK(session); - EXPECT_TRUE(IsAdvertising()); - - broadcast_manager_.StopBroadcast(*session); - broadcast_manager_.StopBroadcast(*session); -} - -TEST_P(BroadcastManagerTest, StopBroadcastInvalidSessionNoSideEffects) { - broadcast_manager_.StopBroadcast(123456); -} - -TEST_P(BroadcastManagerTest, StartBroadcastInvalidRequestFails) { - absl::StatusOr session = - broadcast_manager_.StartBroadcast(BroadcastRequest{}, - CreateBroadcastCallback()); - - EXPECT_THAT(session, StatusIs(absl::StatusCode::kInvalidArgument)); - EXPECT_TRUE(start_broadcast_status_.Get().ok()); - EXPECT_THAT(start_broadcast_status_.Get().GetResult(), - StatusIs(absl::StatusCode::kInvalidArgument)); - EXPECT_FALSE(IsAdvertising()); -} - -TEST_P(BroadcastManagerTest, StartBroadcastPrivateIdentityFails) { - // TODO(b/256249404): Support private identity. - absl::StatusOr session = - broadcast_manager_.StartBroadcast( - CreateBroadcastRequest(internal::IDENTITY_TYPE_PRIVATE_GROUP), - CreateBroadcastCallback()); - - ASSERT_OK(session); - EXPECT_TRUE(start_broadcast_status_.Get().ok()); - EXPECT_THAT(start_broadcast_status_.Get().GetResult(), - StatusIs(absl::StatusCode::kNotFound)); - EXPECT_FALSE(IsAdvertising()); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/connection_authenticator.h b/presence/implementation/connection_authenticator.h deleted file mode 100644 index 7a9b2cb8..00000000 --- a/presence/implementation/connection_authenticator.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2023 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 THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CONNECTION_AUTHENTICATOR_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CONNECTION_AUTHENTICATOR_H_ - -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "internal/proto/credential.pb.h" -#include "internal/proto/local_credential.pb.h" - -namespace nearby { -namespace presence { - -class ConnectionAuthenticator { - public: - struct OneWayInitiatorData { - std::string shared_credential_hash; - }; - - struct TwoWayInitiatorData { - std::string shared_credential_hash; - std::string private_key_signature; - }; - - struct ResponderData { - std::string private_key_signature; - }; - - using InitiatorData = absl::variant; - - virtual ~ConnectionAuthenticator() = default; - - // Builds a signed message to be returned to Nearby Connections for - // authentication on the other side of the connection. - // ukey2_secret - The shared secret derived from the UKEY2 handshake in NC. - // local_credential - The local credential used to sign the derived - // information. If this is std::nullopt, then we will be - // performing one-way authentication. - // shared_credential - The shared credential used to decrypt the advertisement - // from the remote device. - virtual absl::StatusOr BuildSignedMessageAsInitiator( - absl::string_view ukey2_secret, - std::optional local_credential, - const internal::SharedCredential& shared_credential) const = 0; - - // Builds a signed message to be returned to Nearby Connections for - // authentication on the other side of the connection. - // ukey2_secret - The shared secret derived from the UKEY2 handshake in NC. - // local_credential - The local credential used to sign the derived - // information so the initiator can verify against our - // shared credential. - virtual absl::StatusOr BuildSignedMessageAsResponder( - absl::string_view ukey2_secret, - const internal::LocalCredential& local_credential) const = 0; - - // Verifies a signed message received from the responder (broadcaster) of the - // Nearby Presence advertisement. - // authentication_data - the data required to verify the connection, received - // from the responder. - // ukey2_secret - the shared secret derived from the ukey2 handshake in NC. - // shared_credentials - the set of shared credentials that can be used to - // verify the responder data. - virtual absl::Status VerifyMessageAsInitiator( - ResponderData authentication_data, absl::string_view ukey2_secret, - const std::vector& shared_credentials) - const = 0; - - // Verifies a signed message received from the Nearby Connections peer. - // Returns the matched local credential if the verification was successful. - // ukey2_secret - The shared secret derived from the UKEY2 handshake in NC. - // received_frame - The received frame from Nearby Connections. - // local_credentials - The set of local credentials that may contain the - // required keyseed hash. - // shared_credentials - The set of shared credentials that can be used to - // verify the signed contents of the frame. - virtual absl::StatusOr VerifyMessageAsResponder( - absl::string_view ukey2_secret, InitiatorData initiator_data, - const std::vector& local_credentials, - const std::vector& shared_credentials) - const = 0; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CONNECTION_AUTHENTICATOR_H_ diff --git a/presence/implementation/connection_authenticator_impl.cc b/presence/implementation/connection_authenticator_impl.cc deleted file mode 100644 index cbe68aa2..00000000 --- a/presence/implementation/connection_authenticator_impl.cc +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2023 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/connection_authenticator_impl.h" - -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "absl/types/variant.h" -#include "internal/crypto/ed25519.h" -#include "internal/crypto_cros/hkdf.h" -#include "internal/crypto_cros/secure_util.h" -#include "internal/proto/credential.pb.h" -#include "internal/proto/local_credential.pb.h" - -namespace nearby { -namespace presence { - -namespace { -constexpr int kPresenceAuthenticatorVersion = 1; -constexpr int kPresenceAuthenticatorHkdfKeySize = 32; -constexpr char kBroadcasterMessageHeader[] = - "Nearby Presence Broadcaster Signature"; -constexpr char kDiscovererMessageHeader[] = - "Nearby Presence Discoverer Signature"; -constexpr char kHkdfSalt[] = "Google Nearby"; -constexpr char kBroadcasterHkdfInfo[] = - "Nearby Presence Broadcaster Credential Hash"; -constexpr char kDiscovererHkdfInfo[] = - "Nearby Presence Discoverer Credential Hash"; -} // namespace - -absl::StatusOr -ConnectionAuthenticatorImpl::BuildSignedMessageAsInitiator( - absl::string_view ukey2_secret, - std::optional local_credential, - const internal::SharedCredential& shared_credential) const { - auto shared_credential_hash = crypto::HkdfSha256( - absl::StrCat(ukey2_secret, shared_credential.key_seed()), kHkdfSalt, - kDiscovererHkdfInfo, kPresenceAuthenticatorHkdfKeySize); - if (local_credential.has_value()) { - // two-way authentication, private identity. - auto signer = crypto::Ed25519Signer::Create( - (*local_credential).connection_signing_key().key()); - if (!signer.ok()) { - return signer.status(); - } - auto pkey_signature = - signer->Sign(absl::StrCat(kDiscovererMessageHeader, ukey2_secret)); - if (!pkey_signature.has_value()) { - return absl::InternalError("Signing using private key failed."); - } - return ConnectionAuthenticator::TwoWayInitiatorData{ - .shared_credential_hash = shared_credential_hash, - .private_key_signature = *pkey_signature, - }; - } - // one-way authentication, trusted identity. - return ConnectionAuthenticator::OneWayInitiatorData{ - .shared_credential_hash = shared_credential_hash, - }; -} - -absl::StatusOr -ConnectionAuthenticatorImpl::BuildSignedMessageAsResponder( - absl::string_view ukey2_secret, - const internal::LocalCredential& local_credential) const { - auto signer = crypto::Ed25519Signer::Create( - local_credential.connection_signing_key().key()); - if (!signer.ok()) { - return signer.status(); - } - auto pkey_signature = - signer->Sign(absl::StrCat(kBroadcasterMessageHeader, ukey2_secret)); - if (!pkey_signature.has_value()) { - return absl::InternalError("Signing using private key failed."); - } - return ConnectionAuthenticator::ResponderData{.private_key_signature = - *pkey_signature}; -} - -absl::Status ConnectionAuthenticatorImpl::VerifyMessageAsInitiator( - ResponderData authentication_data, absl::string_view ukey2_secret, - const std::vector& shared_credentials) const { - if (authentication_data.private_key_signature.empty()) { - return absl::InvalidArgumentError("Empty private key signature."); - } - for (const auto& shared_credential : shared_credentials) { - auto verifier = crypto::Ed25519Verifier::Create( - shared_credential.connection_signature_verification_key()); - if (!verifier.ok()) { - continue; - } - // Verify ED25519 signature, returning true if verification succeeded. - if (verifier - ->Verify(absl::StrCat(kBroadcasterMessageHeader, ukey2_secret), - authentication_data.private_key_signature) - .ok()) { - return absl::OkStatus(); - } - } - return absl::InternalError("Unable to verify responder's private key sig."); -} - -absl::StatusOr -ConnectionAuthenticatorImpl::VerifyMessageAsResponder( - absl::string_view ukey2_secret, InitiatorData initiator_data, - const std::vector& local_credentials, - const std::vector& shared_credentials) const { - std::string shared_credential_hash; - std::optional matched_local_credential; - if (absl::holds_alternative(initiator_data)) { - // one-way. we only need to verify if the hash matches one of our - // local credentials. - auto auth_data = absl::get(initiator_data); - if (auth_data.shared_credential_hash.size() != - kPresenceAuthenticatorHkdfKeySize) { - return absl::InvalidArgumentError("Invalid shared credential hash size."); - } - for (const auto& local_credential : local_credentials) { - // Verify Credential ID hash. - auto cid_hash = crypto::HkdfSha256( - absl::StrCat(ukey2_secret, local_credential.key_seed()), kHkdfSalt, - kDiscovererHkdfInfo, kPresenceAuthenticatorHkdfKeySize); - if (crypto::SecureMemEqual(cid_hash.c_str(), - auth_data.shared_credential_hash.c_str(), - kPresenceAuthenticatorHkdfKeySize)) { - matched_local_credential = local_credential; - } - } - } else { - // two-way. we need to verify if the hash matches one of our local - // credentials _and_ make sure it matches one of our shared credentials. - // We want to check each shared credential to verify using its public key. - - // Match the local credential. - auto auth_data = absl::get(initiator_data); - if (auth_data.shared_credential_hash.size() != - kPresenceAuthenticatorHkdfKeySize) { - return absl::InvalidArgumentError("Invalid shared credential hash size."); - } - if (auth_data.private_key_signature.empty()) { - return absl::InvalidArgumentError("Empty private key signature."); - } - for (const auto& local_credential : local_credentials) { - // Verify Credential ID hash. - auto cid_hash = crypto::HkdfSha256( - absl::StrCat(ukey2_secret, local_credential.key_seed()), kHkdfSalt, - kDiscovererHkdfInfo, kPresenceAuthenticatorHkdfKeySize); - if (crypto::SecureMemEqual(cid_hash.c_str(), - auth_data.shared_credential_hash.c_str(), - kPresenceAuthenticatorHkdfKeySize)) { - matched_local_credential = local_credential; - } - } - // Now, match our shared credential. - std::optional matched_shared_credential; - for (const auto& shared_credential : shared_credentials) { - auto verifier = crypto::Ed25519Verifier::Create( - shared_credential.connection_signature_verification_key()); - if (!verifier.ok() || - verifier - ->Verify(absl::StrCat(kDiscovererMessageHeader, ukey2_secret), - auth_data.private_key_signature) - .ok()) { - matched_shared_credential = shared_credential; - break; - } - } - if (!matched_shared_credential.has_value()) { - return absl::InternalError("Unable to verify shared credential."); - } - } - if (matched_local_credential.has_value()) { - return *matched_local_credential; - } - return absl::InternalError("Unable to verify local credential."); -} - -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/connection_authenticator_impl.h b/presence/implementation/connection_authenticator_impl.h deleted file mode 100644 index 25f42ddc..00000000 --- a/presence/implementation/connection_authenticator_impl.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2023 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 THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CONNECTION_AUTHENTICATOR_IMPL_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CONNECTION_AUTHENTICATOR_IMPL_H_ - -#include -#include - -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "internal/proto/credential.pb.h" -#include "internal/proto/local_credential.pb.h" -#include "presence/implementation/connection_authenticator.h" - -namespace nearby { -namespace presence { - -class ConnectionAuthenticatorImpl : public ConnectionAuthenticator { - public: - // Builds a signed message to be returned to Nearby Connections for - // authentication on the other side of the connection. - // ukey2_secret - The shared secret derived from the UKEY2 handshake in NC. - // local_credential - The local credential used to sign the derived - // information. If this is std::nullopt, then we will be - // performing one-way authentication. - // shared_credential - The shared credential used to decrypt the advertisement - // from the remote device. - absl::StatusOr BuildSignedMessageAsInitiator( - absl::string_view ukey2_secret, - std::optional local_credential, - const internal::SharedCredential& shared_credential) const override; - - // Builds a signed message to be returned to Nearby Connections for - // authentication on the other side of the connection. - // ukey2_secret - The shared secret derived from the UKEY2 handshake in NC. - // local_credential - The local credential used to sign the derived - // information so the initiator can verify against our - // shared credential. - absl::StatusOr BuildSignedMessageAsResponder( - absl::string_view ukey2_secret, - const internal::LocalCredential& local_credential) const override; - - // Verifies a signed message received from the responder (broadcaster) of the - // Nearby Presence advertisement. - // authentication_data - the data required to verify the connection, received - // from the responder. - // ukey2_secret - the shared secret derived from the ukey2 handshake in NC. - // shared_credentials - the set of shared credentials that can be used to - // verify the responder data. - absl::Status VerifyMessageAsInitiator( - ResponderData authentication_data, absl::string_view ukey2_secret, - const std::vector& shared_credentials) - const override; - - // Verifies a signed message received from the Nearby Connections peer. - // ukey2_secret - The shared secret derived from the UKEY2 handshake in NC. - // received_frame - The received frame from Nearby Connections. - // local_credentials - The set of local credentials that may contain the - // required keyseed hash. - // shared_credentials - The set of shared credentials that can be used to - // verify the signed contents of the frame. - absl::StatusOr VerifyMessageAsResponder( - absl::string_view ukey2_secret, InitiatorData initiator_data, - const std::vector& local_credentials, - const std::vector& shared_credentials) - const override; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CONNECTION_AUTHENTICATOR_IMPL_H_ diff --git a/presence/implementation/connection_authenticator_impl_test.cc b/presence/implementation/connection_authenticator_impl_test.cc deleted file mode 100644 index e28eb477..00000000 --- a/presence/implementation/connection_authenticator_impl_test.cc +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2023 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/connection_authenticator_impl.h" - -#include -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "internal/crypto/ed25519.h" -#include "internal/proto/credential.pb.h" -#include "internal/proto/local_credential.pb.h" - -namespace nearby { -namespace presence { -namespace { - -using ::protobuf_matchers::EqualsProto; -using ::testing::status::StatusIs; - -constexpr char kUkey2Secret[] = {0x34, 0x56, 0x78, 0x90}; -constexpr char kKeySeed1[] = {1, 2, 3, 4, 5, 6, 7, 8}; -constexpr char kKeySeed2[] = {8, 7, 6, 5, 4, 3, 2, 1}; - -internal::LocalCredential BuildLocalCredential( - const crypto::Ed25519KeyPair& key_pair, absl::string_view key_seed) { - internal::LocalCredential local_credential; - local_credential.mutable_connection_signing_key()->set_key( - absl::StrCat(key_pair.private_key, key_pair.public_key)); - local_credential.set_key_seed(key_seed); - return local_credential; -} - -internal::SharedCredential BuildSharedCredential( - const crypto::Ed25519KeyPair& key_pair, absl::string_view key_seed) { - internal::SharedCredential shared_credential; - shared_credential.set_connection_signature_verification_key( - key_pair.public_key); - shared_credential.set_key_seed(key_seed); - return shared_credential; -} - -class PresenceAuthenticatorTest : public ::testing::Test { - protected: - void SetUp() override { - auto key_pair_or_status = crypto::Ed25519Signer::CreateNewKeyPair(); - ASSERT_OK_AND_ASSIGN(auto key_pair1, key_pair_or_status); - auto key_pair2_or_status = crypto::Ed25519Signer::CreateNewKeyPair(); - ASSERT_OK_AND_ASSIGN(auto key_pair2, key_pair2_or_status); - initiator_local_credential_ = BuildLocalCredential(key_pair1, kKeySeed1); - initiator_shared_credential_ = BuildSharedCredential(key_pair1, kKeySeed1); - initiator_shared_credential_wrong_key_ = - BuildSharedCredential(key_pair2, kKeySeed1); - responder_local_credential_ = BuildLocalCredential(key_pair2, kKeySeed2); - responder_shared_credential_ = BuildSharedCredential(key_pair2, kKeySeed2); - responder_shared_credential_wrong_key_ = - BuildSharedCredential(key_pair1, kKeySeed2); - } - - internal::LocalCredential initiator_local_credential_; - internal::LocalCredential responder_local_credential_; - internal::SharedCredential initiator_shared_credential_; - internal::SharedCredential initiator_shared_credential_wrong_key_; - internal::SharedCredential responder_shared_credential_; - internal::SharedCredential responder_shared_credential_wrong_key_; -}; - -TEST_F(PresenceAuthenticatorTest, TestTwoWayInitiatorSignResponderVerify) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::InitiatorData auth_data, - initiator_authenticator.BuildSignedMessageAsInitiator( - kUkey2Secret, initiator_local_credential_, - responder_shared_credential_)); - auto local_credential = responder_authenticator.VerifyMessageAsResponder( - kUkey2Secret, auth_data, {responder_local_credential_}, - {initiator_shared_credential_}); - ASSERT_TRUE(local_credential.ok()); - EXPECT_THAT(*local_credential, EqualsProto(responder_local_credential_)); -} - -TEST_F(PresenceAuthenticatorTest, TestOneWayInitiatorSignResponderVerify) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN( - ConnectionAuthenticator::InitiatorData auth_data, - initiator_authenticator.BuildSignedMessageAsInitiator( - kUkey2Secret, std::nullopt, responder_shared_credential_)); - auto local_credential = responder_authenticator.VerifyMessageAsResponder( - kUkey2Secret, auth_data, {responder_local_credential_}, - {initiator_shared_credential_}); - ASSERT_TRUE(local_credential.ok()); - EXPECT_THAT(*local_credential, EqualsProto(responder_local_credential_)); -} - -TEST_F(PresenceAuthenticatorTest, TestResponderSignInitiatorVerify) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::ResponderData auth_data, - responder_authenticator.BuildSignedMessageAsResponder( - kUkey2Secret, responder_local_credential_)); - EXPECT_OK(initiator_authenticator.VerifyMessageAsInitiator( - auth_data, kUkey2Secret, {responder_shared_credential_})); -} - -TEST_F(PresenceAuthenticatorTest, - TestTwoWayInitiatorSignResponderVerifyNoSharedCredentialMatchFails) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::InitiatorData auth_data, - initiator_authenticator.BuildSignedMessageAsInitiator( - kUkey2Secret, initiator_local_credential_, - responder_shared_credential_)); - EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder( - kUkey2Secret, auth_data, {}, {initiator_shared_credential_}), - StatusIs(absl::StatusCode::kInternal)); -} - -TEST_F(PresenceAuthenticatorTest, - TestOneWayInitiatorSignResponderVerifyNoMatchCredentialFails) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN( - ConnectionAuthenticator::InitiatorData auth_data, - initiator_authenticator.BuildSignedMessageAsInitiator( - kUkey2Secret, std::nullopt, responder_shared_credential_)); - EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder( - kUkey2Secret, auth_data, {}, {initiator_shared_credential_}), - StatusIs(absl::StatusCode::kInternal)); -} - -TEST_F(PresenceAuthenticatorTest, - TestOneWayInitiatorSignResponderVerifyNoCredentialFails) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN( - ConnectionAuthenticator::InitiatorData auth_data, - initiator_authenticator.BuildSignedMessageAsInitiator( - kUkey2Secret, std::nullopt, responder_shared_credential_)); - EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder( - kUkey2Secret, auth_data, {}, {}), - StatusIs(absl::StatusCode::kInternal)); -} - -TEST_F(PresenceAuthenticatorTest, - TestTwoWayInitiatorSignResponderVerifyNoMatchCredentialFails) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::InitiatorData auth_data, - initiator_authenticator.BuildSignedMessageAsInitiator( - kUkey2Secret, initiator_local_credential_, - responder_shared_credential_)); - EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder( - kUkey2Secret, auth_data, {}, {initiator_shared_credential_}), - StatusIs(absl::StatusCode::kInternal)); -} - -TEST_F(PresenceAuthenticatorTest, - TestTwoWayInitiatorSignResponderVerifyWrongKeyFails) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::InitiatorData auth_data, - initiator_authenticator.BuildSignedMessageAsInitiator( - kUkey2Secret, initiator_local_credential_, - responder_shared_credential_)); - EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder( - kUkey2Secret, auth_data, {responder_local_credential_}, - {initiator_shared_credential_wrong_key_}), - StatusIs(absl::StatusCode::kInternal)); -} - -TEST_F(PresenceAuthenticatorTest, - TestResponderSignInitiatorVerifyNoMatchCredentialFails) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::ResponderData auth_data, - responder_authenticator.BuildSignedMessageAsResponder( - kUkey2Secret, responder_local_credential_)); - EXPECT_THAT(initiator_authenticator.VerifyMessageAsInitiator( - auth_data, kUkey2Secret, {}), - StatusIs(absl::StatusCode::kInternal)); -} - -TEST_F(PresenceAuthenticatorTest, - TestResponderSignInitiatorVerifyWrongKeyFails) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::ResponderData auth_data, - responder_authenticator.BuildSignedMessageAsResponder( - kUkey2Secret, responder_local_credential_)); - EXPECT_THAT( - initiator_authenticator.VerifyMessageAsInitiator( - auth_data, kUkey2Secret, {responder_shared_credential_wrong_key_}), - StatusIs(absl::StatusCode::kInternal)); -} - -TEST_F(PresenceAuthenticatorTest, - TestTwoWayInitiatorSignResponderVerifyNoCidHashFails) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::InitiatorData auth_data, - initiator_authenticator.BuildSignedMessageAsInitiator( - kUkey2Secret, initiator_local_credential_, - responder_shared_credential_)); - std::get(auth_data) - .shared_credential_hash.clear(); - EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder( - kUkey2Secret, auth_data, {responder_local_credential_}, - {initiator_shared_credential_wrong_key_}), - StatusIs(absl::StatusCode::kInvalidArgument)); -} - -TEST_F(PresenceAuthenticatorTest, - TestTwoWayInitiatorSignResponderVerifyNoPkeySigFails) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::InitiatorData auth_data, - initiator_authenticator.BuildSignedMessageAsInitiator( - kUkey2Secret, initiator_local_credential_, - responder_shared_credential_)); - std::get(auth_data) - .private_key_signature.clear(); - EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder( - kUkey2Secret, auth_data, {responder_local_credential_}, - {initiator_shared_credential_wrong_key_}), - StatusIs(absl::StatusCode::kInvalidArgument)); -} - -TEST_F(PresenceAuthenticatorTest, - TestOneWayInitiatorSignResponderVerifyNoCidHashFails) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN( - ConnectionAuthenticator::InitiatorData auth_data, - initiator_authenticator.BuildSignedMessageAsInitiator( - kUkey2Secret, std::nullopt, responder_shared_credential_)); - std::get(auth_data) - .shared_credential_hash.clear(); - EXPECT_THAT(responder_authenticator.VerifyMessageAsResponder( - kUkey2Secret, auth_data, {responder_local_credential_}, - {initiator_shared_credential_}), - StatusIs(absl::StatusCode::kInvalidArgument)); -} - -TEST_F(PresenceAuthenticatorTest, - TestResponderSignInitiatorVerifyNoPkeySigFail) { - ConnectionAuthenticatorImpl responder_authenticator; - ConnectionAuthenticatorImpl initiator_authenticator; - ASSERT_OK_AND_ASSIGN(ConnectionAuthenticator::ResponderData auth_data, - responder_authenticator.BuildSignedMessageAsResponder( - kUkey2Secret, responder_local_credential_)); - auth_data.private_key_signature.clear(); - EXPECT_THAT(initiator_authenticator.VerifyMessageAsInitiator( - auth_data, kUkey2Secret, {responder_shared_credential_}), - StatusIs(absl::StatusCode::kInvalidArgument)); -} -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/credential_manager.h b/presence/implementation/credential_manager.h deleted file mode 100644 index 2d4e088b..00000000 --- a/presence/implementation/credential_manager.h +++ /dev/null @@ -1,115 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_H_ - -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "internal/proto/credential.pb.h" -#include "internal/proto/metadata.pb.h" - -namespace nearby { -namespace presence { - -using SubscriberId = uint64_t; - -/* - * The instance of CredentialManager is owned by {@code ServiceControllerImpl}. - * Helping service controller to manage local credentials and coordinate with - * downloaded remote credentials. - */ -class CredentialManager { - public: - CredentialManager() = default; - virtual ~CredentialManager() = default; - - // Used to (re)generate user’s private and public credentials. - // The generated private credentials will be saved to creds storage. - // The generated public credentials will be returned inside the - // credentials_generated_cb for manager app to upload to web. - // The user’s own public credentials won’t be saved on local credential - // storage. - virtual void GenerateCredentials( - const nearby::internal::DeviceIdentityMetaData& device_identity_metadata, - absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) = 0; - - // Update remote public credentials. - virtual void UpdateRemotePublicCredentials( - absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& - remote_public_creds, - UpdateRemotePublicCredentialsCallback credentials_updated_cb) = 0; - - virtual void UpdateLocalCredential( - const CredentialSelector& credential_selector, - nearby::internal::LocalCredential credential, - SaveCredentialsResultCallback result_callback) = 0; - - // Used to fetch private creds when broadcasting. - virtual void GetLocalCredentials( - const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback) = 0; - - // Used to fetch local/remote public creds based on the value - // of public_credential_type. - virtual void GetPublicCredentials( - const CredentialSelector& credential_selector, - PublicCredentialType public_credential_type, - GetPublicCredentialsResultCallback callback) = 0; - - // Subscribes for public credentials updates. The `callback` is triggered when - // the public credentials are fetched initially, and then every time the - // credentials change. - virtual SubscriberId SubscribeForPublicCredentials( - const CredentialSelector& credential_selector, - PublicCredentialType public_credential_type, - GetPublicCredentialsResultCallback callback) = 0; - - // Unsubscribes from public credentials updates. No new callbacks will be - // triggered after this function returns. If there is a callback already - // running, that callback may continue after - // `UnsubscribeFromPublicCredentials()` return. - virtual void UnsubscribeFromPublicCredentials(SubscriberId id) = 0; - - // Decrypts the device identity metadata from a public credential. - // Returns an empty string if decryption fails. - virtual std::string DecryptDeviceIdentityMetaData( - absl::string_view metadata_encryption_key, absl::string_view key_seed, - absl::string_view metadata_string) = 0; - - // If `regen_credentials` is set to true, regenerating credentials. - virtual void SetDeviceIdentityMetaData( - const ::nearby::internal::DeviceIdentityMetaData& - device_identity_metadata, - bool regen_credentials, absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) = 0; - - virtual ::nearby::internal::DeviceIdentityMetaData - GetDeviceIdentityMetaData() = 0; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_H_ diff --git a/presence/implementation/credential_manager_impl.cc b/presence/implementation/credential_manager_impl.cc deleted file mode 100644 index d49d2f56..00000000 --- a/presence/implementation/credential_manager_impl.cc +++ /dev/null @@ -1,834 +0,0 @@ -// 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/credential_manager_impl.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "absl/types/span.h" -#include "absl/types/variant.h" -#include "internal/crypto_cros/aead.h" -#include "internal/crypto_cros/ec_private_key.h" -#include "internal/crypto_cros/hkdf.h" -#include "internal/platform/base64_utils.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/crypto.h" -#include "internal/platform/future.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "internal/platform/implementation/crypto.h" -#include "internal/platform/implementation/system_clock.h" -#include "internal/platform/logging.h" -#include "internal/proto/credential.pb.h" -#include "internal/proto/local_credential.pb.h" -#include "presence/data_types.h" -#include "presence/implementation/base_broadcast_request.h" -#include "presence/implementation/ldt.h" - -namespace nearby { -namespace presence { -namespace { -using ::nearby::Base64Utils; -using ::nearby::Crypto; -using ::nearby::Exception; -using ::nearby::ExceptionOr; -using ::nearby::Future; -using ::nearby::internal::IdentityType; -using ::nearby::internal::LocalCredential; -using ::nearby::internal::SharedCredential; - -// Key to retrieve local device's Private/Public Key Credentials from key store. -constexpr char kPairedKeyAliasPrefix[] = "nearby_presence_paired_key_alias_"; - -// Use an empty string because Chromium only supports 1 account. -// Windows & Apple will have their own Identity Provider. -constexpr absl::string_view kEmptyAccountName = ""; - -// The expected number of valid local credentials to be stored on local device. -constexpr int kExpectedValidLocalCredtialSize = 6; -// The expiration time in days for a credential. -constexpr int kCredentialLifeCycleDays = 5; -// The minimum size of bytes to generate credential id. -constexpr int kExpectedByteSizeOfCredentialId = 8; - -// Returns a random duration in [0, max_duration] range. -absl::Duration RandomDuration(absl::Duration max_duration) { - uint32_t random = nearby::RandData(); - return max_duration * random / std::numeric_limits::max(); -} - -std::string CustomizeBytesSize(absl::string_view bytes, size_t len) { - return crypto::HkdfSha256( - /*ikm=*/std::string(bytes), // NOLINT - /*salt=*/std::string(CredentialManagerImpl::kAuthenticityKeyByteSize, 0), - /*info=*/"", /*derived_key_size=*/len); -} - -} // namespace - -// Returns a positive long value extracted from a byte array. -int64_t GenerateIdFromByteArray(const ByteArray& input) { - size_t inputLength = input.size(); - - ByteArray processed_bytes(kExpectedByteSizeOfCredentialId); - // Only use first 8 bytes if the input is longer than 8 bytes. - if (inputLength > kExpectedByteSizeOfCredentialId) { - processed_bytes.CopyAt(0, input); - } else { - // Extend the input with zeros if it's shorter than 8 bytes - processed_bytes.CopyAt(kExpectedByteSizeOfCredentialId - inputLength, - input); - } - - int64_t id = 0; - for (int i = 0; i < kExpectedByteSizeOfCredentialId; ++i) { - id |= (static_cast(processed_bytes.data()[i]) << (8 * i)); - } - if (id == std::numeric_limits::min()) - return std::numeric_limits::max(); - return std::abs(id); -} - -void CredentialManagerImpl::GenerateCredentials( - const DeviceIdentityMetaData& device_identity_metadata, - absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) { - std::vector public_credentials; - std::vector private_credentials; - - for (auto identity_type : identity_types) { - absl::Time start_time = SystemClock::ElapsedRealtime(); - absl::Duration gap = credential_life_cycle_days * absl::Hours(24); - for (int index = 0; index < contiguous_copy_of_credentials; index++) { - auto public_private_credentials = - CreateLocalCredential(device_identity_metadata, identity_type, - start_time, start_time + gap); - if (public_private_credentials.second.identity_type() != - IdentityType::IDENTITY_TYPE_UNSPECIFIED) { - private_credentials.push_back(public_private_credentials.first); - public_credentials.push_back(public_private_credentials.second); - } - start_time += gap; - } - } - - // Create credential_storage object and invoke SaveCredentials. - credential_storage_ptr_->SaveCredentials( - manager_app_id, kEmptyAccountName, private_credentials, - public_credentials, PublicCredentialType::kLocalPublicCredential, - SaveCredentialsResultCallback{ - .credentials_saved_cb = - [this, manager_app_id = std::string(manager_app_id), - account_name = kEmptyAccountName, - callback = std::move(credentials_generated_cb), - public_credentials](absl::Status status) mutable { - if (!status.ok()) { - LOG(WARNING) << "Save credentials failed with: " << status; - std::move(callback.credentials_generated_cb)(status); - return; - } - std::move(callback.credentials_generated_cb)( - std::move(public_credentials)); - RunOnServiceControllerThread( - "local-creds-changed", - [this, manager_app_id = std::string(manager_app_id), - account_name = std::string(account_name)]() - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { - OnCredentialsChanged( - manager_app_id, account_name, - PublicCredentialType::kLocalPublicCredential); - }); - }}); -} - -void CredentialManagerImpl::UpdateRemotePublicCredentials( - absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& remote_public_creds, - UpdateRemotePublicCredentialsCallback credentials_updated_cb) { - credential_storage_ptr_->SaveCredentials( - manager_app_id, account_name, /* private_credentials */ {}, - remote_public_creds, PublicCredentialType::kRemotePublicCredential, - SaveCredentialsResultCallback{ - .credentials_saved_cb = - [this, manager_app_id = std::string(manager_app_id), - account_name = std::string(account_name), - callback = std::move(credentials_updated_cb)]( - absl::Status status) mutable { - if (!status.ok()) { - LOG(WARNING) - << "Update remote credentials failed with: " << status; - } else { - RunOnServiceControllerThread( - "remote-creds-changed", - [this, manager_app_id = std::string(manager_app_id), - account_name = std::string(account_name)]() - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { - OnCredentialsChanged( - manager_app_id, account_name, - PublicCredentialType::kRemotePublicCredential); - }); - } - std::move(callback.credentials_updated_cb)(status); - }}); -} - -std::pair -CredentialManagerImpl::CreateLocalCredential( - const DeviceIdentityMetaData& device_identity_metadata, - IdentityType identity_type, absl::Time start_time, absl::Time end_time) { - LocalCredential private_credential; - private_credential.set_start_time_millis(absl::ToUnixMillis(start_time)); - private_credential.set_end_time_millis(absl::ToUnixMillis(end_time)); - private_credential.set_identity_type(identity_type); - - // Creates an AES key to encrypt the whole broadcast. - std::string secret_key(kAuthenticityKeyByteSize, 0); - RandBytes(const_cast(secret_key.data()), - secret_key.size()); - private_credential.set_key_seed(secret_key); - - // Uses SHA-256 algorithm to generate the credential ID from the - // authenticity key - auto secret_id = Crypto::Sha256(secret_key); - // Does not expect to fail here since Crypto::Sha256 should not return - // empty ByteArray. - CHECK(!secret_id.Empty()) << "Crypto::Sha256 failed!"; - - private_credential.set_id(GenerateIdFromByteArray(secret_id)); - - std::string alias = Base64Utils::Encode(secret_id); - auto prefixedAlias = kPairedKeyAliasPrefix + alias; - - // Generate key pair. Store the private key in private credential. - auto key_pair = crypto::ECPrivateKey::Create(); - std::vector private_key; - key_pair->ExportPrivateKey(&private_key); - private_credential.mutable_connection_signing_key()->set_key( - std::string(private_key.begin(), private_key.end())); - // Create an AES key to encrypt the device identity metadata. - std::string metadata_key(kBaseMetadataSize, 0); - RandBytes(const_cast(metadata_key.data()), - metadata_key.size()); - private_credential.set_metadata_encryption_key_v0(metadata_key); - - // Generate the public credential - std::vector public_key; - key_pair->ExportPublicKey(&public_key); - - return std::pair( - private_credential, - CreatePublicCredential(private_credential, device_identity_metadata, - public_key)); -} - -SharedCredential CredentialManagerImpl::CreatePublicCredential( - const LocalCredential& private_credential, - const DeviceIdentityMetaData& device_identity_metadata, - const std::vector& public_key) { - // The start time in the public credential should be decreased by a random - // value in 0 - 3 hours range. - // The end time should be increased by a random value in 0 - 3 hours range. - // This improves privacy by making it harder to correlate certificates. - absl::Time start_time = - absl::FromUnixMillis(private_credential.start_time_millis()) - - RandomDuration(absl::Hours(3)); - absl::Time end_time = - absl::FromUnixMillis(private_credential.end_time_millis()) + - RandomDuration(absl::Hours(3)); - SharedCredential public_credential; - public_credential.set_identity_type(private_credential.identity_type()); - public_credential.set_id(private_credential.id()); - public_credential.set_key_seed(private_credential.key_seed()); - public_credential.set_start_time_millis(absl::ToUnixMillis(start_time)); - public_credential.set_end_time_millis(absl::ToUnixMillis(end_time)); - // Set up the public key. Note, we are setting the "connection" key but we are - // not setting the "advertisement" key because the latter is not used yet. - public_credential.set_connection_signature_verification_key( - std::string(public_key.begin(), public_key.end())); - - auto metadata_encryption_key_tag = - Crypto::Sha256(private_credential.metadata_encryption_key_v0()); - public_credential.set_metadata_encryption_key_tag_v0( - std::string(metadata_encryption_key_tag.AsStringView())); - - auto encrypted_meta_data = EncryptDeviceIdentityMetaData( - private_credential.metadata_encryption_key_v0(), - private_credential.key_seed(), - device_identity_metadata.SerializeAsString()); - - if (encrypted_meta_data.empty()) { - LOG(ERROR) << "Fails to encrypt the device identity metadata."; - public_credential.set_identity_type( - IdentityType::IDENTITY_TYPE_UNSPECIFIED); - return public_credential; - } - - public_credential.set_encrypted_metadata_bytes_v0(encrypted_meta_data); - return public_credential; -} - -std::string CredentialManagerImpl::DecryptDeviceIdentityMetaData( - absl::string_view metadata_encryption_key, absl::string_view key_seed, - absl::string_view metadata_string) { - crypto::Aead aead(crypto::Aead::AeadAlgorithm::AES_256_GCM); - - std::vector derived_key = - ExtendMetadataEncryptionKey(metadata_encryption_key); - aead.Init(derived_key); - - auto iv = CustomizeBytesSize(key_seed, CredentialManagerImpl::kAesGcmIVSize); - std::vector iv_bytes(iv.begin(), iv.end()); - std::vector encrypted_metadata_bytes(metadata_string.begin(), - metadata_string.end()); - - auto result = aead.Open(encrypted_metadata_bytes, - /*nonce=*/ - iv_bytes, - /*additional_data=*/absl::Span()); - - return std::string(result.value().begin(), result.value().end()); -} - -std::string CredentialManagerImpl::EncryptDeviceIdentityMetaData( - absl::string_view metadata_encryption_key, absl::string_view key_seed, - absl::string_view metadata_string) { - crypto::Aead aead(crypto::Aead::AeadAlgorithm::AES_256_GCM); - - std::vector derived_key = - ExtendMetadataEncryptionKey(metadata_encryption_key); - - aead.Init(derived_key); - - auto iv = CustomizeBytesSize(key_seed, kAesGcmIVSize); - std::vector iv_bytes(iv.begin(), iv.end()); - - std::vector metadata_bytes(metadata_string.begin(), - metadata_string.end()); - metadata_bytes.resize(metadata_string.size()); - - auto encrypted = aead.Seal(metadata_bytes, - /*nonce=*/ - iv_bytes, - /*additional_data=*/absl::Span()); - - return std::string(encrypted.begin(), encrypted.end()); -} - -std::vector CredentialManagerImpl::ExtendMetadataEncryptionKey( - absl::string_view metadata_encryption_key) { - return crypto::HkdfSha256( - std::vector(metadata_encryption_key.begin(), - metadata_encryption_key.end()), - /*salt=*/absl::Span(), - /*info=*/absl::Span(), kNearbyPresenceNumBytesAesGcmKeySize); -} - -void CredentialManagerImpl::GetLocalCredentials( - const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback) { - credential_storage_ptr_->GetLocalCredentials( - credential_selector, - GetLocalCredentialsResultCallback{ - .credentials_fetched_cb = - [this, credential_selector, callback = std::move(callback)]( - absl::StatusOr> - get_local_credentials_result) mutable { - if (!get_local_credentials_result.ok()) { - callback.credentials_fetched_cb( - get_local_credentials_result.status()); - return; - } - - CheckCredentialsAndRefillIfNeeded( - credential_selector, - /* credentials_list_variant */ - &get_local_credentials_result.value(), - /* callback_for_local_credentials */ - std::move(callback), - /* callback_for_shared_credentials */ - std::nullopt); - }, - }); -} - -void CredentialManagerImpl::GetPublicCredentials( - const CredentialSelector& credential_selector, - PublicCredentialType public_credential_type, - GetPublicCredentialsResultCallback callback) { - // Not going to refill for remote SharedCredentials. - if (public_credential_type == PublicCredentialType::kRemotePublicCredential) { - credential_storage_ptr_->GetPublicCredentials( - credential_selector, public_credential_type, std::move(callback)); - return; - } - - credential_storage_ptr_->GetPublicCredentials( - credential_selector, public_credential_type, - GetPublicCredentialsResultCallback{ - .credentials_fetched_cb = - [this, credential_selector, callback = std::move(callback)]( - absl::StatusOr> - get_shared_credentials_result) mutable { - if (!get_shared_credentials_result.ok()) { - callback.credentials_fetched_cb( - get_shared_credentials_result.status()); - return; - } - - CheckCredentialsAndRefillIfNeeded( - credential_selector, - /* credentials_list_variant */ - &get_shared_credentials_result.value(), - /* callback_for_local_credentials */ std::nullopt, - /* callback_for_shared_credentials */ - std::move(callback)); - }, - }); -} - -ExceptionOr> -CredentialManagerImpl::GetLocalCredentialsSync( - const CredentialSelector& credential_selector, absl::Duration timeout) { - Future> result; - GetLocalCredentials(credential_selector, - {.credentials_fetched_cb = - [result](absl::StatusOr> - credentials) mutable { - if (!credentials.ok()) { - result.SetException({Exception::kFailed}); - } else { - result.Set(std::move(*credentials)); - } - }}); - return result.Get(timeout); -} - -ExceptionOr> -CredentialManagerImpl::GetPublicCredentialsSync( - const CredentialSelector& credential_selector, - PublicCredentialType public_credential_type, absl::Duration timeout) { - Future> result; - GetPublicCredentials( - credential_selector, public_credential_type, - {.credentials_fetched_cb = - [result](absl::StatusOr> - credentials) mutable { - if (!credentials.ok()) { - result.SetException({Exception::kFailed}); - } else { - result.Set(std::move(*credentials)); - } - }}); - return result.Get(timeout); -} - -// TODO(b/326063431): The intent of this method is likely for -// GetPublicCredentials() to be called after the AddSubscriber() calls, but -// it's unlikely that this is happening on a real device. Manually verify. -SubscriberId CredentialManagerImpl::SubscribeForPublicCredentials( - const CredentialSelector& credential_selector, - PublicCredentialType public_credential_type, - GetPublicCredentialsResultCallback callback) { - SubscriberId id = nearby::RandData(); - RunOnServiceControllerThread( - "add-subscriber", - [this, key = SubscriberKey{credential_selector, public_credential_type}, - id, callback = std::move(callback)]() - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) mutable { - AddSubscriber(key, id, std::move(callback)); - }); - GetPublicCredentials(credential_selector, public_credential_type, - CreateNotifySubscribersCallback( - {credential_selector, public_credential_type})); - return id; -} - -void CredentialManagerImpl::UnsubscribeFromPublicCredentials(SubscriberId id) { - RunOnServiceControllerThread("remove-subscriber", - [this, id]() ABSL_EXCLUSIVE_LOCKS_REQUIRED( - *executor_) { RemoveSubscriber(id); }); -} - -void CredentialManagerImpl::AddSubscriber( - SubscriberKey key, SubscriberId id, - GetPublicCredentialsResultCallback callback) { - subscribers_[key].push_back(Subscriber(id, std::move(callback))); -} - -void CredentialManagerImpl::RemoveSubscriber(SubscriberId id) { - for (auto& entry : subscribers_) { - auto it = std::find_if( - entry.second.begin(), entry.second.end(), - [&](Subscriber& subscriber) { return subscriber.GetId() == id; }); - if (it != entry.second.end()) { - entry.second.erase(it); - if (subscribers_[entry.first].empty()) { - subscribers_.erase(entry.first); - } - return; - } - } -} - -absl::flat_hash_set -CredentialManagerImpl::GetSubscribedIdentities( - absl::string_view manager_app_id, absl::string_view account_name, - PublicCredentialType credential_type) const { - absl::flat_hash_set identities; - for (auto& entry : subscribers_) { - const SubscriberKey& key = entry.first; - if (key.public_credential_type == credential_type && - key.credential_selector.manager_app_id == manager_app_id && - key.credential_selector.account_name == account_name) { - identities.insert(key.credential_selector.identity_type); - } - } - return identities; -} - -void CredentialManagerImpl::OnCredentialsChanged( - absl::string_view manager_app_id, absl::string_view account_name, - PublicCredentialType credential_type) { - LOG(INFO) << "OnCredentialsChanged for app " << manager_app_id << ", account " - << account_name; - for (IdentityType identity_type : - GetSubscribedIdentities(manager_app_id, account_name, credential_type)) { - CredentialSelector credential_selector = { - .manager_app_id = std::string(manager_app_id), - .account_name = std::string(account_name), - .identity_type = identity_type}; - GetPublicCredentials(credential_selector, credential_type, - CreateNotifySubscribersCallback( - {credential_selector, credential_type})); - } -} - -GetPublicCredentialsResultCallback -CredentialManagerImpl::CreateNotifySubscribersCallback(SubscriberKey key) { - return GetPublicCredentialsResultCallback{ - .credentials_fetched_cb = - [this, - key](absl::StatusOr> credentials) { - if (!credentials.ok()) { - LOG(WARNING) << "Failed to get public credentials: error code: " - << credentials.status(); - return; - } - RunOnServiceControllerThread( - "notify-subscribers", - [this, key, credentials = std::move(*credentials)]() - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { - NotifySubscribers(key, credentials); - }); - }}; -} - -void CredentialManagerImpl::NotifySubscribers( - const SubscriberKey& key, std::vector credentials) { - // We are on `executor_` thread, so we can iterate over `subscribers_` - // without locking. - auto it = subscribers_.find(key); - if (it == subscribers_.end()) { - LOG(WARNING) << "No subscribers for (app: " - << key.credential_selector.manager_app_id - << ", account: " << key.credential_selector.account_name - << ", identity type: " - << static_cast(key.credential_selector.identity_type) - << ", credential type: " - << static_cast(key.public_credential_type) << ")"; - return; - } - for (auto& subscriber : it->second) { - subscriber.NotifyCredentialsFetched(credentials); - } -} - -void CredentialManagerImpl::Subscriber::NotifyCredentialsFetched( - std::vector& credentials) { - callback_.credentials_fetched_cb(credentials); -} - -void CredentialManagerImpl::UpdateLocalCredential( - const CredentialSelector& credential_selector, - nearby::internal::LocalCredential credential, - SaveCredentialsResultCallback result_callback) { - credential_storage_ptr_->UpdateLocalCredential( - credential_selector.manager_app_id, credential_selector.account_name, - std::move(credential), std::move(result_callback)); -} - -void CredentialManagerImpl::CheckCredentialsAndRefillIfNeeded( - const CredentialSelector& credential_selector, - absl::variant*, - std::vector*> - credential_list_variant, - std::optional - callback_for_local_credentials, - std::optional - callback_for_shared_credentials) { - bool invoked_for_local = false; - int valid_credentials_count = 0; - int64_t current_time_millis = - absl::ToUnixMillis(SystemClock::ElapsedRealtime()); - int64_t last_valid_end_time_millis = current_time_millis; - - std::vector valid_local_credentials; - std::vector valid_shared_credentials; - if (absl::holds_alternative*>( - credential_list_variant) && - callback_for_local_credentials.has_value()) { - invoked_for_local = true; - for (auto& credential : - *absl::get*>( - credential_list_variant)) { - if (credential.end_time_millis() < current_time_millis) { - continue; - } - valid_credentials_count++; - if (last_valid_end_time_millis < credential.end_time_millis()) { - last_valid_end_time_millis = credential.end_time_millis(); - } - valid_local_credentials.push_back(credential); - } - } else if (absl::holds_alternative< - std::vector*>( - credential_list_variant) && - callback_for_shared_credentials.has_value()) { - for (auto& credential : - *absl::get*>( - credential_list_variant)) { - if (credential.end_time_millis() < current_time_millis) { - continue; - } - valid_credentials_count++; - if (last_valid_end_time_millis < credential.end_time_millis()) { - last_valid_end_time_millis = credential.end_time_millis(); - } - valid_shared_credentials.push_back(credential); - } - } else { - LOG(ERROR) << "Bad parameters for CheckCredentialsAndRefillIfNeeded"; - return; - } - - // Most invokes are expected to return early here as it already got enough - // valid credentials, no need to refill. - // Otherwise, the long process of refill (another read, merge, then save) - // would start. - if (valid_credentials_count >= kExpectedValidLocalCredtialSize) { - if (invoked_for_local) { - callback_for_local_credentials.value().credentials_fetched_cb( - valid_local_credentials); - } else { - callback_for_shared_credentials.value().credentials_fetched_cb( - valid_shared_credentials); - } - return; - } - - // Already got the valid credential list for either local or shared. - // Now get the other credentials list from storage, prune them, and begin - // the process of appending new credentials onto them. - if (invoked_for_local) { - credential_storage_ptr_->GetPublicCredentials( - credential_selector, PublicCredentialType::kLocalPublicCredential, - GetPublicCredentialsResultCallback{ - .credentials_fetched_cb = - [this, current_time_millis, last_valid_end_time_millis, - credential_selector, - valid_local_credentials = std::move(valid_local_credentials), - valid_shared_credentials = std::move(valid_shared_credentials), - callback_for_local_credentials = - std::move(callback_for_local_credentials), - callback_for_shared_credentials = - std::move(callback_for_shared_credentials)]( - absl::StatusOr< - std::vector> - result) mutable { - if (!result.ok()) { - callback_for_local_credentials.value() - .credentials_fetched_cb(result.status()); - return; - } - for (const auto& credential : result.value()) { - if (credential.end_time_millis() >= current_time_millis) { - valid_shared_credentials.push_back(credential); - } - } - - RefillRemainingValidCredentialsWithNewCredentials( - credential_selector, valid_local_credentials, - valid_shared_credentials, - /*start_time_to_generate_new_credentials_millis=*/ - last_valid_end_time_millis, - std::move(callback_for_local_credentials), - std::move(callback_for_shared_credentials)); - }, - }); - } else { - credential_storage_ptr_->GetLocalCredentials( - credential_selector, - GetLocalCredentialsResultCallback{ - .credentials_fetched_cb = - [this, current_time_millis, last_valid_end_time_millis, - credential_selector, - valid_local_credentials = std::move(valid_local_credentials), - valid_shared_credentials = std::move(valid_shared_credentials), - callback_for_local_credentials = - std::move(callback_for_local_credentials), - callback_for_shared_credentials = - std::move(callback_for_shared_credentials)]( - absl::StatusOr< - std::vector> - result) mutable { - if (!result.ok()) { - callback_for_local_credentials.value() - .credentials_fetched_cb(result.status()); - return; - } - for (const auto& credential : result.value()) { - if (credential.end_time_millis() >= current_time_millis) { - valid_local_credentials.push_back( - credential); // RESTORE TODO - } - } - - RefillRemainingValidCredentialsWithNewCredentials( - credential_selector, valid_local_credentials, - valid_shared_credentials, - /*start_time_to_generate_new_credentials_millis=*/ - last_valid_end_time_millis, - std::move(callback_for_local_credentials), - std::move(callback_for_shared_credentials)); - }, - }); - } -} - -void CredentialManagerImpl::RefillRemainingValidCredentialsWithNewCredentials( - const CredentialSelector& credential_selector, - std::vector valid_local_credentials, - std::vector valid_shared_credentials, - int64_t start_time_to_generate_new_credentials_millis, - std::optional - callback_for_local_credentials, - std::optional - callback_for_shared_credentials) { - // The number of valid credentials has already been determined by pruning - // valid_local_credentials and valid_shared_credentials. They must match - // in size. - int valid_credentials_count = valid_local_credentials.size(); - CHECK_EQ(valid_credentials_count, valid_shared_credentials.size()); - - std::vector newly_generated_local_credentials; - std::vector newly_generated_shared_credentials; - - // Generate more credential pairs to refill the expired ones. - auto start_time = - absl::FromUnixMillis(start_time_to_generate_new_credentials_millis); - auto gap = kCredentialLifeCycleDays * absl::Hours(24); - for (int i = 0; i < kExpectedValidLocalCredtialSize - valid_credentials_count; - i++) { - auto pair = CreateLocalCredential(device_identity_metadata_, - credential_selector.identity_type, - start_time, start_time + gap); - newly_generated_local_credentials.push_back(std::move(pair.first)); - newly_generated_shared_credentials.push_back(std::move(pair.second)); - start_time += gap; - } - - // Now merge newly generated credentials to already existing valid ones. - valid_local_credentials.insert(valid_local_credentials.end(), - newly_generated_local_credentials.begin(), - newly_generated_local_credentials.end()); - valid_shared_credentials.insert(valid_shared_credentials.end(), - newly_generated_shared_credentials.begin(), - newly_generated_shared_credentials.end()); - - // Save merged local and shared credential lists to storage - credential_storage_ptr_->SaveCredentials( - credential_selector.manager_app_id, credential_selector.account_name, - valid_local_credentials, valid_shared_credentials, - PublicCredentialType::kLocalPublicCredential, - SaveCredentialsResultCallback{ - .credentials_saved_cb = - [this, valid_local_credentials, valid_shared_credentials, - callback_for_local_credentials = - std::move(callback_for_local_credentials), - callback_for_shared_credentials = - std::move(callback_for_shared_credentials)]( - absl::Status status) mutable { - OnCredentialRefillComplete( - std::move(status), valid_local_credentials, - valid_shared_credentials, - std::move(callback_for_local_credentials), - std::move(callback_for_shared_credentials)); - }, - }); -} - -void CredentialManagerImpl::OnCredentialRefillComplete( - absl::Status save_credentials_status, - std::vector valid_local_credentials, - std::vector valid_shared_credentials, - std::optional - callback_for_local_credentials, - std::optional - callback_for_shared_credentials) { - if (!save_credentials_status.ok()) { - LOG(ERROR) << "Save credentials failed with: " << save_credentials_status; - if (callback_for_local_credentials.has_value()) { - callback_for_local_credentials.value().credentials_fetched_cb( - save_credentials_status); - } else { - callback_for_shared_credentials.value().credentials_fetched_cb( - save_credentials_status); - } - return; - } - - if (callback_for_local_credentials.has_value()) { - callback_for_local_credentials.value().credentials_fetched_cb( - valid_local_credentials); - } else { - callback_for_shared_credentials.value().credentials_fetched_cb( - valid_shared_credentials); - } -} - -bool CredentialManagerImpl::WaitForLatch(absl::string_view method_name, - CountDownLatch* latch) { - Exception await_exception = latch->Await(); - if (!await_exception.Ok()) { - LOG(ERROR) << "Blocked in " << method_name - << " with exeception code: " << await_exception.value; - return false; - } - return true; -} -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/credential_manager_impl.h b/presence/implementation/credential_manager_impl.h deleted file mode 100644 index 9cd2c99e..00000000 --- a/presence/implementation/credential_manager_impl.h +++ /dev/null @@ -1,262 +0,0 @@ -// 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 THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_IMPL_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_IMPL_H_ - -#include -#include -#include -#include -#include -#include - -#include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" -#include "absl/log/die_if_null.h" -#include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "absl/types/variant.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/credential_storage_impl.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "internal/platform/runnable.h" -#include "internal/platform/single_thread_executor.h" -#include "internal/proto/credential.pb.h" -#include "internal/proto/metadata.pb.h" -#include "presence/implementation/credential_manager.h" - -namespace nearby { -namespace presence { - -class CredentialManagerImpl : public CredentialManager { - public: - using IdentityType = ::nearby::internal::IdentityType; - using DeviceIdentityMetaData = ::nearby::internal::DeviceIdentityMetaData; - - explicit CredentialManagerImpl(SingleThreadExecutor* executor) - : executor_(ABSL_DIE_IF_NULL(executor)) { - credential_storage_ptr_ = std::make_unique(); - } - - // Test purpose only. - CredentialManagerImpl( - SingleThreadExecutor* executor, - std::unique_ptr credential_storage_ptr) - : executor_(ABSL_DIE_IF_NULL(executor)), - credential_storage_ptr_(std::move(credential_storage_ptr)) {} - - // AES only supports key sizes of 16, 24 or 32 bytes. - static constexpr int kAuthenticityKeyByteSize = 32; - - // Length of key in bytes required by AES-GCM encryption. - static constexpr size_t kNearbyPresenceNumBytesAesGcmKeySize = 32; - - // Modify this to 12 after use real AES. - static constexpr int kAesGcmIVSize = 12; - - void GenerateCredentials( - const DeviceIdentityMetaData& device_identity_metadata, - absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) override; - - void UpdateRemotePublicCredentials( - absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& - remote_public_creds, - UpdateRemotePublicCredentialsCallback credentials_updated_cb) override; - - void UpdateLocalCredential( - const CredentialSelector& credential_selector, - nearby::internal::LocalCredential credential, - SaveCredentialsResultCallback result_callback) override; - - void GetLocalCredentials(const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback) override; - - // Blocking version of `GetLocalCredentials` - nearby::ExceptionOr> - GetLocalCredentialsSync(const CredentialSelector& credential_selector, - absl::Duration timeout); - - // Used to fetch local/remote public creds based on the value of - // public_credential_type. - void GetPublicCredentials( - const CredentialSelector& credential_selector, - PublicCredentialType public_credential_type, - GetPublicCredentialsResultCallback callback) override; - - // Blocking version of `GetPublicCredentials`. - ::nearby::ExceptionOr> - GetPublicCredentialsSync(const CredentialSelector& credential_selector, - PublicCredentialType public_credential_type, - absl::Duration timeout); - - SubscriberId SubscribeForPublicCredentials( - const CredentialSelector& credential_selector, - PublicCredentialType public_credential_type, - GetPublicCredentialsResultCallback callback) override; - - void UnsubscribeFromPublicCredentials(SubscriberId id) override; - - std::string DecryptDeviceIdentityMetaData( - absl::string_view metadata_encryption_key, absl::string_view key_seed, - absl::string_view metadata_string) override; - - std::pair - CreateLocalCredential(const DeviceIdentityMetaData& device_identity_metadata, - IdentityType identity_type, absl::Time start_time, - absl::Time end_time); - - nearby::internal::SharedCredential CreatePublicCredential( - const nearby::internal::LocalCredential& private_credential, - const DeviceIdentityMetaData& device_identity_metadata, - const std::vector& public_key); - - virtual std::string EncryptDeviceIdentityMetaData( - absl::string_view metadata_encryption_key, absl::string_view key_seed, - absl::string_view metadata_string); - - // Extend the key from 16 bytes to 32 bytes. - std::vector ExtendMetadataEncryptionKey( - absl::string_view metadata_encryption_key); - - void SetDeviceIdentityMetaData( - const DeviceIdentityMetaData& device_identity_metadata, - bool regen_credentials, absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) override { - device_identity_metadata_ = device_identity_metadata; - if (regen_credentials) { - GenerateCredentials(device_identity_metadata, manager_app_id, - identity_types, credential_life_cycle_days, - contiguous_copy_of_credentials, - std::move(credentials_generated_cb)); - } - } - - ::nearby::internal::DeviceIdentityMetaData GetDeviceIdentityMetaData() - override { - return device_identity_metadata_; - } - - private: - struct SubscriberKey { - CredentialSelector credential_selector; - PublicCredentialType public_credential_type; - template - friend H AbslHashValue(H h, const SubscriberKey& key) { - return H::combine(std::move(h), key.credential_selector, - key.public_credential_type); - } - friend bool operator==(const SubscriberKey& a, const SubscriberKey& b) { - return a.public_credential_type == b.public_credential_type && - a.credential_selector == b.credential_selector; - } - }; - class Subscriber { - public: - Subscriber(SubscriberId id, GetPublicCredentialsResultCallback callback) - : callback_(std::move(callback)), id_(id) {} - - SubscriberId GetId() const { return id_; } - - // Notifies the subscriber about fetched credentials. - void NotifyCredentialsFetched( - std::vector<::nearby::internal::SharedCredential>& credentials); - - private: - GetPublicCredentialsResultCallback callback_; - SubscriberId id_; - }; - - void RunOnServiceControllerThread(absl::string_view name, - Runnable&& runnable) { - executor_->Execute(std::string(name), std::move(runnable)); - } - - bool WaitForLatch(absl::string_view method_name, CountDownLatch* latch); - - // The similar flow to check-expired-then-refill-if-needed is needed in both - // GetLocalCredentials() and GetPublicCredentials(). The high level flow is: - // check if there're expired creds from the result credentials list from - // GetLocal/GetPublic, if some creds expired, prune the expired, merge with - // newly generated ones. Then get the corresponding(local/shared) creds list - // from the storage, also prune expired, merge with newly - // generated. Then finally, save the newly merged two lists (local & shared) - // to storage. For re-use purpose, this private function is made to be able - // to take in different parameters from both GetLocalCredentials() and - // GetPublicCredentials(). - void CheckCredentialsAndRefillIfNeeded( - const CredentialSelector& credential_selector, - absl::variant*, - std::vector*> - credential_list_variant, - std::optional - callback_for_local_credentials, - std::optional - callback_for_shared_credentials); - void RefillRemainingValidCredentialsWithNewCredentials( - const CredentialSelector& credential_selector, - std::vector valid_local_credentials, - std::vector valid_shared_credentials, - int64_t start_time_to_generate_new_credentials_millis, - std::optional - callback_for_local_credentials, - std::optional - callback_for_shared_credentials); - void OnCredentialRefillComplete( - absl::Status save_credentials_status, - std::vector valid_local_credentials, - std::vector valid_shared_credentials, - std::optional - callback_for_local_credentials, - std::optional - callback_for_shared_credentials); - - void OnCredentialsChanged(absl::string_view manager_app_id, - absl::string_view account_name, - PublicCredentialType credential_type) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); - void NotifySubscribers( - const SubscriberKey& key, - std::vector<::nearby::internal::SharedCredential> credentials) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); - void AddSubscriber(SubscriberKey key, SubscriberId id, - GetPublicCredentialsResultCallback callback) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); - void RemoveSubscriber(SubscriberId id) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); - absl::flat_hash_set GetSubscribedIdentities( - absl::string_view manager_app_id, absl::string_view account_name, - PublicCredentialType credential_type) const - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); - GetPublicCredentialsResultCallback CreateNotifySubscribersCallback( - SubscriberKey key); - - absl::flat_hash_map> subscribers_ - ABSL_GUARDED_BY(*executor_); - SingleThreadExecutor* executor_; - std::unique_ptr credential_storage_ptr_; - DeviceIdentityMetaData device_identity_metadata_; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_IMPL_H_ diff --git a/presence/implementation/credential_manager_impl_test.cc b/presence/implementation/credential_manager_impl_test.cc deleted file mode 100644 index 44d37659..00000000 --- a/presence/implementation/credential_manager_impl_test.cc +++ /dev/null @@ -1,721 +0,0 @@ -// 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/credential_manager_impl.h" - -#include -#include -#include -#include -#include - -#include "net/proto2/contrib/parse_proto/testing.h" -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/escaping.h" -#include "absl/strings/string_view.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/credential_storage_impl.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "internal/platform/implementation/crypto.h" -#include "internal/platform/logging.h" -#include "internal/platform/medium_environment.h" -#include "internal/proto/credential.pb.h" -#include "presence/implementation/base_broadcast_request.h" - -namespace nearby { -namespace presence { -namespace { -using ::nearby::CountDownLatch; -using ::nearby::Crypto; -using ::nearby::MediumEnvironment; -using ::nearby::internal::IdentityType; -using ::nearby::internal::LocalCredential; - -using ::nearby::internal::DeviceIdentityMetaData; -using ::nearby::internal::SharedCredential; -using ::nearby::internal::IdentityType::IDENTITY_TYPE_CONTACTS_GROUP; -using ::nearby::internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP; -using ::protobuf_matchers::EqualsProto; -using ::testing::UnorderedPointwise; -using ::testing::status::StatusIs; - -constexpr absl::string_view kManagerAppId = "TEST_MANAGER_APP"; -constexpr absl::string_view kAccountName = ""; -constexpr int kExpectedPresenceCredentialListSize = 6; -constexpr int kExpectedPresenceCredentialValidDays = 5; - -DeviceIdentityMetaData CreateTestDeviceIdentityMetaData() { - DeviceIdentityMetaData device_identity_metadata; - device_identity_metadata.set_device_type( - internal::DeviceType::DEVICE_TYPE_PHONE); - device_identity_metadata.set_device_name("NP test device"); - device_identity_metadata.set_bluetooth_mac_address("FF:FF:FF:FF:FF:FF"); - device_identity_metadata.set_device_id("\x12\xab\xcd"); - return device_identity_metadata; -} - -CredentialSelector BuildDefaultCredentialSelector() { - CredentialSelector credential_selector; - credential_selector.manager_app_id = std::string(kManagerAppId); - credential_selector.account_name = std::string(kAccountName); - credential_selector.identity_type = IDENTITY_TYPE_PRIVATE_GROUP; - return credential_selector; -} - -class CredentialManagerImplTest : public ::testing::Test { - public: - class MockCredentialStorage : public nearby::CredentialStorageImpl { - public: - MOCK_METHOD(void, SaveCredentials, - (absl::string_view manager_app_id, - absl::string_view account_name, - const std::vector& private_credentials, - const std::vector& public_credentials, - PublicCredentialType public_credential_type, - SaveCredentialsResultCallback callback), - (override)); - MOCK_METHOD( - void, GetPublicCredentials, - (const ::nearby::presence::CredentialSelector& credential_selector, - ::nearby::presence::PublicCredentialType public_credential_type, - ::nearby::presence::GetPublicCredentialsResultCallback callback), - (override)); - }; - - class FakeCredentialStorage : public nearby::CredentialStorageImpl { - public: - // nearby::CredentialStorageImpl: - void SaveCredentials( - absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& private_credentials, - const std::vector& public_credentials, - PublicCredentialType public_credential_type, - SaveCredentialsResultCallback callback) override { - // Capture the credentials before actually saving them, so that they - // can be manipulated later on. - private_credentials_ = private_credentials; - public_credentials_ = public_credentials; - - nearby::CredentialStorageImpl::SaveCredentials( - manager_app_id, account_name, private_credentials, public_credentials, - public_credential_type, std::move(callback)); - } - void GetLocalCredentials( - const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback) override { - if (private_credentials_.has_value()) { - callback.credentials_fetched_cb(private_credentials_.value()); - } else { - nearby::CredentialStorageImpl::GetLocalCredentials(credential_selector, - std::move(callback)); - } - } - void GetPublicCredentials( - const CredentialSelector& credential_selector, - PublicCredentialType public_credential_type, - GetPublicCredentialsResultCallback callback) override { - if (public_credentials_.has_value()) { - callback.credentials_fetched_cb(public_credentials_.value()); - } else { - nearby::CredentialStorageImpl::GetPublicCredentials( - credential_selector, public_credential_type, std::move(callback)); - } - } - - std::optional> - public_credentials_; - std::optional> - private_credentials_; - }; - - class MockCredentialManager : public CredentialManagerImpl { - public: - explicit MockCredentialManager(SingleThreadExecutor* executor) - : CredentialManagerImpl(executor) {} - MOCK_METHOD(std::string, EncryptDeviceIdentityMetaData, - (absl::string_view metadata_encryption_key, - absl::string_view key_seed, absl::string_view metadata_string), - (override)); - }; - - ~CredentialManagerImplTest() override { executor_.Shutdown(); } - - // Waits for active tasks in the background thread to complete. - void Fence() { - // A runnable on medium environment thread can add a task on "our" executor, - // and vice-versa. We need to wait for tasks on both threads in a loop a few - // times to make sure that all tasks have finished. - for (int i = 0; i < 3; i++) { - MediumEnvironment::Instance().Sync(); - CountDownLatch latch(1); - executor_.Execute([&]() { latch.CountDown(); }); - latch.Await(); - } - } - - void AddLocalIdentity(absl::string_view manager_app_id, - absl::string_view account_name, - IdentityType identity_type) { - auto public_credentials = GenerateCredentialsSync( - CreateTestDeviceIdentityMetaData(), manager_app_id, {identity_type}, - /*credential_life_cycle_days=*/kExpectedPresenceCredentialValidDays, - /*contiguous_copy_of_credentials=*/1); - EXPECT_OK(public_credentials); - } - - absl::StatusOr> GenerateCredentialsSync( - const DeviceIdentityMetaData& device_identity_metadata, - absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials) { - absl::StatusOr> public_credentials; - - CountDownLatch latch(1); - credential_manager_.GenerateCredentials( - device_identity_metadata, manager_app_id, identity_types, - credential_life_cycle_days, contiguous_copy_of_credentials, - {.credentials_generated_cb = - [&](absl::StatusOr> credentials) { - public_credentials = credentials; - latch.CountDown(); - }}); - EXPECT_TRUE(latch.Await().Ok()); - - return public_credentials; - } - - std::vector GetLocalCredentialsSync( - CredentialSelector credential_selector) { - auto private_credentials = credential_manager_.GetLocalCredentialsSync( - credential_selector, absl::Seconds(1)); - EXPECT_TRUE(private_credentials.ok()); - return private_credentials.GetResult(); - } - - std::vector GetPublicCredentialsSync( - CredentialSelector credential_selector, - PublicCredentialType public_credential_type) { - auto public_credentials = credential_manager_.GetPublicCredentialsSync( - credential_selector, public_credential_type, absl::Seconds(1)); - EXPECT_TRUE(public_credentials.ok()); - return public_credentials.GetResult(); - } - - protected: - SingleThreadExecutor executor_; - CredentialManagerImpl credential_manager_{&executor_}; - MockCredentialManager mock_credential_manager_{&executor_}; -}; - -TEST_F(CredentialManagerImplTest, CreateOneCredentialSuccessfully) { - auto device_identity_metadata = CreateTestDeviceIdentityMetaData(); - constexpr absl::Time kStartTime = absl::FromUnixSeconds(100000); - constexpr absl::Time kEndTime = absl::FromUnixSeconds(200000); - - auto credentials = credential_manager_.CreateLocalCredential( - device_identity_metadata, IDENTITY_TYPE_PRIVATE_GROUP, kStartTime, - kEndTime); - - LocalCredential private_credential = credentials.first; - // Verify the private credential. - EXPECT_EQ(private_credential.identity_type(), IDENTITY_TYPE_PRIVATE_GROUP); - EXPECT_NE(private_credential.id(), 0); - EXPECT_EQ(private_credential.start_time_millis(), - absl::ToUnixMillis(kStartTime)); - EXPECT_EQ(private_credential.end_time_millis(), absl::ToUnixMillis(kEndTime)); - EXPECT_EQ(private_credential.key_seed().size(), - CredentialManagerImpl::kAuthenticityKeyByteSize); - EXPECT_FALSE(private_credential.connection_signing_key().key().empty()); - EXPECT_EQ(private_credential.metadata_encryption_key_v0().size(), - kBaseMetadataSize); - - SharedCredential public_credential = credentials.second; - // Verify the public credential. - EXPECT_EQ(public_credential.identity_type(), IDENTITY_TYPE_PRIVATE_GROUP); - EXPECT_NE(public_credential.id(), 0); - EXPECT_EQ(private_credential.id(), public_credential.id()); - EXPECT_EQ(private_credential.key_seed(), public_credential.key_seed()); - EXPECT_LE(public_credential.start_time_millis(), - absl::ToUnixMillis(kStartTime)); - EXPECT_GE(public_credential.start_time_millis(), - absl::ToUnixMillis(kStartTime - absl::Hours(3))); - EXPECT_GE(public_credential.end_time_millis(), absl::ToUnixMillis(kEndTime)); - EXPECT_LE(public_credential.end_time_millis(), - absl::ToUnixMillis(kEndTime + absl::Hours(3))); - EXPECT_EQ(Crypto::Sha256(private_credential.metadata_encryption_key_v0()) - .AsStringView(), - public_credential.metadata_encryption_key_tag_v0()); - EXPECT_FALSE( - public_credential.connection_signature_verification_key().empty()); - EXPECT_FALSE(public_credential.encrypted_metadata_bytes_v0().empty()); - - auto decrypted_metadata = credential_manager_.DecryptDeviceIdentityMetaData( - private_credential.metadata_encryption_key_v0(), - public_credential.key_seed(), - public_credential.encrypted_metadata_bytes_v0()); - - EXPECT_EQ(device_identity_metadata.SerializeAsString(), decrypted_metadata); -} - -TEST_F(CredentialManagerImplTest, GenerateCredentialsSuccessfully) { - auto device_identity_metadata = CreateTestDeviceIdentityMetaData(); - std::vector identityTypes{IDENTITY_TYPE_PRIVATE_GROUP}; - absl::Time previous_start_time; - absl::Time previous_end_time; - - auto public_credentials = GenerateCredentialsSync( - device_identity_metadata, kManagerAppId, identityTypes, - kExpectedPresenceCredentialValidDays, - kExpectedPresenceCredentialListSize); - EXPECT_OK(public_credentials); - EXPECT_EQ(public_credentials->size(), kExpectedPresenceCredentialListSize); - - for (int i = 0; i < kExpectedPresenceCredentialListSize; i++) { - SharedCredential& public_credential = public_credentials->at(i); - EXPECT_EQ(public_credential.identity_type(), IDENTITY_TYPE_PRIVATE_GROUP); - EXPECT_NE(public_credential.id(), 0); - absl::Time start_time_millis = - absl::FromUnixMillis(public_credential.start_time_millis()); - absl::Time end_time_millis = - absl::FromUnixMillis(public_credential.end_time_millis()); - if (i > 0) { - EXPECT_GT(start_time_millis, previous_start_time); - EXPECT_GE(previous_end_time, start_time_millis); - EXPECT_GT(end_time_millis, previous_end_time); - } - EXPECT_LT(start_time_millis + - absl::Hours(24) * kExpectedPresenceCredentialValidDays, - end_time_millis); - EXPECT_FALSE(public_credential.encrypted_metadata_bytes_v0().empty()); - previous_start_time = start_time_millis; - previous_end_time = end_time_millis; - } -} - -TEST_F(CredentialManagerImplTest, - SubscribeCallsCallbackWithExistingCredentials) { - absl::StatusOr> public_credentials1; - absl::StatusOr> public_credentials2; - AddLocalIdentity(kManagerAppId, kAccountName, IDENTITY_TYPE_PRIVATE_GROUP); - - SubscriberId id1 = credential_manager_.SubscribeForPublicCredentials( - CredentialSelector{.manager_app_id = std::string(kManagerAppId), - .account_name = std::string(kAccountName), - .identity_type = IDENTITY_TYPE_PRIVATE_GROUP}, - PublicCredentialType::kLocalPublicCredential, - {.credentials_fetched_cb = - [&](absl::StatusOr> credentials) { - public_credentials1 = std::move(credentials); - }}); - SubscriberId id2 = credential_manager_.SubscribeForPublicCredentials( - CredentialSelector{.manager_app_id = std::string(kManagerAppId), - .account_name = std::string(kAccountName), - .identity_type = IDENTITY_TYPE_PRIVATE_GROUP}, - PublicCredentialType::kLocalPublicCredential, - {.credentials_fetched_cb = - [&](absl::StatusOr> credentials) { - public_credentials2 = std::move(credentials); - }}); - - Fence(); - EXPECT_OK(public_credentials1); - EXPECT_OK(public_credentials2); - EXPECT_EQ(public_credentials1->size(), kExpectedPresenceCredentialListSize); - EXPECT_EQ(public_credentials2->size(), kExpectedPresenceCredentialListSize); - // Cleanup - credential_manager_.UnsubscribeFromPublicCredentials(id1); - credential_manager_.UnsubscribeFromPublicCredentials(id2); - Fence(); -} - -TEST_F(CredentialManagerImplTest, - SubscribeCallsCallbackWithUpdatedCredentials) { - absl::StatusOr> public_credentials; - - SubscriberId id = credential_manager_.SubscribeForPublicCredentials( - CredentialSelector{.manager_app_id = std::string(kManagerAppId), - .account_name = std::string(kAccountName), - .identity_type = IDENTITY_TYPE_PRIVATE_GROUP}, - PublicCredentialType::kLocalPublicCredential, - {.credentials_fetched_cb = - [&](absl::StatusOr> credentials) { - public_credentials = std::move(credentials); - }}); - Fence(); - EXPECT_THAT(public_credentials, StatusIs(absl::StatusCode::kUnknown)); - - AddLocalIdentity(kManagerAppId, kAccountName, IDENTITY_TYPE_PRIVATE_GROUP); - - Fence(); - ASSERT_OK(public_credentials); - EXPECT_EQ(public_credentials->size(), kExpectedPresenceCredentialListSize); - // Cleanup - credential_manager_.UnsubscribeFromPublicCredentials(id); - Fence(); -} - -TEST_F(CredentialManagerImplTest, NoCallbacksAfterUnsubscribe) { - absl::StatusOr> public_credentials; - SubscriberId id = credential_manager_.SubscribeForPublicCredentials( - CredentialSelector{.manager_app_id = std::string(kManagerAppId), - .account_name = std::string(kAccountName), - .identity_type = IDENTITY_TYPE_PRIVATE_GROUP}, - PublicCredentialType::kLocalPublicCredential, - {.credentials_fetched_cb = - [&](absl::StatusOr> credentials) { - public_credentials = std::move(credentials); - }}); - - credential_manager_.UnsubscribeFromPublicCredentials(id); - AddLocalIdentity(kManagerAppId, kAccountName, IDENTITY_TYPE_PRIVATE_GROUP); - - Fence(); - EXPECT_THAT(public_credentials, StatusIs(absl::StatusCode::kUnknown)); -} - -TEST_F(CredentialManagerImplTest, - GenerateCredentialsSuccessfullyButStoreFailed) { - auto device_identity_metadata = CreateTestDeviceIdentityMetaData(); - auto credential_storage_ptr = - std::make_unique(); - EXPECT_CALL(*credential_storage_ptr, SaveCredentials) - .WillOnce(::testing::Invoke( - [](absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& private_credentials, - const std::vector& public_credentials, - PublicCredentialType public_credential_type, - SaveCredentialsResultCallback callback) { - callback.credentials_saved_cb( - absl::FailedPreconditionError("Expected failure")); - })); - credential_manager_ = - CredentialManagerImpl(&executor_, std::move(credential_storage_ptr)); - std::vector identityTypes{IDENTITY_TYPE_PRIVATE_GROUP}; - - auto public_credentials = GenerateCredentialsSync( - device_identity_metadata, kManagerAppId, identityTypes, - kExpectedPresenceCredentialValidDays, - kExpectedPresenceCredentialListSize); - EXPECT_THAT(public_credentials, - StatusIs(absl::StatusCode::kFailedPrecondition)); -} - -TEST_F(CredentialManagerImplTest, UpdateRemotePublicCredentialsSuccessfully) { - SharedCredential public_credential_for_test; - public_credential_for_test.set_identity_type( - IdentityType::IDENTITY_TYPE_CONTACTS_GROUP); - std::vector public_credentials{ - {public_credential_for_test}}; - - nearby::CountDownLatch updated_latch(1); - UpdateRemotePublicCredentialsCallback update_credentials_cb{ - .credentials_updated_cb = - [&updated_latch](absl::Status status) { - if (status.ok()) { - updated_latch.CountDown(); - } - }, - }; - - credential_manager_.UpdateRemotePublicCredentials( - kManagerAppId, kAccountName, public_credentials, - std::move(update_credentials_cb)); - - EXPECT_TRUE(updated_latch.Await().Ok()); -} - -TEST_F(CredentialManagerImplTest, - UpdateRemotePublicCredentialsNotifiesSubscribers) { - absl::StatusOr> subscribed_credentials; - SharedCredential public_credential_for_test; - public_credential_for_test.set_identity_type( - IdentityType::IDENTITY_TYPE_PRIVATE_GROUP); - std::vector public_credentials{ - {public_credential_for_test}}; - nearby::CountDownLatch updated_latch(1); - UpdateRemotePublicCredentialsCallback update_credentials_cb{ - .credentials_updated_cb = - [&updated_latch](absl::Status status) { - if (status.ok()) { - updated_latch.CountDown(); - } - }, - }; - SubscriberId id1 = credential_manager_.SubscribeForPublicCredentials( - CredentialSelector{ - .manager_app_id = std::string(kManagerAppId), - .account_name = std::string(kAccountName), - .identity_type = internal::IDENTITY_TYPE_PRIVATE_GROUP}, - PublicCredentialType::kRemotePublicCredential, - {.credentials_fetched_cb = - [&](absl::StatusOr> credentials) { - subscribed_credentials = std::move(credentials); - }}); - SubscriberId id2 = credential_manager_.SubscribeForPublicCredentials( - CredentialSelector{ - .manager_app_id = std::string(kManagerAppId), - .account_name = std::string(kAccountName), - .identity_type = internal::IDENTITY_TYPE_CONTACTS_GROUP}, - PublicCredentialType::kRemotePublicCredential, - {.credentials_fetched_cb = - [&](absl::StatusOr> credentials) { - // This callback should not be called because there are no Trusted - // credentials in this test. - GTEST_FAIL(); - }}); - - credential_manager_.UpdateRemotePublicCredentials( - kManagerAppId, kAccountName, public_credentials, - std::move(update_credentials_cb)); - - EXPECT_TRUE(updated_latch.Await().Ok()); - Fence(); - EXPECT_OK(subscribed_credentials); - EXPECT_EQ(subscribed_credentials->size(), 1); - credential_manager_.UnsubscribeFromPublicCredentials(id1); - credential_manager_.UnsubscribeFromPublicCredentials(id2); -} - -TEST_F(CredentialManagerImplTest, GetLocalCredentialsFailed) { - absl::StatusOr> private_credentials; - CredentialSelector credential_selector = BuildDefaultCredentialSelector(); - - credential_manager_.GetLocalCredentials( - credential_selector, - {.credentials_fetched_cb = - [&](absl::StatusOr> credentials) { - private_credentials = std::move(credentials); - }}); - - EXPECT_THAT(private_credentials, StatusIs(absl::StatusCode::kNotFound)); -} - -TEST_F(CredentialManagerImplTest, GetPublicCredentialsFailed) { - absl::StatusOr> public_credentials; - CredentialSelector credential_selector = BuildDefaultCredentialSelector(); - - CountDownLatch latch(1); - credential_manager_.GetPublicCredentials( - credential_selector, PublicCredentialType::kLocalPublicCredential, - {.credentials_fetched_cb = - [&](absl::StatusOr> credentials) { - public_credentials = std::move(credentials); - latch.CountDown(); - }}); - EXPECT_TRUE(latch.Await().Ok()); - - EXPECT_THAT(public_credentials, StatusIs(absl::StatusCode::kNotFound)); -} - -TEST_F(CredentialManagerImplTest, GetCredentialsSuccessfully) { - auto device_identity_metadata = CreateTestDeviceIdentityMetaData(); - std::vector identity_types{IDENTITY_TYPE_PRIVATE_GROUP}; - CredentialSelector credential_selector = BuildDefaultCredentialSelector(); - - auto public_credentials = GenerateCredentialsSync( - device_identity_metadata, kManagerAppId, identity_types, - kExpectedPresenceCredentialValidDays, - kExpectedPresenceCredentialListSize); - EXPECT_OK(public_credentials); - EXPECT_EQ(public_credentials->size(), kExpectedPresenceCredentialListSize); - - auto private_credentials = GetLocalCredentialsSync(credential_selector); - EXPECT_FALSE(private_credentials.empty()); -} - -TEST_F(CredentialManagerImplTest, PublicCredentialsFailEncryption) { - auto device_identity_metadata = CreateTestDeviceIdentityMetaData(); - absl::StatusOr> public_credentials; - auto credential_manager_ptr = - std::make_unique( - &executor_); - EXPECT_CALL(*credential_manager_ptr, EncryptDeviceIdentityMetaData) - .WillOnce(::testing::Invoke( - [](absl::string_view metadata_encryption_key, - absl::string_view key_seed, - absl::string_view metadata_string) { return ""; })); - std::vector identity_types{IDENTITY_TYPE_PRIVATE_GROUP}; - - CountDownLatch latch(1); - credential_manager_ptr->GenerateCredentials( - device_identity_metadata, kManagerAppId, identity_types, - kExpectedPresenceCredentialValidDays, 1, - {.credentials_generated_cb = - [&](absl::StatusOr> credentials) { - public_credentials = std::move(credentials); - latch.CountDown(); - }}); - EXPECT_TRUE(latch.Await().Ok()); - - EXPECT_THAT(public_credentials, StatusIs(absl::StatusCode::kInvalidArgument)); -} - -TEST_F(CredentialManagerImplTest, UpdateLocalCredential) { - constexpr int kSelectedCredentialId = 2; - constexpr uint16_t kSalt = 1000; - absl::Status update_status = absl::UnknownError(""); - auto device_identity_metadata = CreateTestDeviceIdentityMetaData(); - std::vector identity_types{IDENTITY_TYPE_PRIVATE_GROUP, - IDENTITY_TYPE_CONTACTS_GROUP}; - CredentialSelector credential_selector = BuildDefaultCredentialSelector(); - auto public_credentials = GenerateCredentialsSync( - device_identity_metadata, kManagerAppId, identity_types, - kExpectedPresenceCredentialValidDays, - kExpectedPresenceCredentialListSize); - - auto private_credentials = GetLocalCredentialsSync(credential_selector); - EXPECT_EQ(kExpectedPresenceCredentialListSize, private_credentials.size()); - - ASSERT_OK(public_credentials); - - // Modify a private credential - auto credential = private_credentials.at(kSelectedCredentialId); - EXPECT_TRUE( - private_credentials.at(kSelectedCredentialId).consumed_salts().empty()); - credential.mutable_consumed_salts()->insert({kSalt, true}); - - credential_manager_.UpdateLocalCredential( - credential_selector, credential, - {[&](absl::Status status) { update_status = status; }}); - - EXPECT_OK(update_status); - - // Verify that the modified credential has the new field in the new - // retrieved list of credentials. - auto modified_private_credentials = - GetLocalCredentialsSync(credential_selector); - EXPECT_TRUE(modified_private_credentials.at(kSelectedCredentialId) - .consumed_salts() - .at(kSalt)); -} - -TEST_F(CredentialManagerImplTest, EncryptAndDecryptDeviceIdentityMetaData) { - constexpr absl::string_view kMetadataEncryptionKeyBase16 = - "6331578C6E244074111B2ED0BBDB"; - constexpr absl::string_view kSeed = "123456"; - - auto encrypted_meta_data = credential_manager_.EncryptDeviceIdentityMetaData( - kMetadataEncryptionKeyBase16, kSeed, - CreateTestDeviceIdentityMetaData().SerializeAsString()); - - auto decrypted_meta_data = credential_manager_.DecryptDeviceIdentityMetaData( - kMetadataEncryptionKeyBase16, kSeed, encrypted_meta_data); - - DeviceIdentityMetaData device_identity_metadata; - ASSERT_TRUE(device_identity_metadata.ParseFromString(decrypted_meta_data)); - EXPECT_EQ(device_identity_metadata.device_id(), "\x12\xab\xcd"); - EXPECT_EQ(device_identity_metadata.device_type(), - internal::DeviceType::DEVICE_TYPE_PHONE); - EXPECT_EQ(device_identity_metadata.device_name(), "NP test device"); - EXPECT_EQ(device_identity_metadata.bluetooth_mac_address(), - "FF:FF:FF:FF:FF:FF"); -} - -TEST_F(CredentialManagerImplTest, RefillCredentialsInGetLocalCredentials) { - auto device_identity_metadata = CreateTestDeviceIdentityMetaData(); - std::vector identity_types{IDENTITY_TYPE_PRIVATE_GROUP}; - CredentialSelector credential_selector = BuildDefaultCredentialSelector(); - - auto public_credentials = GenerateCredentialsSync( - device_identity_metadata, kManagerAppId, identity_types, - kExpectedPresenceCredentialValidDays, 1); - - EXPECT_OK(public_credentials); - EXPECT_EQ(1, public_credentials->size()); - - // only generate 1 creds, expecting GetLocal would trigger refill to - // kExpectedPresenceCredentialListSize. - auto private_credentials = GetLocalCredentialsSync(credential_selector); - EXPECT_EQ(kExpectedPresenceCredentialListSize, private_credentials.size()); -} - -TEST_F(CredentialManagerImplTest, RefillCredentialsInGetSharedCredentials) { - auto device_identity_metadata = CreateTestDeviceIdentityMetaData(); - std::vector identity_types{IDENTITY_TYPE_PRIVATE_GROUP}; - CredentialSelector credential_selector = BuildDefaultCredentialSelector(); - - auto public_credentials = GenerateCredentialsSync( - device_identity_metadata, kManagerAppId, identity_types, - kExpectedPresenceCredentialValidDays, 1); - EXPECT_OK(public_credentials); - EXPECT_EQ(1, public_credentials->size()); - - // Only generated 1 creds, expecting GetPublicCredentials for - // kLocalPublicCredential type would trigger refill to - // kExpectedPresenceCredentialListSize. - auto refilled_public_credentials = GetPublicCredentialsSync( - credential_selector, PublicCredentialType::kLocalPublicCredential); - EXPECT_EQ(kExpectedPresenceCredentialListSize, - refilled_public_credentials.size()); -} - -TEST_F(CredentialManagerImplTest, RefillExpiredCredsInGetLocal) { - auto device_identity_metadata = CreateTestDeviceIdentityMetaData(); - std::vector identity_types{IDENTITY_TYPE_PRIVATE_GROUP}; - CredentialSelector credential_selector = BuildDefaultCredentialSelector(); - - auto credential_storage = - std::make_unique(); - auto* credential_storage_ptr = credential_storage.get(); - credential_manager_ = - CredentialManagerImpl(&executor_, std::move(credential_storage)); - - auto public_credentials = GenerateCredentialsSync( - device_identity_metadata, kManagerAppId, identity_types, - kExpectedPresenceCredentialValidDays, - kExpectedPresenceCredentialListSize); - - ASSERT_OK(public_credentials); - EXPECT_EQ(public_credentials->size(), kExpectedPresenceCredentialListSize); - - // Now that we have generated kExpectedPresenceCredentialListSize valid creds, - // tweak the first credential's end time, in both credential lists, to - // make them expired. - auto expiry_time = absl::ToUnixMillis(absl::Now() - absl::Hours(1)); - credential_storage_ptr->private_credentials_.value() - .at(0) - .set_end_time_millis(expiry_time); - credential_storage_ptr->public_credentials_.value().at(0).set_end_time_millis( - expiry_time); - - auto old_private_credentials = - credential_storage_ptr->private_credentials_.value(); - - auto refilled_private_credentials = - GetLocalCredentialsSync(credential_selector); - EXPECT_EQ(kExpectedPresenceCredentialListSize, - refilled_private_credentials.size()); - - // Verifying the expired one private_credentials->at(0) is pruned in the new - // list. - EXPECT_EQ(old_private_credentials.at(1).secret_id(), - refilled_private_credentials.at(0).secret_id()); - // Verifying the new generated cred's start time is the same as previously - // existing list's last cred's end time. - EXPECT_EQ( - old_private_credentials.at(5).end_time_millis(), - refilled_private_credentials.at(kExpectedPresenceCredentialListSize - 1) - .start_time_millis()); -} - -} // namespace - -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/ldt.cc b/presence/implementation/ldt.cc deleted file mode 100644 index 28eac763..00000000 --- a/presence/implementation/ldt.cc +++ /dev/null @@ -1,109 +0,0 @@ -// 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/ldt.h" - -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" -#ifdef NEARBY_CHROMIUM -#include "third_party/nearby/src/presence/implementation/np_ldt.h" -#else -#include "np_ldt.h" -#endif - -namespace nearby { -namespace presence { - -namespace { -// NP LDT library says that 0 is returned when `NpLdtCreate()` fails. -constexpr uint64_t kInvalidLdtHandle = 0; - -template -T FromStringView(absl::string_view data) { - T result{ - .bytes = {0}, - }; - memcpy(result.bytes, data.data(), - std::min(sizeof(result.bytes), data.size())); - return result; -} -} // namespace - -LdtEncryptor::LdtEncryptor(LdtEncryptor&& other) - : ldt_encrypt_handle_(other.ldt_encrypt_handle_), - ldt_decrypt_handle_(other.ldt_decrypt_handle_) { - other.ldt_encrypt_handle_.handle = kInvalidLdtHandle; - other.ldt_decrypt_handle_.handle = kInvalidLdtHandle; -} - -LdtEncryptor::~LdtEncryptor() { - if (ldt_encrypt_handle_.handle != kInvalidLdtHandle) { - NpLdtEncryptClose(ldt_encrypt_handle_); - } - if (ldt_decrypt_handle_.handle != kInvalidLdtHandle) { - NpLdtDecryptClose(ldt_decrypt_handle_); - } -} - -absl::StatusOr LdtEncryptor::Create( - absl::string_view key_seed, absl::string_view known_hmac) { - NpLdtEncryptHandle encrypt_handle = - NpLdtEncryptCreate(FromStringView(key_seed)); - NpLdtDecryptHandle decrypt_handle = - NpLdtDecryptCreate(FromStringView(key_seed), - FromStringView(known_hmac)); - if (encrypt_handle.handle == kInvalidLdtHandle) { - return absl::UnavailableError("Failed to create LDT encryptor"); - } - if (decrypt_handle.handle == kInvalidLdtHandle) { - return absl::UnavailableError("Failed to create LDT decrypter"); - } - - return LdtEncryptor(encrypt_handle, decrypt_handle); -} - -absl::StatusOr LdtEncryptor::Encrypt(absl::string_view data, - absl::string_view salt) { - std::string encrypted = std::string(data); - NP_LDT_RESULT result = NpLdtEncrypt( - ldt_encrypt_handle_, reinterpret_cast(encrypted.data()), - encrypted.size(), FromStringView(salt)); - if (result == NP_LDT_SUCCESS) { - return encrypted; - } - return absl::InternalError( - absl::StrFormat("LDT encryption failed, errorcode %d", result)); -} - -absl::StatusOr LdtEncryptor::DecryptAndVerify( - absl::string_view data, absl::string_view salt) { - std::string encrypted = std::string(data); - NP_LDT_RESULT result = NpLdtDecryptAndVerify( - ldt_decrypt_handle_, reinterpret_cast(encrypted.data()), - encrypted.size(), FromStringView(salt)); - if (result == NP_LDT_SUCCESS) { - return encrypted; - } - return absl::InternalError( - absl::StrFormat("LDT encryption failed, errorcode %d", result)); -} - -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/ldt.h b/presence/implementation/ldt.h deleted file mode 100644 index d86ecc10..00000000 --- a/presence/implementation/ldt.h +++ /dev/null @@ -1,76 +0,0 @@ -// 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 THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_LDT_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_LDT_H_ - -#include -#include - -#ifdef NEARBY_CHROMIUM -#include "third_party/nearby/src/presence/implementation/np_ldt.h" -#else -#include "np_ldt.h" -#endif - -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" - -namespace nearby { -namespace presence { - -// C++ abstraction on top of LDT C API. -class LdtEncryptor { - public: - LdtEncryptor(const LdtEncryptor&) = delete; - LdtEncryptor(LdtEncryptor&& other); - LdtEncryptor& operator=(const LdtEncryptor&) = delete; - LdtEncryptor& operator=(LdtEncryptor&& other) { - std::swap(ldt_encrypt_handle_, other.ldt_encrypt_handle_); - std::swap(ldt_decrypt_handle_, other.ldt_decrypt_handle_); - return *this; - } - ~LdtEncryptor(); - - // Creates an instance of `LdtEncryptor`. - // `key_seed` is used to generate LDT encryption and decryption keys. - // `known_hmac` is used during decryption to verify if the message was - // encrypted with the expected key. - static absl::StatusOr Create(absl::string_view key_seed, - absl::string_view known_hmac); - - // Encrypts `data`, which must be 16 - 31 bytes long. - absl::StatusOr Encrypt(absl::string_view data, - absl::string_view salt); - - // Decrypts `data` and verifies if it was encrypted with a key generated from - // `key_seed`. - absl::StatusOr DecryptAndVerify(absl::string_view data, - absl::string_view salt); - - private: - explicit LdtEncryptor(NpLdtEncryptHandle ldt_encrypt_handle, - NpLdtDecryptHandle ldt_decrypt_handle) - : ldt_encrypt_handle_(ldt_encrypt_handle), - ldt_decrypt_handle_(ldt_decrypt_handle) {} - // An opaque handle to the underlying LDT implementation. It can be null iff - // this object has already been destroyed. - NpLdtEncryptHandle ldt_encrypt_handle_; - NpLdtDecryptHandle ldt_decrypt_handle_; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_LDT_H_ diff --git a/presence/implementation/ldt_stub.c b/presence/implementation/ldt_stub.c deleted file mode 100644 index cc930bd5..00000000 --- a/presence/implementation/ldt_stub.c +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2025 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/np_ldt.h" - -// Placeholder, empty implementations of LDT utilities. They will be replaced -// with implementations in Rust. - -NpLdtEncryptHandle NpLdtEncryptCreate(NpLdtKeySeed key_seed) { - NpLdtEncryptHandle handle = {0}; - return handle; -} - -NpLdtDecryptHandle NpLdtDecryptCreate(NpLdtKeySeed key_seed, - NpMetadataKeyHmac hmac_tag) { - NpLdtDecryptHandle handle = {0}; - return handle; -} - -NP_LDT_RESULT NpLdtEncryptClose(NpLdtEncryptHandle handle) { - return NP_LDT_SUCCESS; -} - -NP_LDT_RESULT NpLdtDecryptClose(NpLdtDecryptHandle handle) { - return NP_LDT_SUCCESS; -} - -NP_LDT_RESULT NpLdtEncrypt(NpLdtEncryptHandle handle, uint8_t* buffer, - size_t buffer_len, NpLdtSalt salt) { - return NP_LDT_SUCCESS; -} - -NP_LDT_RESULT NpLdtDecryptAndVerify(NpLdtDecryptHandle handle, uint8_t* buffer, - size_t buffer_len, NpLdtSalt salt) { - return NP_LDT_SUCCESS; -} \ No newline at end of file diff --git a/presence/implementation/ldt_test.cc b/presence/implementation/ldt_test.cc deleted file mode 100644 index b574cc7d..00000000 --- a/presence/implementation/ldt_test.cc +++ /dev/null @@ -1,98 +0,0 @@ -// 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/ldt.h" - -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/status/statusor.h" -#include "absl/strings/escaping.h" -#include "absl/strings/string_view.h" -#include "internal/platform/byte_array.h" - -namespace nearby { -namespace presence { - -namespace { -using ::nearby::ByteArray; - -// Test data from Android tests. -constexpr absl::string_view kKeySeedBase16 = - "CCDB2489E9FCAC42B39348B8941ED19A1D360E75E098C8C15E6B1CC2B620CD39"; -constexpr absl::string_view kKnownMacBase16 = - "B4C59FA599241B81758D976B5A621C05232FE1BF89AE5987CA254C3554DCE50E"; -constexpr absl::string_view kPlainTextBase16 = - "CD683FE1A1D1F846543D0A13D4AEA40040C8D67B"; -constexpr absl::string_view kCipherTextBase16 = - "61E481C12F4DE24F2D4AB22D8908F80D3A3F9B40"; -constexpr absl::string_view kSaltBase16 = "0C0F"; - -TEST(Ldt, EncryptAndDecrypt) { - // Test data copied from NP LDT tests - ByteArray seed({204, 219, 36, 137, 233, 252, 172, 66, 179, 147, 72, - 184, 148, 30, 209, 154, 29, 54, 14, 117, 224, 152, - 200, 193, 94, 107, 28, 194, 182, 32, 205, 57}); - ByteArray known_mac({0xB4, 0xC5, 0x9F, 0xA5, 0x99, 0x24, 0x1B, 0x81, - 0x75, 0x8D, 0x97, 0x6B, 0x5A, 0x62, 0x1C, 0x05, - 0x23, 0x2F, 0xE1, 0xBF, 0x89, 0xAE, 0x59, 0x87, - 0xCA, 0x25, 0x4C, 0x35, 0x54, 0xDC, 0xE5, 0x0E}); - ByteArray test_data({205, 104, 63, 225, 161, 209, 248, 70, 84, 61, - 10, 19, 212, 174, 164, 0, 64, 200, 214, 123}); - ByteArray salt({12, 15}); - - absl::StatusOr encryptor = - LdtEncryptor::Create(seed.AsStringView(), known_mac.AsStringView()); - ASSERT_OK(encryptor); - absl::StatusOr encrypted = - encryptor->Encrypt(test_data.AsStringView(), salt.AsStringView()); - ASSERT_OK(encrypted); - absl::StatusOr decrypted = - encryptor->DecryptAndVerify(*encrypted, salt.AsStringView()); - ASSERT_OK(decrypted); - EXPECT_EQ(*decrypted, test_data.AsStringView()); -} - -TEST(Ldt, EncryptAndroidData) { - absl::StatusOr encryptor = - LdtEncryptor::Create(absl::HexStringToBytes(kKeySeedBase16), - absl::HexStringToBytes(kKnownMacBase16)); - ASSERT_OK(encryptor); - - absl::StatusOr encrypted = - encryptor->Encrypt(absl::HexStringToBytes(kPlainTextBase16), - absl::HexStringToBytes(kSaltBase16)); - - ASSERT_OK(encrypted); - EXPECT_EQ(*encrypted, absl::HexStringToBytes(kCipherTextBase16)); -} - -TEST(Ldt, DecryptAndroidData) { - absl::StatusOr encryptor = - LdtEncryptor::Create(absl::HexStringToBytes(kKeySeedBase16), - absl::HexStringToBytes(kKnownMacBase16)); - ASSERT_OK(encryptor); - - absl::StatusOr decrypted = - encryptor->DecryptAndVerify(absl::HexStringToBytes(kCipherTextBase16), - absl::HexStringToBytes(kSaltBase16)); - - ASSERT_OK(decrypted); - EXPECT_EQ(*decrypted, absl::HexStringToBytes(kPlainTextBase16)); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/mediums/BUILD b/presence/implementation/mediums/BUILD deleted file mode 100644 index 80dc9214..00000000 --- a/presence/implementation/mediums/BUILD +++ /dev/null @@ -1,70 +0,0 @@ -# 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. - -load("@rules_cc//cc:cc_library.bzl", "cc_library") -load("@rules_cc//cc:cc_test.bzl", "cc_test") - -licenses(["notice"]) - -cc_library( - name = "mediums", - srcs = [ - ], - hdrs = [ - "advertisement_data.h", - "ble.h", - "mediums.h", - ], - visibility = [ - "//presence:__subpackages__", - ], - deps = [ - "//internal/platform:base", - "//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", - "//presence:types", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/status", - "@com_google_absl//absl/time", - "@com_google_absl//absl/types:variant", - "@com_google_googletest//:gtest_main", - ] + select({ - "@platforms//os:windows": [ - "//internal/platform/implementation/windows", - ], - "//conditions:default": [ - "//internal/platform/implementation/g3", - ], - }), -) diff --git a/presence/implementation/mediums/advertisement_data.h b/presence/implementation/mediums/advertisement_data.h deleted file mode 100644 index 654f89b9..00000000 --- a/presence/implementation/mediums/advertisement_data.h +++ /dev/null @@ -1,34 +0,0 @@ -// 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 THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_ADVERTISEMENT_DATA_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_ADVERTISEMENT_DATA_H_ - -#include - -namespace nearby { -namespace presence { - -// Nearby Presence advertisement data over the air. -struct AdvertisementData { - // If true, the advertisement needs to be broadcasted over BLE 5.0. - bool is_extended_advertisement; - // The advertised data. - std::string content; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_ADVERTISEMENT_DATA_H_ diff --git a/presence/implementation/mediums/ble.h b/presence/implementation/mediums/ble.h deleted file mode 100644 index 715c0b9b..00000000 --- a/presence/implementation/mediums/ble.h +++ /dev/null @@ -1,111 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_BLE_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_BLE_H_ - -#include -#include - -#include "internal/platform/ble.h" -#include "internal/platform/bluetooth_adapter.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/implementation/ble.h" -#include "internal/platform/uuid.h" -#include "presence/implementation/mediums/advertisement_data.h" -#include "presence/power_mode.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { - -/** Presence advertisement service data uuid. */ -ABSL_CONST_INIT const nearby::Uuid kPresenceServiceUuid(0x0000fcf100001000, - 0x800000805f9b34fb); - -/* - * This Ble class utilizes platform/ble BleMedium, 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. - */ -class Ble { - public: - using TxPowerLevel = ::nearby::api::ble::TxPowerLevel; - using ScanningSession = ::nearby::api::ble::BleMedium::ScanningSession; - using ScanningCallback = ::nearby::api::ble::BleMedium::ScanningCallback; - using AdvertiseParameters = ::nearby::api::ble::AdvertiseParameters; - using AdvertisingSession = ::nearby::api::ble::BleMedium::AdvertisingSession; - using AdvertisingCallback = - ::nearby::api::ble::BleMedium::AdvertisingCallback; - using BleAdvertisementData = ::nearby::api::ble::BleAdvertisementData; - using BleMedium = ::nearby::api::ble::BleMedium; - - explicit Ble(nearby::BluetoothAdapter& bluetooth_adapter) - : medium_(bluetooth_adapter) {} - - bool IsAvailable() const { return medium_.IsValid(); } - - // Starts broadcasting NP advertisement in `payload`. The caller should use - // the returned `AdvertisingSession` to stop the broadcast. - std::unique_ptr StartAdvertising( - const AdvertisementData& payload, PowerMode power_mode, - AdvertisingCallback callback) { - BleAdvertisementData advertising_data = { - .is_extended_advertisement = payload.is_extended_advertisement}; - advertising_data.service_data.insert( - {kPresenceServiceUuid, nearby::ByteArray(payload.content)}); - AdvertiseParameters advertise_set_parameters = { - .tx_power_level = ConvertPowerModeToPowerLevel(power_mode), - .is_connectable = true, - }; - return medium_.StartAdvertising(advertising_data, advertise_set_parameters, - std::move(callback)); - } - - // Starts scanning for NP advertisements. The caller should use the returned - // `ScanningSession` to stop scanning. - std::unique_ptr StartScanning(ScanRequest scan_request, - ScanningCallback callback) { - return medium_.StartScanning( - kPresenceServiceUuid, - ConvertPowerModeToPowerLevel(scan_request.power_mode), - std::move(callback)); - } - - // Provides access to platform implementation. It's used in tests. - BleMedium* GetImpl() const { return medium_.GetImpl(); } - - private: - 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; - } - - nearby::BleMedium medium_; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_BLE_H_ diff --git a/presence/implementation/mediums/ble_test.cc b/presence/implementation/mediums/ble_test.cc deleted file mode 100644 index 70633ad7..00000000 --- a/presence/implementation/mediums/ble_test.cc +++ /dev/null @@ -1,177 +0,0 @@ -// 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 - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/status/status.h" -#include "absl/time/time.h" -#include "absl/types/variant.h" -#include "internal/platform/bluetooth_adapter.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/feature_flags.h" -#include "internal/platform/implementation/ble.h" -#include "internal/platform/medium_environment.h" -#include "internal/platform/uuid.h" -#include "presence/data_element.h" -#include "presence/implementation/mediums/advertisement_data.h" -#include "presence/power_mode.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { -namespace { - -using FeatureFlags = ::nearby::FeatureFlags::Flags; -using BleMediumStatus = ::nearby::MediumEnvironment::BleMediumStatus; -using ScanningSession = ::nearby::api::ble::BleMedium::ScanningSession; -using TxPowerLevel = ::nearby::api::ble::TxPowerLevel; -using ScanningCallback = ::nearby::api::ble::BleMedium::ScanningCallback; -using Uuid = ::nearby::Uuid; -using ::nearby::api::ble::BleAdvertisementData; -using ::nearby::api::ble::BlePeripheral; -using AdvertisingCallback = ::nearby::api::ble::BleMedium::AdvertisingCallback; -using AdvertisingSession = ::nearby::api::ble::BleMedium::AdvertisingSession; - -constexpr FeatureFlags kTestCases[] = { - FeatureFlags{}, -}; - -class BleTest : public testing::TestWithParam { - public: - 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_CONTACTS_GROUP, - }; - 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: - std::optional GetBleStatus(const Ble& ble) { - return env_.GetBleMediumStatus(*ble.GetImpl()); - } - nearby::MediumEnvironment& env_{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(); - ::nearby::BluetoothAdapter adapter; - Ble ble(adapter); - - ScanRequest scan_request{ - .power_mode = PowerMode::kBalanced, - }; - ScanningCallback scanning_callback; - nearby::CountDownLatch started_scanning_latch(1); - - std::unique_ptr scannning_session = ble.StartScanning( - scan_request, ScanningCallback{ - .start_scanning_result = - [&started_scanning_latch](absl::Status status) { - if (status.ok()) { - started_scanning_latch.CountDown(); - } - }, - }); - - EXPECT_TRUE(started_scanning_latch.Await(kWaitDuration).result()); - EXPECT_TRUE(GetBleStatus(ble).has_value() && - GetBleStatus(ble).value().is_scanning == true); - absl::Status stop_scanning_status = scannning_session->stop_scanning(); - EXPECT_OK(stop_scanning_status); - EXPECT_TRUE(GetBleStatus(ble).has_value() && - GetBleStatus(ble).value().is_scanning == false); - env_.Stop(); -} - -TEST_P(BleTest, AdvertiseAndScan) { - // Create two Ble devices, one advertises, the other one scans, and verify - // that the NP advertisement was sent from one to the other. - env_.Start(); - nearby::BluetoothAdapter client_adapter; - Ble client(client_adapter); - nearby::BluetoothAdapter server_adapter; - Ble server(server_adapter); - AdvertisementData advert_data = {.is_extended_advertisement = false, - .content = "my advertisement"}; - ScanRequest scan_request{ - .power_mode = PowerMode::kBalanced, - }; - nearby::CountDownLatch advertise_latch(1); - nearby::CountDownLatch scan_latch(1); - std::vector advertisements; - std::unique_ptr scanning_session = client.StartScanning( - scan_request, - ScanningCallback{.advertisement_found_cb = - [&](BlePeripheral::UniqueId peripheral_id, - BleAdvertisementData advertisement_data) { - advertisements.push_back(advertisement_data); - scan_latch.CountDown(); - }}); - std::unique_ptr - advertising_session = server.StartAdvertising( - advert_data, PowerMode::kBalanced, - AdvertisingCallback{ - .start_advertising_result = [&](absl::Status status) { - advertise_latch.CountDown(); - }}); - - EXPECT_TRUE(advertise_latch.Await(kWaitDuration).result()); - EXPECT_TRUE(scan_latch.Await(kWaitDuration).result()); - EXPECT_OK(scanning_session->stop_scanning()); - EXPECT_OK(advertising_session->stop_advertising()); - ASSERT_FALSE(advertisements.empty()); - EXPECT_EQ(advertisements[0] - .service_data.find(kPresenceServiceUuid) - ->second.AsStringView(), - advert_data.content); - env_.Stop(); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/mediums/mediums.h b/presence/implementation/mediums/mediums.h deleted file mode 100644 index a47679da..00000000 --- a/presence/implementation/mediums/mediums.h +++ /dev/null @@ -1,41 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_MEDIUMS_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_MEDIUMS_H_ - -#include "internal/platform/bluetooth_adapter.h" -#include "presence/implementation/mediums/ble.h" - -namespace nearby { -namespace presence { - -/* - * This class owns medium instance like Ble and etc. And the instance of - * this class will be owned in {@code ServiceControllerImpl}. - */ -class Mediums { - public: - // Returns a handle to the Ble medium. - Ble& GetBle() { return ble_; } - - private: - nearby::BluetoothAdapter adapter_; - Ble ble_{adapter_}; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_MEDIUMS_H_ diff --git a/presence/implementation/mock_connection_authenticator.h b/presence/implementation/mock_connection_authenticator.h deleted file mode 100644 index 24e860eb..00000000 --- a/presence/implementation/mock_connection_authenticator.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2023 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 THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_CONNECTION_AUTHENTICATOR_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_CONNECTION_AUTHENTICATOR_H_ - -#include -#include - -#include "gmock/gmock.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "internal/proto/credential.pb.h" -#include "internal/proto/local_credential.pb.h" -#include "presence/implementation/connection_authenticator.h" - -namespace nearby { -namespace presence { - -/* - * This class is for unit tests, mocking {@code ConnectionAuthenticator} - * functions in `PresenceDeviceProviderTest`. - */ -class MockConnectionAuthenticator : public ConnectionAuthenticator { - public: - MOCK_METHOD(absl::StatusOr, BuildSignedMessageAsInitiator, - (absl::string_view ukey2_secret, - std::optional local_credential, - const internal::SharedCredential& shared_credential), - (const, override)); - MOCK_METHOD(absl::StatusOr, BuildSignedMessageAsResponder, - (absl::string_view ukey2_secret, - const internal::LocalCredential& local_credential), - (const, override)); - MOCK_METHOD( - absl::Status, VerifyMessageAsInitiator, - (ResponderData authentication_data, absl::string_view ukey2_secret, - const std::vector& shared_credentials), - (const, override)); - MOCK_METHOD( - absl::StatusOr, VerifyMessageAsResponder, - (absl::string_view ukey2_secret, InitiatorData initiator_data, - const std::vector& local_credentials, - const std::vector& shared_credentials), - (const, override)); -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_CONNECTION_AUTHENTICATOR_H_ diff --git a/presence/implementation/mock_credential_manager.h b/presence/implementation/mock_credential_manager.h deleted file mode 100644 index 64d6e848..00000000 --- a/presence/implementation/mock_credential_manager.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2023 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 THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_CREDENTIAL_MANAGER_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_CREDENTIAL_MANAGER_H_ - -#include -#include - -#include "gmock/gmock.h" -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "presence/implementation/credential_manager.h" - -namespace nearby { -namespace presence { - -class MockCredentialManager : public CredentialManager { - public: - MOCK_METHOD( - void, GenerateCredentials, - (const nearby::internal::DeviceIdentityMetaData& device_identity_metadata, - absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb), - (override)); - MOCK_METHOD(void, UpdateRemotePublicCredentials, - (absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& - remote_public_creds, - UpdateRemotePublicCredentialsCallback credentials_updated_cb), - (override)); - MOCK_METHOD(void, UpdateLocalCredential, - (const CredentialSelector& credential_selector, - nearby::internal::LocalCredential credential, - SaveCredentialsResultCallback result_callback), - (override)); - MOCK_METHOD(void, GetLocalCredentials, - (const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback), - (override)); - MOCK_METHOD(void, GetPublicCredentials, - (const CredentialSelector& credential_selector, - PublicCredentialType public_credential_type, - GetPublicCredentialsResultCallback callback), - (override)); - MOCK_METHOD(SubscriberId, SubscribeForPublicCredentials, - (const CredentialSelector& credential_selector, - PublicCredentialType public_credential_type, - GetPublicCredentialsResultCallback callback), - (override)); - MOCK_METHOD(void, UnsubscribeFromPublicCredentials, (SubscriberId id), - (override)); - MOCK_METHOD(std::string, DecryptDeviceIdentityMetaData, - (absl::string_view metadata_encryption_key, - absl::string_view key_seed, absl::string_view metadata_string), - (override)); - MOCK_METHOD( - void, SetDeviceIdentityMetaData, - (const ::nearby::internal::DeviceIdentityMetaData& - device_identity_metadata, - bool regen_credentials, absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb), - (override)); - MOCK_METHOD(::nearby::internal::DeviceIdentityMetaData, - GetDeviceIdentityMetaData, (), (override)); -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_CREDENTIAL_MANAGER_H_ diff --git a/presence/implementation/mock_service_controller.h b/presence/implementation/mock_service_controller.h deleted file mode 100644 index 90c3b31d..00000000 --- a/presence/implementation/mock_service_controller.h +++ /dev/null @@ -1,82 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_SERVICE_CONTROLLER_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_SERVICE_CONTROLLER_H_ - -#include -#include - -#include "gmock/gmock.h" -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "presence/implementation/service_controller.h" - -namespace nearby { -namespace presence { - -/* - * This class is for unit test, mocking {@code ServiceController} functions. - */ -class MockServiceController : public ServiceController { - public: - MockServiceController() = default; - ~MockServiceController() override = default; - - MOCK_METHOD(absl::StatusOr, StartScan, - (ScanRequest scan_request, ScanCallback callback), (override)); - MOCK_METHOD(void, StopScan, (ScanSessionId session_id), (override)); - MOCK_METHOD(absl::StatusOr, StartBroadcast, - (BroadcastRequest broadcast_request, BroadcastCallback callback), - (override)); - MOCK_METHOD(void, StopBroadcast, (BroadcastSessionId session_id), (override)); - MOCK_METHOD( - void, UpdateLocalDeviceMetadata, - (const ::nearby::internal::Metadata& metadata, bool regen_credentials, - absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb), - (override)); - MOCK_METHOD( - void, UpdateDeviceIdentityMetaData, - (const ::nearby::internal::DeviceIdentityMetaData& - device_identity_metadata, - bool regen_credentials, absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb), - (override)); - MOCK_METHOD(::nearby::internal::DeviceIdentityMetaData, - GetDeviceIdentityMetaData, (), (override)); - MOCK_METHOD(void, GetLocalPublicCredentials, - (const CredentialSelector& credential_selector, - GetPublicCredentialsResultCallback callback), - (override)); - MOCK_METHOD(void, UpdateRemotePublicCredentials, - (absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& - remote_public_creds, - UpdateRemotePublicCredentialsCallback credentials_updated_cb), - (override)); - MOCK_METHOD(void, GetLocalCredentials, - (const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback), - (override)); -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MOCK_SERVICE_CONTROLLER_H_ diff --git a/presence/implementation/np_ldt.h b/presence/implementation/np_ldt.h deleted file mode 100644 index 9047c12a..00000000 --- a/presence/implementation/np_ldt.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_NP_LDT_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_NP_LDT_H_ - -// 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. -// C API for Rust implementation of LDT [1], tailored to Nearby Presence's -// BLE 4.2 legacy format advertisement parsing usecase. -// -// [1] https://eprint.iacr.org/2017/841.pdf - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -// Individual encrypt/decrypt API, useful when creating advertisements or when -// decrypting advertisements from a known origin - -// The allocated handle to use for encryption -typedef struct { - uint64_t handle; -} NpLdtEncryptHandle; - -// The allocated handle to use for decryption -typedef struct { - uint64_t handle; -} NpLdtDecryptHandle; - -// Key material from the Nearby Presence credential from which keys will be -// derived. -typedef struct { - uint8_t bytes[32]; -} NpLdtKeySeed; - -typedef struct { - uint8_t bytes[32]; -} NpMetadataKeyHmac; - -typedef struct { - uint8_t bytes[2]; -} NpLdtSalt; - -// Possible result codes returned from the LDT NP API's -typedef enum { - // Call to api was succesful - NP_LDT_SUCCESS = 0, - // Payload of invalid length was provided must be >= 16 and <=31 bytes - NP_LDT_ERROR_INVALID_LENGTH = -1, - // The provided metadata hmac did not match the calculated hmac on call to - // decrypt and verify - NP_LDT_ERROR_MAC_MISMATCH = -2, -} NP_LDT_RESULT; - -// Allocate an LDT-XTS-AES128 Decryption cipher using the "swap" mix function. -// -// `key_seed` is the key material from the Nearby Presence credential from which -// the LDT key will be derived. -// 'hmac_tag' is the hmac auth tag calculated on the metadata key used to verify -// decryption was successful -// -// Returns 0 on error, or a non-zero handle on success. -NpLdtDecryptHandle NpLdtDecryptCreate(NpLdtKeySeed key_seed, - NpMetadataKeyHmac hmac_tag); - -// Allocate an LDT-XTS-AES128 Encryption cipher using the "swap" mix function. -// -// `key_seed` is the key material from the Nearby Presence credential from which -// the LDT key will be derived. -// -// Returns 0 on error, or a non-zero handle on success. -NpLdtEncryptHandle NpLdtEncryptCreate(NpLdtKeySeed key_seed); - -// Release allocated resources for an NpLdtEncryptHandle -// -// Returns 0 on success or an NP_LDT_RESULT error code on failure -NP_LDT_RESULT NpLdtEncryptClose(NpLdtEncryptHandle handle); - -// Release allocated resources for an NpLdtDecryptHandle -// -// Returns 0 on success or an NP_LDT_RESULT error code on failure -NP_LDT_RESULT NpLdtDecryptClose(NpLdtDecryptHandle handle); - -// Encrypt a 16-31 byte buffer in-place. -// -// `buffer` is a pointer to a 16-31 byte plaintext, with length in `buffer_len`. -// `salt` is the big-endian 2 byte salt that will be used in the Nearby -// Presence advertisement, which will be incorporated into the tweaks LDT uses -// while encrypting. -// -// Returns 0 on success, in which case `buffer` will now contain ciphertext. -// Returns an NP_LDT_RESULT error code on failure -NP_LDT_RESULT NpLdtEncrypt(NpLdtEncryptHandle handle, uint8_t* buffer, - size_t buffer_len, NpLdtSalt salt); - -// Decrypt a 16-31 byte buffer in-place. -// -// `buffer` is a pointer to a 16-31 byte ciphertext, with length in -// `buffer_len`. -// `salt` is the big-endian 2 byte salt found in the Nearby Presence -// advertisement, which will be incorporated into the tweaks LDT uses while -// decrypting. -// -// Returns 0 on success, in which case `buffer` will now contain plaintext. -// Returns an NP_LDT_RESULT error code on failure -NP_LDT_RESULT NpLdtDecryptAndVerify(NpLdtDecryptHandle handle, uint8_t* buffer, - size_t buffer_len, NpLdtSalt salt); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_NP_LDT_H_ diff --git a/presence/implementation/scan_manager.cc b/presence/implementation/scan_manager.cc deleted file mode 100644 index 3f6d7e03..00000000 --- a/presence/implementation/scan_manager.cc +++ /dev/null @@ -1,280 +0,0 @@ -// 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/scan_manager.h" - -#include - -#include -#include -#include -#include -#include - -#include "absl/base/thread_annotations.h" -#include "absl/status/status.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "internal/platform/future.h" -#include "internal/platform/implementation/ble.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "internal/platform/implementation/crypto.h" -#include "internal/platform/logging.h" -#include "presence//implementation/advertisement_filter.h" -#include "presence/data_element.h" -#include "presence/data_types.h" -#include "presence/device_motion.h" -#include "presence/implementation/advertisement_decoder.h" -#include "presence/implementation/mediums/ble.h" -#include "presence/presence_action.h" -#include "presence/presence_device.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { - -namespace { -using BleAdvertisementData = ::nearby::api::ble::BleAdvertisementData; -using BlePeripheral = ::nearby::api::ble::BlePeripheral; -using ScanningSession = ::nearby::api::ble::BleMedium::ScanningSession; -using ScanningCallback = ::nearby::api::ble::BleMedium::ScanningCallback; -} // namespace - -ScanSessionId ScanManager::StartScan(ScanRequest scan_request, - ScanCallback cb) { - ScanSessionId id = nearby::RandData(); - RunOnServiceControllerThread( - "start-scan", - [this, id, scan_request, - scan_callback = - std::move(cb)]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) mutable { - ScanningCallback callback = ScanningCallback{ - .start_scanning_result = - [start_scan_client = std::move(scan_callback.start_scan_cb)]( - absl::Status ble_status) mutable { - start_scan_client(ble_status); - }, - .advertisement_found_cb = - [this, id](BlePeripheral::UniqueId peripheral_id, - BleAdvertisementData data) { - RunOnServiceControllerThread( - "notify-found-ble", - [this, id, data = std::move(data), peripheral_id]() - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { - NotifyFoundBle(id, data, peripheral_id); - }); - }, - .advertisement_lost_cb = - [this, id](BlePeripheral::UniqueId peripheral_id) { - RunOnServiceControllerThread( - "notify-lost-ble", - [this, id, peripheral_id]() ABSL_EXCLUSIVE_LOCKS_REQUIRED( - *executor_) { NotifyLostBle(id, peripheral_id); }); - }}; - FetchCredentials(id, scan_request); - scan_sessions_.insert( - {id, ScanSessionState{ - .request = scan_request, - .callback = std::move(scan_callback), - .decoder = AdvertisementDecoderImpl(), - .advertisement_filter = AdvertisementFilter(scan_request), - .scanning_session = mediums_->GetBle().StartScanning( - scan_request, std::move(callback))}}); - }); - return id; -} - -void ScanManager::StopScan(ScanSessionId id) { - RunOnServiceControllerThread( - "stop-scan", [this, id]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { - auto it = scan_sessions_.find(id); - if (it == scan_sessions_.end()) { - return; - } - if (it->second.scanning_session) { - absl::Status status = it->second.scanning_session->stop_scanning(); - if (!status.ok()) { - LOG(WARNING) << "StopScan error: " << status; - } - } - scan_sessions_.erase(it); - }); -} - -void ScanManager::NotifyFoundBle(ScanSessionId id, BleAdvertisementData data, - BlePeripheral::UniqueId peripheral_id) { - auto it = scan_sessions_.find(id); - if (it == scan_sessions_.end()) { - return; - } - - auto advertisement_data = - data.service_data[kPresenceServiceUuid].AsStringView(); - - auto advert = it->second.decoder.DecodeAdvertisement(advertisement_data); - if (!advert.ok()) { - // This advertisement is not relevant to the current element, skip. - return; - } - - std::string remote_address = absl::StrCat(absl::Hex(peripheral_id)); - if (it->second.advertisement_filter.MatchesScanFilter(*advert)) { - internal::DeviceIdentityMetaData device_identity_metadata; - device_identity_metadata.set_bluetooth_mac_address(remote_address); - - if (!device_unique_id_to_endpoint_id_map_.contains(peripheral_id)) { - PresenceDevice device(DeviceMotion(), device_identity_metadata, - advert->identity_type); - // Ok if the advertisement is for trusted/private identity. - if (advert->public_credential.ok()) { - device.SetDecryptSharedCredential(*(advert->public_credential)); - } - device.AddExtendedProperties(advert->data_elements); - for (const auto& data_element : advert->data_elements) { - if (data_element.GetType() == DataElement::kActionFieldType) { - device.AddAction(PresenceAction(static_cast( - static_cast(data_element.GetValue()[0])))); - } - } - - device_unique_id_to_endpoint_id_map_.emplace(peripheral_id, - device.GetEndpointId()); - - it->second.callback.on_discovered_cb(std::move(device)); - } else { - PresenceDevice device( - device_unique_id_to_endpoint_id_map_.at(peripheral_id)); - device.SetDeviceIdentityMetaData(device_identity_metadata); - // Ok if the advertisement is for trusted/private identity. - if (advert->public_credential.ok()) { - device.SetDecryptSharedCredential(*(advert->public_credential)); - } - device.AddExtendedProperties(advert->data_elements); - for (const auto& data_element : advert->data_elements) { - if (data_element.GetType() == DataElement::kActionFieldType) { - device.AddAction(PresenceAction(static_cast( - static_cast(data_element.GetValue()[0])))); - } - } - - it->second.callback.on_updated_cb(std::move(device)); - } - } -} - -void ScanManager::NotifyLostBle(ScanSessionId id, - BlePeripheral::UniqueId peripheral_id) { - auto it = scan_sessions_.find(id); - if (it == scan_sessions_.end()) { - return; - } - - std::string remote_address = absl::StrCat(absl::Hex(peripheral_id)); - if (device_unique_id_to_endpoint_id_map_.contains(peripheral_id)) { - internal::DeviceIdentityMetaData device_identity_metadata; - device_identity_metadata.set_bluetooth_mac_address( - std::string(remote_address)); - PresenceDevice device( - device_unique_id_to_endpoint_id_map_.at(peripheral_id)); - device.SetDeviceIdentityMetaData(device_identity_metadata); - - device_unique_id_to_endpoint_id_map_.erase(peripheral_id); - - it->second.callback.on_lost_cb(std::move(device)); - } -} - -std::vector GetCredentialSelectors( - const ScanRequest& scan_request) { - std::vector all_types = { - nearby::internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP, - nearby::internal::IdentityType::IDENTITY_TYPE_CONTACTS_GROUP, - nearby::internal::IdentityType::IDENTITY_TYPE_PUBLIC}; - std::vector selectors; - for (auto identity_type : - (scan_request.identity_types.empty() ? all_types - : scan_request.identity_types)) { - selectors.push_back( - CredentialSelector{.manager_app_id = scan_request.manager_app_id, - .account_name = scan_request.account_name, - .identity_type = identity_type}); - } - return selectors; -} - -void ScanManager::FetchCredentials(ScanSessionId id, - const ScanRequest& scan_request) { - std::vector credential_selectors = - GetCredentialSelectors(scan_request); - for (const CredentialSelector& selector : credential_selectors) { - // Not fetching for PUBLIC. - if (selector.identity_type == internal::IDENTITY_TYPE_UNSPECIFIED || - selector.identity_type == internal::IDENTITY_TYPE_PUBLIC) { - LOG(INFO) << __func__ << ": skip feteching creds for identity type: " - << selector.identity_type; - continue; - } - credential_manager_->GetPublicCredentials( - selector, PublicCredentialType::kRemotePublicCredential, - {.credentials_fetched_cb = - [this, id, identity_type = selector.identity_type]( - absl::StatusOr< - std::vector<::nearby::internal::SharedCredential>> - credentials) { - if (!credentials.ok()) { - LOG(WARNING) - << "Failed to fetch credentials: " << credentials.status(); - return; - } - RunOnServiceControllerThread( - "update-credentials", - [this, id, identity_type, - credentials = std::move(*credentials)]() - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { - UpdateCredentials(id, identity_type, - std::move(credentials)); - }); - }}); - } -} - -void ScanManager::UpdateCredentials(ScanSessionId id, - IdentityType identity_type, - std::vector credentials) { - // Credentials should never get fetched for PUBLIC of No-Identity requests - assert(identity_type != internal::IDENTITY_TYPE_UNSPECIFIED); - assert(identity_type != internal::IDENTITY_TYPE_PUBLIC); - - auto it = scan_sessions_.find(id); - - if (it == scan_sessions_.end()) { - return; - } - - ScanSessionState& session = it->second; - session.credentials[identity_type] = std::move(credentials); - session.decoder = AdvertisementDecoderImpl(&session.credentials); -} - -int ScanManager::ScanningCallbacksLengthForTest() { - ::nearby::Future count; - RunOnServiceControllerThread("callbacks-size", - [&]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { - count.Set(scan_sessions_.size()); - }); - return count.Get().GetResult(); -} - -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/scan_manager.h b/presence/implementation/scan_manager.h deleted file mode 100644 index a174884f..00000000 --- a/presence/implementation/scan_manager.h +++ /dev/null @@ -1,109 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SCAN_MANAGER_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SCAN_MANAGER_H_ - -#include -#include -#include -#include - -#include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/ble.h" -#include "internal/platform/mutex.h" -#include "internal/platform/mutex_lock.h" -#include "internal/platform/runnable.h" -#include "internal/platform/single_thread_executor.h" -#include "internal/proto/credential.pb.h" -#include "presence/data_types.h" -#include "presence/implementation/advertisement_filter.h" -#include "presence/implementation/credential_manager.h" -#include "presence/implementation/mediums/mediums.h" -#include "presence/scan_request.h" - -#ifdef USE_RUST_DECODER -#include "presence/implementation/advertisement_decoder_rust_impl.h" -#else -#include "presence/implementation/advertisement_decoder_impl.h" -#endif - -namespace nearby { -namespace presence { - -// The instance of ScanManager is owned by `ServiceControllerImpl`. -// Helping service controller to manage scan requests and callbacks. -class ScanManager { - public: - using SingleThreadExecutor = ::nearby::SingleThreadExecutor; - using Mutex = ::nearby::Mutex; - using MutexLock = ::nearby::MutexLock; - using ScanningSession = ::nearby::api::ble::BleMedium::ScanningSession; - using Runnable = ::nearby::Runnable; - using BleAdvertisementData = ::nearby::api::ble::BleAdvertisementData; - using SharedCredential = ::nearby::internal::SharedCredential; - using IdentityType = ::nearby::internal::IdentityType; - - ScanManager(Mediums& mediums, CredentialManager& credential_manager, - SingleThreadExecutor& executor) { - mediums_ = &mediums, credential_manager_ = &credential_manager; - executor_ = &executor; - } - ~ScanManager() = default; - - ScanSessionId StartScan(ScanRequest scan_request, ScanCallback cb); - void StopScan(ScanSessionId session_id); - // Below functions are test only. - // Reference: go/totw/135#augmenting-the-public-api-for-tests - int ScanningCallbacksLengthForTest(); - - private: - struct ScanSessionState { - ScanRequest request; - ScanCallback callback; - absl::flat_hash_map> - credentials; - AdvertisementDecoderImpl decoder; - AdvertisementFilter advertisement_filter; - std::unique_ptr scanning_session; - }; - void NotifyFoundBle(ScanSessionId id, BleAdvertisementData data, - nearby::api::ble::BlePeripheral::UniqueId peripheral_id) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); - void NotifyLostBle(ScanSessionId id, - nearby::api::ble::BlePeripheral::UniqueId peripheral_id) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); - void FetchCredentials(ScanSessionId id, const ScanRequest& scan_request) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); - void UpdateCredentials(ScanSessionId id, IdentityType identity_type, - std::vector credentials) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); - void RunOnServiceControllerThread(absl::string_view name, Runnable runnable) { - executor_->Execute(std::string(name), std::move(runnable)); - } - Mediums* mediums_; - CredentialManager* credential_manager_; - absl::flat_hash_map scan_sessions_ - ABSL_GUARDED_BY(*executor_); - absl::flat_hash_map - device_unique_id_to_endpoint_id_map_ ABSL_GUARDED_BY(*executor_); - SingleThreadExecutor* executor_; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SCAN_MANAGER_H_ diff --git a/presence/implementation/scan_manager_test.cc b/presence/implementation/scan_manager_test.cc deleted file mode 100644 index 4ddee9ee..00000000 --- a/presence/implementation/scan_manager_test.cc +++ /dev/null @@ -1,453 +0,0 @@ -// 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/scan_manager.h" - -#include -#include -#include -#include -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/strings/escaping.h" -#include "absl/strings/str_cat.h" -#include "absl/types/variant.h" -#include "internal/platform/bluetooth_adapter.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/implementation/ble.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "internal/platform/logging.h" -#include "internal/platform/mac_address.h" -#include "internal/platform/medium_environment.h" -#include "internal/platform/single_thread_executor.h" -#include "internal/proto/credential.proto.h" -#include "presence/data_element.h" -#include "presence/data_types.h" -#include "presence/implementation/credential_manager_impl.h" -#include "presence/implementation/mediums/advertisement_data.h" -#include "presence/implementation/mediums/ble.h" -#include "presence/implementation/mediums/mediums.h" -#include "presence/implementation/mock_credential_manager.h" -#include "presence/power_mode.h" -#include "presence/presence_action.h" -#include "presence/presence_device.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { -namespace { - -using AdvertisingSession = ::nearby::api::ble::BleMedium::AdvertisingSession; -using AdvertisingCallback = - ::nearby::api::ble::BleMedium::AdvertisingCallback; -using ::nearby::SingleThreadExecutor; - -using CountDownLatch = ::nearby::CountDownLatch; -using ::testing::Contains; - -class ScanManagerTest : public testing::Test { - protected: - void SetUp() override { env_.Start(); } - void TearDown() override { - executor_.Shutdown(); - env_.Stop(); - } - - std::unique_ptr StartAdvertisingOn(Ble& ble) { - auto advertisement = AdvertisementData{ - .is_extended_advertisement = false, - .content = {0x00, 0x26, 0x00, 0x40}, - }; - std::unique_ptr session = ble.StartAdvertising( - advertisement, PowerMode::kLowPower, - AdvertisingCallback{.start_advertising_result = [](absl::Status) {}}); - env_.Sync(); - return session; - } - - ScanRequest MakeDefaultScanRequest() { - std::vector> - filters = {PresenceScanFilter{ - .scan_type = ScanType::kPresenceScan, - .extended_properties = MakeDefaultExtendedProperties(), - }}; - return { - .account_name = "Test account", - .identity_types = MakeDefaultIdentityTypes(), - .scan_filters = filters, - .use_ble = true, - .scan_type = ScanType::kPresenceScan, - .power_mode = PowerMode::kBalanced, - .scan_only_when_screen_on = true, - }; - } - - ScanCallback MakeDefaultScanCallback() { - return { - .start_scan_cb = - [this](absl::Status status) { - if (status.ok()) { - start_latch_.CountDown(); - } - }, - .on_discovered_cb = - [this](PresenceDevice pd) { found_latch_.CountDown(); }, - .on_updated_cb = - [this](PresenceDevice pd) { updated_latch_.CountDown(); }, - .on_lost_cb = [this](PresenceDevice pd) { lost_latch_.CountDown(); }}; - } - - std::vector MakeDefaultIdentityTypes() { - return { - nearby::internal::IdentityType::IDENTITY_TYPE_PUBLIC, - }; - } - std::vector MakeDefaultExtendedProperties() { - return {DataElement(ActionBit::kNearbyShareAction)}; - } - SingleThreadExecutor executor_; - CredentialManagerImpl credential_manager_{&executor_}; - nearby::MediumEnvironment& env_ = {nearby::MediumEnvironment::Instance()}; - CountDownLatch start_latch_{1}; - CountDownLatch found_latch_{1}; - CountDownLatch updated_latch_{1}; - CountDownLatch lost_latch_{1}; -}; - -TEST_F(ScanManagerTest, CanStartThenStopScanning) { - Mediums mediums; - ScanManager manager(mediums, credential_manager_, executor_); - // Set up advertiser - nearby::BluetoothAdapter server_adapter; - Ble ble2(server_adapter); - std::unique_ptr advertising_session = - StartAdvertisingOn(ble2); - - // Start scanning - ScanSessionId scan_session = - manager.StartScan(MakeDefaultScanRequest(), MakeDefaultScanCallback()); - EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 1); - EXPECT_TRUE(start_latch_.Await().Ok()); - EXPECT_TRUE(found_latch_.Await().Ok()); - manager.StopScan(scan_session); - EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); -} - -TEST_F(ScanManagerTest, CannotStopScanTwice) { - Mediums mediums; - ScanManager manager(mediums, credential_manager_, executor_); - - ScanSessionId scan_session = - manager.StartScan(MakeDefaultScanRequest(), MakeDefaultScanCallback()); - - LOG(INFO) << "Start scan"; - EXPECT_TRUE(start_latch_.Await().Ok()); - // Ensure that we have started scanning before we try to stop. - env_.Sync(); - LOG(INFO) << "Stop scan"; - manager.StopScan(scan_session); - EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); - LOG(INFO) << "Stop scan again"; - manager.StopScan(scan_session); - EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); -} - -TEST_F(ScanManagerTest, TestNoFilter) { - Mediums mediums; - ScanManager manager(mediums, credential_manager_, executor_); - // Set up advertiser - nearby::BluetoothAdapter server_adapter; - Ble ble2(server_adapter); - std::unique_ptr advertising_session = - StartAdvertisingOn(ble2); - - // Start scanning - ScanRequest scan_request_no_filter = MakeDefaultScanRequest(); - scan_request_no_filter.scan_filters.clear(); - ScanSessionId scan_session = - manager.StartScan(scan_request_no_filter, MakeDefaultScanCallback()); - - ASSERT_EQ(manager.ScanningCallbacksLengthForTest(), 1); - ASSERT_TRUE(mediums.GetBle().IsAvailable()); - EXPECT_TRUE(start_latch_.Await().Ok()); - EXPECT_TRUE(found_latch_.Await().Ok()); - manager.StopScan(scan_session); - EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); -} - -TEST_F(ScanManagerTest, PresenceMetadataIsRetained) { - Mediums mediums; - ScanManager manager(mediums, credential_manager_, executor_); - // Set up advertiser - nearby::BluetoothAdapter server_adapter; - Ble ble2(server_adapter); - std::unique_ptr advertising_session = - StartAdvertisingOn(ble2); - std::string address = - absl::StrCat(absl::Hex(server_adapter.GetAddress().address())); - ScanCallback callback = { - .start_scan_cb = - [this](absl::Status status) { - if (status.ok()) { - start_latch_.CountDown(); - } - }, - .on_discovered_cb = - [this, &address](PresenceDevice pd) { - if (pd.GetDeviceIdentityMetadata().bluetooth_mac_address() == - address) { - EXPECT_THAT(pd.GetExtendedProperties(), - Contains(DataElement(ActionBit::kNearbyShareAction)) - .Times(1)); - EXPECT_THAT( - pd.GetActions(), - Contains(PresenceAction{(int)ActionBit::kNearbyShareAction}) - .Times(1)); - - found_latch_.CountDown(); - } - }, - .on_updated_cb = - [this, &address](PresenceDevice pd) { - if (pd.GetDeviceIdentityMetadata().bluetooth_mac_address() == - address) { - EXPECT_THAT(pd.GetExtendedProperties(), - Contains(DataElement(ActionBit::kNearbyShareAction)) - .Times(1)); - EXPECT_THAT( - pd.GetActions(), - Contains(PresenceAction{(int)ActionBit::kNearbyShareAction}) - .Times(1)); - - updated_latch_.CountDown(); - } - }}; - // Start scanning - ScanRequest scan_request_no_filter = MakeDefaultScanRequest(); - scan_request_no_filter.scan_filters.clear(); - auto scan_session = - manager.StartScan(scan_request_no_filter, std::move(callback)); - - ASSERT_EQ(manager.ScanningCallbacksLengthForTest(), 1); - ASSERT_TRUE(mediums.GetBle().IsAvailable()); - EXPECT_TRUE(start_latch_.Await().Ok()); - EXPECT_TRUE(found_latch_.Await().Ok()); - - // Advertise again to trigger `on_updated_cb` - advertising_session = StartAdvertisingOn(ble2); - - EXPECT_TRUE(updated_latch_.Await().Ok()); - manager.StopScan(scan_session); - EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); -} - -TEST_F(ScanManagerTest, DiscoverThenLoseAdvertisement) { - Mediums mediums; - ScanManager manager(mediums, credential_manager_, executor_); - // Set up advertiser - nearby::BluetoothAdapter server_adapter; - Ble ble2(server_adapter); - std::unique_ptr advertising_session = - StartAdvertisingOn(ble2); - - // Start scanning - ScanSessionId scan_session = - manager.StartScan(MakeDefaultScanRequest(), MakeDefaultScanCallback()); - - EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 1); - EXPECT_TRUE(start_latch_.Await().Ok()); - EXPECT_TRUE(found_latch_.Await().Ok()); - - // Stop advertising to trigger `on_lost_cb` - EXPECT_OK(advertising_session->stop_advertising()); - env_.Sync(); - - EXPECT_TRUE(lost_latch_.Await().Ok()); - manager.StopScan(scan_session); - EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); -} - -TEST_F(ScanManagerTest, StopOneSessionFromAnotherDeadlock) { - Mediums mediums; - ScanManager manager(mediums, credential_manager_, executor_); - CountDownLatch start_latch2{1}; - CountDownLatch found_latch2{1}; - - // Start scanning - std::vector extended_properties_mismatch = { - DataElement(ActionBit::kInstantTetheringAction)}; - std::vector> - mismatch_filters = {PresenceScanFilter{ - .scan_type = ScanType::kPresenceScan, - .extended_properties = extended_properties_mismatch, - }}; - ScanRequest scan_request_mismatch = { - .account_name = "Test account", - .identity_types = MakeDefaultIdentityTypes(), - .scan_filters = mismatch_filters, - .use_ble = true, - .scan_type = ScanType::kPresenceScan, - .power_mode = PowerMode::kBalanced, - .scan_only_when_screen_on = true, - }; - // we use scan_request_mismatch so this session's discovery doesn't get - // triggered. - ScanSessionId scan_session = - manager.StartScan(scan_request_mismatch, MakeDefaultScanCallback()); - ScanCallback scanning_callback2 = {.start_scan_cb = - [&](absl::Status status) { - if (status.ok()) { - start_latch2.CountDown(); - } - }, - .on_discovered_cb = - [&](PresenceDevice pd) { - LOG(INFO) << "scansession2 found"; - found_latch2.CountDown(); - manager.StopScan(scan_session); - }}; - ScanSessionId scan_session2 = manager.StartScan( - MakeDefaultScanRequest(), std::move(scanning_callback2)); - - ASSERT_EQ(manager.ScanningCallbacksLengthForTest(), 2); - - // Set up advertiser - nearby::BluetoothAdapter server_adapter; - Ble ble2(server_adapter); - std::unique_ptr advertising_session = - StartAdvertisingOn(ble2); - - EXPECT_TRUE(found_latch2.Await().Ok()); - // Session was stopped before, this should not be able to stop successfully. - manager.StopScan(scan_session); - EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 1); - ASSERT_TRUE(mediums.GetBle().IsAvailable()); - manager.StopScan(scan_session2); - EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); -} - -// Receive a BLE advertisement after StopScan. `on_discovered_cb` -// must not be called. -TEST_F(ScanManagerTest, NoDeviceFoundAfterStopScan) { - Mediums mediums; - ScanManager manager(mediums, credential_manager_, executor_); - CountDownLatch start_scan_latch{1}; - nearby::BluetoothAdapter server_adapter; - Ble ble2(server_adapter); - std::atomic_bool stopped = false; - ScanSessionId scan_session = manager.StartScan( - MakeDefaultScanRequest(), - ScanCallback{.start_scan_cb = - [&start_scan_latch](absl::Status status) { - if (status.ok()) { - start_scan_latch.CountDown(); - } - }, - .on_discovered_cb = - [&](PresenceDevice pd) { EXPECT_FALSE(stopped); }}); - - start_scan_latch.Await(); - manager.StopScan(scan_session); - stopped = true; - std::unique_ptr advertising_session = - StartAdvertisingOn(ble2); - - EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); - executor_.Shutdown(); -} - -internal::SharedCredential GetPublicCredential() { - // Values copied from LDT tests - ByteArray seed({ - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - }); - ByteArray known_mac({0x09, 0xFE, 0x9E, 0x81, 0xB7, 0x3E, 0x5E, 0xCC, - 0x76, 0x59, 0x57, 0x71, 0xE0, 0x1F, 0xFB, 0x34, - 0x38, 0xE7, 0x5F, 0x24, 0xA7, 0x69, 0x56, 0xA0, - 0xB8, 0xEA, 0x67, 0xD1, 0x1C, 0x3E, 0x36, 0xFD}); - internal::SharedCredential public_credential; - public_credential.set_key_seed(seed.AsStringView()); - public_credential.set_metadata_encryption_key_tag_v0( - known_mac.AsStringView()); - return public_credential; -} - -std::vector BuildSharedCredentials() { - return {GetPublicCredential()}; -} - -TEST_F(ScanManagerTest, ScanningE2EWithEncryptedAdvertisementAndCredentials) { - Mediums mediums; - auto mock_credential_manager = MockCredentialManager(); - EXPECT_CALL(mock_credential_manager, GetPublicCredentials) - .WillOnce([&](const CredentialSelector& credential_selector, - PublicCredentialType public_credential_type, - GetPublicCredentialsResultCallback callback) { - callback.credentials_fetched_cb(BuildSharedCredentials()); - }); - ScanManager manager(mediums, mock_credential_manager, executor_); - - // Set up advertiser to broadcast a private identity adv - nearby::BluetoothAdapter server_adapter; - Ble ble2(server_adapter); - std::string V0AdvEncryptedBytes = "042222D82212EF16DBF872F2A3A7C0FA5248EC"; - std::string payload = absl::HexStringToBytes(V0AdvEncryptedBytes); - auto advertisement = AdvertisementData{ - .is_extended_advertisement = false, - .content = payload, - }; - - std::unique_ptr session = ble2.StartAdvertising( - advertisement, PowerMode::kLowPower, - AdvertisingCallback{.start_advertising_result = [](absl::Status) {}}); - env_.Sync(); - - std::vector< - absl::variant> // NOLINT - filters = {PresenceScanFilter{ - .scan_type = ScanType::kPresenceScan, - .extended_properties = {DataElement(DataElement::kTxPowerFieldType, - 3)}, - }}; - - ScanRequest scan_request = { - .account_name = "Test account", - .identity_types = - {nearby::internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP}, - .scan_filters = filters, - .use_ble = true, - .scan_type = ScanType::kPresenceScan, - .power_mode = PowerMode::kBalanced, - .scan_only_when_screen_on = true, - }; - - // Start scanning - ScanSessionId scan_session = - manager.StartScan(scan_request, MakeDefaultScanCallback()); - EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 1); - EXPECT_TRUE(start_latch_.Await().Ok()); - EXPECT_TRUE(found_latch_.Await().Ok()); - manager.StopScan(scan_session); - EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/sensor_fusion.h b/presence/implementation/sensor_fusion.h deleted file mode 100644 index 31639bd7..00000000 --- a/presence/implementation/sensor_fusion.h +++ /dev/null @@ -1,153 +0,0 @@ -// 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 THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SENSOR_FUSION_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SENSOR_FUSION_H_ - -#include -#include -#include -#include - -#include "absl/functional/any_invocable.h" -#include "absl/status/status.h" -#include "presence/device_motion.h" -#include "presence/presence_zone.h" - -namespace nearby { -namespace presence { - -enum class DataSource { - kUnknown = 0, - kBle = 1, - kUwb = 2, - kNanRtt = 4, -}; - -struct RangingMeasurement { - // [0.0, 1.0], 1.0 is the max confidence. - float confidence_level; - float value; -}; - -struct RangingPosition { - RangingMeasurement distance; - std::optional azimuth; - std::optional elevation; - uint64_t elapsed_realtime_millis; -}; - -struct ZoneTransition { - PresenceZone::DistanceBoundary::RangeType distance_range_type; - float confidence_level; -}; - -struct RangingData { - DataSource data_source; - RangingPosition position; - std::optional zone_transition; - std::vector device_motions; -}; - -struct ZoneTransitionCallback { - absl::AnyInvocable - on_proximity_zone_changed = - [](uint64_t device_id, - PresenceZone::DistanceBoundary::RangeType proximity_zone) {}; - absl::AnyInvocable on_callback_id_generated = - [](uint64_t callback_id) {}; -}; - -class SensorFusion { - public: - virtual ~SensorFusion() = default; - - // Called when a device motion gesture is detected. - typedef std::function - DeviceMotionCallback; - - /** - * Returns a list of data sources would be used by the sensor fusion if they - * are available. - * This is to control what kinds of sources the NP scan engine should use for - * ranging. For instance, if both NAN RTT and UWB are supported, FPP may - * decide NAN RTT isn't useful at a certain moment, so NP scan engine won't - * try to request NAN RTT. - * - * @param elapsed_realtime_millis Elapsed timestamp since boot of the data - * source query. - * @param available_sources A bit mask of data sources that are available. - */ - virtual std::vector GetDataSources( - uint64_t elapsed_realtime_millis, - const std::vector& available_sources) = 0; - - /** - * Updates BLE scanned results to Sensor Fusion. - * - * @param device_id A unique device id of the peer device. - * @param txPower Calibrated TX power of the scan result, {@code - * std::nullopt} if the calibrated TX power is not available. - * @param rssi Received signal strength indicator for the scan result. - * @param elapsed_realtime_millis Elapsed timestamp since boot when the - * scan result is discovered. - */ - virtual absl::Status UpdateBleScanResult( - uint64_t device_id, std::optional txPower, int rssi, - uint64_t elapsed_realtime_millis) = 0; - /** - * Updates UWB ranging results to Sensor Fusion. - * - * @param device_id A unique device id of the peer device. - * @param position UWB ranging result (distance and optionally angle) - */ - virtual void UpdateUwbRangingResult(uint64_t device_id, - RangingPosition position) = 0; - - /** - * Adds callback for updates of proximity zone transitions. - */ - virtual void RequestZoneTransitionUpdates( - ZoneTransitionCallback callback) = 0; - - /** - * Removes callback for updates of proximity zone transitions. - */ - virtual void RemoveZoneTransitionUpdates(uint64_t callback_id) = 0; - - /** - * Adds callback for updates of device motion events. - */ - virtual void RequestDeviceMotionUpdates(DeviceMotionCallback callback) = 0; - - /** - * Remove callback for updates of device motion events. - */ - virtual void RemoveDeviceMotionUpdates(DeviceMotionCallback callback) = 0; - - /** - * Returns the best ranging estimate to a given device. Returns {@code - * std::nullopt} if the sensor fusion cannot produce a ranging estimate. - * - * @param device_id Id of the peer device. - */ - virtual std::optional GetRangingData(uint64_t device_id) = 0; -}; - -} // namespace presence -} // namespace nearby -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SENSOR_FUSION_H_ - diff --git a/presence/implementation/service_controller.h b/presence/implementation/service_controller.h deleted file mode 100644 index 9389741e..00000000 --- a/presence/implementation/service_controller.h +++ /dev/null @@ -1,76 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SERVICE_CONTROLLER_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SERVICE_CONTROLLER_H_ - -#include - -#include "absl/status/statusor.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "internal/proto/metadata.pb.h" -#include "presence/broadcast_request.h" -#include "presence/data_types.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { - -/* - * This class is owned in {@code PresenceService}. It specifies the function - * signatures. {@code ServiceControllerImpl} and {@code MockServiceController} - * inherit this class and provides real implementation and mock impl for tests. - */ -class ServiceController { - public: - ServiceController() = default; - virtual ~ServiceController() = default; - virtual absl::StatusOr StartScan(ScanRequest scan_request, - ScanCallback callback) = 0; - virtual void StopScan(ScanSessionId session_id) = 0; - virtual absl::StatusOr StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback) = 0; - virtual void StopBroadcast(BroadcastSessionId session_id) = 0; - virtual void UpdateLocalDeviceMetadata( - const ::nearby::internal::Metadata& metadata, bool regen_credentials, - absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) = 0; - virtual void UpdateDeviceIdentityMetaData( - const ::nearby::internal::DeviceIdentityMetaData& - device_identity_metadata, - bool regen_credentials, absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) = 0; - virtual ::nearby::internal::DeviceIdentityMetaData - GetDeviceIdentityMetaData() = 0; - virtual void GetLocalPublicCredentials( - const CredentialSelector& credential_selector, - GetPublicCredentialsResultCallback callback) = 0; - virtual void UpdateRemotePublicCredentials( - absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& - remote_public_creds, - UpdateRemotePublicCredentialsCallback credentials_updated_cb) = 0; - virtual void GetLocalCredentials( - const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback) = 0; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SERVICE_CONTROLLER_H_ diff --git a/presence/implementation/service_controller_impl.cc b/presence/implementation/service_controller_impl.cc deleted file mode 100644 index 3c158fc6..00000000 --- a/presence/implementation/service_controller_impl.cc +++ /dev/null @@ -1,91 +0,0 @@ -// 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 "presence/implementation/service_controller_impl.h" - -#include -#include - -#include "absl/status/statusor.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "presence/data_types.h" -#include "presence/implementation/credential_manager.h" - -namespace nearby { -namespace presence { - -absl::StatusOr ServiceControllerImpl::StartScan( - ScanRequest scan_request, ScanCallback callback) { - return scan_manager_.StartScan(scan_request, std::move(callback)); -} -void ServiceControllerImpl::StopScan(ScanSessionId id) { - scan_manager_.StopScan(id); -} - -absl::StatusOr ServiceControllerImpl::StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback) { - return broadcast_manager_.StartBroadcast(broadcast_request, - std::move(callback)); -} - -void ServiceControllerImpl::StopBroadcast(BroadcastSessionId id) { - broadcast_manager_.StopBroadcast(id); -} - -// TODO(b/327629276): Remove this function. -void ServiceControllerImpl::UpdateLocalDeviceMetadata( - const ::nearby::internal::Metadata& metadata, bool regen_credentials, - absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) {} - -void ServiceControllerImpl::UpdateDeviceIdentityMetaData( - const ::nearby::internal::DeviceIdentityMetaData& device_identity_metadata, - bool regen_credentials, absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) { - credential_manager_.SetDeviceIdentityMetaData( - device_identity_metadata, regen_credentials, manager_app_id, - identity_types, credential_life_cycle_days, - contiguous_copy_of_credentials, std::move(credentials_generated_cb)); -} - -void ServiceControllerImpl::GetLocalPublicCredentials( - const CredentialSelector& credential_selector, - GetPublicCredentialsResultCallback callback) { - credential_manager_.GetPublicCredentials( - credential_selector, PublicCredentialType::kLocalPublicCredential, - std::move(callback)); -} - -void ServiceControllerImpl::UpdateRemotePublicCredentials( - absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& remote_public_creds, - UpdateRemotePublicCredentialsCallback credentials_updated_cb) { - credential_manager_.UpdateRemotePublicCredentials( - manager_app_id, account_name, remote_public_creds, - std::move(credentials_updated_cb)); -} - -void ServiceControllerImpl::GetLocalCredentials( - const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback) { - credential_manager_.GetLocalCredentials(credential_selector, - std::move(callback)); -} - -} // namespace presence -} // namespace nearby diff --git a/presence/implementation/service_controller_impl.h b/presence/implementation/service_controller_impl.h deleted file mode 100644 index b858b7b3..00000000 --- a/presence/implementation/service_controller_impl.h +++ /dev/null @@ -1,110 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SERVICE_CONTROLLER_IMPL_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SERVICE_CONTROLLER_IMPL_H_ - -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "internal/platform/runnable.h" -#include "internal/platform/single_thread_executor.h" -#include "internal/proto/metadata.pb.h" -#include "presence/broadcast_request.h" -#include "presence/data_types.h" -#include "presence/implementation/broadcast_manager.h" -#include "presence/implementation/credential_manager.h" -#include "presence/implementation/scan_manager.h" -#include "presence/implementation/service_controller.h" -#include "presence/scan_request.h" - -/* - * This class implements {@code ServiceController} functions. Owns mediums and - * other managers instances. - */ -namespace nearby { -namespace presence { - -class ServiceControllerImpl : public ServiceController { - public: - using SingleThreadExecutor = ::nearby::SingleThreadExecutor; - - ServiceControllerImpl(SingleThreadExecutor* executor, - CredentialManager* credential_manager, - ScanManager* scan_manager, - BroadcastManager* broadcast_manager) - : executor_(*executor), - credential_manager_(*credential_manager), - scan_manager_(*scan_manager), - broadcast_manager_(*broadcast_manager) {} - ~ServiceControllerImpl() override { executor_.Shutdown(); } - - absl::StatusOr StartScan(ScanRequest scan_request, - ScanCallback callback) override; - void StopScan(ScanSessionId session_id) override; - absl::StatusOr StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback) override; - void StopBroadcast(BroadcastSessionId) override; - void UpdateLocalDeviceMetadata( - const ::nearby::internal::Metadata& metadata, bool regen_credentials, - absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) override; - void UpdateDeviceIdentityMetaData( - const ::nearby::internal::DeviceIdentityMetaData& - device_identity_metadata, - bool regen_credentials, absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) override; - - ::nearby::internal::DeviceIdentityMetaData GetDeviceIdentityMetaData() - override { - return credential_manager_.GetDeviceIdentityMetaData(); - } - void GetLocalPublicCredentials( - const CredentialSelector& credential_selector, - GetPublicCredentialsResultCallback callback) override; - void UpdateRemotePublicCredentials( - absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& - remote_public_creds, - UpdateRemotePublicCredentialsCallback credentials_updated_cb) override; - void GetLocalCredentials(const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback) override; - - SingleThreadExecutor& GetBackgroundExecutor() { return executor_; } - - private: - void NotifyStartCallbackStatus(BroadcastSessionId id, absl::Status status); - void RunOnServiceControllerThread(absl::string_view name, Runnable runnable) { - executor_.Execute(std::string(name), std::move(runnable)); - } - - SingleThreadExecutor& executor_; - CredentialManager& credential_manager_; - ScanManager& scan_manager_; - BroadcastManager& broadcast_manager_; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SERVICE_CONTROLLER_IMPL_H_ diff --git a/presence/implementation/service_controller_impl_test.cc b/presence/implementation/service_controller_impl_test.cc deleted file mode 100644 index 5d638186..00000000 --- a/presence/implementation/service_controller_impl_test.cc +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2023 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 -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "internal/platform/single_thread_executor.h" -#include "internal/proto/credential.pb.h" -#include "presence/implementation/broadcast_manager.h" -#include "presence/implementation/mediums/mediums.h" -#include "presence/implementation/mock_credential_manager.h" -#include "presence/implementation/scan_manager.h" - -namespace nearby { -namespace presence { -namespace { - -constexpr absl::string_view kManagerAppId = "TEST_MANAGER_APP"; -constexpr absl::string_view kAccountName = "test account"; -constexpr absl::string_view kSecretId1 = "1111111"; -constexpr absl::string_view kSecretId2 = "2222222"; -constexpr absl::string_view kSecretId3 = "3333333"; - -CredentialSelector BuildDefaultCredentialSelector() { - CredentialSelector credential_selector; - credential_selector.manager_app_id = std::string(kManagerAppId); - credential_selector.account_name = std::string(kAccountName); - credential_selector.identity_type = - ::nearby::internal::IdentityType::IDENTITY_TYPE_PRIVATE_GROUP; - return credential_selector; -} - -std::vector BuildLocalCredentials() { - internal::LocalCredential local_credential1; - local_credential1.set_secret_id(kSecretId1); - internal::LocalCredential local_credential2; - local_credential2.set_secret_id(kSecretId2); - internal::LocalCredential local_credential3; - local_credential3.set_secret_id(kSecretId3); - return {local_credential1, local_credential2, local_credential3}; -} - -TEST(ServiceControllerImplTest, GetLocalCredentials) { - auto mock_credential_manager = std::make_unique(); - EXPECT_CALL(*mock_credential_manager.get(), GetLocalCredentials) - .WillOnce([&](const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback) { - callback.credentials_fetched_cb(BuildLocalCredentials()); - }); - - Mediums mediums; - SingleThreadExecutor executor; - ScanManager scan_manager{mediums, *mock_credential_manager, executor}; - BroadcastManager broadcast_manager{mediums, *mock_credential_manager, - executor}; - - auto service_controller = std::make_unique( - &executor, mock_credential_manager.get(), &scan_manager, - &broadcast_manager); - CredentialSelector credential_selector = BuildDefaultCredentialSelector(); - - absl::StatusOr> - private_credentials; - service_controller->GetLocalCredentials( - credential_selector, - {.credentials_fetched_cb = - [&](absl::StatusOr> - credentials) { - private_credentials = std::move(credentials); - }}); - - EXPECT_OK(private_credentials); - ASSERT_EQ(3u, private_credentials->size()); - ASSERT_EQ(private_credentials->at(0).secret_id(), kSecretId1); - ASSERT_EQ(private_credentials->at(1).secret_id(), kSecretId2); - ASSERT_EQ(private_credentials->at(2).secret_id(), kSecretId3); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/power_mode.h b/presence/power_mode.h deleted file mode 100644 index 229c59fd..00000000 --- a/presence/power_mode.h +++ /dev/null @@ -1,35 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_POWER_MODE_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_POWER_MODE_H_ - -namespace nearby { -namespace presence { - -// High level concept of Power mode for Scan and Broadcast. -// More frequent, more power consumption, but less interval and latency. -// Native platforms would decide the specific interval based on their own -// configs. -enum class PowerMode { - kNoPower = 0, - kLowPower = 1, - kBalanced = 2, - kLowLatency = 3, -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_POWER_MODE_H_ diff --git a/presence/presence_action.h b/presence/presence_action.h deleted file mode 100644 index a073f6b6..00000000 --- a/presence/presence_action.h +++ /dev/null @@ -1,41 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_ACTION_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_ACTION_H_ - -namespace nearby { -namespace presence { -class PresenceAction { - public: - PresenceAction(int action_identifier = 1); - int GetActionIdentifier() const; - - private: - static constexpr int kMinActionIdentifierValue = 1; - static constexpr int kMaxActionIdentifierValue = 255; - const int action_identifier_; -}; - -inline bool operator==(const PresenceAction& a1, const PresenceAction& a2) { - return a1.GetActionIdentifier() == a2.GetActionIdentifier(); -} - -inline bool operator!=(const PresenceAction& a1, const PresenceAction& a2) { - return !(a1 == a2); -} -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_ACTION_H_ diff --git a/presence/presence_action_test.cc b/presence/presence_action_test.cc deleted file mode 100644 index 64b5e78e..00000000 --- a/presence/presence_action_test.cc +++ /dev/null @@ -1,59 +0,0 @@ -// 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 "presence/presence_action.h" - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" - -namespace nearby { -namespace presence { -namespace { -constexpr int kDefaultActionIdentifier = 1; -constexpr int kTestActionIdentifier = 2; -TEST(PresenceActionTest, DefaultConstructorWorks) { - PresenceAction action; - EXPECT_EQ(action.GetActionIdentifier(), kDefaultActionIdentifier); -} - -TEST(PresenceActionTest, DefaultEquals) { - PresenceAction action1; - PresenceAction action2; - EXPECT_EQ(action1, action2); -} - -TEST(PresenceActionTest, ExplicitInitEquals) { - PresenceAction action1 = {kTestActionIdentifier}; - PresenceAction action2 = {kTestActionIdentifier}; - EXPECT_EQ(action1, action2); - EXPECT_EQ(action1.GetActionIdentifier(), kTestActionIdentifier); -} - -TEST(PresenceActionTest, ExplicitInitNotEquals) { - PresenceAction action1 = {kDefaultActionIdentifier}; - PresenceAction action2 = {kTestActionIdentifier}; - EXPECT_NE(action1, action2); -} - -TEST(PresenceActionTest, CopyInitEquals) { - PresenceAction action1 = {kTestActionIdentifier}; - PresenceAction action2 = {action1}; - - EXPECT_EQ(action1, action2); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/presence_advertiser_demo.cc b/presence/presence_advertiser_demo.cc deleted file mode 100644 index 19302e73..00000000 --- a/presence/presence_advertiser_demo.cc +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2026 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 - -#include -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/synchronization/notification.h" -#include "internal/platform/system_clock.h" -#include "internal/platform/single_thread_executor.h" -#include "presence/broadcast_request.h" -#include "presence/data_element.h" -#include "presence/implementation/broadcast_manager.h" -#include "presence/implementation/credential_manager.h" -#include "presence/implementation/mediums/mediums.h" -#include "presence/power_mode.h" - -namespace { - -absl::Notification g_shutdown; - -void QuitHandler(int, siginfo_t*, void*) { - if (!g_shutdown.HasBeenNotified()) g_shutdown.Notify(); -} - -void PrintUsage(const char* program) { - std::cerr - << "Usage: " << program << " [--name ] [--tx_power ]\n" - << "Example:\n" - << " " << program << " --name \"Linux Presence Demo\" --tx_power 20\n"; -} - -// For a public-identity broadcast demo, credentials are never requested. -class DemoCredentialManager : public nearby::presence::CredentialManager { - public: - void GenerateCredentials( - const nearby::internal::DeviceIdentityMetaData&, - absl::string_view, - const std::vector&, - int, - int, - nearby::presence::GenerateCredentialsResultCallback callback) override { - callback.credentials_generated_cb( - absl::UnimplementedError("Not used in public broadcast demo")); - } - - void UpdateRemotePublicCredentials( - absl::string_view, - absl::string_view, - const std::vector&, - nearby::presence::UpdateRemotePublicCredentialsCallback callback) - override { - callback.credentials_updated_cb( - absl::UnimplementedError("Not used in public broadcast demo")); - } - - void UpdateLocalCredential( - const nearby::presence::CredentialSelector&, - nearby::internal::LocalCredential, - nearby::presence::SaveCredentialsResultCallback callback) override { - callback.credentials_saved_cb( - absl::UnimplementedError("Not used in public broadcast demo")); - } - - void GetLocalCredentials( - const nearby::presence::CredentialSelector&, - nearby::presence::GetLocalCredentialsResultCallback callback) override { - callback.credentials_fetched_cb( - absl::NotFoundError("No local credentials in demo manager")); - } - - void GetPublicCredentials( - const nearby::presence::CredentialSelector&, - nearby::presence::PublicCredentialType, - nearby::presence::GetPublicCredentialsResultCallback callback) override { - callback.credentials_fetched_cb( - absl::NotFoundError("No public credentials in demo manager")); - } - - nearby::presence::SubscriberId SubscribeForPublicCredentials( - const nearby::presence::CredentialSelector&, - nearby::presence::PublicCredentialType, - nearby::presence::GetPublicCredentialsResultCallback callback) override { - callback.credentials_fetched_cb( - absl::NotFoundError("No subscriptions in demo manager")); - return 0; - } - - void UnsubscribeFromPublicCredentials(nearby::presence::SubscriberId) override { - } - - std::string DecryptDeviceIdentityMetaData( - absl::string_view, - absl::string_view, - absl::string_view) override { - return ""; - } - - void SetDeviceIdentityMetaData( - const nearby::internal::DeviceIdentityMetaData& device_identity_metadata, - bool, - absl::string_view, - const std::vector&, - int, - int, - nearby::presence::GenerateCredentialsResultCallback) override { - metadata_ = device_identity_metadata; - } - - nearby::internal::DeviceIdentityMetaData GetDeviceIdentityMetaData() - override { - return metadata_; - } - - private: - nearby::internal::DeviceIdentityMetaData metadata_; -}; - -} // namespace - -int main(int argc, char** argv) { - // Force-link the platform SystemClock implementation from static archives. - (void)nearby::SystemClock::ElapsedRealtime(); - - std::string device_name = "Nearby Presence Demo"; - int tx_power = 20; - - for (int i = 1; i < argc; ++i) { - std::string arg = argv[i]; - if ((arg == "-h") || (arg == "--help")) { - PrintUsage(argv[0]); - return 0; - } - if (arg == "--name") { - if (i + 1 >= argc) { - std::cerr << "Missing value for --name\n"; - return 2; - } - device_name = argv[++i]; - continue; - } - if (arg == "--tx_power") { - if (i + 1 >= argc) { - std::cerr << "Missing value for --tx_power\n"; - return 2; - } - tx_power = std::atoi(argv[++i]); - continue; - } - std::cerr << "Unknown argument: " << arg << "\n"; - PrintUsage(argv[0]); - return 2; - } - - struct sigaction action {}; - action.sa_sigaction = QuitHandler; - action.sa_flags = SA_SIGINFO; - sigaction(SIGINT, &action, nullptr); - sigaction(SIGTERM, &action, nullptr); - - nearby::SingleThreadExecutor executor; - nearby::presence::Mediums mediums; - DemoCredentialManager credential_manager; - nearby::presence::BroadcastManager manager(mediums, credential_manager, - executor); - - nearby::presence::PresenceBroadcast::BroadcastSection section = { - .identity = nearby::internal::IdentityType::IDENTITY_TYPE_PUBLIC, - .extended_properties = { - nearby::presence::DataElement( - nearby::presence::ActionBit::kNearbyShareAction), - nearby::presence::DataElement( - nearby::presence::ActionBit::kPresenceManagerAction), - }, - .account_name = "", - .manager_app_id = "", - }; - nearby::presence::PresenceBroadcast presence_broadcast = { - .sections = {section}, - }; - nearby::presence::BroadcastRequest request = { - .tx_power = tx_power, - .power_mode = nearby::presence::PowerMode::kLowLatency, - .variant = presence_broadcast, - }; - - absl::Status broadcast_status; - absl::Notification started; - auto session = manager.StartBroadcast( - request, - nearby::presence::BroadcastCallback{ - .start_broadcast_cb = - [&broadcast_status, &started](absl::Status status) { - broadcast_status = status; - started.Notify(); - }, - }); - if (!session.ok()) { - std::cerr << "Failed to initiate broadcast: " << session.status() << "\n"; - return 1; - } - - started.WaitForNotification(); - if (!broadcast_status.ok()) { - std::cerr << "Broadcast start failed: " << broadcast_status << "\n"; - manager.StopBroadcast(*session); - executor.Shutdown(); - return 1; - } - - std::cout << "Nearby Presence advertising started.\n" - << " device_name: " << device_name << "\n" - << " session_id: " << *session << "\n" - << "Press Ctrl+C to stop.\n"; - - g_shutdown.WaitForNotification(); - manager.StopBroadcast(*session); - executor.Shutdown(); - std::cout << "Stopped Presence advertising.\n"; - return 0; -} diff --git a/presence/presence_client.h b/presence/presence_client.h deleted file mode 100644 index 92807d95..00000000 --- a/presence/presence_client.h +++ /dev/null @@ -1,85 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_CLIENT_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_CLIENT_H_ - -#include - -#include "absl/status/statusor.h" -#include "internal/platform/borrowable.h" -#include "presence/broadcast_request.h" -#include "presence/data_types.h" -#include "presence/presence_device.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { - -class PresenceService; -/** - * Interface for detecting and interacting with nearby devices that are also - * part of the Presence ecosystem. - */ -class PresenceClient { - public: - using BorrowablePresenceService = ::nearby::Borrowable; - - virtual ~PresenceClient() = default; - - // Starts a Nearby Presence scan and registers `ScanCallback` - // which will be invoked when a matching `PresenceDevice` is detected, - // lost, and status changed. - // The session can be terminated with `StopScan()`. - // - // `ScanCallback` is kept in the Nearby Presence service until `StopScan()` is - // called. - // - // `ScanRequest` contains the options like scan power mode - // and type; the filters including credentials, actions and extended - // properties. - virtual absl::StatusOr StartScan(ScanRequest scan_request, - ScanCallback callback) = 0; - - // Terminates the scan session. Does nothing if the session is already - // terminated. - virtual void StopScan(ScanSessionId session_id) = 0; - - // Starts a Nearby Presence broadcast and registers `BroadcastCallback` - // which will be invoked after broadcast is started. - // The session can be terminated with `StopBroadcast()`. - // - // `BroadcastCallback` is kept in the Nearby Presence service until - // `StopBroadcast()` is called. - // - // `BroadcastRequest` contains the options like tx_power, - // the credential info like salt and private credential, the actions and - // extended properties. - virtual absl::StatusOr StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback) = 0; - - // Terminates a broadcast session. Does nothing if the session is already - // terminated. - virtual void StopBroadcast(BroadcastSessionId session_id) = 0; - - // Returns the local PresenceDevice describing the current device's actions, - // connectivity info and unique identifier for use in Connections and - // Presence. - virtual std::optional GetLocalDevice() = 0; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_CLIENT_H_ diff --git a/presence/presence_client_impl.cc b/presence/presence_client_impl.cc deleted file mode 100644 index 706e52f2..00000000 --- a/presence/presence_client_impl.cc +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2023 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/presence_client_impl.h" - -#include -#include -#include - -#include "absl/status/status.h" -#include "internal/platform/borrowable.h" -#include "internal/platform/logging.h" -#include "presence/presence_device.h" -#include "presence/presence_service.h" - -namespace nearby { -namespace presence { - -// static -PresenceClientImpl::Factory* PresenceClientImpl::Factory::g_test_factory_ = - nullptr; - -// static -std::unique_ptr PresenceClientImpl::Factory::Create( - BorrowablePresenceService service) { - if (g_test_factory_) { - return g_test_factory_->CreateInstance(service); - } - return absl::WrapUnique(new PresenceClientImpl(service)); -} - -// static -void PresenceClientImpl::Factory::SetFactoryForTesting( - Factory* g_test_factory) { - g_test_factory_ = g_test_factory; -} - -PresenceClientImpl::Factory::~Factory() = default; - -absl::StatusOr PresenceClientImpl::StartScan( - ScanRequest scan_request, ScanCallback callback) { - ::nearby::Borrowed borrowed = service_.Borrow(); - if (!borrowed) { - return absl::FailedPreconditionError( - "Can't start scan, presence service is gone"); - } - return (*borrowed)->StartScan(scan_request, std::move(callback)); -} - -void PresenceClientImpl::StopScan(ScanSessionId id) { - ::nearby::Borrowed borrowed = service_.Borrow(); - if (borrowed) { - (*borrowed)->StopScan(id); - } -} - -absl::StatusOr PresenceClientImpl::StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback) { - ::nearby::Borrowed borrowed = service_.Borrow(); - if (!borrowed) { - return absl::FailedPreconditionError( - "Can't start broadcast, presence service is gone"); - } - return (*borrowed)->StartBroadcast(broadcast_request, std::move(callback)); -} - -void PresenceClientImpl::StopBroadcast(BroadcastSessionId session_id) { - ::nearby::Borrowed borrowed = service_.Borrow(); - if (borrowed) { - (*borrowed)->StopBroadcast(session_id); - } else { - VLOG(1) << "Session already finished, id: " << session_id; - } -} - -std::optional PresenceClientImpl::GetLocalDevice() { - ::nearby::Borrowed borrowed = service_.Borrow(); - if (borrowed) { - const PresenceDevice* device = static_cast( - (*borrowed)->GetLocalDeviceProvider()->GetLocalDevice()); - return PresenceDevice(*device); - } - return std::nullopt; -} - -} // namespace presence -} // namespace nearby diff --git a/presence/presence_client_impl.h b/presence/presence_client_impl.h deleted file mode 100644 index e7f376c8..00000000 --- a/presence/presence_client_impl.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2023 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 THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_CLIENT_IMPL_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_CLIENT_IMPL_H_ - -#include -#include - -#include "absl/status/statusor.h" -#include "internal/platform/borrowable.h" -#include "presence/broadcast_request.h" -#include "presence/data_types.h" -#include "presence/presence_client.h" -#include "presence/presence_device.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { - -class PresenceClientImpl : public PresenceClient{ - public: - using BorrowablePresenceService = ::nearby::Borrowable; - - class Factory { - public: - static std::unique_ptr Create( - BorrowablePresenceService service); - static void SetFactoryForTesting(Factory* test_factory); - - protected: - virtual ~Factory(); - virtual std::unique_ptr CreateInstance( - BorrowablePresenceService service) = 0; - - private: - static Factory* g_test_factory_; - }; - - PresenceClientImpl(const PresenceClientImpl&) = delete; - PresenceClientImpl(PresenceClientImpl&&) = default; - PresenceClientImpl& operator=(const PresenceClientImpl&) = delete; - ~PresenceClientImpl() override = default; - - // PresenceClient: - absl::StatusOr StartScan(ScanRequest scan_request, - ScanCallback callback) override; - void StopScan(ScanSessionId session_id) override; - absl::StatusOr StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback) override; - void StopBroadcast(BroadcastSessionId session_id) override; - std::optional GetLocalDevice() override; - - private: - explicit PresenceClientImpl(BorrowablePresenceService service) - : service_(service) {} - - BorrowablePresenceService service_; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_CLIENT_IMPL_H_ diff --git a/presence/presence_client_test.cc b/presence/presence_client_test.cc deleted file mode 100644 index 2c43f26f..00000000 --- a/presence/presence_client_test.cc +++ /dev/null @@ -1,151 +0,0 @@ -// 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 "presence/presence_client.h" - -#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/status/statusor.h" -#include "absl/strings/string_view.h" -#include "internal/platform/future.h" -#include "internal/platform/medium_environment.h" -#include "presence/data_types.h" -#include "presence/presence_device.h" -#include "presence/presence_service_impl.h" - -namespace nearby { -namespace presence { -namespace { - -using ::nearby::internal::DeviceIdentityMetaData; -using ::testing::status::StatusIs; - -constexpr absl::string_view kMacAddr = "\x4C\x8B\x1D\xCE\xBA\xD1"; - -// Creates a PresenceClient and destroys PresenceServiceImpl that was used to -// create it. -std::unique_ptr CreateDefunctPresenceClient() { - PresenceServiceImpl presence_service; - return presence_service.CreatePresenceClient(); -} - -DeviceIdentityMetaData CreateTestDeviceIdentityMetaData() { - DeviceIdentityMetaData device_identity_metadata; - device_identity_metadata.set_device_type( - internal::DeviceType::DEVICE_TYPE_PHONE); - device_identity_metadata.set_device_name("NP test device"); - device_identity_metadata.set_bluetooth_mac_address(kMacAddr); - device_identity_metadata.set_device_id("\x12\xab\xcd"); - return device_identity_metadata; -} - -class PresenceClientTest : public testing::Test { - protected: - nearby::MediumEnvironment& env_{nearby::MediumEnvironment::Instance()}; -}; - -TEST_F(PresenceClientTest, StartBroadcastWithDefaultConstructor) { - env_.Start(); - absl::Status broadcast_result; - - PresenceServiceImpl presence_service; - std::unique_ptr presence_client = - presence_service.CreatePresenceClient(); - auto unused = presence_client->StartBroadcast( - {}, { - .start_broadcast_cb = - [&](absl::Status status) { broadcast_result = status; }, - }); - - EXPECT_THAT(broadcast_result, StatusIs(absl::StatusCode::kInvalidArgument)); - env_.Stop(); -} - -TEST_F(PresenceClientTest, StartBroadcastFailsWhenPresenceServiceIsGone) { - env_.Start(); - absl::Status broadcast_result = absl::UnknownError(""); - - absl::StatusOr session_id = - CreateDefunctPresenceClient()->StartBroadcast( - {}, { - .start_broadcast_cb = - [&](absl::Status status) { broadcast_result = status; }, - }); - - EXPECT_THAT(session_id, StatusIs(absl::StatusCode::kFailedPrecondition)); - EXPECT_THAT(broadcast_result, StatusIs(absl::StatusCode::kUnknown)); - env_.Stop(); -} - -TEST_F(PresenceClientTest, StartScanWithDefaultConstructor) { - env_.Start(); - ::nearby::Future scan_result; - ScanCallback scan_callback = { - .start_scan_cb = [&](absl::Status status) { scan_result.Set(status); }, - }; - - PresenceServiceImpl presence_service; - std::unique_ptr presence_client = - presence_service.CreatePresenceClient(); - EXPECT_OK(presence_client->StartScan({}, std::move(scan_callback))); - - EXPECT_TRUE(scan_result.Get().ok()); - EXPECT_OK(scan_result.Get().GetResult()); - env_.Stop(); -} - -TEST_F(PresenceClientTest, StartScanFailsWhenPresenceServiceIsGone) { - env_.Start(); - absl::Status scan_result = absl::UnknownError(""); - - absl::StatusOr session_id = - CreateDefunctPresenceClient()->StartScan( - {}, { - .start_scan_cb = - [&](absl::Status status) { scan_result = status; }, - }); - - EXPECT_THAT(session_id, StatusIs(absl::StatusCode::kFailedPrecondition)); - EXPECT_THAT(scan_result, StatusIs(absl::StatusCode::kUnknown)); - env_.Stop(); -} - -TEST_F(PresenceClientTest, GettingDeviceWorks) { - PresenceServiceImpl presence_service; - std::unique_ptr presence_client = - presence_service.CreatePresenceClient(); - presence_service.UpdateDeviceIdentityMetaData( - CreateTestDeviceIdentityMetaData(), false, "", {}, 0, 0, {}); - auto device = presence_client->GetLocalDevice(); - ASSERT_NE(device, std::nullopt); - EXPECT_EQ(device->GetEndpointId().length(), kEndpointIdLength); - EXPECT_EQ(device->GetDeviceIdentityMetadata().SerializeAsString(), - CreateTestDeviceIdentityMetaData().SerializeAsString()); -} - -TEST_F(PresenceClientTest, TestGettingDeviceDefunct) { - std::unique_ptr presence_client = - CreateDefunctPresenceClient(); - auto device = presence_client->GetLocalDevice(); - EXPECT_EQ(device, std::nullopt); -} -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/presence_device.cc b/presence/presence_device.cc deleted file mode 100644 index e2651089..00000000 --- a/presence/presence_device.cc +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2023 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/presence_device.h" - -#include -#include - -#include "absl/strings/string_view.h" -#include "absl/types/variant.h" -#include "connections/implementation/proto/offline_wire_formats.pb.h" -#include "internal/interop/device.h" -#include "internal/platform/ble_connection_info.h" -#include "internal/platform/implementation/system_clock.h" -#include "internal/platform/prng.h" -#include "internal/proto/credential.pb.h" -#include "internal/proto/metadata.pb.h" -#include "presence/device_motion.h" - -namespace nearby { -namespace presence { - -namespace { -constexpr char kEndpointIdChars[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; - -// LINT.IfChange -constexpr int kAndroidIdentityTypeUnknown = -1; -constexpr int kAndroidIdentityTypePrivateGroup = 0; -constexpr int kAndroidIdentityTypeContactsGroup = 1; -constexpr int kAndroidIdentityTypePublic = 2; -// LINT.ThenChange( -// //depot/google3/java/com/google/android/gmscore/integ/client/nearby/src/com/google/android/gms/nearby/presence/PresenceIdentity.java -// ) - -std::string GenerateRandomEndpointId() { - std::string result(kEndpointIdLength, 0); - nearby::Prng prng; - for (int i = 0; i < kEndpointIdLength; i++) { - result[i] = kEndpointIdChars[prng.NextUint32() % sizeof(kEndpointIdChars)]; - } - return result; -} - -location::nearby::connections::PresenceDevice::DeviceType -ConvertToConnectionsDeviceType(internal::DeviceType device_type) { - switch (device_type) { - case internal::DEVICE_TYPE_FOLDABLE: - case internal::DEVICE_TYPE_PHONE: - return location::nearby::connections::PresenceDevice::PHONE; - case internal::DEVICE_TYPE_TABLET: - return location::nearby::connections::PresenceDevice::TABLET; - case internal::DEVICE_TYPE_DISPLAY: - return location::nearby::connections::PresenceDevice::DISPLAY; - case internal::DEVICE_TYPE_CHROMEOS: - case internal::DEVICE_TYPE_LAPTOP: - return location::nearby::connections::PresenceDevice::LAPTOP; - case internal::DEVICE_TYPE_TV: - return location::nearby::connections::PresenceDevice::TV; - case internal::DEVICE_TYPE_WATCH: - return location::nearby::connections::PresenceDevice::WATCH; - default: - return location::nearby::connections::PresenceDevice::UNKNOWN; - } -} - -int ConvertToAndroidIdentityType(nearby::internal::IdentityType identity_type) { - switch (identity_type) { - case internal::IDENTITY_TYPE_PRIVATE_GROUP: - return kAndroidIdentityTypePrivateGroup; - case internal::IDENTITY_TYPE_CONTACTS_GROUP: - return kAndroidIdentityTypeContactsGroup; - case internal::IDENTITY_TYPE_PUBLIC: - return kAndroidIdentityTypePublic; - default: - // Unknown identity. - return kAndroidIdentityTypeUnknown; - } -} -} // namespace - -PresenceDevice::PresenceDevice(absl::string_view endpoint_id) noexcept - : endpoint_id_(endpoint_id) {} - -PresenceDevice::PresenceDevice( - DeviceIdentityMetaData device_identity_metadata) noexcept - : discovery_timestamp_(nearby::SystemClock::ElapsedRealtime()), - device_motion_(DeviceMotion()), - device_identity_metadata_(device_identity_metadata) { - endpoint_id_ = GenerateRandomEndpointId(); -} -PresenceDevice::PresenceDevice( - DeviceMotion device_motion, - DeviceIdentityMetaData device_identity_metadata) noexcept - : discovery_timestamp_(nearby::SystemClock::ElapsedRealtime()), - device_motion_(device_motion), - device_identity_metadata_(device_identity_metadata) { - endpoint_id_ = GenerateRandomEndpointId(); -} - -PresenceDevice::PresenceDevice( - DeviceMotion device_motion, DeviceIdentityMetaData device_identity_metadata, - nearby::internal::IdentityType identity_type) noexcept - : discovery_timestamp_(nearby::SystemClock::ElapsedRealtime()), - device_motion_(device_motion), - device_identity_metadata_(device_identity_metadata), - identity_type_(identity_type) { - endpoint_id_ = GenerateRandomEndpointId(); -} - -std::vector PresenceDevice::GetConnectionInfos() - const { - std::vector transformed_actions; - transformed_actions.reserve(actions_.size()); - for (const auto& action : actions_) { - transformed_actions.push_back(action.GetActionIdentifier()); - } - return {nearby::BleConnectionInfo( - device_identity_metadata_.bluetooth_mac_address(), - /*gatt_characteristic=*/"", /*psm=*/"", transformed_actions)}; -} - -std::string PresenceDevice::ToProtoBytes() const { - location::nearby::connections::PresenceDevice device; - device.set_endpoint_id(endpoint_id_); - device.add_identity_type(ConvertToAndroidIdentityType(identity_type_)); - device.set_endpoint_type( - location::nearby::connections::EndpointType::PRESENCE_ENDPOINT); - auto* actions = device.mutable_actions(); - for (const auto& action : actions_) { - actions->Add(action.GetActionIdentifier()); - } - std::string connection_infos = ""; - for (const auto& connection_info : GetConnectionInfos()) { - if (absl::holds_alternative(connection_info)) { - continue; - } - if (absl::holds_alternative(connection_info)) { - connection_infos += - absl::get(connection_info).ToDataElementBytes(); - } - if (absl::holds_alternative(connection_info)) { - connection_infos += absl::get(connection_info) - .ToDataElementBytes(); - } - if (absl::holds_alternative(connection_info)) { - connection_infos += absl::get(connection_info) - .ToDataElementBytes(); - } - } - device.set_device_type( - ConvertToConnectionsDeviceType(device_identity_metadata_.device_type())); - device.set_device_name(device_identity_metadata_.device_name()); - device.set_connectivity_info_list(connection_infos); - device.set_device_image_url("dummy url"); // Not used. - return device.SerializeAsString(); -} -} // namespace presence -} // namespace nearby diff --git a/presence/presence_device.h b/presence/presence_device.h deleted file mode 100644 index 93bac818..00000000 --- a/presence/presence_device.h +++ /dev/null @@ -1,129 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_H_ - -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "internal/interop/device.h" -#include "internal/proto/credential.pb.h" -#include "internal/proto/metadata.pb.h" -#include "presence/data_element.h" -#include "presence/device_motion.h" -#include "presence/presence_action.h" - -namespace nearby { -namespace presence { - -inline constexpr int kEndpointIdLength = 4; - -class PresenceDevice : public nearby::NearbyDevice { - using Metadata = ::nearby::internal::Metadata; - using DeviceIdentityMetaData = ::nearby::internal::DeviceIdentityMetaData; - - public: - explicit PresenceDevice(absl::string_view endpoint_id) noexcept; - explicit PresenceDevice( - DeviceIdentityMetaData device_identity_metadata) noexcept; - explicit PresenceDevice( - DeviceMotion device_motion, - DeviceIdentityMetaData device_identity_metadata) noexcept; - explicit PresenceDevice( - DeviceMotion device_motion, - DeviceIdentityMetaData device_identity_metadata, - nearby::internal::IdentityType identity_type) noexcept; - std::string GetEndpointId() const override { return endpoint_id_; } - std::vector GetConnectionInfos() - const override; - std::string ToProtoBytes() const override; - void AddExtendedProperty(const DataElement& data_element) { - extended_properties_.push_back(data_element); - } - void AddExtendedProperties(const std::vector& properties) { - extended_properties_.insert(extended_properties_.end(), properties.begin(), - properties.end()); - } - std::vector GetExtendedProperties() const { - return extended_properties_; - } - void AddAction(const PresenceAction& action) { actions_.push_back(action); } - std::vector GetActions() const { return actions_; } - NearbyDevice::Type GetType() const override { - return NearbyDevice::Type::kPresenceDevice; - } - DeviceMotion GetDeviceMotion() const { return device_motion_; } - DeviceIdentityMetaData GetDeviceIdentityMetadata() const { - return device_identity_metadata_; - } - void SetDeviceIdentityMetaData( - const DeviceIdentityMetaData& device_identity_metadata) { - device_identity_metadata_ = device_identity_metadata; - } - void SetDecryptSharedCredential( - const internal::SharedCredential& decrypt_shared_credential) { - decrypt_shared_credential_ = decrypt_shared_credential; - } - const std::optional& GetDecryptSharedCredential() - const { - return decrypt_shared_credential_; - } - absl::Time GetDiscoveryTimestamp() const { return discovery_timestamp_; } - internal::IdentityType GetIdentityType() const { return identity_type_; } - - private: - const absl::Time discovery_timestamp_; - const DeviceMotion device_motion_; - DeviceIdentityMetaData device_identity_metadata_; - std::vector extended_properties_; - std::vector actions_; - std::string endpoint_id_; - internal::IdentityType identity_type_ = internal::IDENTITY_TYPE_UNSPECIFIED; - std::optional decrypt_shared_credential_; -}; - -// Timestamp is not used for equality since if the same device is discovered -// twice, they will have different timestamps and thus will show up as two -// different devices when they are the same device. -inline bool operator==(const PresenceDevice& d1, const PresenceDevice& d2) { - bool shared_credential_equality = true; - shared_credential_equality &= d1.GetDecryptSharedCredential().has_value() == - d2.GetDecryptSharedCredential().has_value(); - if (shared_credential_equality && - d1.GetDecryptSharedCredential().has_value()) { - shared_credential_equality &= - d1.GetDecryptSharedCredential()->SerializeAsString() == - d2.GetDecryptSharedCredential()->SerializeAsString(); - } - return d1.GetDeviceMotion() == d2.GetDeviceMotion() && - d1.GetDeviceIdentityMetadata().SerializeAsString() == - d2.GetDeviceIdentityMetadata().SerializeAsString() && - d1.GetActions() == d2.GetActions() && - d1.GetExtendedProperties() == d2.GetExtendedProperties() && - d1.GetIdentityType() == d2.GetIdentityType() && - shared_credential_equality; -} - -inline bool operator!=(const PresenceDevice& d1, const PresenceDevice& d2) { - return !(d1 == d2); -} - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_H_ diff --git a/presence/presence_device_provider.cc b/presence/presence_device_provider.cc deleted file mode 100644 index 1b0d4258..00000000 --- a/presence/presence_device_provider.cc +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2023 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/presence_device_provider.h" - -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "absl/types/variant.h" -#include "internal/interop/authentication_status.h" -#include "internal/interop/authentication_transport.h" -#include "internal/interop/device.h" -#include "internal/platform/exception.h" -#include "internal/platform/future.h" -#include "internal/platform/implementation/system_clock.h" -#include "internal/platform/logging.h" -#include "presence/implementation/connection_authenticator.h" -#include "presence/implementation/service_controller.h" -#include "presence/presence_device.h" -#include "presence/proto/presence_frame.pb.h" - -namespace nearby { -namespace presence { - -namespace { - -constexpr int kPresenceVersion = 1; - -// TODO(b/317215548): Use Status code rather than custom defined -// authentication status. -std::string AuthenticationErrorToString(AuthenticationStatus status) { - switch (status) { - case AuthenticationStatus::kUnknown: - return "AuthenticationStatus::kUnknown"; - case AuthenticationStatus::kSuccess: - return "AuthenticationStatus::kSuccess"; - case AuthenticationStatus::kFailure: - return "AuthenticationStatus::kFailure"; - } - LOG(ERROR) << "Unexpected value for AuthenticationStatus: " - << static_cast(status); - return "AuthenticationStatus::kUnknown"; -} - -std::optional GetValidCredential( - std::vector local_credentials) { - absl::Time now = SystemClock::ElapsedRealtime(); - for (auto& credential : local_credentials) { - if (absl::FromUnixMillis(credential.start_time_millis()) <= now && - absl::FromUnixMillis(credential.end_time_millis()) > now) { - return credential; - } - } - return std::nullopt; -} - -PresenceAuthenticationFrame BuildInitiatorPresenceAuthenticationFrame( - ConnectionAuthenticator::InitiatorData initiator_data_variant) { - // It is expected that the `PresenceAuthenticationFrame` built for the - // initator role always is `TwoWayInitiatorData`, since the local device is - // always expected to have a valid local credential to be used, and this is - // verified in AuthenticateAsInitiator(), which returns failure if no valid - // local credential is found (which is expected to not happen, since valid - // credentials will be generated if needed before the authentiation is - // called). - // - // Note: std::holds_alternative and std::get cannot be used here because - // they are not supported in Chromium. - DCHECK(absl::holds_alternative( - initiator_data_variant)); - auto two_way_initiator_data = - absl::get( - initiator_data_variant); - - PresenceAuthenticationFrame authentication_frame; - authentication_frame.set_version(kPresenceVersion); - authentication_frame.set_private_key_signature( - two_way_initiator_data.private_key_signature); - authentication_frame.set_shared_credential_id_hash( - two_way_initiator_data.shared_credential_hash); - return authentication_frame; -} - -} // namespace - -PresenceDeviceProvider::PresenceDeviceProvider( - ServiceController* service_controller, - const ConnectionAuthenticator* connection_authenticator) - : service_controller_(*service_controller), - device_(service_controller_.GetDeviceIdentityMetaData()), - connection_authenticator_(*connection_authenticator) { - CHECK(connection_authenticator); -} - -AuthenticationStatus PresenceDeviceProvider::AuthenticateAsInitiator( - const NearbyDevice& remote_device, absl::string_view shared_secret, - const AuthenticationTransport& authentication_transport) const { - Future response; - - // 1. Fetch the local credentials and select the correct one to use - // for authentication by calling `GetValidCredential()`, which - // iterates over the returned list and returns the local credential - // that corresponds with the current time. - // - // TODO(b/304843571): Add support for additional IdentityTypes and for - // AuthenticationStatus::kUnknown. Currently, only `IDENTITY_TYPE_PRIVATE` is - // supported in order to unblock Nearby Presence MVP on CrOS, however in - // order to support future IdentityTypes, there needs to be a way to - // plumb in the requested identity type, as well as report back the - // unknown result to callers in NC. - service_controller_.GetLocalCredentials( - /*credential_selector=*/{.manager_app_id = manager_app_id_, - .account_name = "dummy_account_name", - .identity_type = ::nearby::internal:: - IdentityType::IDENTITY_TYPE_PRIVATE_GROUP}, - /*callback=*/{.credentials_fetched_cb = [this, &response, &remote_device, - &authentication_transport, - &shared_secret]( - auto status_or_credentials) { - if (!status_or_credentials.ok()) { - LOG(INFO) << __func__ << ": failure to fetch local credentials"; - response.Set(AuthenticationStatus::kFailure); - return; - } - - auto credential = GetValidCredential(status_or_credentials.value()); - if (!credential.has_value()) { - LOG(INFO) << __func__ << ": failure to find a valid local credential"; - response.Set(AuthenticationStatus::kFailure); - return; - } - - // 2. Construct the frame and write to the - // |authentication_transport|. - if (!WriteToRemoteDevice( - /*remote_device=*/remote_device, - /*shared_secret=*/shared_secret, - /*authentication_transport=*/authentication_transport, - /*local_credential=*/credential.value(), - /*response=*/response)) { - response.Set(AuthenticationStatus::kFailure); - return; - } - - // 3. Read the message from the remote device via - // |authentication_transport| and verify the response data. - if (!ReadAndVerifyRemoteDeviceData( - /*remote_device=*/remote_device, - /*shared_secret=*/shared_secret, - /*authentication_transport=*/authentication_transport)) { - response.Set(AuthenticationStatus::kFailure); - return; - } - - // 4. Return the status of the authentication to the callers. - response.Set(AuthenticationStatus::kSuccess); - }}); - - LOG(INFO) << __func__ << ": Waiting for future to complete"; - ExceptionOr result = response.Get(); - CHECK(result.ok()); - - LOG(INFO) << "Future:[" << __func__ << "] completed with status:" - << AuthenticationErrorToString(result.result()); - return result.result(); -} - -bool PresenceDeviceProvider::WriteToRemoteDevice( - const NearbyDevice& remote_device, absl::string_view shared_secret, - const AuthenticationTransport& authentication_transport, - const internal::LocalCredential& local_credential, - Future& response) const { - // Cast the |remote_device| to a `PresenceDevice` in order to retrieve - // it's shared credentials, which is safe to do since the |remote_device| - // passed to the `PresenceDeviceProvider` will always be a `PresenceDevice`. - const PresenceDevice* remote_presence_device = - static_cast(&remote_device); - auto shared_credential = remote_presence_device->GetDecryptSharedCredential(); - if (!shared_credential.has_value()) { - LOG(INFO) - << __func__ - << ": failure due to no decrypt shared credential from remote device"; - return false; - } - - auto status_or_initiator_data = - connection_authenticator_.BuildSignedMessageAsInitiator( - /*ukey2_secret=*/shared_secret, /*local_credential=*/local_credential, - /*shared_credential=*/shared_credential.value()); - if (!status_or_initiator_data.ok()) { - LOG(INFO) << __func__ << ": failure to build signed message as initiator"; - return false; - } - - // Once the initiator data has been built, construct the Presence frame - // which will be written to the device with the built data. - authentication_transport.WriteMessage( - BuildInitiatorPresenceAuthenticationFrame( - status_or_initiator_data.value()) - .SerializeAsString()); - return true; -} - -bool PresenceDeviceProvider::ReadAndVerifyRemoteDeviceData( - const NearbyDevice& remote_device, absl::string_view shared_secret, - const AuthenticationTransport& authentication_transport) const { - // Fetch the local public credentials to be used to verify the response data. - Future read_and_verify_result; - service_controller_.GetLocalPublicCredentials( - /*credential_selector=*/{.manager_app_id = manager_app_id_, - .account_name = "dummy_account_name", - .identity_type = ::nearby::internal:: - IdentityType::IDENTITY_TYPE_PRIVATE_GROUP}, - /*callback=*/{.credentials_fetched_cb = [this, &read_and_verify_result, - &authentication_transport, - &shared_secret]( - auto status_or_credentials) { - if (!status_or_credentials.ok()) { - LOG(INFO) << __func__ - << ": failure to fetch local public credentials"; - read_and_verify_result.Set(/*success=*/false); - return; - } - - std::string response_data = authentication_transport.ReadMessage(); - auto status = connection_authenticator_.VerifyMessageAsInitiator( - /*authentication_data=*/{.private_key_signature = response_data}, - /*ukey2_secret=*/shared_secret, - /*shared_credential=*/status_or_credentials.value()); - if (!status.ok()) { - LOG(INFO) << __func__ << ": failure to verify remote device"; - read_and_verify_result.Set(/*success=*/false); - return; - } - - read_and_verify_result.Set(/*success=*/true); - }}); - - LOG(INFO) << __func__ << ": Waiting for future to complete"; - ExceptionOr result = read_and_verify_result.Get(); - LOG(INFO) << "Future:[" << __func__ - << "] completed with status:" << result.result(); - return result.result(); -} - -} // namespace presence -} // namespace nearby diff --git a/presence/presence_device_provider.h b/presence/presence_device_provider.h deleted file mode 100644 index afa1df51..00000000 --- a/presence/presence_device_provider.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2023 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 THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_PROVIDER_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_PROVIDER_H_ - -#include -#include - -#include "absl/strings/string_view.h" -#include "internal/interop/authentication_status.h" -#include "internal/interop/authentication_transport.h" -#include "internal/interop/device.h" -#include "internal/interop/device_provider.h" -#include "internal/platform/future.h" -#include "internal/proto/local_credential.pb.h" -#include "internal/proto/metadata.pb.h" -#include "presence/implementation/connection_authenticator.h" -#include "presence/presence_device.h" - -namespace nearby { -namespace presence { - -class ServiceController; - -class PresenceDeviceProvider : public NearbyDeviceProvider { - public: - PresenceDeviceProvider( - ServiceController* service_controller, - const ConnectionAuthenticator* connection_authenticator); - - const NearbyDevice* GetLocalDevice() override { return &device_; } - - // To authenticate as an initiator (when the device is in the scanning role), - // the PresenceDeviceProvider will block and: - // 1. Fetch the local credentials and select the correct one to use for - // authentication. - // 2. Construct the frame and write to the |authentication_transport|. - // 3. Read the message from the remote device via |authentication_transport|. - // 4. Return the status of the authentication to the callers. - AuthenticationStatus AuthenticateAsInitiator( - const NearbyDevice& remote_device, absl::string_view shared_secret, - const AuthenticationTransport& authentication_transport) const override; - - AuthenticationStatus AuthenticateAsResponder( - absl::string_view shared_secret, - const AuthenticationTransport& authentication_transport) const override { - // TODO(b/282027237): Implement. - return AuthenticationStatus::kUnknown; - } - - void UpdateDeviceIdentityMetaData( - const ::nearby::internal::DeviceIdentityMetaData& - device_identity_metadata) { - device_.SetDeviceIdentityMetaData(device_identity_metadata); - } - - void SetManagerAppId(absl::string_view manager_app_id) { - manager_app_id_ = manager_app_id; - } - - std::string GetManagerAppId() { return manager_app_id_; } - - private: - bool WriteToRemoteDevice( - const NearbyDevice& remote_device, absl::string_view shared_secret, - const AuthenticationTransport& authentication_transport, - const internal::LocalCredential& local_credential, - Future& response) const; - bool ReadAndVerifyRemoteDeviceData( - const NearbyDevice& remote_device, absl::string_view shared_secret, - const AuthenticationTransport& authentication_transport) const; - - ServiceController& service_controller_; - PresenceDevice device_; - std::string manager_app_id_; - const ConnectionAuthenticator& connection_authenticator_; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_DEVICE_PROVIDER_H_ diff --git a/presence/presence_device_provider_test.cc b/presence/presence_device_provider_test.cc deleted file mode 100644 index a8985270..00000000 --- a/presence/presence_device_provider_test.cc +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright 2023 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/presence_device_provider.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/str_cat.h" -#include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "internal/crypto/ed25519.h" -#include "internal/interop/authentication_status.h" -#include "internal/interop/authentication_transport.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "internal/platform/implementation/system_clock.h" -#include "internal/proto/credential.pb.h" -#include "internal/proto/local_credential.pb.h" -#include "internal/proto/metadata.pb.h" -#include "presence/implementation/connection_authenticator.h" -#include "presence/implementation/mock_connection_authenticator.h" -#include "presence/implementation/mock_service_controller.h" -#include "presence/presence_device.h" -#include "presence/proto/presence_frame.pb.h" - -namespace nearby { -namespace presence { -namespace { -using ::nearby::internal::DeviceIdentityMetaData; - -constexpr absl::string_view kMacAddr = "\x4C\x8B\x1D\xCE\xBA\xD1"; -constexpr absl::string_view kManagerAppId = "test_app_id"; -constexpr char kUkey2Secret[] = {0x34, 0x56, 0x78, 0x90}; -constexpr char kKeySeed[] = {1, 2, 3, 4, 5, 6, 7, 8}; -constexpr int kPresenceVersion = 1; -constexpr absl::string_view kSharedCredentialHash = "shared_cred_hash"; -constexpr absl::string_view kPrivateKeySignature = "private_key_signature"; - -DeviceIdentityMetaData CreateTestDeviceIdentityMetaData() { - DeviceIdentityMetaData device_identity_metadata; - device_identity_metadata.set_device_type( - internal::DeviceType::DEVICE_TYPE_PHONE); - device_identity_metadata.set_device_name("NP test device"); - device_identity_metadata.set_bluetooth_mac_address(kMacAddr); - device_identity_metadata.set_device_id("\x12\xab\xcd"); - return device_identity_metadata; -} - -nearby::internal::LocalCredential CreateValidLocalCredential( - const crypto::Ed25519KeyPair& key_pair) { - nearby::internal::LocalCredential credential; - absl::Time now = SystemClock::ElapsedRealtime(); - credential.set_start_time_millis(absl::ToUnixMillis(now)); - credential.set_end_time_millis(absl::ToUnixMillis(now + absl::Minutes(10))); - credential.mutable_connection_signing_key()->set_key( - absl::StrCat(key_pair.private_key, key_pair.public_key)); - credential.set_key_seed(kKeySeed); - return credential; -} - -nearby::internal::LocalCredential CreateExpiredLocalCredential() { - nearby::internal::LocalCredential credential; - absl::Time now = SystemClock::ElapsedRealtime(); - credential.set_start_time_millis(absl::ToUnixMillis(now - absl::Minutes(30))); - credential.set_end_time_millis(absl::ToUnixMillis(now - absl::Minutes(10))); - return credential; -} - -internal::SharedCredential BuildSharedCredential( - const crypto::Ed25519KeyPair& key_pair) { - internal::SharedCredential shared_credential; - shared_credential.set_connection_signature_verification_key( - key_pair.public_key); - shared_credential.set_key_seed(kKeySeed); - return shared_credential; -} - -ConnectionAuthenticator::TwoWayInitiatorData BuildDefaultInitiatorData() { - ConnectionAuthenticator::TwoWayInitiatorData data; - data.shared_credential_hash = kSharedCredentialHash; - data.private_key_signature = kPrivateKeySignature; - return data; -} - -class MockAuthenticationTransport : public AuthenticationTransport { - public: - MOCK_METHOD(void, WriteMessage, (absl::string_view), (const, override)); - MOCK_METHOD(std::string, ReadMessage, (), (const, override)); -}; - -class PresenceDeviceProviderTest : public ::testing::Test { - public: - PresenceDeviceProviderTest() { - ON_CALL(mock_service_controller_, GetDeviceIdentityMetaData) - .WillByDefault(testing::Return(CreateTestDeviceIdentityMetaData())); - provider_ = std::make_unique( - &mock_service_controller_, &mock_connection_authenticator_); - } - - void SetUp() override { - auto key_pair_or_status = crypto::Ed25519Signer::CreateNewKeyPair(); - ASSERT_OK_AND_ASSIGN(key_pair_, key_pair_or_status); - } - - protected: - MockServiceController mock_service_controller_; - std::unique_ptr provider_; - crypto::Ed25519KeyPair key_pair_; - MockConnectionAuthenticator mock_connection_authenticator_; -}; - -TEST_F(PresenceDeviceProviderTest, ProviderIsNotTriviallyConstructible) { - EXPECT_FALSE(std::is_trivially_constructible::value); -} - -TEST_F(PresenceDeviceProviderTest, DeviceProviderWorks) { - auto device = provider_->GetLocalDevice(); - ASSERT_EQ(device->GetType(), NearbyDevice::Type::kPresenceDevice); - auto presence_device = static_cast(device); - EXPECT_EQ(presence_device->GetDeviceIdentityMetadata().SerializeAsString(), - CreateTestDeviceIdentityMetaData().SerializeAsString()); -} - -TEST_F(PresenceDeviceProviderTest, DeviceProviderCanUpdateDevice) { - auto device = provider_->GetLocalDevice(); - ASSERT_EQ(device->GetType(), NearbyDevice::Type::kPresenceDevice); - auto presence_device = static_cast(device); - EXPECT_EQ(presence_device->GetDeviceIdentityMetadata().SerializeAsString(), - CreateTestDeviceIdentityMetaData().SerializeAsString()); - auto new_metadata = CreateTestDeviceIdentityMetaData(); - new_metadata.set_device_name("NP interop device"); - provider_->UpdateDeviceIdentityMetaData(new_metadata); - EXPECT_EQ(presence_device->GetDeviceIdentityMetadata().SerializeAsString(), - new_metadata.SerializeAsString()); -} - -TEST_F(PresenceDeviceProviderTest, SetGetManagerAppId) { - provider_->SetManagerAppId(kManagerAppId); - EXPECT_EQ(provider_->GetManagerAppId(), kManagerAppId); -} - -TEST_F(PresenceDeviceProviderTest, - AuthenticateAsInitiatorFails_FailToFetchCredentials) { - EXPECT_CALL(mock_service_controller_, GetLocalCredentials) - .WillOnce([&](const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback) { - std::move(callback.credentials_fetched_cb)( - absl::Status(absl::StatusCode::kCancelled, /*msg=*/std::string())); - }); - - PresenceDevice remote_device(CreateTestDeviceIdentityMetaData()); - MockAuthenticationTransport authentication_transport; - auto status = provider_->AuthenticateAsInitiator( - /*remote_device=*/remote_device, /*shared_secret=*/kUkey2Secret, - /*authentication_transport=*/authentication_transport); - EXPECT_EQ(AuthenticationStatus::kFailure, status); -} - -TEST_F(PresenceDeviceProviderTest, - AuthenticateAsInitiatorFails_NoValidCredentials) { - EXPECT_CALL(mock_service_controller_, GetLocalCredentials) - .WillOnce([&](const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback) { - std::vector credentials; - credentials.push_back(CreateExpiredLocalCredential()); - std::move(callback.credentials_fetched_cb)(credentials); - }); - - PresenceDevice remote_device(CreateTestDeviceIdentityMetaData()); - MockAuthenticationTransport authentication_transport; - auto status = provider_->AuthenticateAsInitiator( - /*remote_device=*/remote_device, /*shared_secret=*/kUkey2Secret, - /*authentication_transport=*/authentication_transport); - EXPECT_EQ(AuthenticationStatus::kFailure, status); -} - -TEST_F(PresenceDeviceProviderTest, - AuthenticateAsInitiator_NoRemoteSharedCredential) { - EXPECT_CALL(mock_service_controller_, GetLocalCredentials) - .WillOnce([&](const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback) { - std::vector credentials; - credentials.push_back(CreateValidLocalCredential(key_pair_)); - std::move(callback.credentials_fetched_cb)(credentials); - }); - - PresenceDevice remote_device(CreateTestDeviceIdentityMetaData()); - MockAuthenticationTransport authentication_transport; - auto status = provider_->AuthenticateAsInitiator( - /*remote_device=*/remote_device, /*shared_secret=*/kUkey2Secret, - /*authentication_transport=*/authentication_transport); - - EXPECT_EQ(AuthenticationStatus::kFailure, status); -} - -TEST_F(PresenceDeviceProviderTest, AuthenticateAsInitiator_FailureToVerify) { - EXPECT_CALL(mock_service_controller_, GetLocalCredentials) - .WillOnce([&](const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback) { - std::vector credentials; - credentials.push_back(CreateValidLocalCredential(key_pair_)); - std::move(callback.credentials_fetched_cb)(credentials); - }); - EXPECT_CALL(mock_service_controller_, GetLocalPublicCredentials) - .WillOnce([&](const CredentialSelector& credential_selector, - GetPublicCredentialsResultCallback callback) { - std::vector credentials; - credentials.push_back(BuildSharedCredential(key_pair_)); - std::move(callback.credentials_fetched_cb)(credentials); - }); - - PresenceDevice remote_device(CreateTestDeviceIdentityMetaData()); - remote_device.SetDecryptSharedCredential(BuildSharedCredential(key_pair_)); - - MockAuthenticationTransport authentication_transport; - EXPECT_CALL(authentication_transport, WriteMessage) - .WillOnce([&](absl::string_view message) { - PresenceAuthenticationFrame authentication_frame; - EXPECT_TRUE(authentication_frame.ParseFromString(message)); - EXPECT_EQ(kPresenceVersion, authentication_frame.version()); - }); - EXPECT_CALL(authentication_transport, ReadMessage).WillOnce([&]() { - PresenceAuthenticationFrame authentication_frame; - return authentication_frame.SerializeAsString(); - }); - - EXPECT_CALL(mock_connection_authenticator_, BuildSignedMessageAsInitiator) - .WillOnce(testing::Return(BuildDefaultInitiatorData())); - EXPECT_CALL(mock_connection_authenticator_, VerifyMessageAsInitiator) - .WillOnce(testing::Return( - absl::Status(absl::StatusCode::kCancelled, /*msg=*/std::string()))); - - auto status = provider_->AuthenticateAsInitiator( - /*remote_device=*/remote_device, /*shared_secret=*/kUkey2Secret, - /*authentication_transport=*/authentication_transport); - EXPECT_EQ(AuthenticationStatus::kFailure, status); -} - -TEST_F(PresenceDeviceProviderTest, AuthenticateAsInitiator_Success) { - EXPECT_CALL(mock_service_controller_, GetLocalCredentials) - .WillOnce([&](const CredentialSelector& credential_selector, - GetLocalCredentialsResultCallback callback) { - std::vector credentials; - credentials.push_back(CreateValidLocalCredential(key_pair_)); - std::move(callback.credentials_fetched_cb)(credentials); - }); - EXPECT_CALL(mock_service_controller_, GetLocalPublicCredentials) - .WillOnce([&](const CredentialSelector& credential_selector, - GetPublicCredentialsResultCallback callback) { - std::vector credentials; - credentials.push_back(BuildSharedCredential(key_pair_)); - std::move(callback.credentials_fetched_cb)(std::move(credentials)); - }); - - PresenceDevice remote_device(CreateTestDeviceIdentityMetaData()); - remote_device.SetDecryptSharedCredential(BuildSharedCredential(key_pair_)); - - ON_CALL(mock_connection_authenticator_, BuildSignedMessageAsInitiator) - .WillByDefault(testing::Return(BuildDefaultInitiatorData())); - - MockAuthenticationTransport authentication_transport; - EXPECT_CALL(authentication_transport, WriteMessage) - .WillOnce([&](absl::string_view message) { - PresenceAuthenticationFrame authentication_frame; - EXPECT_TRUE(authentication_frame.ParseFromString(message)); - EXPECT_EQ(kPresenceVersion, authentication_frame.version()); - }); - - auto status = provider_->AuthenticateAsInitiator( - /*remote_device=*/remote_device, /*shared_secret=*/kUkey2Secret, - /*authentication_transport=*/authentication_transport); - - EXPECT_EQ(AuthenticationStatus::kSuccess, status); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/presence_device_test.cc b/presence/presence_device_test.cc deleted file mode 100644 index e567d749..00000000 --- a/presence/presence_device_test.cc +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2023 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/presence_device.h" - -#include -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "connections/implementation/proto/offline_wire_formats.pb.h" -#include "internal/platform/ble_connection_info.h" -#include "internal/proto/credential.pb.h" -#include "internal/proto/metadata.pb.h" -#include "presence/data_element.h" -#include "presence/presence_action.h" - -namespace nearby { -namespace presence { -namespace { - -using ::nearby::internal::DeviceIdentityMetaData; -using ::testing::Contains; - -constexpr DeviceMotion::MotionType kDefaultMotionType = - DeviceMotion::MotionType::kPointAndHold; -constexpr float kDefaultConfidence = 0; -constexpr float kTestConfidence = 0.1; -constexpr absl::string_view kMacAddr = "\x4C\x8B\x1D\xCE\xBA\xD1"; -constexpr int kDataElementType = DataElement::kBatteryFieldType; -constexpr absl::string_view kDataElementValue = "15"; -constexpr char kEndpointId[] = "endpoint_id"; -constexpr int kTestAction = 3; - -DeviceIdentityMetaData CreateTestDeviceIdentityMetaData() { - DeviceIdentityMetaData device_identity_metadata; - device_identity_metadata.set_device_type( - internal::DeviceType::DEVICE_TYPE_LAPTOP); - device_identity_metadata.set_device_name("NP test device"); - device_identity_metadata.set_bluetooth_mac_address(kMacAddr); - device_identity_metadata.set_device_id("\x12\xab\xcd"); - return device_identity_metadata; -} -TEST(PresenceDeviceTest, EndpointIdConstructor) { - PresenceDevice device(kEndpointId); - EXPECT_EQ(device.GetEndpointId(), kEndpointId); -} - -TEST(PresenceDeviceTest, DefaultMotionEquals) { - DeviceIdentityMetaData device_identity_metadata = - CreateTestDeviceIdentityMetaData(); - PresenceDevice device1(device_identity_metadata); - PresenceDevice device2(device_identity_metadata); - EXPECT_EQ(device1, device2); -} - -TEST(PresenceDeviceTest, ExplicitInitEquals) { - DeviceIdentityMetaData device_identity_metadata = - CreateTestDeviceIdentityMetaData(); - internal::SharedCredential shared_credential; - shared_credential.set_credential_type(internal::CREDENTIAL_TYPE_GAIA); - PresenceDevice device1 = - PresenceDevice({kDefaultMotionType, kTestConfidence}, - device_identity_metadata, internal::IDENTITY_TYPE_PUBLIC); - device1.SetDecryptSharedCredential(shared_credential); - PresenceDevice device2 = - PresenceDevice({kDefaultMotionType, kTestConfidence}, - device_identity_metadata, internal::IDENTITY_TYPE_PUBLIC); - device2.SetDecryptSharedCredential(shared_credential); - EXPECT_EQ(device1, device2); -} - -TEST(PresenceDeviceTest, ExplicitInitNotEquals) { - DeviceIdentityMetaData device_identity_metadata = - CreateTestDeviceIdentityMetaData(); - PresenceDevice device1 = - PresenceDevice({kDefaultMotionType}, device_identity_metadata, - internal::IDENTITY_TYPE_PUBLIC); - PresenceDevice device2 = PresenceDevice( - {kDefaultMotionType, kTestConfidence}, device_identity_metadata, - internal::IDENTITY_TYPE_PRIVATE_GROUP); - EXPECT_NE(device1, device2); -} - -TEST(PresenceDeviceTest, TestGetBleConnectionInfo) { - DeviceIdentityMetaData device_identity_metadata = - CreateTestDeviceIdentityMetaData(); - PresenceDevice device = - PresenceDevice({kDefaultMotionType}, device_identity_metadata); - device.AddAction(PresenceAction(kTestAction)); - auto info = (device.GetConnectionInfos().at(0)); - ASSERT_TRUE(std::holds_alternative(info)); - auto ble_info = std::get(info); - EXPECT_EQ(ble_info.GetMacAddress(), kMacAddr); - EXPECT_EQ(ble_info.GetActions(), std::vector{kTestAction}); -} - -TEST(PresenceDeviceTest, TestGetAddExtendedProperties) { - DeviceIdentityMetaData device_identity_metadata = - CreateTestDeviceIdentityMetaData(); - PresenceDevice device = - PresenceDevice({kDefaultMotionType}, device_identity_metadata); - device.AddExtendedProperty({kDataElementType, kDataElementValue}); - ASSERT_EQ(device.GetExtendedProperties().size(), 1); - EXPECT_EQ(device.GetExtendedProperties()[0], - DataElement(kDataElementType, kDataElementValue)); -} - -TEST(PresenceDeviceTest, TestGetAddExtendedPropertiesVector) { - DeviceIdentityMetaData device_identity_metadata = - CreateTestDeviceIdentityMetaData(); - PresenceDevice device = - PresenceDevice({kDefaultMotionType}, device_identity_metadata); - device.AddExtendedProperties( - {DataElement(kDataElementType, kDataElementValue)}); - ASSERT_EQ(device.GetExtendedProperties().size(), 1); - EXPECT_EQ(device.GetExtendedProperties()[0], - DataElement(kDataElementType, kDataElementValue)); -} - -TEST(PresenceDeviceTest, TestAddGetActions) { - DeviceIdentityMetaData device_identity_metadata = - CreateTestDeviceIdentityMetaData(); - PresenceDevice device = - PresenceDevice({kDefaultMotionType}, device_identity_metadata); - device.AddAction({kTestAction}); - ASSERT_EQ(device.GetActions().size(), 1); - EXPECT_EQ(device.GetActions()[0], PresenceAction(kTestAction)); -} - -TEST(PresenceDeviceTest, TestEndpointIdIsCorrectLength) { - DeviceIdentityMetaData device_identity_metadata = - CreateTestDeviceIdentityMetaData(); - PresenceDevice device = - PresenceDevice({kDefaultMotionType}, device_identity_metadata); - EXPECT_EQ(device.GetEndpointId().length(), kEndpointIdLength); -} - -TEST(PresenceDeviceTest, TestEndpointIdIsRandom) { - DeviceIdentityMetaData device_identity_metadata = - CreateTestDeviceIdentityMetaData(); - PresenceDevice device = - PresenceDevice({kDefaultMotionType}, device_identity_metadata); - EXPECT_EQ(device.GetEndpointId().length(), kEndpointIdLength); - EXPECT_NE(device.GetEndpointId(), std::string(kEndpointIdLength, 0)); -} - -TEST(PresenceDeviceTest, TestGetIdentityType) { - DeviceIdentityMetaData device_identity_metadata = - CreateTestDeviceIdentityMetaData(); - PresenceDevice device = PresenceDevice( - DeviceMotion(), device_identity_metadata, internal::IDENTITY_TYPE_PUBLIC); - EXPECT_EQ(device.GetIdentityType(), internal::IDENTITY_TYPE_PUBLIC); -} - -TEST(PresenceDeviceTest, TestGetDecryptSharedCredential) { - DeviceIdentityMetaData device_identity_metadata = - CreateTestDeviceIdentityMetaData(); - PresenceDevice device = PresenceDevice( - DeviceMotion(), device_identity_metadata, internal::IDENTITY_TYPE_PUBLIC); - EXPECT_EQ(device.GetDecryptSharedCredential(), std::nullopt); - internal::SharedCredential shared_credential; - shared_credential.set_credential_type(internal::CREDENTIAL_TYPE_GAIA); - device.SetDecryptSharedCredential(shared_credential); - EXPECT_EQ(device.GetDecryptSharedCredential()->SerializeAsString(), - shared_credential.SerializeAsString()); -} - -TEST(PresenceDeviceTest, TestToProtoBytes) { - DeviceIdentityMetaData device_identity_metadata = - CreateTestDeviceIdentityMetaData(); - PresenceDevice device = PresenceDevice( - DeviceMotion(), device_identity_metadata, internal::IDENTITY_TYPE_PUBLIC); - std::string proto_bytes = device.ToProtoBytes(); - location::nearby::connections::PresenceDevice device_frame; - ASSERT_TRUE(device_frame.ParseFromString(proto_bytes)); - // Public identity. - EXPECT_THAT(device_frame.identity_type(), Contains(2)); - EXPECT_EQ(device_frame.endpoint_type(), - location::nearby::connections::PRESENCE_ENDPOINT); - EXPECT_EQ(device_frame.endpoint_id(), device.GetEndpointId()); - EXPECT_EQ(device_frame.device_type(), - location::nearby::connections::PresenceDevice::LAPTOP); - EXPECT_EQ(device_frame.device_name(), "NP test device"); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/presence_identity_test.cc b/presence/presence_identity_test.cc deleted file mode 100644 index 5b45704d..00000000 --- a/presence/presence_identity_test.cc +++ /dev/null @@ -1,44 +0,0 @@ -// 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 "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "internal/proto/credential.pb.h" - -namespace nearby { -namespace presence { -namespace { -using ::nearby::internal::IdentityType; - -constexpr IdentityType kTestIdentityType = - IdentityType::IDENTITY_TYPE_CONTACTS_GROUP; - -TEST(PresenceIdentityTest, ExplicitInitEquals) { - IdentityType identity1 = {kTestIdentityType}; - IdentityType identity2 = {kTestIdentityType}; - EXPECT_EQ(identity1, identity2); - EXPECT_EQ(identity1, kTestIdentityType); -} - - -TEST(PresenceIdentityTest, CopyInitEquals) { - IdentityType identity1 = {kTestIdentityType}; - IdentityType identity2 = {identity1}; - EXPECT_EQ(identity1, identity2); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/presence_scanner_demo.cc b/presence/presence_scanner_demo.cc deleted file mode 100644 index 3d6a755d..00000000 --- a/presence/presence_scanner_demo.cc +++ /dev/null @@ -1,444 +0,0 @@ -// Copyright 2026 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "absl/synchronization/notification.h" -#include "internal/platform/bluetooth_adapter.h" -#include "internal/platform/system_clock.h" -#include "internal/proto/credential.pb.h" -#include "presence/power_mode.h" -#include "presence/presence_device.h" -#include "presence/presence_service_impl.h" -#include "presence/scan_request.h" - -#if defined(__linux__) -#include "internal/platform/implementation/linux/bluetooth_adapter.h" -#include "internal/platform/implementation/linux/bluetooth_devices.h" -#endif - -namespace { - -absl::Notification g_shutdown; - -void QuitHandler(int, siginfo_t*, void*) { - if (!g_shutdown.HasBeenNotified()) g_shutdown.Notify(); -} - -std::string RangeTypeToString( - const std::string& range) { - return range; -} - -std::string EstimateZone(double distance_meters) { - constexpr double kTapDistanceThresholdMeters = 0.08; - constexpr double kReachDistanceThresholdMeters = 0.56; - constexpr double kShortRangeDistanceThresholdMeters = 1.26; - constexpr double kLongRangeDistanceThresholdMeters = 3.06; - - if (distance_meters <= kTapDistanceThresholdMeters) return "tap"; - if (distance_meters <= kReachDistanceThresholdMeters) return "reach"; - if (distance_meters <= kShortRangeDistanceThresholdMeters) return "short"; - if (distance_meters <= kLongRangeDistanceThresholdMeters) return "long"; - return "far"; -} - -double EstimateDistanceMeters(int adjusted_rssi) { - // Mirrors presence/fpp/fpp/src/fspl_converter.rs. - constexpr int kTxPowerAtZeroMetersDb = -20; - constexpr int kFsplAtOneMeterDb = 40; - const int fspl = kTxPowerAtZeroMetersDb - adjusted_rssi; - return std::pow(10.0, static_cast(fspl - kFsplAtOneMeterDb) / 20.0); -} - -std::string FormatProximity(int rssi, std::optional tx_power) { - const int adjusted_rssi = rssi + tx_power.value_or(0); - const double distance_meters = EstimateDistanceMeters(adjusted_rssi); - const std::string zone = EstimateZone(distance_meters); - std::ostringstream stream; - stream << std::fixed << std::setprecision(2) << "distance_m=" - << distance_meters << " zone=" << RangeTypeToString(zone) - << " rssi=" << rssi; - if (tx_power.has_value()) { - stream << " tx_power=" << static_cast(*tx_power); - } - return stream.str(); -} - -std::string FormatProximityUnavailable() { return "proximity=unavailable"; } - -std::string IdentityTypeToString(nearby::internal::IdentityType identity_type) { - switch (identity_type) { - case nearby::internal::IDENTITY_TYPE_PUBLIC: - return "public"; - case nearby::internal::IDENTITY_TYPE_PRIVATE_GROUP: - return "private_group"; - case nearby::internal::IDENTITY_TYPE_CONTACTS_GROUP: - return "contacts_group"; - default: - return "unspecified"; - } -} - -std::optional ParseHexId(const std::string& hex_id) { - if (hex_id.empty()) return std::nullopt; - try { - size_t parsed_size = 0; - uint64_t id = std::stoull(hex_id, &parsed_size, 16); - if (parsed_size != hex_id.size()) return std::nullopt; - return id; - } catch (...) { - return std::nullopt; - } -} - -class SignalProvider { - public: - SignalProvider() { -#if defined(__linux__) - nearby::BluetoothAdapter adapter; - auto* linux_adapter = - dynamic_cast(&adapter.GetImpl()); - if (linux_adapter == nullptr) { - return; - } - shared_devices_ = nearby::linux::GetSharedBluetoothDevices( - linux_adapter->GetConnection(), linux_adapter->GetObjectPath()); -#endif - } - - std::optional GetRssi(uint64_t device_id) const { -#if defined(__linux__) - if (!shared_devices_) return std::nullopt; - auto device = shared_devices_->devices->get_device_by_unique_id(device_id); - if (!device) return std::nullopt; - auto rssi = device->GetRssi(); - if (!rssi.has_value()) return std::nullopt; - return static_cast(*rssi); -#else - (void)device_id; - return std::nullopt; -#endif - } - - std::optional GetTxPower(uint64_t device_id) const { -#if defined(__linux__) - if (!shared_devices_) return std::nullopt; - auto device = shared_devices_->devices->get_device_by_unique_id(device_id); - if (!device) return std::nullopt; - auto tx_power = device->GetTxPower(); - if (!tx_power.has_value()) return std::nullopt; - if (*tx_power < std::numeric_limits::min() || - *tx_power > std::numeric_limits::max()) { - return std::nullopt; - } - return static_cast(*tx_power); -#else - (void)device_id; - return std::nullopt; -#endif - } - - private: -#if defined(__linux__) - std::shared_ptr shared_devices_; -#endif -}; - -void PrintUsage(const char* program) { - std::cerr << "Usage: " << program - << " [--power low|balanced|low-latency] [--screen-on-only]" - << " [--identities all|public|private|contacts|csv]" - << " [--manager-app-id ] [--account-name ]\n"; -} - -nearby::presence::PowerMode ParsePowerMode(const std::string& arg) { - if (arg == "low") return nearby::presence::PowerMode::kLowPower; - if (arg == "low-latency") return nearby::presence::PowerMode::kLowLatency; - return nearby::presence::PowerMode::kBalanced; -} - -bool AddIdentityTypeFromToken( - const std::string& token, - std::vector& identity_types) { - using IdentityType = nearby::internal::IdentityType; - IdentityType identity = IdentityType::IDENTITY_TYPE_UNSPECIFIED; - if (token == "public") { - identity = IdentityType::IDENTITY_TYPE_PUBLIC; - } else if (token == "private") { - identity = IdentityType::IDENTITY_TYPE_PRIVATE_GROUP; - } else if (token == "contacts") { - identity = IdentityType::IDENTITY_TYPE_CONTACTS_GROUP; - } else { - return false; - } - - for (auto existing : identity_types) { - if (existing == identity) return true; - } - identity_types.push_back(identity); - return true; -} - -bool ParseIdentityTypes( - const std::string& arg, - std::vector& identity_types) { - identity_types.clear(); - if (arg == "all") { - return true; - } - size_t start = 0; - while (start < arg.size()) { - size_t comma = arg.find(',', start); - std::string token = arg.substr( - start, comma == std::string::npos ? std::string::npos : comma - start); - if (!AddIdentityTypeFromToken(token, identity_types)) return false; - if (comma == std::string::npos) break; - start = comma + 1; - } - return !identity_types.empty(); -} - -bool ContainsIdentityType(const std::vector& ids, - nearby::internal::IdentityType identity_type) { - for (auto id : ids) { - if (id == identity_type) return true; - } - return false; -} - -std::string IdentitySelectionToString( - const std::vector& identity_types) { - if (identity_types.empty()) { - return "all"; - } - std::string result; - for (size_t i = 0; i < identity_types.size(); ++i) { - if (i > 0) result += ","; - result += IdentityTypeToString(identity_types[i]); - } - return result; -} - -} // namespace - -int main(int argc, char** argv) { - (void)nearby::SystemClock::ElapsedRealtime(); - - nearby::presence::PowerMode power_mode = nearby::presence::PowerMode::kBalanced; - bool screen_on_only = false; - std::string identities_arg = "all"; - std::string manager_app_id; - std::string account_name; - for (int i = 1; i < argc; ++i) { - std::string arg = argv[i]; - if ((arg == "-h") || (arg == "--help")) { - PrintUsage(argv[0]); - return 0; - } - if (arg == "--power") { - if (i + 1 >= argc) { - std::cerr << "Missing value for --power\n"; - return 2; - } - power_mode = ParsePowerMode(argv[++i]); - continue; - } - if (arg == "--screen-on-only") { - screen_on_only = true; - continue; - } - if (arg == "--identities") { - if (i + 1 >= argc) { - std::cerr << "Missing value for --identities\n"; - return 2; - } - identities_arg = argv[++i]; - continue; - } - if (arg == "--manager-app-id") { - if (i + 1 >= argc) { - std::cerr << "Missing value for --manager-app-id\n"; - return 2; - } - manager_app_id = argv[++i]; - continue; - } - if (arg == "--account-name") { - if (i + 1 >= argc) { - std::cerr << "Missing value for --account-name\n"; - return 2; - } - account_name = argv[++i]; - continue; - } - std::cerr << "Unknown argument: " << arg << "\n"; - PrintUsage(argv[0]); - return 2; - } - - std::vector identity_types; - if (!ParseIdentityTypes(identities_arg, identity_types)) { - std::cerr << "Invalid --identities value: " << identities_arg << "\n"; - PrintUsage(argv[0]); - return 2; - } - - struct sigaction action {}; - action.sa_sigaction = QuitHandler; - action.sa_flags = SA_SIGINFO; - sigaction(SIGINT, &action, nullptr); - sigaction(SIGTERM, &action, nullptr); - - SignalProvider signal_provider; - nearby::presence::PresenceServiceImpl service; - auto client = service.CreatePresenceClient(); - - nearby::presence::ScanRequest request; - request.use_ble = true; - request.scan_type = nearby::presence::ScanType::kPresenceScan; - request.power_mode = power_mode; - request.scan_only_when_screen_on = screen_on_only; - request.manager_app_id = manager_app_id; - request.account_name = account_name; - request.identity_types = identity_types; - - const bool scans_non_public = - request.identity_types.empty() || - ContainsIdentityType(request.identity_types, - nearby::internal::IdentityType:: - IDENTITY_TYPE_PRIVATE_GROUP) || - ContainsIdentityType(request.identity_types, - nearby::internal::IdentityType:: - IDENTITY_TYPE_CONTACTS_GROUP); - if (scans_non_public && request.manager_app_id.empty()) { - std::cerr << "Warning: scanning private/contact identities without " - "--manager-app-id usually results in empty remote credentials.\n"; - } - - absl::Status start_status; - absl::Notification started; - auto scan_session = client->StartScan( - request, - nearby::presence::ScanCallback{ - .start_scan_cb = - [&start_status, &started](absl::Status status) { - start_status = status; - started.Notify(); - }, - .on_discovered_cb = - [&signal_provider](nearby::presence::PresenceDevice device) { - std::string remote_hex = - device.GetDeviceIdentityMetadata().bluetooth_mac_address(); - std::optional device_id = ParseHexId(remote_hex); - if (!device_id.has_value()) { - std::cout << "[DISCOVERED] endpoint=" << device.GetEndpointId() - << " identity=" - << IdentityTypeToString(device.GetIdentityType()) - << " remote_hex=" << remote_hex - << " " << FormatProximityUnavailable() << "\n"; - return; - } - std::optional rssi = signal_provider.GetRssi(*device_id); - std::optional tx_power = - signal_provider.GetTxPower(*device_id); - std::string proximity_desc = FormatProximityUnavailable(); - if (rssi.has_value()) { - proximity_desc = FormatProximity(*rssi, tx_power); - } - std::cout << "[DISCOVERED] endpoint=" << device.GetEndpointId() - << " identity=" - << IdentityTypeToString(device.GetIdentityType()) - << " remote_hex=" << remote_hex << " " - << proximity_desc << "\n"; - }, - .on_updated_cb = - [&signal_provider](nearby::presence::PresenceDevice device) { - std::string remote_hex = - device.GetDeviceIdentityMetadata().bluetooth_mac_address(); - std::optional device_id = ParseHexId(remote_hex); - if (!device_id.has_value()) { - std::cout << "[UPDATED] endpoint=" << device.GetEndpointId() - << " identity=" - << IdentityTypeToString(device.GetIdentityType()) - << " remote_hex=" << remote_hex - << " " << FormatProximityUnavailable() << "\n"; - return; - } - std::optional rssi = signal_provider.GetRssi(*device_id); - std::optional tx_power = - signal_provider.GetTxPower(*device_id); - std::string proximity_desc = FormatProximityUnavailable(); - if (rssi.has_value()) { - proximity_desc = FormatProximity(*rssi, tx_power); - } - std::cout << "[UPDATED] endpoint=" << device.GetEndpointId() - << " identity=" - << IdentityTypeToString(device.GetIdentityType()) - << " remote_hex=" << remote_hex << " " - << proximity_desc << "\n"; - }, - .on_lost_cb = - [](nearby::presence::PresenceDevice device) { - std::cout << "[LOST] endpoint=" << device.GetEndpointId() - << " remote_hex=" - << device.GetDeviceIdentityMetadata() - .bluetooth_mac_address() - << "\n"; - }}); - - if (!scan_session.ok()) { - std::cerr << "Failed to initiate scan: " << scan_session.status() << "\n"; - return 1; - } - - started.WaitForNotification(); - if (!start_status.ok()) { - std::cerr << "Scan start failed: " << start_status << "\n"; - client->StopScan(*scan_session); - return 1; - } - - std::cout << "Presence scan started.\n" - << " session_id: " << *scan_session << "\n" - << " identities: " - << IdentitySelectionToString(request.identity_types) << "\n" - << " manager_app_id: " - << (request.manager_app_id.empty() ? "" - : request.manager_app_id) - << "\n" - << " account_name: " - << (request.account_name.empty() ? "" : request.account_name) - << "\n" - << " mode: printing discovered/updated/lost events with proximity " - "estimates when RSSI is available\n" - << "Press Ctrl+C to stop.\n"; - - g_shutdown.WaitForNotification(); - client->StopScan(*scan_session); - std::cout << "Stopped Presence scan.\n"; - return 0; -} diff --git a/presence/presence_service.h b/presence/presence_service.h deleted file mode 100644 index 8fe11637..00000000 --- a/presence/presence_service.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2020-2023 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 THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_SERVICE_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_SERVICE_H_ - -#include -#include - -#include "internal/interop/device_provider.h" -#include "internal/proto/metadata.pb.h" -#include "presence/data_types.h" -#include "presence/presence_client.h" - -namespace nearby { -namespace presence { - -class PresenceService { - public: - virtual ~PresenceService() = default; - - virtual std::unique_ptr CreatePresenceClient() = 0; - - virtual absl::StatusOr StartScan(ScanRequest scan_request, - ScanCallback callback) = 0; - virtual void StopScan(ScanSessionId session_id) = 0; - - virtual absl::StatusOr StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback) = 0; - - virtual void StopBroadcast(BroadcastSessionId session_id) = 0; - - virtual void UpdateDeviceIdentityMetaData( - const ::nearby::internal::DeviceIdentityMetaData& - device_identity_metadata, - bool regen_credentials, absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) = 0; - - virtual NearbyDeviceProvider* GetLocalDeviceProvider() = 0; - - virtual void GetLocalPublicCredentials( - const CredentialSelector& credential_selector, - GetPublicCredentialsResultCallback callback) = 0; - - virtual void UpdateRemotePublicCredentials( - absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& - remote_public_creds, - UpdateRemotePublicCredentialsCallback credentials_updated_cb) = 0; - - // Testing only. - virtual ::nearby::internal::DeviceIdentityMetaData - GetDeviceIdentityMetaData() = 0; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_SERVICE_H_ diff --git a/presence/presence_service_impl.cc b/presence/presence_service_impl.cc deleted file mode 100644 index d107e618..00000000 --- a/presence/presence_service_impl.cc +++ /dev/null @@ -1,83 +0,0 @@ -// 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 "presence/presence_service_impl.h" - -#include -#include -#include - -#include "internal/platform/borrowable.h" -#include "presence/data_types.h" -#include "presence/presence_client_impl.h" -#include "presence/presence_device_provider.h" - -namespace nearby { -namespace presence { - -std::unique_ptr PresenceServiceImpl::CreatePresenceClient() { - return PresenceClientImpl::Factory::Create(lender_.GetBorrowable()); -} - -absl::StatusOr PresenceServiceImpl::StartScan( - ScanRequest scan_request, ScanCallback callback) { - return service_controller_.StartScan(scan_request, std::move(callback)); -} - -void PresenceServiceImpl::StopScan(ScanSessionId id) { - service_controller_.StopScan(id); -} - -absl::StatusOr PresenceServiceImpl::StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback) { - return service_controller_.StartBroadcast(broadcast_request, - std::move(callback)); -} - -void PresenceServiceImpl::StopBroadcast(BroadcastSessionId session) { - service_controller_.StopBroadcast(session); -} - -void PresenceServiceImpl::UpdateDeviceIdentityMetaData( - const ::nearby::internal::DeviceIdentityMetaData& device_identity_metadata, - bool regen_credentials, absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) { - provider_.UpdateDeviceIdentityMetaData(device_identity_metadata); - provider_.SetManagerAppId(manager_app_id); - service_controller_.UpdateDeviceIdentityMetaData( - device_identity_metadata, regen_credentials, manager_app_id, - identity_types, credential_life_cycle_days, - contiguous_copy_of_credentials, std::move(credentials_generated_cb)); -} - -void PresenceServiceImpl::GetLocalPublicCredentials( - const CredentialSelector& credential_selector, - GetPublicCredentialsResultCallback callback) { - service_controller_.GetLocalPublicCredentials(credential_selector, - std::move(callback)); -} - -void PresenceServiceImpl::UpdateRemotePublicCredentials( - absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& remote_public_creds, - UpdateRemotePublicCredentialsCallback credentials_updated_cb) { - service_controller_.UpdateRemotePublicCredentials( - manager_app_id, account_name, remote_public_creds, - std::move(credentials_updated_cb)); -} - -} // namespace presence -} // namespace nearby diff --git a/presence/presence_service_impl.h b/presence/presence_service_impl.h deleted file mode 100644 index a8d5a219..00000000 --- a/presence/presence_service_impl.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2023 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 THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_SERVICE_IMPL_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_SERVICE_IMPL_H_ - -#include -#include - -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "internal/platform/borrowable.h" -#include "internal/platform/implementation/credential_callbacks.h" -#include "internal/platform/single_thread_executor.h" -#include "internal/proto/metadata.pb.h" -#include "presence/broadcast_request.h" -#include "presence/data_types.h" -#include "presence/implementation/broadcast_manager.h" -#include "presence/implementation/connection_authenticator_impl.h" -#include "presence/implementation/credential_manager_impl.h" -#include "presence/implementation/mediums/mediums.h" -#include "presence/implementation/scan_manager.h" -#include "presence/implementation/service_controller_impl.h" -#include "presence/presence_client.h" -#include "presence/presence_device_provider.h" -#include "presence/presence_service.h" -#include "presence/scan_request.h" -#include "internal/interop/device_provider.h" - -namespace nearby { -namespace presence { - -/* - * PresenceService hosts presence functions by routing invokes to the unique - * {@code ServiceController}. PresenceService should be initialized once and - * only once in the process that hosting presence functions. - */ -class PresenceServiceImpl : public PresenceService { - public: - PresenceServiceImpl() = default; - ~PresenceServiceImpl() override { lender_.Release(); } - - std::unique_ptr CreatePresenceClient() override; - - absl::StatusOr StartScan(ScanRequest scan_request, - ScanCallback callback) override; - void StopScan(ScanSessionId session_id) override; - - absl::StatusOr StartBroadcast( - BroadcastRequest broadcast_request, BroadcastCallback callback) override; - - void StopBroadcast(BroadcastSessionId session_id) override; - - void UpdateDeviceIdentityMetaData( - const ::nearby::internal::DeviceIdentityMetaData& - device_identity_metadata, - bool regen_credentials, absl::string_view manager_app_id, - const std::vector& identity_types, - int credential_life_cycle_days, int contiguous_copy_of_credentials, - GenerateCredentialsResultCallback credentials_generated_cb) override; - - NearbyDeviceProvider* GetLocalDeviceProvider() override { - return &provider_; - } - - ::nearby::internal::DeviceIdentityMetaData GetDeviceIdentityMetaData() - override { - return service_controller_.GetDeviceIdentityMetaData(); - } - - void GetLocalPublicCredentials( - const CredentialSelector& credential_selector, - GetPublicCredentialsResultCallback callback) override; - - void UpdateRemotePublicCredentials( - absl::string_view manager_app_id, absl::string_view account_name, - const std::vector& - remote_public_creds, - UpdateRemotePublicCredentialsCallback credentials_updated_cb) override; - - private: - SingleThreadExecutor executor_; - Mediums mediums_; - CredentialManagerImpl credential_manager_{&executor_}; - ScanManager scan_manager_{mediums_, credential_manager_, executor_}; - BroadcastManager broadcast_manager_{mediums_, credential_manager_, executor_}; - ServiceControllerImpl service_controller_{ - &executor_, &credential_manager_, &scan_manager_, &broadcast_manager_}; - ConnectionAuthenticatorImpl connection_authenticator_; - ::nearby::Lender lender_{this}; - PresenceDeviceProvider provider_{&service_controller_, - &connection_authenticator_}; -}; - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_SERVICE_IMPL_H_ diff --git a/presence/presence_service_test.cc b/presence/presence_service_test.cc deleted file mode 100644 index 1f24275b..00000000 --- a/presence/presence_service_test.cc +++ /dev/null @@ -1,152 +0,0 @@ -// 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 "presence/presence_service.h" - -#include -#include -#include -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "internal/platform/count_down_latch.h" -#include "internal/platform/medium_environment.h" -#include "presence/presence_client.h" -#include "presence/presence_service_impl.h" - -namespace nearby { -namespace presence { -namespace { -using DeviceIdentityMetaData = ::nearby::internal::DeviceIdentityMetaData; - -constexpr absl::string_view kManagerAppId = "TEST_MANAGER_APP"; -constexpr absl::string_view kAccountName = "dummy account"; - -class PresenceServiceTest : public testing::Test { - protected: - nearby::MediumEnvironment& env_{nearby::MediumEnvironment::Instance()}; -}; - -DeviceIdentityMetaData CreateTestDeviceIdentityMetaData() { - DeviceIdentityMetaData device_identity_metadata; - device_identity_metadata.set_device_type( - internal::DeviceType::DEVICE_TYPE_PHONE); - device_identity_metadata.set_device_name("NP test device"); - device_identity_metadata.set_bluetooth_mac_address( - "\xFF\xFF\xFF\xFF\xFF\xFF"); - device_identity_metadata.set_device_id("\x12\xab\xcd"); - return device_identity_metadata; -} - -CredentialSelector BuildDefaultCredentialSelector() { - CredentialSelector credential_selector; - credential_selector.manager_app_id = std::string(kManagerAppId); - credential_selector.account_name = std::string(kAccountName); - credential_selector.identity_type = internal::IDENTITY_TYPE_PRIVATE_GROUP; - return credential_selector; -} - -TEST_F(PresenceServiceTest, DefaultConstructorWorks) { - PresenceServiceImpl presence_service; -} - -TEST_F(PresenceServiceTest, StartThenStopScan) { - env_.Start(); - absl::Status scan_result; - ScanCallback scan_callback = { - .start_scan_cb = [&](absl::Status status) { scan_result = status; }, - }; - PresenceServiceImpl presence_service; - std::unique_ptr client = - presence_service.CreatePresenceClient(); - - absl::StatusOr scan_session = client->StartScan( - {}, - { - .start_scan_cb = [&](absl::Status status) { scan_result = status; }, - }); - absl::StatusOr scan_session_with_default_params = - client->StartScan(ScanRequest(), ScanCallback()); - - ASSERT_OK(scan_session); - ASSERT_OK(scan_session_with_default_params); - EXPECT_NE(*scan_session, *scan_session_with_default_params); - - client->StopScan(*scan_session); - client->StopScan(*scan_session_with_default_params); - env_.Stop(); -} - -TEST_F(PresenceServiceTest, UpdatingDeviceIdentityMetaDataWorks) { - PresenceServiceImpl presence_service; - presence_service.UpdateDeviceIdentityMetaData( - CreateTestDeviceIdentityMetaData(), false, "Test app", {}, 3, 1, {}); - EXPECT_EQ(presence_service.GetDeviceIdentityMetaData().SerializeAsString(), - CreateTestDeviceIdentityMetaData().SerializeAsString()); -} - -TEST_F(PresenceServiceTest, TestGetDeviceProvider) { - PresenceServiceImpl presence_service; - EXPECT_NE(presence_service.GetLocalDeviceProvider(), nullptr); -} - -TEST_F(PresenceServiceTest, TestGetPublicCredentials) { - PresenceServiceImpl presence_service; - CredentialSelector selector = BuildDefaultCredentialSelector(); - absl::Status status; - nearby::CountDownLatch fetched_latch(1); - presence_service.GetLocalPublicCredentials( - selector, - {.credentials_fetched_cb = - [&status, &fetched_latch]( - absl::StatusOr> - result) { - status = result.status(); - fetched_latch.CountDown(); - }}); - EXPECT_TRUE(fetched_latch.Await().Ok()); - EXPECT_THAT(status, testing::status::StatusIs(absl::StatusCode::kNotFound)); -} - -TEST_F(PresenceServiceTest, TestUpdateRemotePublicCredentials) { - PresenceServiceImpl presence_service; - internal::SharedCredential public_credential_for_test; - public_credential_for_test.set_identity_type( - internal::IdentityType::IDENTITY_TYPE_CONTACTS_GROUP); - std::vector public_credentials{ - {public_credential_for_test}}; - - nearby::CountDownLatch updated_latch(1); - UpdateRemotePublicCredentialsCallback update_credentials_cb{ - .credentials_updated_cb = - [&updated_latch](absl::Status status) { - if (status.ok()) { - updated_latch.CountDown(); - } - }, - }; - - presence_service.UpdateRemotePublicCredentials( - kManagerAppId, kAccountName, public_credentials, - std::move(update_credentials_cb)); - - EXPECT_TRUE(updated_latch.Await().Ok()); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/presence_zone.cc b/presence/presence_zone.cc deleted file mode 100644 index 2fcdad45..00000000 --- a/presence/presence_zone.cc +++ /dev/null @@ -1,82 +0,0 @@ -// 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 "presence/presence_zone.h" - -namespace nearby { -namespace presence { - -PresenceZone::DistanceBoundary::DistanceBoundary(float min_distance_meters, - float max_distance_meters, - RangeType range_type) noexcept - : min_distance_meters_(min_distance_meters), - max_distance_meters_(max_distance_meters), - range_type_(range_type) {} - -float PresenceZone::DistanceBoundary::GetMinDistanceMeters() const { - return min_distance_meters_; -} - -float PresenceZone::DistanceBoundary::GetMaxDistanceMeters() const { - return max_distance_meters_; -} - -PresenceZone::DistanceBoundary::RangeType -PresenceZone::DistanceBoundary::GetRangeType() const { - return range_type_; -} - -PresenceZone::AngleOfArrivalBoundary::AngleOfArrivalBoundary( - float min_angle_degrees, float max_angle_degrees) noexcept - : min_angle_degrees_(min_angle_degrees), - max_angle_degrees_(max_angle_degrees) {} - -float PresenceZone::AngleOfArrivalBoundary::GetMinAngleDegrees() const { - return min_angle_degrees_; -} - -float PresenceZone::AngleOfArrivalBoundary::GetMaxAngleDegrees() const { - return max_angle_degrees_; -} - -PresenceZone::PresenceZone( - const DistanceBoundary& distance_boundary, - const AngleOfArrivalBoundary& azimuth_angle_boundary, - const AngleOfArrivalBoundary& elevation_angle_boundary, - const std::vector& device_motions) - : distance_boundary_(distance_boundary), - azimuth_angle_boundary_(azimuth_angle_boundary), - elevation_angle_boundary_(elevation_angle_boundary), - device_motions_(device_motions) {} - -PresenceZone::DistanceBoundary PresenceZone::GetDistanceBoundary() const { - return distance_boundary_; -} - -PresenceZone::AngleOfArrivalBoundary PresenceZone::GetAzimuthAngleBoundary() - const { - return azimuth_angle_boundary_; -} - -PresenceZone::AngleOfArrivalBoundary PresenceZone::GetElevationAngleBoundary() - const { - return elevation_angle_boundary_; -} - -std::vector PresenceZone::GetLocalDeviceMotions() const { - return device_motions_; -} - -} // namespace presence -} // namespace nearby diff --git a/presence/presence_zone.h b/presence/presence_zone.h deleted file mode 100644 index c2883100..00000000 --- a/presence/presence_zone.h +++ /dev/null @@ -1,108 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_ZONE_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_ZONE_H_ - -#include - -#include "presence/device_motion.h" -namespace nearby { -namespace presence { -class PresenceZone { - public: - class DistanceBoundary { - public: - enum class RangeType { - kRangeUnknown = 0, - kFar, // Distance is very far away from the peer device. - kWithinReach, // Distance is very close to the peer device, typically - // within one meter or less. - kWithinTap, // Distance is within tap range to the peer device, typically - // within ~0.127 meters. - }; - DistanceBoundary(float min_distance_meters = 0, - float max_distance_meters = 0, - RangeType range_type = RangeType::kRangeUnknown) noexcept; - float GetMinDistanceMeters() const; - float GetMaxDistanceMeters() const; - RangeType GetRangeType() const; - - private: - const float min_distance_meters_; - const float max_distance_meters_; - const RangeType range_type_; - }; - - class AngleOfArrivalBoundary { - public: - AngleOfArrivalBoundary(float min_angle_degrees = 0, - float max_angle_degrees = 0) noexcept; - float GetMinAngleDegrees() const; - float GetMaxAngleDegrees() const; - - private: - const float min_angle_degrees_; - const float max_angle_degrees_; - }; - - PresenceZone(const DistanceBoundary& = {}, const AngleOfArrivalBoundary& = {}, - const AngleOfArrivalBoundary& = {}, - const std::vector& = {}); - DistanceBoundary GetDistanceBoundary() const; - AngleOfArrivalBoundary GetAzimuthAngleBoundary() const; - AngleOfArrivalBoundary GetElevationAngleBoundary() const; - std::vector GetLocalDeviceMotions() const; - - private: - const DistanceBoundary distance_boundary_; - const AngleOfArrivalBoundary azimuth_angle_boundary_; - const AngleOfArrivalBoundary elevation_angle_boundary_; - const std::vector device_motions_; -}; - -inline bool operator==(const PresenceZone::DistanceBoundary& d1, - const PresenceZone::DistanceBoundary& d2) { - return d1.GetMinDistanceMeters() == d2.GetMinDistanceMeters() && - d1.GetMaxDistanceMeters() == d2.GetMaxDistanceMeters() && - d1.GetRangeType() == d2.GetRangeType(); -} -inline bool operator!=(const PresenceZone::DistanceBoundary& d1, - const PresenceZone::DistanceBoundary& d2) { - return !(d1 == d2); -} -inline bool operator==(const PresenceZone::AngleOfArrivalBoundary& a1, - const PresenceZone::AngleOfArrivalBoundary& a2) { - return a1.GetMinAngleDegrees() == a2.GetMinAngleDegrees() && - a1.GetMaxAngleDegrees() == a2.GetMaxAngleDegrees(); -} -inline bool operator!=(const PresenceZone::AngleOfArrivalBoundary& a1, - const PresenceZone::AngleOfArrivalBoundary& a2) { - return !(a1 == a2); -} - -inline bool operator==(const PresenceZone& z1, const PresenceZone& z2) { - return z1.GetDistanceBoundary() == z2.GetDistanceBoundary() && - z1.GetAzimuthAngleBoundary() == z2.GetAzimuthAngleBoundary() && - z1.GetElevationAngleBoundary() == z2.GetElevationAngleBoundary() && - z1.GetLocalDeviceMotions() == z2.GetLocalDeviceMotions(); -} -inline bool operator!=(const PresenceZone& z1, const PresenceZone& z2) { - return !(z1 == z2); -} - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_PRESENCE_ZONE_H_ diff --git a/presence/presence_zone_test.cc b/presence/presence_zone_test.cc deleted file mode 100644 index e813bf48..00000000 --- a/presence/presence_zone_test.cc +++ /dev/null @@ -1,221 +0,0 @@ -// 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 "presence/presence_zone.h" - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "presence/device_motion.h" - -namespace nearby { -namespace presence { -namespace { - -using DistanceBoundary = nearby::presence::PresenceZone::DistanceBoundary; -using RangeType = nearby::presence::PresenceZone::DistanceBoundary::RangeType; -using AngleOfArrivalBoundary = - nearby::presence::PresenceZone::AngleOfArrivalBoundary; - -static const float kDefaultDistanceMeters = 0; -static const float kTestMinDistanceMeters = 1; -static const float kTestMaxDistanceMeters = 2; - -static const float kDefaultDegrees = 0; -static const float kTestMinAngleDegrees = 10; -static const float kTestMaxAngleDegrees = 20; - -static const float kTestConfidence = 0.1; - -static const RangeType kDefaultRangeType = RangeType::kRangeUnknown; -static const RangeType kTestRangeType = RangeType::kFar; - -static const DistanceBoundary kDefaultDistanceBoundary; -static const DistanceBoundary kTestDistanceBoundary = { - kTestMinDistanceMeters, kTestMaxDistanceMeters, kTestRangeType}; -static const AngleOfArrivalBoundary kDefaultAngleBoundary; -static const AngleOfArrivalBoundary kTestAzimuthAngleBoundary = { - kTestMinAngleDegrees, kTestMaxAngleDegrees}; -static const AngleOfArrivalBoundary kTestElevationAngleBoundary = { - kTestMinAngleDegrees, kTestMaxAngleDegrees}; -static const DeviceMotion kTestDeviceMotion = { - DeviceMotion::MotionType::kPointAndHold, kTestConfidence}; - -TEST(DistanceBoundaryTest, DefaultConstructorWorks) { - DistanceBoundary boundary; - EXPECT_EQ(boundary.GetMinDistanceMeters(), kDefaultDistanceMeters); - EXPECT_EQ(boundary.GetMaxDistanceMeters(), kDefaultDistanceMeters); - EXPECT_EQ(boundary.GetRangeType(), kDefaultRangeType); -} - -TEST(DistanceBoundaryTest, DefaultEquals) { - DistanceBoundary boundary1; - DistanceBoundary boundary2; - EXPECT_EQ(boundary1, boundary2); -} - -TEST(DistanceBoundaryTest, PartiallyInitializationWorks) { - DistanceBoundary boundary1 = {kTestMinDistanceMeters, kTestMaxDistanceMeters}; - DistanceBoundary boundary2 = {kTestMinDistanceMeters}; - EXPECT_EQ(boundary1.GetMinDistanceMeters(), kTestMinDistanceMeters); - EXPECT_EQ(boundary1.GetMaxDistanceMeters(), kTestMaxDistanceMeters); - EXPECT_EQ(boundary1.GetRangeType(), kDefaultRangeType); - EXPECT_EQ(boundary2.GetMinDistanceMeters(), kTestMinDistanceMeters); - EXPECT_EQ(boundary2.GetMaxDistanceMeters(), kDefaultDistanceMeters); - EXPECT_EQ(boundary2.GetRangeType(), kDefaultRangeType); -} - -TEST(DistanceBoundaryTest, ExplicitInitEquals) { - DistanceBoundary boundary1 = {kTestMinDistanceMeters, kTestMaxDistanceMeters, - kTestRangeType}; - DistanceBoundary boundary2 = {kTestMinDistanceMeters, kTestMaxDistanceMeters, - kTestRangeType}; - EXPECT_EQ(boundary1.GetMinDistanceMeters(), kTestMinDistanceMeters); - EXPECT_EQ(boundary1.GetMaxDistanceMeters(), kTestMaxDistanceMeters); - EXPECT_EQ(boundary1.GetRangeType(), kTestRangeType); - EXPECT_EQ(boundary1, boundary2); -} - -TEST(DistanceBoundaryTest, ExplicitInitNotEquals) { - DistanceBoundary boundary1 = {kTestMinDistanceMeters, kTestMaxDistanceMeters, - kTestRangeType}; - DistanceBoundary boundary2 = {kTestMinDistanceMeters + 0.1f, - kTestMaxDistanceMeters, kTestRangeType}; - EXPECT_NE(boundary1, boundary2); -} - -TEST(DistanceBoundaryTest, CopyInitEquals) { - DistanceBoundary boundary1 = {kTestMinDistanceMeters, kTestMaxDistanceMeters, - kTestRangeType}; - DistanceBoundary boundary2 = {boundary1}; - EXPECT_EQ(boundary1, boundary2); -} - -TEST(AngleOfArrivalBoundaryTest, DefaultConstructorWorks) { - AngleOfArrivalBoundary aoa_boundary; - EXPECT_EQ(aoa_boundary.GetMinAngleDegrees(), kDefaultDegrees); - EXPECT_EQ(aoa_boundary.GetMaxAngleDegrees(), kDefaultDegrees); -} - -TEST(AngleOfArrivalBoundaryTest, DefaultEquals) { - AngleOfArrivalBoundary aoa_boundary1; - AngleOfArrivalBoundary aoa_boundary2; - EXPECT_EQ(aoa_boundary1, aoa_boundary2); -} - -TEST(AngleOfArrivalBoundaryTest, PartiallyInitializationWorks) { - AngleOfArrivalBoundary aoa_boundary = {kTestMinAngleDegrees}; - EXPECT_EQ(aoa_boundary.GetMinAngleDegrees(), kTestMinAngleDegrees); - EXPECT_EQ(aoa_boundary.GetMaxAngleDegrees(), kDefaultDegrees); -} - -TEST(AngleOfArrivalBoundaryTest, ExplicitInitEquals) { - AngleOfArrivalBoundary aoa_boundary1 = {kTestMinAngleDegrees, - kTestMaxAngleDegrees}; - AngleOfArrivalBoundary aoa_boundary2 = {kTestMinAngleDegrees, - kTestMaxAngleDegrees}; - EXPECT_EQ(aoa_boundary1.GetMinAngleDegrees(), kTestMinAngleDegrees); - EXPECT_EQ(aoa_boundary1.GetMaxAngleDegrees(), kTestMaxAngleDegrees); - EXPECT_EQ(aoa_boundary1, aoa_boundary2); -} - -TEST(AngleOfArrivalBoundaryTest, ExplicitInitNotEquals) { - AngleOfArrivalBoundary aoa_boundary1 = {kTestMinAngleDegrees, - kTestMaxAngleDegrees}; - AngleOfArrivalBoundary aoa_boundary2 = {kTestMinAngleDegrees, - kTestMaxAngleDegrees + 0.1f}; - EXPECT_NE(aoa_boundary1, aoa_boundary2); -} - -TEST(AngleOfArrivalBoundaryTest, CopyInitEquals) { - AngleOfArrivalBoundary aoa_boundary1 = {kTestMinAngleDegrees, - kTestMaxAngleDegrees}; - AngleOfArrivalBoundary aoa_boundary2 = {aoa_boundary1}; - EXPECT_EQ(aoa_boundary1, aoa_boundary2); -} - -TEST(PresenceZoneTest, DefaultConstructorWorks) { - PresenceZone zone; - EXPECT_EQ(zone.GetDistanceBoundary(), kDefaultDistanceBoundary); - EXPECT_EQ(zone.GetAzimuthAngleBoundary(), kDefaultAngleBoundary); - EXPECT_EQ(zone.GetElevationAngleBoundary(), kDefaultAngleBoundary); - EXPECT_EQ(zone.GetLocalDeviceMotions().capacity(), 0); -} - -TEST(PresenceZoneTest, DefaultEquals) { - PresenceZone zone1; - PresenceZone zone2; - EXPECT_EQ(zone1, zone2); -} - -TEST(PresenceZoneTest, PartiallyInitializationWorks) { - PresenceZone zone1 = {kTestDistanceBoundary, kTestAzimuthAngleBoundary, - kTestElevationAngleBoundary}; - PresenceZone zone2 = {kTestDistanceBoundary, kTestAzimuthAngleBoundary}; - PresenceZone zone3 = {kTestDistanceBoundary}; - EXPECT_EQ(zone1.GetDistanceBoundary(), kTestDistanceBoundary); - EXPECT_EQ(zone1.GetAzimuthAngleBoundary(), kTestAzimuthAngleBoundary); - EXPECT_EQ(zone1.GetElevationAngleBoundary(), kTestElevationAngleBoundary); - EXPECT_EQ(zone1.GetLocalDeviceMotions().capacity(), 0); - EXPECT_EQ(zone2.GetDistanceBoundary(), kTestDistanceBoundary); - EXPECT_EQ(zone2.GetAzimuthAngleBoundary(), kTestAzimuthAngleBoundary); - EXPECT_EQ(zone2.GetElevationAngleBoundary(), kDefaultAngleBoundary); - EXPECT_EQ(zone2.GetLocalDeviceMotions().capacity(), 0); - EXPECT_EQ(zone3.GetDistanceBoundary(), kTestDistanceBoundary); - EXPECT_EQ(zone3.GetAzimuthAngleBoundary(), kDefaultAngleBoundary); - EXPECT_EQ(zone3.GetElevationAngleBoundary(), kDefaultAngleBoundary); - EXPECT_EQ(zone3.GetLocalDeviceMotions().capacity(), 0); -} - -TEST(PresenceZoneTest, ExplicitInitEquals) { - PresenceZone zone1 = {kTestDistanceBoundary, - kTestAzimuthAngleBoundary, - kTestElevationAngleBoundary, - {kTestDeviceMotion}}; - PresenceZone zone2 = {kTestDistanceBoundary, - kTestAzimuthAngleBoundary, - kTestElevationAngleBoundary, - {kTestDeviceMotion}}; - EXPECT_EQ(zone1.GetDistanceBoundary(), kTestDistanceBoundary); - EXPECT_EQ(zone1.GetAzimuthAngleBoundary(), kTestAzimuthAngleBoundary); - EXPECT_EQ(zone1.GetElevationAngleBoundary(), kTestElevationAngleBoundary); - EXPECT_EQ(zone1.GetLocalDeviceMotions().size(), 1); - EXPECT_EQ(zone1.GetLocalDeviceMotions()[0], kTestDeviceMotion); - EXPECT_EQ(zone1, zone2); -} - -TEST(PresenceZoneTest, ExplicitInitNotEquals) { - PresenceZone zone1 = {kTestDistanceBoundary, - kTestAzimuthAngleBoundary, - kTestElevationAngleBoundary, - {kTestDeviceMotion}}; - PresenceZone zone2 = {kTestDistanceBoundary, - kTestAzimuthAngleBoundary, - kTestElevationAngleBoundary, - {}}; - EXPECT_NE(zone1, zone2); -} - -TEST(PresenceZoneTest, CopyInitEquals) { - PresenceZone zone1 = {kTestDistanceBoundary, - kTestAzimuthAngleBoundary, - kTestElevationAngleBoundary, - {kTestDeviceMotion}}; - PresenceZone zone2 = {zone1}; - EXPECT_EQ(zone1, zone2); -} - -} // namespace -} // namespace presence -} // namespace nearby diff --git a/presence/proto/BUILD b/presence/proto/BUILD deleted file mode 100644 index 6351491c..00000000 --- a/presence/proto/BUILD +++ /dev/null @@ -1,29 +0,0 @@ -# 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. - -load("@com_google_protobuf//bazel:cc_proto_library.bzl", "cc_proto_library") -load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library") - -proto_library( - name = "presence_frame_proto", - srcs = ["presence_frame.proto"], -) - -cc_proto_library( - name = "presence_frame_cc_proto", - visibility = [ - "//presence:__subpackages__", - ], - deps = [":presence_frame_proto"], -) diff --git a/presence/proto/presence_frame.proto b/presence/proto/presence_frame.proto deleted file mode 100644 index 1f18cbcd..00000000 --- a/presence/proto/presence_frame.proto +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2023 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. - -syntax = "proto2"; - -package nearby.presence; - -// import "storage/datapol/annotations/proto/semantic_annotations.proto"; - -option optimize_for = LITE_RUNTIME; -option java_package = "com.google.android.gms.nearby.presence"; -option java_outer_classname = "PresenceFrameProtocol"; - -/** - * Nearby Presence’s wire frame format - */ -message PresenceFrame { - /** The version of the frame. */ - enum Version { - UNKNOWN_VERSION = 0; - VERSION_1 = 1; - } - - /** The version 1 frame for Nearby Presence. */ - optional V1Frame v1_frame = 1; -} - -/** - * Nearby Presence’s v1 wire frame format - */ -message V1Frame { - oneof Message { - // Control messages for connection status update. - ControlFrame control_frame = 1; - - // This frame will be shared by public and provisioned identities - DeviceIdentityFrame device_identity_frame = 2; - - // First message from discovery device to broadcaster. - ConnectionInitFrame connection_init_frame = 3; - - // Sent by broadcaster to notify discoverer its UWB capability. - UwbControleeCapabilities uwb_controlee_capabilities_frame = 4; - - // Sent by discoverer to notify broadcaster the UWB ranging parameters. - UwbConnectionInfo uwb_connection_info = 5; - - // Used for identity authentication. - PresenceAuthenticationFrame authentication_frame = 6; - } -} - -/** - * A frame contains the local device’s information, shared by public and - * provisioned identities. - */ -message DeviceIdentityFrame { - optional string device_name = 1; - - // Without this field, the device will not be connectable. - optional bytes bluetooth_mac_address = 2; // deprecated - - optional string device_image_url = 3; - - optional string model_id = 4; - - repeated int32 action = 5 [packed = true]; // deprecated - - optional string device_model_name = 6; - - optional int32 device_type = 7; -} - -/** - * A frame sent from discovery device to broadcast device when connection - * initialized, or when UWB needs to be restarted, or when dedup hint rotates. - */ -message ConnectionInitFrame { - // Discovery-side action list - repeated int32 actions = 1 [packed = true]; - - // Discovery-side identity type - optional int32 identity_type = 2; - - // Should the broadcaster (re)-start UWB OOB process or not. - optional bool uwb_enable = 3; - - // Used for device de-duplicate. Same device ID means the same physical - // device. When dedup hint rotates, this will be updated and send again. - optional int64 device_unique_id = 4; -} - -/** - * A frame that describes the controlee's UWB capabilities. - */ -message UwbControleeCapabilities { - optional bytes controlee_address = 1; - - repeated int32 supported_config_ids = 2 [packed = true]; - - repeated int32 supported_channels = 3 [packed = true]; - - optional int32 min_ranging_interval_ms = 4; - - optional bytes sub_session_id = 5 /* type = ST_SESSION_ID */; - - optional bytes sub_session_key = 6 - /* type = ST_SECURITY_MATERIAL */; - - optional bool ranging_disabled = 7; - - // Used for device de-duplicate. Same device ID means the same physical - // device. - optional int64 device_unique_id = 8; - - optional bool is_distance_supported = 9 [default = true]; - optional bool is_azimuth_supported = 10 [default = true]; - optional bool is_elevation_supported = 11 [default = false]; - optional float min_slot_duration_ms = 12 [default = 2.0]; - repeated int32 supported_ntf_configs = 13 [packed = true]; - optional bool is_ranging_interval_reconfigure_supported = 14 - [default = false]; - repeated int32 supported_slot_durations = 15 [packed = true]; - repeated int32 supported_ranging_update_rates = 16 [packed = true]; - optional int32 chip_count = 17 [default = 1]; - repeated UwbMultiChipInfo multi_chip_info = 18; - optional bool is_background_ranging_supported = 19 [default = false]; -} - -/* A frame containing info needed per chip in a multi-chip environment. */ -message UwbMultiChipInfo { - optional bytes controlee_address = 1; - optional string chip_id = 2; -} - -/** - * A frame that describes the connection info of the UWB ranging session. - */ -message UwbConnectionInfo { - optional bytes controller_address = 1; - - optional int32 channel = 2; - - optional int32 preamble_index = 3; - - optional int32 config_id = 4; - - optional int32 ranging_interval_ms = 5; - - optional int32 session_id = 6 /* type = ST_SESSION_ID */; - - optional bytes vendor_id = 7; - - optional bytes static_sts_iv = 8; - - optional bytes session_key = 9 - /* type = ST_SECURITY_MATERIAL */; - - optional bool ranging_disabled = 10; -} - -/** - * Control frames that used for connection status update. - */ -message ControlFrame { - /** The defined Control type of the frame. */ - enum ControlType { - UNKNOWN_TYPE = 0; - - // Keeps the connection alive. - KEEP_ALIVE = 1; - - // Notifies the peer that the connection will be closed immediately. - DISCONNECT = 2; - } - - optional ControlType type = 1; -} - -message PresenceAuthenticationFrame { - // The version of this frame and protocol. - optional int32 version = 1; - - // A signature signed by the private key in the LocalCredential. - optional bytes private_key_signature = 2; - - // A hash of a shared credential's id. Used to prove ownership of shared - // credentials used in discovery. - optional bytes shared_credential_id_hash = 3; - - // A hash of a local credential's id. Used to expedite local credential - // verification. - optional bytes credential_id_hash = 4 [deprecated = true]; -} diff --git a/presence/rust/README b/presence/rust/README deleted file mode 100644 index 9ca03064..00000000 --- a/presence/rust/README +++ /dev/null @@ -1 +0,0 @@ -This directory contains Rust implementation of Nearby Presence. diff --git a/presence/scan_request.h b/presence/scan_request.h deleted file mode 100644 index 69aaad01..00000000 --- a/presence/scan_request.h +++ /dev/null @@ -1,151 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_SCAN_REQUEST_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_SCAN_REQUEST_H_ - -#include -#include - -#include "absl/types/variant.h" -#include "internal/proto/credential.pb.h" -#include "presence/data_element.h" -#include "presence/power_mode.h" - -namespace nearby { -namespace presence { - -constexpr char kPresenceScanFilterName[] = "PresenceScanFilter"; -constexpr char kLegacyPresenceScanFilterName[] = "LegacyPresenceScanFilter"; - -enum class ScanType { - kUnspecifiedScan = 0, - kFastPairScan = 1, - kPresenceScan = 2, -}; - -/** - * Filter for scanning a nearby presence device. - * Supports Android U and above. - */ -struct PresenceScanFilter { - ScanType scan_type; - // A bundle of extended properties for matching. - std::vector extended_properties; -}; - -/** - * Used to support legacy Android T. Filter for scanning a nearby presence - * device. - */ -struct LegacyPresenceScanFilter { - ScanType scan_type; - // Minimum path loss threshold of the received scan result. - int path_loss_threshold; - - // Android T needs clients to provide remote public credentials in scan - // requests. - std::vector remote_public_credentials; - - // A list of presence actions for matching. Matching condition is met as - // long as there’s one or more equal actions between Scan actions and - // Broadcast actions. - // Considered to use enum, and team agreed to use int to support potential - // un-reserved values. Already existing reserved interger values are defined - // in {@code ActionFactory}. - std::vector actions; - - // A bundle of extended properties for matching. - std::vector extended_properties; -}; - -inline bool operator==(const PresenceScanFilter& a, - const PresenceScanFilter& b) { - return a.scan_type == b.scan_type && - a.extended_properties == b.extended_properties; -} - -inline bool operator!=(const PresenceScanFilter& a, - const PresenceScanFilter& b) { - return !(a == b); -} - -inline bool operator==(const LegacyPresenceScanFilter& a, - const LegacyPresenceScanFilter& b) { - if (a.scan_type != b.scan_type || - a.path_loss_threshold != b.path_loss_threshold || - a.actions != b.actions || - a.remote_public_credentials.size() != - b.remote_public_credentials.size() || - a.extended_properties != b.extended_properties) - return false; - for (size_t i = 0; i < a.remote_public_credentials.size(); ++i) { - if (a.remote_public_credentials[i].SerializeAsString() != - b.remote_public_credentials[i].SerializeAsString()) - return false; - } - return true; -} - -inline bool operator!=(const LegacyPresenceScanFilter& a, - const LegacyPresenceScanFilter& b) { - return !(a == b); -} - -/** - * An encapsulation of various parameters for requesting nearby scans. - */ -struct ScanRequest { - // Same as Metadata.account_name, to fetch private credential - // to broadcast. - std::string account_name; - - // Specifies which manager app to use to get credendentials for scan. - std::string manager_app_id; - - // Used to specify which types of remote SharedCredential to use during the - // scan. If empty, use all available types of remote SharedCredential. - std::vector identity_types; - - // For new Nearby SDK client (like chromeOs and Android U), use - // PresenceScanFilter; for Android T, use LegacyPresenceScanFilter. - std::vector > - scan_filters; - - // Whether to use BLE in the scan. - bool use_ble = false; - - ScanType scan_type = ScanType::kUnspecifiedScan; - PowerMode power_mode = PowerMode::kNoPower; - bool scan_only_when_screen_on = false; -}; - -inline bool operator==(const ScanRequest& a, const ScanRequest& b) { - if (a.identity_types != b.identity_types) return false; - if (a.scan_filters != b.scan_filters) return false; - return a.scan_only_when_screen_on == b.scan_only_when_screen_on && - a.power_mode == b.power_mode && a.scan_type == b.scan_type && - a.use_ble == b.use_ble && a.account_name == b.account_name && - a.identity_types == b.identity_types && - a.manager_app_id == b.manager_app_id; -} - -inline bool operator!=(const ScanRequest& a, const ScanRequest& b) { - return !(a == b); -} - -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_SCAN_REQUEST_H_ diff --git a/presence/scan_request_builder.cc b/presence/scan_request_builder.cc deleted file mode 100644 index 2d9b5740..00000000 --- a/presence/scan_request_builder.cc +++ /dev/null @@ -1,89 +0,0 @@ -// 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 "presence/scan_request_builder.h" - -#include -#include - -#include "absl/strings/string_view.h" -#include "absl/types/variant.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { - -using ::nearby::internal::IdentityType; - -ScanRequestBuilder& ScanRequestBuilder::SetAccountName( - absl::string_view account_name) { - request_.account_name = std::string(account_name); - return *this; -} - -ScanRequestBuilder& ScanRequestBuilder::SetPowerMode(PowerMode power_mode) { - request_.power_mode = power_mode; - return *this; -} - -ScanRequestBuilder& ScanRequestBuilder::SetScanType(ScanType scan_type) { - request_.scan_type = scan_type; - return *this; -} - -ScanRequestBuilder& ScanRequestBuilder::AddIdentityType( - IdentityType identity_type) { - request_.identity_types.push_back(identity_type); - return *this; -} - -ScanRequestBuilder& ScanRequestBuilder::SetIdentityTypes( - std::vector types) { - request_.identity_types = types; - return *this; -} - -ScanRequestBuilder& ScanRequestBuilder::AddScanFilter( - absl::variant scan_filter) { - request_.scan_filters.push_back(scan_filter); - return *this; -} - -ScanRequestBuilder& ScanRequestBuilder::SetScanFilters( - std::vector> - filters) { - request_.scan_filters = filters; - return *this; -} - -ScanRequestBuilder& ScanRequestBuilder::SetUseBle(bool use_ble) { - request_.use_ble = use_ble; - return *this; -} - -ScanRequestBuilder& ScanRequestBuilder::SetOnlyScreenOnScan( - bool screen_on_only_scan) { - request_.scan_only_when_screen_on = screen_on_only_scan; - return *this; -} - -ScanRequestBuilder& ScanRequestBuilder::SetManagerAppId( - absl::string_view manager_app_id) { - request_.manager_app_id = std::string(manager_app_id); - return *this; -} - -ScanRequest ScanRequestBuilder::Build() { return this->request_; } - -} // namespace presence -} // namespace nearby diff --git a/presence/scan_request_builder.h b/presence/scan_request_builder.h deleted file mode 100644 index 80bf770d..00000000 --- a/presence/scan_request_builder.h +++ /dev/null @@ -1,56 +0,0 @@ -// 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. - -#ifndef THIRD_PARTY_NEARBY_PRESENCE_SCAN_REQUEST_BUILDER_H_ -#define THIRD_PARTY_NEARBY_PRESENCE_SCAN_REQUEST_BUILDER_H_ - -#include - -#include "absl/strings/string_view.h" -#include "internal/proto/credential.pb.h" -#include "presence/power_mode.h" -#include "presence/presence_zone.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { -class ScanRequestBuilder { - private: - ScanRequest request_; - - public: - ScanRequestBuilder& SetAccountName(absl::string_view account_name); - ScanRequestBuilder& SetPowerMode(PowerMode power_mode); - ScanRequestBuilder& SetScanType(ScanType scan_type); - ScanRequestBuilder& AddIdentityType( - nearby::internal::IdentityType identity_type); - ScanRequestBuilder& SetIdentityTypes( - std::vector types); - ScanRequestBuilder& AddScanFilter( - absl::variant scan_filter); - ScanRequestBuilder& SetScanFilters( - std::vector> - scan_filters); - ScanRequestBuilder& SetUseBle(bool use_ble); - ScanRequestBuilder& SetOnlyScreenOnScan(bool screen_on_only_scan); - ScanRequestBuilder& SetManagerAppId(absl::string_view manager_app_id); - ScanRequest Build(); - inline bool operator==(const ScanRequestBuilder& other) const { - return request_ == other.request_; - } -}; -} // namespace presence -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_PRESENCE_SCAN_REQUEST_BUILDER_H_ diff --git a/presence/scan_request_builder_test.cc b/presence/scan_request_builder_test.cc deleted file mode 100644 index 1d0fd6be..00000000 --- a/presence/scan_request_builder_test.cc +++ /dev/null @@ -1,194 +0,0 @@ -// 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 "presence/scan_request_builder.h" - -#include - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "internal/proto/credential.pb.h" -#include "presence/power_mode.h" -#include "presence/scan_request.h" - -namespace nearby { -namespace presence { -namespace { - -using ::nearby::internal::IdentityType; - -constexpr absl::string_view kAccountName = "Google User"; -constexpr bool kUseBle = true; -constexpr bool kOnlyScreenOnScan = true; -const IdentityType kIdentity = IdentityType::IDENTITY_TYPE_PRIVATE_GROUP; -const ScanType kScanType = ScanType::kPresenceScan; -const PowerMode powerMode = PowerMode::kLowLatency; -constexpr absl::string_view kManagerAppId = "Google App Manager"; -DataElement CreateTestDataElement() { - return {DataElement::kTxPowerFieldType, "1"}; -} -PresenceScanFilter CreateTestPresenceScanFilter() { - return {.scan_type = kScanType, - .extended_properties = {CreateTestDataElement()}}; -} -LegacyPresenceScanFilter CreateTestLegacyPresenceScanFilter() { - return {.scan_type = kScanType}; -} - -TEST(ScanRequestBuilderTest, TestConstructor) { - EXPECT_FALSE(std::is_trivially_constructible::value); -} - -TEST(ScanRequestBuilderTest, TestSetAccountName) { - ScanRequestBuilder builder; - builder.SetAccountName(kAccountName); - ScanRequest sr = builder.Build(); - EXPECT_EQ(sr.account_name, kAccountName); -} - -TEST(ScanRequestBuilderTest, TestSetPowerMode) { - ScanRequestBuilder builder; - builder.SetPowerMode(powerMode); - ScanRequest sr = builder.Build(); - EXPECT_EQ(sr.power_mode, powerMode); -} - -TEST(ScanRequestBuilderTest, TestSetScanType) { - ScanRequestBuilder builder; - builder.SetScanType(kScanType); - ScanRequest sr = builder.Build(); - EXPECT_EQ(sr.scan_type, kScanType); -} - -TEST(ScanRequestBuilderTest, TestAddIdentityType) { - ScanRequestBuilder builder; - builder.AddIdentityType(kIdentity); - ScanRequest sr = builder.Build(); - EXPECT_EQ(sr.identity_types.size(), 1); - EXPECT_EQ(sr.identity_types[0], kIdentity); -} - -TEST(ScanRequestBuilderTest, TestSetIdentityTypes) { - ScanRequestBuilder builder; - std::vector types = {kIdentity}; - builder.SetIdentityTypes(types); - ScanRequest sr = builder.Build(); - EXPECT_EQ(sr.identity_types.size(), 1); - EXPECT_EQ(sr.identity_types, types); -} - -TEST(ScanRequestBuilderTest, TestAddScanFilter) { - ScanRequestBuilder builder; - PresenceScanFilter presenceScanFilter = CreateTestPresenceScanFilter(); - LegacyPresenceScanFilter legacyPresenceScanFilter = - CreateTestLegacyPresenceScanFilter(); - builder.AddScanFilter(presenceScanFilter); - builder.AddScanFilter(legacyPresenceScanFilter); - ScanRequest sr = builder.Build(); - EXPECT_EQ(sr.scan_filters.size(), 2); - EXPECT_TRUE(absl::holds_alternative(sr.scan_filters[0])); - EXPECT_NE(&absl::get(sr.scan_filters[0]), - &presenceScanFilter); - EXPECT_EQ(absl::get(sr.scan_filters[0]), - presenceScanFilter); - EXPECT_TRUE( - absl::holds_alternative(sr.scan_filters[1])); - EXPECT_NE(&absl::get(sr.scan_filters[1]), - &legacyPresenceScanFilter); - EXPECT_EQ(absl::get(sr.scan_filters[1]), - legacyPresenceScanFilter); -} - -TEST(ScanRequestBuilderTest, TestSetScanFilters) { - PresenceScanFilter presenceScanFilter = CreateTestPresenceScanFilter(); - LegacyPresenceScanFilter legacyPresenceScanFilter = - CreateTestLegacyPresenceScanFilter(); - std::vector> - filterList = {presenceScanFilter, legacyPresenceScanFilter}; - ScanRequestBuilder builder; - builder.SetScanFilters(filterList); - ScanRequest sr = builder.Build(); - EXPECT_EQ(sr.scan_filters.size(), 2); - EXPECT_TRUE(absl::holds_alternative(sr.scan_filters[0])); - EXPECT_NE(&absl::get(sr.scan_filters[0]), - &presenceScanFilter); - EXPECT_EQ(absl::get(sr.scan_filters[0]), - presenceScanFilter); - EXPECT_TRUE( - absl::holds_alternative(sr.scan_filters[1])); - EXPECT_NE(&absl::get(sr.scan_filters[1]), - &legacyPresenceScanFilter); - EXPECT_EQ(absl::get(sr.scan_filters[1]), - legacyPresenceScanFilter); -} - -TEST(ScanRequestBuilderTest, TestNotEqualScanFilter) { - ScanRequestBuilder builderLegacy, builderModern; - ScanRequest legacy = - builderLegacy.AddScanFilter(LegacyPresenceScanFilter{}).Build(); - ScanRequest modern = - builderModern.AddScanFilter(PresenceScanFilter{}).Build(); - EXPECT_NE(legacy, modern); -} - -TEST(ScanRequestBuilderTest, TestSetUseBle) { - ScanRequestBuilder builder; - builder.SetUseBle(kUseBle); - ScanRequest sr = builder.Build(); - EXPECT_EQ(sr.use_ble, kUseBle); -} - -TEST(ScanRequestBuilderTest, TestSetManagerAppId) { - ScanRequestBuilder builder; - builder.SetManagerAppId(kManagerAppId); - ScanRequest sr = builder.Build(); - EXPECT_EQ(sr.manager_app_id, kManagerAppId); -} - -TEST(ScanRequestBuilderTest, TestSetOnlyScreenOnScan) { - ScanRequestBuilder builder; - builder.SetOnlyScreenOnScan(kOnlyScreenOnScan); - ScanRequest sr = builder.Build(); - EXPECT_EQ(sr.scan_only_when_screen_on, kOnlyScreenOnScan); -} - -TEST(ScanRequestBuilderTest, TestChainCalls) { - ScanRequestBuilder builder; - ScanRequest sr = builder.SetAccountName(kAccountName) - .SetPowerMode(powerMode) - .SetOnlyScreenOnScan(kOnlyScreenOnScan) - .SetUseBle(kUseBle) - .SetManagerAppId(kManagerAppId) - .Build(); - EXPECT_EQ(sr.account_name, kAccountName); - EXPECT_EQ(sr.scan_only_when_screen_on, kOnlyScreenOnScan); - EXPECT_EQ(sr.power_mode, powerMode); - EXPECT_EQ(sr.use_ble, kUseBle); - EXPECT_EQ(sr.manager_app_id, kManagerAppId); -} - -TEST(ScanRequestBuilderTest, TestCopy) { - ScanRequestBuilder builder1; - builder1.SetOnlyScreenOnScan(kOnlyScreenOnScan).SetUseBle(kUseBle); - ScanRequestBuilder builder2 = {builder1}; - EXPECT_EQ(builder1, builder2); - ScanRequest s1 = builder1.Build(); - ScanRequest s2 = builder2.Build(); - EXPECT_EQ(s1, s2); -} -} // namespace -} // namespace presence -} // namespace nearby diff --git a/proto/connections_enums.proto b/proto/connections_enums.proto index db519643..b31bbe69 100644 --- a/proto/connections_enums.proto +++ b/proto/connections_enums.proto @@ -110,9 +110,10 @@ enum WifiDirectAuthType { // WifiDirect type that uses ssid/password for authentication. Android // supports this type, but Windows does not. WIFI_DIRECT_WITH_PASSWORD = 1; - // WifiDirect type that uses service_name/pin for authentication. Android - // and Windows both support this type. - WIFI_DIRECT_WITH_PIN = 2; + // WifiDirect type that uses device_name for discovery and connect. + // Android and Windows both support this type. + WIFI_DIRECT_WITH_PIN = 2 [deprecated = true]; + WIFI_DIRECT_WITH_DEVICE_NAME = 3; } // LINT.ThenChange(//depot/google3/third_party/nearby/connections/implementation/proto/offline_wire_formats.proto) @@ -1447,6 +1448,11 @@ enum OperationResultCode { DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_HOST_NETWORK_NOT_AVAILABLE = 5058; // Failed to upgrade to high speed medium because no incoming HTTP connection DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_NO_INCOMING_HTTP_CONNECTION = 5059; + // Failed to upgrade to high speed medium because there is no USB device + // connected + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_USB_NO_CONNECTED_DEVICE = 5060; + // Failed to upgrade to high speed medium because the upgrade is interrupted + DCT_ERROR_UPGRADE_HIGH_SPEED_MEDIUM_FAILED_INTERRUPTED = 5061; } enum StopAdvertisingReason { diff --git a/proto/sharing_enums.proto b/proto/sharing_enums.proto index 7adafdd3..e6b055bc 100644 --- a/proto/sharing_enums.proto +++ b/proto/sharing_enums.proto @@ -321,6 +321,7 @@ enum EventType { /*[ device_role = DEVICE_ROLE_INITIATOR ]*/; CLOUD_REGISTER_RECEIVER = 75 /*[ device_role = DEVICE_ROLE_REMOTE ]*/; + // Cloud upload events (routing updated in nearby_event_codes.proto). CLOUD_UPLOAD_START = 76 /*[ device_role = DEVICE_ROLE_INITIATOR ]*/; CLOUD_UPLOAD_END = 77 /*[ device_role = DEVICE_ROLE_INITIATOR ]*/; @@ -361,6 +362,7 @@ enum Visibility { SELECTED_CONTACTS_ONLY = 3 [deprecated = true]; HIDDEN = 4; SELF_SHARE = 5; + FAMILY = 6; } enum DataUsage { @@ -845,6 +847,15 @@ enum SharingUseCase { USE_CASE_NEARBY_SHARE_WITH_QR_CODE = 7 [deprecated = true]; // The user was redirected from Bluetooth sharing UI to Nearby Share USE_CASE_REDIRECTED_FROM_BLUETOOTH_SHARE = 8; + USE_CASE_TAP_TO_SHARE = 9; +} + +enum SharingSurface { + SURFACE_UNKNOWN = 0; + + SURFACE_QUICK_SHARE = 1; + SURFACE_APP = 2; + SURFACE_SYSTEM_SHARE_SHEET = 3; } // Used only for Windows App now. diff --git a/sharing/BUILD b/sharing/BUILD index 83c10b5b..d093ef0e 100644 --- a/sharing/BUILD +++ b/sharing/BUILD @@ -24,7 +24,6 @@ cc_library( "//internal/base:file_path", "//internal/base:files", "//internal/crypto_cros", # buildcleaner: keep - "//internal/platform:base", "//internal/interop:authentication_status", "@com_google_absl//absl/random", "@com_google_absl//absl/strings:string_view", @@ -51,6 +50,7 @@ cc_library( visibility = [ "//location/nearby/apps/better_together/windows/nearby_share:__subpackages__", "//location/nearby/cpp/sharing:__subpackages__", + "//location/nearby/sharing/lib:__subpackages__", "//location/nearby/sharing/sdk/quick_share_server:__pkg__", "//location/nearby/testing/nearby_native:__subpackages__", "//sharing:__subpackages__", @@ -89,6 +89,7 @@ cc_library( visibility = [ "//location/nearby/apps/better_together/windows/nearby_share:__subpackages__", "//location/nearby/cpp/sharing:__subpackages__", + "//location/nearby/sharing/lib:__subpackages__", "//location/nearby/sharing/sdk/quick_share_server:__pkg__", "//location/nearby/testing/nearby_native:__subpackages__", "//sharing:__subpackages__", @@ -108,6 +109,16 @@ cc_library( ], ) +cc_library( + name = "share_session_usage", + hdrs = ["share_session_usage.h"], + visibility = [ + "//location/nearby/cpp/sharing:__subpackages__", + "//location/nearby/sharing/lib:__subpackages__", + "//sharing:__subpackages__", + ], +) + cc_library( name = "transfer_metadata", srcs = [ @@ -121,11 +132,13 @@ cc_library( visibility = [ "//location/nearby/apps/better_together/windows/nearby_share:__subpackages__", "//location/nearby/cpp/sharing:__subpackages__", + "//location/nearby/sharing/lib:__subpackages__", "//location/nearby/sharing/sdk/quick_share_server:__pkg__", "//location/nearby/testing/nearby_native:__subpackages__", "//sharing:__subpackages__", ], deps = [ + ":share_session_usage", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", ], @@ -167,6 +180,7 @@ cc_library( "//sharing/internal/public:logging", "//sharing/proto:wire_format_cc_proto", "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/memory", "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", @@ -186,6 +200,7 @@ cc_library( "//sharing/proto:enums_cc_proto", "//sharing/proto:share_cc_proto", "//sharing/proto:wire_format_cc_proto", + "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/time", ], @@ -226,6 +241,7 @@ cc_library( ":incoming_frame_reader", ":nearby_sharing_util", ":paired_key_verification_runner", + ":share_session_usage", ":thread_timer", ":transfer_metadata", ":types", @@ -233,7 +249,7 @@ cc_library( "//internal/base:file_path", "//internal/base:files", "//internal/platform:types", - "//sharing/linux/stubs:sync", + "//location/nearby/sharing/lib/sync:sync_manager", "//proto:sharing_enums_cc_proto", "//sharing/analytics", "//sharing/certificates", @@ -256,12 +272,10 @@ cc_library( srcs = ["nearby_connection_impl.cc"], hdrs = ["nearby_connection_impl.h"], deps = [ - ":connection_types", ":types", - "//internal/platform:types", + "//internal/platform/implementation:types", "//sharing/internal/public:logging", "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/strings", "@com_google_absl//absl/synchronization", ], ) @@ -270,6 +284,9 @@ cc_library( name = "outgoing_targets_manager", srcs = ["outgoing_targets_manager.cc"], hdrs = ["outgoing_targets_manager.h"], + visibility = [ + "//location/nearby/sharing/lib:__subpackages__", + ], deps = [ ":share_session", ":thread_timer", @@ -298,8 +315,6 @@ cc_library( hdrs = ["nearby_sharing_util.h"], deps = [ ":types", - "//internal/base:file_path", - "//internal/platform:types", "//proto:sharing_enums_cc_proto", "//sharing/certificates", "//sharing/common:enum", @@ -353,6 +368,7 @@ cc_library( ], visibility = [ "//location/nearby/cpp/sharing:__subpackages__", + "//location/nearby/sharing/lib:__subpackages__", "//location/nearby/sharing/sdk/quick_share_server:__pkg__", "//location/nearby/testing/nearby_native:__subpackages__", "//sharing:__subpackages__", @@ -367,32 +383,35 @@ cc_library( ":outgoing_targets_manager", ":paired_key_verification_runner", ":share_session", + ":share_session_usage", ":thread_timer", ":transfer_metadata", ":types", "//connections:core", "//connections:core_types", "//connections/implementation:internal", - "//internal/analytics:event_logger", + "//connections/implementation/analytics:analytics_recorder_impl", "//internal/base", "//internal/base:file_path", "//internal/flags:nearby_flags", "//internal/network:url", "//internal/platform:base", + "//internal/platform:comm", "//internal/platform:logging", "//internal/platform:mac_address", "//internal/platform:types", - "//internal/platform/implementation:account_manager", "//internal/platform/implementation:types", - "//sharing/linux/stubs:rpc", - "//sharing/linux/stubs:sync", + #"//location/nearby/analytics/cpp/logging:event_logger", + "//location/nearby/sharing/lib/account:account_manager", + "//location/nearby/sharing/lib/rpc:grpc_async_client_factory", + "//location/nearby/sharing/lib/rpc:sharing_rpc_client", + "//location/nearby/sharing/lib/sync:sync_binding_prefs_cc_proto", + "//location/nearby/sharing/lib/sync:sync_manager", "//proto:sharing_enums_cc_proto", "//sharing/analytics", "//sharing/certificates", "//sharing/common", "//sharing/common:enum", - "//sharing/contacts", - "//sharing/contacts:contacts_interface", "//sharing/fast_initiation:nearby_fast_initiation", "//sharing/flags/generated:generated_flags", "//sharing/internal/api:platform", @@ -439,22 +458,33 @@ cc_library( ":attachments", ":connection_types", ":nearby_sharing_service", + ":outgoing_targets_manager", + ":share_session", ":transfer_metadata", ":types", "//internal/base", "//internal/base:file_path", "//internal/platform:types", + "//internal/test", + "//location/nearby/sharing/lib/analytics", + "//location/nearby/sharing/lib/rpc:fake_nearby_share_client", + "//location/nearby/sharing/lib/rpc:sharing_rpc_client", + "//location/nearby/sharing/lib/sync:sync_manager", + "//sharing/certificates", "//sharing/common:enum", "//sharing/internal/api:platform", "//sharing/internal/public:logging", - "//sharing/local_device_data", + "//sharing/internal/test:nearby_test", "//sharing/proto:enums_cc_proto", "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", ], ) @@ -519,12 +549,12 @@ cc_test( "//sharing/certificates", "//sharing/certificates:test_support", "//sharing/internal/public:logging", - "//sharing/internal/public:types", - "//sharing/internal/test:nearby_test", "//sharing/proto:enums_cc_proto", "//sharing/proto:share_cc_proto", "//sharing/proto:wire_format_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", ], @@ -605,7 +635,6 @@ cc_test( "@com_google_absl//absl/strings:string_view", "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", - "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", "@com_google_googletest//:gtest_main", ], @@ -621,25 +650,26 @@ cc_test( ":nearby_connection_impl", ":nearby_sharing_service", ":share_session", + ":share_session_usage", ":test_support", ":transfer_metadata", ":transfer_metadata_matchers", ":types", - "//internal/analytics:mock_event_logger", "//internal/base:file_path", "//internal/base:files", "//internal/flags:nearby_flags", "//internal/platform/implementation:platform_impl", - "//internal/platform/implementation:signin_attempt", "//internal/test", - "//internal/test:mocks", - "//sharing/linux/stubs:rpc", - "//sharing/analytics", + "//location/nearby/analytics/cpp/logging:mock_event_logger", + "//location/nearby/sharing/lib/account:fake_account_manager", + "//location/nearby/sharing/lib/account:mock_account_manager", + "//location/nearby/sharing/lib/account:signin_attempt", + "//location/nearby/sharing/lib/analytics", + "//location/nearby/sharing/lib/rpc:fake_nearby_share_client", "//sharing/certificates", "//sharing/certificates:test_support", "//sharing/common", "//sharing/common:enum", - "//sharing/contacts:test_support", "//sharing/fast_initiation:nearby_fast_initiation", "//sharing/fast_initiation:test_support", "//sharing/flags/generated:generated_flags", @@ -826,6 +856,7 @@ cc_test( name = "transfer_metadata_test", srcs = ["transfer_metadata_test.cc"], deps = [ + ":share_session_usage", ":transfer_metadata", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_googletest//:gtest_main", @@ -844,11 +875,10 @@ cc_test( ":transfer_metadata", ":transfer_metadata_matchers", ":types", - "//internal/analytics:mock_event_logger", - "//internal/base:file_path", "//internal/platform/implementation:platform_impl", "//internal/test", - "//sharing/analytics", + "//location/nearby/analytics/cpp/logging:mock_event_logger", + "//location/nearby/sharing/lib/analytics", "//sharing/certificates:test_support", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/strings:string_view", @@ -890,23 +920,24 @@ cc_test( ":attachments", ":connection_types", ":nearby_connection_impl", - ":paired_key_verification_runner", ":share_session", + ":share_session_usage", ":test_support", ":transfer_metadata", ":transfer_metadata_matchers", ":types", - "//internal/analytics:mock_event_logger", "//internal/base:file_path", "//internal/base:files", "//internal/network:url", "//internal/platform/implementation:platform_impl", "//internal/test", - "//sharing/analytics", + "//location/nearby/analytics/cpp/logging:mock_event_logger", + "//location/nearby/analytics/cpp/proto:sharing_log_cc_proto", + "//location/nearby/sharing/lib/analytics", + "//net/proto2/contrib/parse_proto:parse_text_proto", "//sharing/certificates:test_support", "//sharing/common:enum", "//sharing/proto:wire_format_cc_proto", - "//sharing/proto/analytics:sharing_log_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/strings:string_view", "@com_google_absl//absl/time", @@ -922,21 +953,21 @@ cc_test( ":attachments", ":connection_types", ":nearby_connection_impl", - ":paired_key_verification_runner", ":share_session", + ":share_session_usage", ":test_support", ":transfer_metadata", ":transfer_metadata_matchers", ":types", - "//internal/analytics:mock_event_logger", "//internal/base:file_path", "//internal/platform/implementation:platform_impl", "//internal/test", + "//location/nearby/analytics/cpp/logging:mock_event_logger", + "//location/nearby/analytics/cpp/proto:sharing_log_cc_proto", + "//location/nearby/sharing/lib/analytics", "//proto:sharing_enums_cc_proto", - "//sharing/analytics", "//sharing/internal/public:logging", "//sharing/proto:wire_format_cc_proto", - "//sharing/proto/analytics:sharing_log_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/strings:string_view", "@com_google_absl//absl/time", @@ -967,7 +998,7 @@ cc_test( ":nearby_sharing_service", "//connections:core_types", "//internal/platform:base", - "//internal/platform:types", + "//internal/platform:comm", "//internal/platform/implementation:platform_impl", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_googletest//:gtest_main", @@ -983,6 +1014,7 @@ cc_test( "//internal/test", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", ], ) @@ -999,10 +1031,11 @@ cc_test( ":test_support", ":transfer_metadata", ":types", - "//internal/base:file_path", "//internal/platform/implementation:platform_impl", "//internal/test", - "//sharing/analytics", + "//location/nearby/sharing/lib/analytics", + "//sharing/certificates", + "//sharing/certificates:test_support", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/strings:string_view", "@com_google_googletest//:gtest_main", diff --git a/sharing/analytics/BUILD b/sharing/analytics/BUILD index b1ff308b..8294c9f9 100644 --- a/sharing/analytics/BUILD +++ b/sharing/analytics/BUILD @@ -13,15 +13,11 @@ # limitations under the License. load("@rules_cc//cc:cc_library.bzl", "cc_library") -load("@rules_cc//cc:cc_test.bzl", "cc_test") licenses(["notice"]) cc_library( name = "analytics", - srcs = [ - "analytics_recorder.cc", - ], hdrs = [ "analytics_device_settings.h", "analytics_information.h", @@ -29,38 +25,12 @@ cc_library( ], visibility = ["//visibility:public"], deps = [ - "//internal/analytics:event_logger", "//proto:sharing_enums_cc_proto", "//sharing:attachments", "//sharing:types", "//sharing/common:enum", "//sharing/proto:enums_cc_proto", - "//sharing/proto/analytics:sharing_log_cc_proto", - "@com_google_absl//absl/random", "@com_google_absl//absl/strings:string_view", "@com_google_absl//absl/time", - "@com_google_protobuf//:protobuf", - ], -) - -cc_test( - name = "analytics_test", - srcs = ["analytics_recorder_test.cc"], - deps = [ - ":analytics", - "//internal/analytics:mock_event_logger", - "//internal/platform/implementation:platform_impl", - "//proto:sharing_enums_cc_proto", - "//sharing:attachments", - "//sharing:types", - "//sharing/common:enum", - "//sharing/proto:enums_cc_proto", - "//sharing/proto:wire_format_cc_proto", - "//sharing/proto/analytics:sharing_log_cc_proto", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - "@com_google_googletest//:gtest_main", - "@com_google_protobuf//:protobuf", ], ) diff --git a/sharing/analytics/analytics_recorder.cc b/sharing/analytics/analytics_recorder.cc deleted file mode 100644 index 3a37df92..00000000 --- a/sharing/analytics/analytics_recorder.cc +++ /dev/null @@ -1,838 +0,0 @@ -// Copyright 2022-2023 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 "sharing/analytics/analytics_recorder.h" - -#include -#include -#include -#include - -#include "google/protobuf/duration.pb.h" -#include "absl/random/random.h" -#include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "proto/sharing_enums.pb.h" -#include "sharing/analytics/analytics_device_settings.h" -#include "sharing/analytics/analytics_information.h" -#include "sharing/attachment_container.h" -#include "sharing/common/nearby_share_enums.h" -#include "sharing/file_attachment.h" -#include "sharing/proto/analytics/nearby_sharing_log.pb.h" -#include "sharing/proto/enums.pb.h" -#include "sharing/share_target.h" -#include "sharing/wifi_credentials_attachment.h" - -namespace nearby { -namespace sharing { -namespace analytics { -namespace { - -using ::location::nearby::proto::sharing::DeviceRelationship; -using ::location::nearby::proto::sharing::DeviceType; -using ::location::nearby::proto::sharing::EstablishConnectionStatus; -using ::location::nearby::proto::sharing::EventCategory; -using ::location::nearby::proto::sharing::EventType; -using ::location::nearby::proto::sharing::OSType; -using ::location::nearby::proto::sharing::ProcessReceivedAttachmentsStatus; -using ::location::nearby::proto::sharing::ShowNotificationStatus; -using ::location::nearby::proto::sharing::Visibility; - -using ::nearby::sharing::analytics::proto::SharingLog; -using ::nearby::sharing::proto::DataUsage; -using ::nearby::sharing::proto::DeviceVisibility; - -DeviceRelationship GetLoggerDeviceRelationship( - const ShareTarget& share_target) { - if (share_target.for_self_share) { - return DeviceRelationship::IS_SELF; - } else if (share_target.is_known) { - return DeviceRelationship::IS_CONTACT; - } else { - return DeviceRelationship::IS_STRANGER; - } -} - -DeviceType GetLoggerDeviceType(ShareTargetType type) { - switch (type) { - case ShareTargetType::kLaptop: - return DeviceType::LAPTOP; - case ShareTargetType::kPhone: - return DeviceType::PHONE; - case ShareTargetType::kTablet: - return DeviceType::TABLET; - default: - return DeviceType::UNKNOWN_DEVICE_TYPE; - } -} - -Visibility GetLoggerVisibility(DeviceVisibility visibility) { - switch (visibility) { - case DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS: - return Visibility::CONTACTS_ONLY; - case DeviceVisibility::DEVICE_VISIBILITY_SELECTED_CONTACTS: - return Visibility::SELECTED_CONTACTS_ONLY; - case DeviceVisibility::DEVICE_VISIBILITY_EVERYONE: - return Visibility::EVERYONE; - case DeviceVisibility::DEVICE_VISIBILITY_SELF_SHARE: - return Visibility::SELF_SHARE; - case DeviceVisibility::DEVICE_VISIBILITY_HIDDEN: - return Visibility::HIDDEN; - case DeviceVisibility::DEVICE_VISIBILITY_UNSPECIFIED: - default: - return Visibility::UNKNOWN_VISIBILITY; - } -} - -location::nearby::proto::sharing::DataUsage GetLoggerDataUsage( - DataUsage data_usage) { - switch (data_usage) { - case DataUsage::OFFLINE_DATA_USAGE: - return location::nearby::proto::sharing::DataUsage::OFFLINE; - case DataUsage::ONLINE_DATA_USAGE: - return location::nearby::proto::sharing::DataUsage::ONLINE; - case DataUsage::WIFI_ONLY_DATA_USAGE: - return location::nearby::proto::sharing::DataUsage::WIFI_ONLY; - default: - return location::nearby::proto::sharing::DataUsage::UNKNOWN_DATA_USAGE; - } -} - -void SetShareTargetInfo(SharingLog::ShareTargetInfo* share_target_info, - ShareTargetType device_type, - DeviceRelationship relationship, - OSType os_type = OSType::UNKNOWN_OS_TYPE) { - share_target_info->set_device_relationship(relationship); - share_target_info->set_device_type(GetLoggerDeviceType(device_type)); - if (os_type == OSType::UNKNOWN_OS_TYPE && - device_type == ShareTargetType::kPhone) { - // If the device type is phone, just set the OS type to android because - // no other phone OS for now. - share_target_info->set_os_type(OSType::ANDROID); - } else { - share_target_info->set_os_type(os_type); - } -} - -void SetShareTargetInfo(SharingLog::ShareTargetInfo* share_target_info, - const ShareTarget& share_target, - OSType os_type = OSType::UNKNOWN_OS_TYPE) { - share_target_info->set_device_relationship( - GetLoggerDeviceRelationship(share_target)); - share_target_info->set_device_type(GetLoggerDeviceType(share_target.type)); - if (os_type == OSType::UNKNOWN_OS_TYPE && - share_target.type == ShareTargetType::kPhone) { - // If the device type is phone, just set the OS type to android because - // no other phone OS for now. - share_target_info->set_os_type(OSType::ANDROID); - } else { - share_target_info->set_os_type(os_type); - } -} - -void SetAttachmentInfo(SharingLog::AttachmentsInfo* attachments_info, - const AttachmentContainer& attachments) { - for (const auto& attachment : attachments.GetTextAttachments()) { - SharingLog::TextAttachment::Type type = - SharingLog::TextAttachment::UNKNOWN_TEXT_TYPE; - switch (attachment.GetShareType()) { - case ShareType::kPhone: - type = SharingLog::TextAttachment::PHONE_NUMBER; - break; - case ShareType::kUrl: - type = SharingLog::TextAttachment::URL; - break; - case ShareType::kAddress: - type = SharingLog::TextAttachment::ADDRESS; - break; - case ShareType::kText: - // Apply UNKNOWN_TEXT_TYPE for it based on analytics design. - break; - default: - break; - } - SharingLog::TextAttachment* text_attachment = - attachments_info->mutable_text_attachment()->Add(); - text_attachment->set_type(type); - text_attachment->set_size_bytes(attachment.size()); - text_attachment->set_source_type(attachment.source_type()); - text_attachment->set_batch_id(attachment.batch_id()); - } - - for (const auto& attachment : attachments.GetFileAttachments()) { - SharingLog::FileAttachment::Type type = - SharingLog::FileAttachment::UNKNOWN_FILE_TYPE; - switch (attachment.GetShareType()) { - case ShareType::kImageFile: - type = SharingLog::FileAttachment::IMAGE; - break; - case ShareType::kVideoFile: - type = SharingLog::FileAttachment::VIDEO; - break; - case ShareType::kAudioFile: - type = SharingLog::FileAttachment::AUDIO; - break; - case ShareType::kPdfFile: - case ShareType::kTextFile: - case ShareType::kGoogleDocsFile: - case ShareType::kGoogleSheetsFile: - case ShareType::kGoogleSlidesFile: - type = SharingLog::FileAttachment::DOCUMENT; - break; - case ShareType::kUnknownFile: - // The default type is set to type. - break; - default: - break; - } - SharingLog::FileAttachment* file_attachment = - attachments_info->mutable_file_attachment()->Add(); - file_attachment->set_type(type); - file_attachment->set_size_bytes(attachment.size()); - file_attachment->set_offset_bytes(0); - file_attachment->set_source_type(attachment.source_type()); - file_attachment->set_batch_id(attachment.batch_id()); - } - - for (const auto& attachment : attachments.GetWifiCredentialsAttachments()) { - SharingLog::WifiCredentialsAttachment* wifi_credentials_attachment = - attachments_info->mutable_wifi_credentials_attachment()->Add(); - wifi_credentials_attachment->set_source_type(attachment.source_type()); - wifi_credentials_attachment->set_batch_id(attachment.batch_id()); - } -} - -} // namespace - -void AnalyticsRecorder::NewEstablishConnection( - int64_t session_id, EstablishConnectionStatus connection_status, - const ShareTarget& share_target, int transfer_position, - int concurrent_connections, int64_t duration_millis, - std::optional referrer_package) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SENDING_EVENT, EventType::ESTABLISH_CONNECTION); - - auto* establish_connection = sharing_log->mutable_establish_connection(); - - establish_connection->set_session_id(session_id); - establish_connection->set_status(connection_status); - SetShareTargetInfo(establish_connection->mutable_share_target_info(), - share_target); - establish_connection->set_transfer_position(transfer_position); - establish_connection->set_concurrent_connections(concurrent_connections); - establish_connection->set_duration_millis(duration_millis); - if (referrer_package.has_value()) { - establish_connection->set_referrer_name(*referrer_package); - } - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewAcceptAgreements() { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::ACCEPT_AGREEMENTS); - - sharing_log->mutable_accept_agreements(); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewDeclineAgreements() { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::DECLINE_AGREEMENTS); - - sharing_log->mutable_decline_agreements(); - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewAddContact() { - std::unique_ptr sharing_log = - CreateSharingLog(EventCategory::SETTINGS_EVENT, EventType::ADD_CONTACT); - - sharing_log->mutable_add_contact(); - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewRemoveContact() { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::REMOVE_CONTACT); - - sharing_log->mutable_remove_contact(); - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewTapFeedback() { - std::unique_ptr sharing_log = - CreateSharingLog(EventCategory::SETTINGS_EVENT, EventType::TAP_FEEDBACK); - - sharing_log->mutable_tap_feedback(); - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewTapHelp() { - std::unique_ptr sharing_log = - CreateSharingLog(EventCategory::SETTINGS_EVENT, EventType::TAP_HELP); - - sharing_log->mutable_tap_help(); - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewLaunchDeviceContactConsent( - ::location::nearby::proto::sharing::ConsentAcceptanceStatus status) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::LAUNCH_CONSENT); - - auto* launch_consent = sharing_log->mutable_launch_consent(); - launch_consent->set_status(status); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewAdvertiseDevicePresenceEnd(int64_t session_id) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::RECEIVING_EVENT, EventType::ADVERTISE_DEVICE_PRESENCE_END); - - auto* advertise_device_presence_end = - sharing_log->mutable_advertise_device_presence_end(); - advertise_device_presence_end->set_session_id(session_id); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewAdvertiseDevicePresenceStart( - int64_t session_id, DeviceVisibility visibility, - ::location::nearby::proto::sharing::SessionStatus status, - DataUsage data_usage, std::optional referrer_package) { - std::unique_ptr sharing_log = - CreateSharingLog(EventCategory::RECEIVING_EVENT, - EventType::ADVERTISE_DEVICE_PRESENCE_START); - - auto* advertise_device_presence_start = - sharing_log->mutable_advertise_device_presence_start(); - advertise_device_presence_start->set_session_id(session_id); - advertise_device_presence_start->set_visibility( - GetLoggerVisibility(visibility)); - advertise_device_presence_start->set_status(status); - advertise_device_presence_start->set_data_usage( - GetLoggerDataUsage(data_usage)); - if (referrer_package.has_value()) { - advertise_device_presence_start->set_referrer_name(*referrer_package); - } - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewDescribeAttachments( - const AttachmentContainer& attachments) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SENDING_EVENT, EventType::DESCRIBE_ATTACHMENTS); - - auto* describe_attachments = sharing_log->mutable_describe_attachments(); - SetAttachmentInfo(describe_attachments->mutable_attachments_info(), - attachments); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewDiscoverShareTarget( - const ShareTarget& share_target, int64_t session_id, - int64_t latency_since_scanning_start_millis, int64_t flow_id, - std::optional referrer_package, - int64_t latency_since_send_surface_registered_millis) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SENDING_EVENT, EventType::DISCOVER_SHARE_TARGET); - - auto* discover_share_target = sharing_log->mutable_discover_share_target(); - discover_share_target->set_session_id(session_id); - auto* duration = discover_share_target->mutable_duration_since_scanning(); - duration->set_seconds(latency_since_scanning_start_millis / 1000); - duration->set_nanos((latency_since_scanning_start_millis % 1000) * 1000000); - SetShareTargetInfo(discover_share_target->mutable_share_target_info(), - share_target); - discover_share_target->set_session_id(session_id); - discover_share_target->set_flow_id(flow_id); - - discover_share_target->set_latency_since_activity_start_millis( - latency_since_send_surface_registered_millis > 0 - ? latency_since_send_surface_registered_millis - : -1); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewEnableNearbySharing( - ::location::nearby::proto::sharing::NearbySharingStatus status) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::ENABLE_NEARBY_SHARING); - - auto* enable_nearby_sharing = sharing_log->mutable_enable_nearby_sharing(); - enable_nearby_sharing->set_status(status); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewOpenReceivedAttachments( - const AttachmentContainer& attachments, int64_t session_id) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::RECEIVING_EVENT, EventType::OPEN_RECEIVED_ATTACHMENTS); - - auto* open_received_attachments = - sharing_log->mutable_open_received_attachments(); - SetAttachmentInfo(open_received_attachments->mutable_attachments_info(), - attachments); - open_received_attachments->set_session_id(session_id); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewProcessReceivedAttachmentsEnd( - int64_t session_id, ProcessReceivedAttachmentsStatus status) { - std::unique_ptr sharing_log = - CreateSharingLog(EventCategory::RECEIVING_EVENT, - EventType::PROCESS_RECEIVED_ATTACHMENTS_END); - - auto* process_received_attachments_end = - sharing_log->mutable_process_received_attachments_end(); - process_received_attachments_end->set_status(status); - process_received_attachments_end->set_session_id(session_id); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewReceiveAttachmentsEnd( - int64_t session_id, int64_t received_bytes, - ::location::nearby::proto::sharing::AttachmentTransmissionStatus status, - std::optional referrer_package) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::RECEIVING_EVENT, EventType::RECEIVE_ATTACHMENTS_END); - - auto* receive_attachments_end = - sharing_log->mutable_receive_attachments_end(); - receive_attachments_end->set_session_id(session_id); - receive_attachments_end->set_received_bytes(received_bytes); - receive_attachments_end->set_status(status); - if (referrer_package.has_value()) { - receive_attachments_end->set_referrer_name(*referrer_package); - } - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewReceiveAttachmentsStart( - int64_t session_id, const AttachmentContainer& attachments) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::RECEIVING_EVENT, EventType::RECEIVE_ATTACHMENTS_START); - - auto* receive_attachments_start = - sharing_log->mutable_receive_attachments_start(); - SetAttachmentInfo(receive_attachments_start->mutable_attachments_info(), - attachments); - receive_attachments_start->set_session_id(session_id); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewReceiveFastInitialization( - int64_t timeElapseSinceScreenUnlockMillis) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::RECEIVING_EVENT, EventType::RECEIVE_FAST_INITIALIZATION); - - auto* receive_fast_initialization = - sharing_log->mutable_receive_initialization(); - - receive_fast_initialization->set_time_elapse_since_screen_unlock_millis( - timeElapseSinceScreenUnlockMillis); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewAcceptFastInitialization() { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::RECEIVING_EVENT, EventType::ACCEPT_FAST_INITIALIZATION); - - sharing_log->mutable_accept_fast_initialization(); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewDismissFastInitialization() { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::RECEIVING_EVENT, EventType::DISMISS_FAST_INITIALIZATION); - - sharing_log->mutable_dismiss_fast_initialization(); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewReceiveIntroduction( - int64_t session_id, const ShareTarget& share_target, - std::optional referrer_package, - ::location::nearby::proto::sharing::OSType share_target_os_type) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::RECEIVING_EVENT, EventType::RECEIVE_INTRODUCTION); - - auto* receive_introduction = sharing_log->mutable_receive_introduction(); - receive_introduction->set_session_id(session_id); - SetShareTargetInfo(receive_introduction->mutable_share_target_info(), - share_target, share_target_os_type); - if (referrer_package.has_value()) { - receive_introduction->set_referrer_name(*referrer_package); - } - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewRespondToIntroduction( - ::location::nearby::proto::sharing::ResponseToIntroduction action, - int64_t session_id) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::RECEIVING_EVENT, EventType::RESPOND_TO_INTRODUCTION); - - auto* respond_to_introduction = sharing_log->mutable_respond_introduction(); - respond_to_introduction->set_session_id(session_id); - respond_to_introduction->set_action(action); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewTapPrivacyNotification() { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::RECEIVING_EVENT, EventType::TAP_PRIVACY_NOTIFICATION); - - sharing_log->mutable_tap_privacy_notification(); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewDismissPrivacyNotification() { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::RECEIVING_EVENT, EventType::DISMISS_PRIVACY_NOTIFICATION); - - sharing_log->mutable_dismiss_privacy_notification(); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewScanForShareTargetsEnd(int64_t session_id) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SENDING_EVENT, EventType::SCAN_FOR_SHARE_TARGETS_END); - - auto* scan_for_share_targets_end = - sharing_log->mutable_scan_for_share_targets_end(); - scan_for_share_targets_end->set_session_id(session_id); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewScanForShareTargetsStart( - int64_t session_id, - ::location::nearby::proto::sharing::SessionStatus status, - AnalyticsInformation analytics_information, int64_t flow_id, - std::optional referrer_package) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SENDING_EVENT, EventType::SCAN_FOR_SHARE_TARGETS_START); - - auto* scan_for_share_targets_start = - sharing_log->mutable_scan_for_share_targets_start(); - scan_for_share_targets_start->set_session_id(session_id); - scan_for_share_targets_start->set_status(status); - scan_for_share_targets_start->set_scan_type( - static_cast<::location::nearby::proto::sharing::ScanType>( - analytics_information.send_surface_state)); - scan_for_share_targets_start->set_flow_id(flow_id); - if (referrer_package.has_value()) { - scan_for_share_targets_start->set_referrer_name(*referrer_package); - } - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewSendAttachmentsEnd( - int64_t session_id, int64_t sent_bytes, const ShareTarget& share_target, - ::location::nearby::proto::sharing::AttachmentTransmissionStatus status, - int transfer_position, int concurrent_connections, int64_t duration_millis, - std::optional referrer_package, - ::location::nearby::proto::sharing::ConnectionLayerStatus - connection_layer_status, - ::location::nearby::proto::sharing::OSType share_target_os_type) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SENDING_EVENT, EventType::SEND_ATTACHMENTS_END); - - auto* send_attachments_end = sharing_log->mutable_send_attachments_end(); - send_attachments_end->set_session_id(session_id); - send_attachments_end->set_sent_bytes(sent_bytes); - SetShareTargetInfo(send_attachments_end->mutable_share_target_info(), - share_target, share_target_os_type); - send_attachments_end->set_status(status); - send_attachments_end->set_transfer_position(transfer_position); - send_attachments_end->set_concurrent_connections(concurrent_connections); - send_attachments_end->set_duration_millis(duration_millis); - if (referrer_package.has_value()) { - send_attachments_end->set_referrer_name(*referrer_package); - } - send_attachments_end->set_connection_layer_status(connection_layer_status); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewSendAttachmentsStart( - int64_t session_id, const AttachmentContainer& attachments, - int transfer_position, int concurrent_connections, - bool advanced_protection_enabled, bool advanced_protection_mismatch) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SENDING_EVENT, EventType::SEND_ATTACHMENTS_START); - - auto* send_attachments_start = sharing_log->mutable_send_attachments_start(); - send_attachments_start->set_session_id(session_id); - SetAttachmentInfo(send_attachments_start->mutable_attachments_info(), - attachments); - send_attachments_start->set_transfer_position(transfer_position); - send_attachments_start->set_concurrent_connections(concurrent_connections); - send_attachments_start->set_advanced_protection_enabled( - advanced_protection_enabled); - send_attachments_start->set_advanced_protection_mismatch( - advanced_protection_mismatch); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewSendFastInitialization() { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SENDING_EVENT, EventType::SEND_FAST_INITIALIZATION); - - sharing_log->mutable_send_initialization(); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewSendStart(int64_t session_id, int transfer_position, - int concurrent_connections, - const ShareTarget& share_target) { - std::unique_ptr sharing_log = - CreateSharingLog(EventCategory::SENDING_EVENT, EventType::SEND_START); - - auto* send_start = sharing_log->mutable_send_start(); - send_start->set_session_id(session_id); - send_start->set_transfer_position(transfer_position); - send_start->set_concurrent_connections(concurrent_connections); - SetShareTargetInfo(send_start->mutable_share_target_info(), share_target); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewSendIntroduction( - ShareTargetType target_type, int64_t session_id, - DeviceRelationship relationship, - ::location::nearby::proto::sharing::OSType share_target_os_type) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SENDING_EVENT, EventType::SEND_INTRODUCTION); - auto* send_introduction = sharing_log->mutable_send_introduction(); - SetShareTargetInfo(send_introduction->mutable_share_target_info(), - target_type, relationship, share_target_os_type); - send_introduction->set_session_id(session_id); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewSendIntroduction( - int64_t session_id, const ShareTarget& share_target, int transfer_position, - int concurrent_connections, - ::location::nearby::proto::sharing::OSType share_target_os_type) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SENDING_EVENT, EventType::SEND_INTRODUCTION); - - auto* send_introduction = sharing_log->mutable_send_introduction(); - SetShareTargetInfo(send_introduction->mutable_share_target_info(), - share_target, share_target_os_type); - send_introduction->set_session_id(session_id); - send_introduction->set_transfer_position(transfer_position); - send_introduction->set_concurrent_connections(concurrent_connections); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewSetVisibility(DeviceVisibility src_visibility, - DeviceVisibility dst_visibility, - int64_t duration_millis) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::SET_VISIBILITY); - - auto* set_visibility = sharing_log->mutable_set_visibility(); - set_visibility->set_visibility(GetLoggerVisibility(dst_visibility)); - set_visibility->set_source_visibility(GetLoggerVisibility(src_visibility)); - set_visibility->set_duration_millis(duration_millis); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewDeviceSettings(AnalyticsDeviceSettings settings) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::DEVICE_SETTINGS); - - auto* device_settings = sharing_log->mutable_device_settings(); - device_settings->set_data_usage(GetLoggerDataUsage(settings.data_usage)); - device_settings->set_device_name_size(settings.device_name_size); - device_settings->set_is_show_notification_enabled( - settings.is_fast_init_notification_enabled); - device_settings->set_visibility(GetLoggerVisibility(settings.visibility)); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewSetDataUsage(DataUsage original_preference, - DataUsage preference) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::SET_DATA_USAGE); - - auto* set_data_usage = sharing_log->mutable_set_data_usage(); - set_data_usage->set_original_preference( - GetLoggerDataUsage(original_preference)); - set_data_usage->set_preference(GetLoggerDataUsage(preference)); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewAddQuickSettingsTile() { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::ADD_QUICK_SETTINGS_TILE); - - sharing_log->mutable_add_quick_settings_tile(); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewRemoveQuickSettingsTile() { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::REMOVE_QUICK_SETTINGS_TILE); - - sharing_log->mutable_remove_quick_settings_tile(); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewTapQuickSettingsTile() { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::TAP_QUICK_SETTINGS_TILE); - - sharing_log->mutable_tap_quick_settings_tile(); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewToggleShowNotification( - ShowNotificationStatus prev_status, ShowNotificationStatus current_status) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::TOGGLE_SHOW_NOTIFICATION); - - auto* toggle_show_notification = - sharing_log->mutable_toggle_show_notification(); - toggle_show_notification->set_current_status(current_status); - toggle_show_notification->set_previous_status(prev_status); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewSetDeviceName(int device_name_size) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::SET_DEVICE_NAME); - - auto* set_device_name = sharing_log->mutable_set_device_name(); - set_device_name->set_device_name_size(device_name_size); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewRequestSettingPermissions( - ::location::nearby::proto::sharing::PermissionRequestType type, - ::location::nearby::proto::sharing::PermissionRequestResult result) { - std::unique_ptr sharing_log = CreateSharingLog( - EventCategory::SETTINGS_EVENT, EventType::REQUEST_SETTING_PERMISSIONS); - - auto* request_setting_permissions = - sharing_log->mutable_request_setting_permissions(); - request_setting_permissions->set_permission_type(type); - request_setting_permissions->set_permission_request_result(result); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewInstallAPKStatus( - ::location::nearby::proto::sharing::InstallAPKStatus status, - ::location::nearby::proto::sharing::ApkSource source) { - std::unique_ptr sharing_log = - CreateSharingLog(EventCategory::RECEIVING_EVENT, EventType::INSTALL_APK); - - auto* install_apk_status = sharing_log->mutable_install_apk_status(); - install_apk_status->add_status(status); - install_apk_status->add_source(source); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewVerifyAPKStatus( - ::location::nearby::proto::sharing::VerifyAPKStatus status, - ::location::nearby::proto::sharing::ApkSource source) { - std::unique_ptr sharing_log = - CreateSharingLog(EventCategory::RECEIVING_EVENT, EventType::VERIFY_APK); - - auto* verify_apk_status = sharing_log->mutable_verify_apk_status(); - verify_apk_status->add_status(status); - verify_apk_status->add_source(source); - - LogEvent(*sharing_log); -} - -void AnalyticsRecorder::NewRpcCallStatus( - absl::string_view rpc_name, - SharingLog::RpcCallStatus::RpcDirection direction, - int error_code, absl::Duration latency) { - std::unique_ptr sharing_log = - CreateSharingLog(EventCategory::RPC_EVENT, EventType::RPC_CALL_STATUS); - - auto* rpc_call_status = sharing_log->mutable_rpc_call_status(); - rpc_call_status->set_rpc_name(std::string(rpc_name)); - rpc_call_status->set_direction(direction); - rpc_call_status->set_error_code(error_code); - rpc_call_status->set_latency_millis(absl::ToInt64Milliseconds(latency)); - - LogEvent(*sharing_log); -} - -// Start private methods. - -std::unique_ptr AnalyticsRecorder::CreateSharingLog( - EventCategory event_category, EventType event_type) { - auto sharing_log = std::make_unique(); - sharing_log->set_event_category(event_category); - sharing_log->set_event_type(event_type); - sharing_log->mutable_event_metadata()->set_vendor_id(vendor_id_); - return sharing_log; -} - -void AnalyticsRecorder::LogEvent(const SharingLog& message) { - if (event_logger_ == nullptr) { - return; - } - - event_logger_->Log(message); -} - -int64_t AnalyticsRecorder::GenerateNextId() { - absl::BitGen bit_gen; - return absl::Uniform(bit_gen, 0, INT64_MAX - 1) + 1; -} - -} // namespace analytics -} // namespace sharing -} // namespace nearby diff --git a/sharing/analytics/analytics_recorder.h b/sharing/analytics/analytics_recorder.h index e3b776b9..d1bbba4e 100644 --- a/sharing/analytics/analytics_recorder.h +++ b/sharing/analytics/analytics_recorder.h @@ -16,205 +16,195 @@ #define THIRD_PARTY_NEARBY_SHARING_ANALYTICS_ANALYTICS_RECORDER_H_ #include -#include #include #include #include "absl/strings/string_view.h" #include "absl/time/time.h" -#include "internal/analytics/event_logger.h" #include "proto/sharing_enums.pb.h" #include "sharing/analytics/analytics_device_settings.h" #include "sharing/analytics/analytics_information.h" #include "sharing/attachment_container.h" #include "sharing/common/nearby_share_enums.h" -#include "sharing/proto/analytics/nearby_sharing_log.pb.h" #include "sharing/proto/enums.pb.h" #include "sharing/share_target.h" -namespace nearby { -namespace sharing { -namespace analytics { +namespace nearby::sharing::analytics { class AnalyticsRecorder { public: - explicit AnalyticsRecorder(int32_t vendor_id, - nearby::analytics::EventLogger* event_logger) - : vendor_id_(vendor_id), event_logger_(event_logger) {} - ~AnalyticsRecorder() = default; + enum class RpcDirection { + kUnknown = 0, + kIncoming = 1, + kOutgoing = 2, + }; - void NewEstablishConnection( + AnalyticsRecorder() = default; + virtual ~AnalyticsRecorder() = default; + + virtual void NewEstablishConnection( int64_t session_id, location::nearby::proto::sharing::EstablishConnectionStatus connection_status, const ShareTarget& share_target, int transfer_position, int concurrent_connections, int64_t duration_millis, - std::optional referrer_package); + std::optional referrer_package) = 0; - void NewAcceptAgreements(); + virtual void NewAcceptAgreements() = 0; - void NewDeclineAgreements(); + virtual void NewDeclineAgreements() = 0; - void NewAddContact(); + virtual void NewAddContact() = 0; - void NewRemoveContact(); + virtual void NewRemoveContact() = 0; - void NewTapFeedback(); + virtual void NewTapFeedback() = 0; - void NewTapHelp(); + virtual void NewTapHelp() = 0; - void NewLaunchDeviceContactConsent( - location::nearby::proto::sharing::ConsentAcceptanceStatus status); + virtual void NewLaunchDeviceContactConsent( + location::nearby::proto::sharing::ConsentAcceptanceStatus status) = 0; - void NewAdvertiseDevicePresenceEnd(int64_t session_id); + virtual void NewAdvertiseDevicePresenceEnd(int64_t session_id) = 0; - void NewAdvertiseDevicePresenceStart( + virtual void NewAdvertiseDevicePresenceStart( int64_t session_id, nearby::sharing::proto::DeviceVisibility visibility, location::nearby::proto::sharing::SessionStatus status, nearby::sharing::proto::DataUsage data_usage, - std::optional referrer_package); + std::optional referrer_package) = 0; - void NewDescribeAttachments(const AttachmentContainer& attachments); + virtual void NewDescribeAttachments( + const AttachmentContainer& attachments) = 0; - void NewDiscoverShareTarget( + virtual void NewDiscoverShareTarget( const ShareTarget& share_target, int64_t session_id, int64_t latency_since_scanning_start_millis, int64_t flow_id, std::optional referrer_package, - int64_t latency_since_send_surface_registered_millis); + int64_t latency_since_send_surface_registered_millis) = 0; - void NewEnableNearbySharing( - location::nearby::proto::sharing::NearbySharingStatus status); + virtual void NewEnableNearbySharing( + location::nearby::proto::sharing::NearbySharingStatus status) = 0; - void NewOpenReceivedAttachments(const AttachmentContainer& attachments, - int64_t session_id); + virtual void NewOpenReceivedAttachments( + const AttachmentContainer& attachments, int64_t session_id) = 0; - void NewProcessReceivedAttachmentsEnd( + virtual void NewProcessReceivedAttachmentsEnd( int64_t session_id, location::nearby::proto::sharing::ProcessReceivedAttachmentsStatus - status); + status) = 0; - void NewReceiveAttachmentsEnd( + virtual void NewReceiveAttachmentsEnd( int64_t session_id, int64_t received_bytes, location::nearby::proto::sharing::AttachmentTransmissionStatus status, - std::optional referrer_package); + std::optional referrer_package) = 0; - void NewReceiveAttachmentsStart(int64_t session_id, - const AttachmentContainer& attachments); + virtual void NewReceiveAttachmentsStart( + int64_t session_id, const AttachmentContainer& attachments) = 0; - void NewReceiveFastInitialization(int64_t timeElapseSinceScreenUnlockMillis); + virtual void NewReceiveFastInitialization( + int64_t timeElapseSinceScreenUnlockMillis) = 0; - void NewAcceptFastInitialization(); + virtual void NewAcceptFastInitialization() = 0; - void NewDismissFastInitialization(); + virtual void NewDismissFastInitialization() = 0; - void NewReceiveIntroduction( + virtual void NewReceiveIntroduction( int64_t session_id, const ShareTarget& share_target, std::optional referrer_package, - location::nearby::proto::sharing::OSType share_target_os_type); + location::nearby::proto::sharing::OSType share_target_os_type) = 0; - void NewRespondToIntroduction( + virtual void NewRespondToIntroduction( location::nearby::proto::sharing::ResponseToIntroduction action, - int64_t session_id); + int64_t session_id) = 0; - void NewTapPrivacyNotification(); + virtual void NewTapPrivacyNotification() = 0; - void NewDismissPrivacyNotification(); + virtual void NewDismissPrivacyNotification() = 0; - void NewScanForShareTargetsEnd(int64_t session_id); + virtual void NewScanForShareTargetsEnd(int64_t session_id) = 0; - void NewScanForShareTargetsStart( + virtual void NewScanForShareTargetsStart( int64_t session_id, location::nearby::proto::sharing::SessionStatus status, AnalyticsInformation analytics_information, int64_t flow_id, - std::optional referrer_package); + std::optional referrer_package) = 0; - void NewSendAttachmentsEnd( + virtual void NewSendAttachmentsEnd( int64_t session_id, int64_t sent_bytes, const ShareTarget& share_target, location::nearby::proto::sharing::AttachmentTransmissionStatus status, int transfer_position, int concurrent_connections, int64_t duration_millis, std::optional referrer_package, location::nearby::proto::sharing::ConnectionLayerStatus connection_layer_status, - location::nearby::proto::sharing::OSType share_target_os_type); + location::nearby::proto::sharing::OSType share_target_os_type) = 0; - void NewSendAttachmentsStart(int64_t session_id, - const AttachmentContainer& attachments, - int transfer_position, - int concurrent_connections, - bool advanced_protection_enabled, - bool advanced_protection_mismatch); + virtual void NewSendAttachmentsStart(int64_t session_id, + const AttachmentContainer& attachments, + int transfer_position, + int concurrent_connections, + bool advanced_protection_enabled, + bool advanced_protection_mismatch) = 0; - void NewSendFastInitialization(); + virtual void NewSendFastInitialization() = 0; - void NewSendStart(int64_t session_id, int transfer_position, - int concurrent_connections, - const ShareTarget& share_target); + virtual void NewSendStart(int64_t session_id, int transfer_position, + int concurrent_connections, + const ShareTarget& share_target) = 0; - void NewSendIntroduction( + virtual void NewSendIntroduction( ShareTargetType target_type, int64_t session_id, location::nearby::proto::sharing::DeviceRelationship relationship, - location::nearby::proto::sharing::OSType share_target_os_type); + location::nearby::proto::sharing::OSType share_target_os_type) = 0; - void NewSendIntroduction( + virtual void NewSendIntroduction( int64_t session_id, const ShareTarget& share_target, int transfer_position, int concurrent_connections, - location::nearby::proto::sharing::OSType share_target_os_type); + location::nearby::proto::sharing::OSType share_target_os_type) = 0; - void NewSetVisibility(nearby::sharing::proto::DeviceVisibility src_visibility, - nearby::sharing::proto::DeviceVisibility dst_visibility, - int64_t duration_millis); + virtual void NewSetVisibility( + nearby::sharing::proto::DeviceVisibility src_visibility, + nearby::sharing::proto::DeviceVisibility dst_visibility, + int64_t duration_millis) = 0; - void NewDeviceSettings(AnalyticsDeviceSettings settings); + virtual void NewDeviceSettings(AnalyticsDeviceSettings settings) = 0; - void NewSetDataUsage(nearby::sharing::proto::DataUsage original_preference, - nearby::sharing::proto::DataUsage preference); + virtual void NewSetDataUsage( + nearby::sharing::proto::DataUsage original_preference, + nearby::sharing::proto::DataUsage preference) = 0; - void NewAddQuickSettingsTile(); + virtual void NewAddQuickSettingsTile() = 0; - void NewRemoveQuickSettingsTile(); + virtual void NewRemoveQuickSettingsTile() = 0; - void NewTapQuickSettingsTile(); + virtual void NewTapQuickSettingsTile() = 0; - void NewToggleShowNotification( + virtual void NewToggleShowNotification( location::nearby::proto::sharing::ShowNotificationStatus prev_status, - location::nearby::proto::sharing::ShowNotificationStatus current_status); + location::nearby::proto::sharing::ShowNotificationStatus + current_status) = 0; - void NewSetDeviceName(int device_name_size); + virtual void NewSetDeviceName(int device_name_size) = 0; - void NewRequestSettingPermissions( + virtual void NewRequestSettingPermissions( location::nearby::proto::sharing::PermissionRequestType type, - location::nearby::proto::sharing::PermissionRequestResult result); + location::nearby::proto::sharing::PermissionRequestResult result) = 0; - void NewInstallAPKStatus( + virtual void NewInstallAPKStatus( location::nearby::proto::sharing::InstallAPKStatus status, - location::nearby::proto::sharing::ApkSource source); + location::nearby::proto::sharing::ApkSource source) = 0; - void NewVerifyAPKStatus( + virtual void NewVerifyAPKStatus( location::nearby::proto::sharing::VerifyAPKStatus status, - location::nearby::proto::sharing::ApkSource source); + location::nearby::proto::sharing::ApkSource source) = 0; - void NewRpcCallStatus( - absl::string_view rpc_name, - nearby::sharing::analytics::proto::SharingLog::RpcCallStatus::RpcDirection - direction, - int error_code, absl::Duration latency); + virtual void NewRpcCallStatus(absl::string_view rpc_name, + RpcDirection direction, int error_code, + absl::Duration latency) = 0; // Generates a random number for session ID or flow ID. - int64_t GenerateNextId(); - - private: - std::unique_ptr - CreateSharingLog( - location::nearby::proto::sharing::EventCategory event_category, - location::nearby::proto::sharing::EventType event_type); - void LogEvent(const nearby::sharing::analytics::proto::SharingLog& message); - - const int32_t vendor_id_; - nearby::analytics::EventLogger* event_logger_ = nullptr; + virtual int64_t GenerateNextId() = 0; }; -} // namespace analytics -} // namespace sharing -} // namespace nearby +} // namespace nearby::sharing::analytics #endif // THIRD_PARTY_NEARBY_SHARING_ANALYTICS_ANALYTICS_RECORDER_H_ diff --git a/sharing/analytics/analytics_recorder_test.cc b/sharing/analytics/analytics_recorder_test.cc deleted file mode 100644 index 1686947d..00000000 --- a/sharing/analytics/analytics_recorder_test.cc +++ /dev/null @@ -1,901 +0,0 @@ -// Copyright 2022-2023 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 "sharing/analytics/analytics_recorder.h" - -#include - -#include -#include -#include - -#include "google/protobuf/duration.pb.h" -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "internal/analytics/mock_event_logger.h" -#include "proto/sharing_enums.pb.h" -#include "sharing/analytics/analytics_device_settings.h" -#include "sharing/analytics/analytics_information.h" -#include "sharing/attachment_container.h" -#include "sharing/common/nearby_share_enums.h" -#include "sharing/file_attachment.h" -#include "sharing/proto/analytics/nearby_sharing_log.pb.h" -#include "sharing/proto/enums.pb.h" -#include "sharing/proto/wire_format.pb.h" -#include "sharing/share_target.h" -#include "sharing/text_attachment.h" - -namespace nearby::sharing::analytics { -namespace { - -using ::location::nearby::proto::sharing::EventCategory; -using ::location::nearby::proto::sharing::EventType; -using ::location::nearby::proto::sharing::OSType; -using ::nearby::analytics::MockEventLogger; -using ::nearby::sharing::analytics::proto::SharingLog; -using ::nearby::sharing::proto::DataUsage; -using ::nearby::sharing::proto::DeviceVisibility; -using ::testing::An; - -constexpr absl::string_view kFileName = "fileName"; -constexpr absl::string_view kTextBody = "textBody"; -constexpr absl::string_view kFileDocumentName = "abc.pdf"; -constexpr absl::string_view kFileMimeType = "application/pdf"; -constexpr absl::string_view kTextMimeType = "text/plain"; -constexpr absl::string_view kAppPackageName = "com.google.android.youtube"; - -class AnalyticsRecorderTest : public ::testing::Test { - public: - AnalyticsRecorderTest() = default; - ~AnalyticsRecorderTest() override = default; - - MockEventLogger& event_logger() { return event_logger_; } - - AnalyticsRecorder analytics_recoder() { return analytics_recorder_; } - - private: - MockEventLogger event_logger_; - AnalyticsRecorder analytics_recorder_{/*vendor_id=*/0, &event_logger_}; -}; - -TEST_F(AnalyticsRecorderTest, NewEstablishConnection) { - ShareTarget share_target; - share_target.device_name = "share_target"; - share_target.type = ShareTargetType::kPhone; - - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::ESTABLISH_CONNECTION); - EXPECT_EQ(log.event_category(), EventCategory::SENDING_EVENT); - EXPECT_EQ(log.establish_connection().status(), - location::nearby::proto::sharing::EstablishConnectionStatus:: - CONNECTION_STATUS_SUCCESS); - EXPECT_EQ(log.establish_connection().session_id(), 1); - EXPECT_EQ(log.establish_connection().transfer_position(), 1); - EXPECT_EQ(log.establish_connection().concurrent_connections(), 1); - EXPECT_EQ(log.establish_connection().duration_millis(), 100); - EXPECT_EQ(log.establish_connection().share_target_info().os_type(), - location::nearby::proto::sharing::OSType::ANDROID); - EXPECT_EQ(log.establish_connection().referrer_name(), kAppPackageName); - }); - - analytics_recoder().NewEstablishConnection( - 1, - location::nearby::proto::sharing::EstablishConnectionStatus:: - CONNECTION_STATUS_SUCCESS, - share_target, 1, 1, 100, std::string(kAppPackageName)); -} - -TEST_F(AnalyticsRecorderTest, NewAcceptAgreements) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::ACCEPT_AGREEMENTS); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - }); - - analytics_recoder().NewAcceptAgreements(); -} - -TEST_F(AnalyticsRecorderTest, NewDeclineAgreements) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::DECLINE_AGREEMENTS); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - }); - - analytics_recoder().NewDeclineAgreements(); -} - -TEST_F(AnalyticsRecorderTest, NewAddContact) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::ADD_CONTACT); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - }); - - analytics_recoder().NewAddContact(); -} - -TEST_F(AnalyticsRecorderTest, NewRemoveContact) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::REMOVE_CONTACT); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - }); - - analytics_recoder().NewRemoveContact(); -} - -TEST_F(AnalyticsRecorderTest, NewTapFeedback) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::TAP_FEEDBACK); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - }); - - analytics_recoder().NewTapFeedback(); -} - -TEST_F(AnalyticsRecorderTest, NewTapHelp) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::TAP_HELP); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - }); - - analytics_recoder().NewTapHelp(); -} - -TEST_F(AnalyticsRecorderTest, NewLaunchDeviceContactConsent) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::LAUNCH_CONSENT); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - EXPECT_EQ(log.launch_consent().status(), - location::nearby::proto::sharing::ConsentAcceptanceStatus:: - CONSENT_ACCEPTED); - }); - - analytics_recoder().NewLaunchDeviceContactConsent( - ::location::nearby::proto::sharing::ConsentAcceptanceStatus:: - CONSENT_ACCEPTED); -} - -TEST_F(AnalyticsRecorderTest, NewAdvertiseDevicePresenceEnd) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::ADVERTISE_DEVICE_PRESENCE_END); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - EXPECT_EQ(log.advertise_device_presence_end().session_id(), 100); - }); - - analytics_recoder().NewAdvertiseDevicePresenceEnd(100); -} - -TEST_F(AnalyticsRecorderTest, NewAdvertiseDevicePresenceStart) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::ADVERTISE_DEVICE_PRESENCE_START); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - EXPECT_EQ(log.advertise_device_presence_start().visibility(), - location::nearby::proto::sharing::Visibility::CONTACTS_ONLY); - EXPECT_EQ(log.advertise_device_presence_start().status(), - location::nearby::proto::sharing::SessionStatus:: - SUCCEEDED_SESSION_STATUS); - EXPECT_EQ(log.advertise_device_presence_start().data_usage(), - location::nearby::proto::sharing::DataUsage::OFFLINE); - EXPECT_EQ(log.advertise_device_presence_start().referrer_name(), - kAppPackageName); - EXPECT_EQ(log.advertise_device_presence_start().session_id(), 100); - }); - - analytics_recoder().NewAdvertiseDevicePresenceStart( - 100, DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, - location::nearby::proto::sharing::SessionStatus::SUCCEEDED_SESSION_STATUS, - DataUsage::OFFLINE_DATA_USAGE, std::string(kAppPackageName)); -} - -TEST_F(AnalyticsRecorderTest, NewDescribeAttachments) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::DESCRIBE_ATTACHMENTS); - EXPECT_EQ(log.event_category(), EventCategory::SENDING_EVENT); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .text_attachment_size(), - 5); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .text_attachment(0) - .size_bytes(), - kTextBody.size()); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .text_attachment(0) - .type(), - SharingLog::TextAttachment::UNKNOWN_TEXT_TYPE); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .text_attachment(1) - .type(), - SharingLog::TextAttachment::PHONE_NUMBER); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .text_attachment(2) - .type(), - SharingLog::TextAttachment::URL); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .text_attachment(3) - .type(), - SharingLog::TextAttachment::ADDRESS); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .text_attachment(4) - .type(), - SharingLog::TextAttachment::UNKNOWN_TEXT_TYPE); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .file_attachment_size(), - 4); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .file_attachment(0) - .size_bytes(), - 2); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .file_attachment(0) - .type(), - SharingLog::FileAttachment::IMAGE); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .file_attachment(1) - .type(), - SharingLog::FileAttachment::DOCUMENT); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .file_attachment(2) - .type(), - SharingLog::FileAttachment::AUDIO); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .file_attachment(3) - .type(), - SharingLog::FileAttachment::DOCUMENT); - }); - - std::unique_ptr attachments = - AttachmentContainer::Builder( - {TextAttachment(5, service::proto::TextMetadata::TEXT, - std::string(kTextBody), kTextBody.size()), - TextAttachment(6, service::proto::TextMetadata::PHONE_NUMBER, - std::string(kTextBody), kTextBody.size()), - TextAttachment(7, service::proto::TextMetadata::URL, - std::string(kTextBody), kTextBody.size()), - TextAttachment(8, service::proto::TextMetadata::ADDRESS, - std::string(kTextBody), kTextBody.size()), - TextAttachment(9, service::proto::TextMetadata::UNKNOWN, - std::string(kTextBody), kTextBody.size())}, - {FileAttachment(1, 2, std::string(kFileName), "", - service::proto::FileMetadata::IMAGE), - FileAttachment(2, 3, std::string(kFileDocumentName), - std::string(kFileMimeType), - service::proto::FileMetadata::DOCUMENT), - FileAttachment(3, 4, std::string(kFileName), "", - service::proto::FileMetadata::AUDIO), - FileAttachment(4, 5, std::string(kFileName), - std::string(kTextMimeType), - service::proto::FileMetadata::DOCUMENT)}, - {}) - .Build(); - - analytics_recoder().NewDescribeAttachments(*attachments); -} - -TEST_F(AnalyticsRecorderTest, EmptyDescribeAttachments) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::DESCRIBE_ATTACHMENTS); - EXPECT_EQ(log.event_category(), EventCategory::SENDING_EVENT); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .text_attachment_size(), - 0); - EXPECT_EQ(log.describe_attachments() - .attachments_info() - .file_attachment_size(), - 0); - }); - - analytics_recoder().NewDescribeAttachments(AttachmentContainer()); -} - -TEST_F(AnalyticsRecorderTest, NewDiscoverShareTarget) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::DISCOVER_SHARE_TARGET); - EXPECT_EQ(log.event_category(), EventCategory::SENDING_EVENT); - EXPECT_EQ(log.discover_share_target().duration_since_scanning().nanos(), - (2100 % 1000) * 1000000); - EXPECT_EQ( - log.discover_share_target().duration_since_scanning().seconds(), - 2100 / 1000); - EXPECT_EQ( - log.discover_share_target() - .share_target_info() - .device_relationship(), - ::location::nearby::proto::sharing::DeviceRelationship::IS_CONTACT); - EXPECT_EQ(log.discover_share_target().share_target_info().device_type(), - ::location::nearby::proto::sharing::DeviceType::LAPTOP); - EXPECT_EQ(log.discover_share_target().share_target_info().os_type(), - ::location::nearby::proto::sharing::OSType::UNKNOWN_OS_TYPE); - EXPECT_EQ(log.discover_share_target().session_id(), 1); - EXPECT_EQ(log.discover_share_target().flow_id(), 100); - EXPECT_FALSE(log.discover_share_target().has_referrer_name()); - EXPECT_EQ( - log.discover_share_target().latency_since_activity_start_millis(), - 2); - }); - - ShareTarget share_target; - share_target.device_name = "share_target"; - share_target.type = ShareTargetType::kLaptop; - share_target.is_incoming = true; - share_target.is_known = true; - - analytics_recoder().NewDiscoverShareTarget(share_target, 1, 2100, 100, - std::nullopt, 2); -} - -TEST_F(AnalyticsRecorderTest, NewEnableNearbySharing) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::ENABLE_NEARBY_SHARING); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - EXPECT_EQ(log.enable_nearby_sharing().status(), - location::nearby::proto::sharing::NearbySharingStatus::ON); - }); - - analytics_recoder().NewEnableNearbySharing( - ::location::nearby::proto::sharing::NearbySharingStatus::ON); -} - -TEST_F(AnalyticsRecorderTest, NewOpenReceivedAttachments) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::OPEN_RECEIVED_ATTACHMENTS); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - EXPECT_EQ(log.open_received_attachments() - .attachments_info() - .text_attachment_size(), - 0); - EXPECT_EQ(log.open_received_attachments() - .attachments_info() - .file_attachment_size(), - 0); - EXPECT_EQ(log.open_received_attachments().session_id(), 1); - }); - - analytics_recoder().NewOpenReceivedAttachments(AttachmentContainer(), 1); -} - -TEST_F(AnalyticsRecorderTest, NewProcessReceivedAttachmentsEnd) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), - EventType::PROCESS_RECEIVED_ATTACHMENTS_END); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - EXPECT_EQ(log.process_received_attachments_end().session_id(), 1); - EXPECT_EQ( - log.process_received_attachments_end().status(), - location::nearby::proto::sharing::ProcessReceivedAttachmentsStatus:: - PROCESSING_STATUS_COMPLETE_PROCESSING_ATTACHMENTS); - }); - - analytics_recoder().NewProcessReceivedAttachmentsEnd( - 1, location::nearby::proto::sharing::ProcessReceivedAttachmentsStatus:: - PROCESSING_STATUS_COMPLETE_PROCESSING_ATTACHMENTS); -} - -TEST_F(AnalyticsRecorderTest, NewReceiveAttachmentsEnd) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::RECEIVE_ATTACHMENTS_END); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - EXPECT_EQ(log.receive_attachments_end().session_id(), 1); - EXPECT_EQ(log.receive_attachments_end().received_bytes(), 2); - EXPECT_EQ( - log.receive_attachments_end().status(), - ::location::nearby::proto::sharing::AttachmentTransmissionStatus:: - COMPLETE_ATTACHMENT_TRANSMISSION_STATUS); - EXPECT_EQ(log.receive_attachments_end().referrer_name(), - kAppPackageName); - }); - - analytics_recoder().NewReceiveAttachmentsEnd( - 1, 2, - ::location::nearby::proto::sharing::AttachmentTransmissionStatus:: - COMPLETE_ATTACHMENT_TRANSMISSION_STATUS, - std::string(kAppPackageName)); -} - -TEST_F(AnalyticsRecorderTest, NewReceiveAttachmentsStart) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::RECEIVE_ATTACHMENTS_START); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - EXPECT_EQ(log.receive_attachments_start().session_id(), 1); - EXPECT_EQ(log.receive_attachments_start() - .attachments_info() - .file_attachment_size(), - 0); - }); - - analytics_recoder().NewReceiveAttachmentsStart(1, AttachmentContainer()); -} - -TEST_F(AnalyticsRecorderTest, NewReceiveFastInitialization) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::RECEIVE_FAST_INITIALIZATION); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - EXPECT_EQ(log.receive_initialization() - .time_elapse_since_screen_unlock_millis(), - 1); - }); - - analytics_recoder().NewReceiveFastInitialization(1); -} - -TEST_F(AnalyticsRecorderTest, NewAcceptFastInitialization) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::ACCEPT_FAST_INITIALIZATION); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - }); - - analytics_recoder().NewAcceptFastInitialization(); -} - -TEST_F(AnalyticsRecorderTest, NewDismissFastInitialization) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::DISMISS_FAST_INITIALIZATION); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - }); - - analytics_recoder().NewDismissFastInitialization(); -} - -TEST_F(AnalyticsRecorderTest, NewReceiveIntroduction) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::RECEIVE_INTRODUCTION); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - EXPECT_EQ(log.receive_introduction().session_id(), 1); - EXPECT_EQ(log.receive_introduction().share_target_info().os_type(), - ::location::nearby::proto::sharing::OSType::WINDOWS); - EXPECT_EQ(log.receive_introduction().share_target_info().device_type(), - ::location::nearby::proto::sharing::DeviceType::PHONE); - EXPECT_EQ(log.receive_introduction().referrer_name(), kAppPackageName); - }); - - ShareTarget share_target; - share_target.device_name = "share_target"; - share_target.type = ShareTargetType::kPhone; - analytics_recoder().NewReceiveIntroduction( - 1, share_target, std::string(kAppPackageName), OSType::WINDOWS); -} - -TEST_F(AnalyticsRecorderTest, NewRespondToIntroduction) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::RESPOND_TO_INTRODUCTION); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - EXPECT_EQ(log.respond_introduction().session_id(), 1); - EXPECT_EQ(log.respond_introduction().action(), - ::location::nearby::proto::sharing::ResponseToIntroduction:: - ACCEPT_INTRODUCTION); - }); - - analytics_recoder().NewRespondToIntroduction( - ::location::nearby::proto::sharing::ResponseToIntroduction:: - ACCEPT_INTRODUCTION, - 1); -} - -TEST_F(AnalyticsRecorderTest, NewTapPrivacyNotification) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::TAP_PRIVACY_NOTIFICATION); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - }); - - analytics_recoder().NewTapPrivacyNotification(); -} - -TEST_F(AnalyticsRecorderTest, NewDismissPrivacyNotification) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::DISMISS_PRIVACY_NOTIFICATION); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - }); - - analytics_recoder().NewDismissPrivacyNotification(); -} - -TEST_F(AnalyticsRecorderTest, NewScanForShareTargetsEnd) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::SCAN_FOR_SHARE_TARGETS_END); - EXPECT_EQ(log.event_category(), EventCategory::SENDING_EVENT); - EXPECT_EQ(log.scan_for_share_targets_end().session_id(), 100); - }); - - analytics_recoder().NewScanForShareTargetsEnd(100); -} - -TEST_F(AnalyticsRecorderTest, NewScanForShareTargetsStart) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::SCAN_FOR_SHARE_TARGETS_START); - EXPECT_EQ(log.event_category(), EventCategory::SENDING_EVENT); - EXPECT_EQ(log.scan_for_share_targets_start().session_id(), 3); - EXPECT_EQ(log.scan_for_share_targets_start().status(), - ::location::nearby::proto::sharing::SessionStatus:: - FAILED_SESSION_STATUS); - EXPECT_EQ(log.scan_for_share_targets_start().flow_id(), 100); - EXPECT_EQ( - log.scan_for_share_targets_start().scan_type(), - ::location::nearby::proto::sharing::ScanType::FOREGROUND_SCAN); - EXPECT_FALSE(log.scan_for_share_targets_start().has_referrer_name()); - }); - - analytics_recoder().NewScanForShareTargetsStart( - 3, - ::location::nearby::proto::sharing::SessionStatus::FAILED_SESSION_STATUS, - AnalyticsInformation{SendSurfaceState::kForeground}, 100, std::nullopt); -} - -TEST_F(AnalyticsRecorderTest, NewSendAttachmentsEnd) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::SEND_ATTACHMENTS_END); - EXPECT_EQ(log.event_category(), EventCategory::SENDING_EVENT); - EXPECT_EQ(log.send_attachments_end().session_id(), 1); - EXPECT_EQ(log.send_attachments_end().sent_bytes(), 2); - EXPECT_EQ(log.send_attachments_end().share_target_info().os_type(), - ::location::nearby::proto::sharing::OSType::ANDROID); - EXPECT_EQ(log.send_attachments_end().share_target_info().device_type(), - ::location::nearby::proto::sharing::DeviceType::PHONE); - EXPECT_EQ(log.send_attachments_end().transfer_position(), 1); - EXPECT_EQ(log.send_attachments_end().concurrent_connections(), 2); - EXPECT_EQ(log.send_attachments_end().duration_millis(), 100); - EXPECT_EQ( - log.send_attachments_end().status(), - ::location::nearby::proto::sharing::AttachmentTransmissionStatus:: - COMPLETE_ATTACHMENT_TRANSMISSION_STATUS); - EXPECT_EQ(log.send_attachments_end().referrer_name(), kAppPackageName); - }); - - ShareTarget share_target; - share_target.device_name = "share_target"; - share_target.type = ShareTargetType::kPhone; - analytics_recoder().NewSendAttachmentsEnd( - 1, 2, share_target, - ::location::nearby::proto::sharing::AttachmentTransmissionStatus:: - COMPLETE_ATTACHMENT_TRANSMISSION_STATUS, - 1, 2, 100, std::string(kAppPackageName), - ::location::nearby::proto::sharing::ConnectionLayerStatus:: - CONNECTION_LAYER_STATUS_UNKNOWN, - OSType::UNKNOWN_OS_TYPE); -} - -TEST_F(AnalyticsRecorderTest, NewSendAttachmentsStart) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::SEND_ATTACHMENTS_START); - EXPECT_EQ(log.event_category(), EventCategory::SENDING_EVENT); - EXPECT_EQ(log.send_attachments_start().session_id(), 1); - EXPECT_EQ(log.send_attachments_start() - .attachments_info() - .file_attachment_size(), - 0); - EXPECT_EQ(log.send_attachments_start().transfer_position(), 100); - EXPECT_EQ(log.send_attachments_start().concurrent_connections(), 200); - EXPECT_EQ(log.send_attachments_start().advanced_protection_enabled(), - true); - EXPECT_EQ(log.send_attachments_start().advanced_protection_mismatch(), - true); - }); - - analytics_recoder().NewSendAttachmentsStart(1, AttachmentContainer(), 100, - 200, true, true); -} - -TEST_F(AnalyticsRecorderTest, NewSendFastInitialization) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::SEND_FAST_INITIALIZATION); - EXPECT_EQ(log.event_category(), EventCategory::SENDING_EVENT); - }); - - analytics_recoder().NewSendFastInitialization(); -} - -TEST_F(AnalyticsRecorderTest, NewSendStart) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::SEND_START); - EXPECT_EQ(log.event_category(), EventCategory::SENDING_EVENT); - EXPECT_EQ(log.send_start().session_id(), 123); - EXPECT_EQ(log.send_start().transfer_position(), 1); - EXPECT_EQ(log.send_start().concurrent_connections(), 2); - EXPECT_EQ(log.send_start().share_target_info().device_type(), - ::location::nearby::proto::sharing::DeviceType::LAPTOP); - EXPECT_EQ(log.send_start().share_target_info().os_type(), - ::location::nearby::proto::sharing::OSType::UNKNOWN_OS_TYPE); - }); - - ShareTarget share_target; - share_target.device_name = "share_target"; - share_target.type = ShareTargetType::kLaptop; - share_target.is_known = true; - share_target.is_incoming = true; - analytics_recoder().NewSendStart(123, 1, 2, share_target); -} - -TEST_F(AnalyticsRecorderTest, NewSendIntroductionWithRelationship) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::SEND_INTRODUCTION); - EXPECT_EQ(log.event_category(), EventCategory::SENDING_EVENT); - EXPECT_EQ(log.send_introduction().session_id(), 5); - EXPECT_EQ(log.send_introduction().share_target_info().device_type(), - ::location::nearby::proto::sharing::DeviceType::LAPTOP); - EXPECT_EQ(log.send_introduction().share_target_info().os_type(), - ::location::nearby::proto::sharing::OSType::MACOS); - EXPECT_EQ( - log.send_introduction().share_target_info().device_relationship(), - ::location::nearby::proto::sharing::DeviceRelationship::IS_CONTACT); - }); - - analytics_recoder().NewSendIntroduction( - ShareTargetType::kLaptop, 5, - ::location::nearby::proto::sharing::DeviceRelationship::IS_CONTACT, - OSType::MACOS); -} - -TEST_F(AnalyticsRecorderTest, NewSendIntroduction) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::SEND_INTRODUCTION); - EXPECT_EQ(log.event_category(), EventCategory::SENDING_EVENT); - EXPECT_EQ(log.send_introduction().session_id(), 1); - EXPECT_EQ(log.send_introduction().transfer_position(), 2); - EXPECT_EQ(log.send_introduction().concurrent_connections(), 3); - EXPECT_EQ(log.send_introduction().share_target_info().device_type(), - ::location::nearby::proto::sharing::DeviceType::LAPTOP); - EXPECT_EQ(log.send_introduction().share_target_info().os_type(), - ::location::nearby::proto::sharing::OSType::UNKNOWN_OS_TYPE); - }); - - ShareTarget share_target; - share_target.device_name = "share_target"; - share_target.type = ShareTargetType::kLaptop; - analytics_recoder().NewSendIntroduction(1, share_target, 2, 3, - OSType::UNKNOWN_OS_TYPE); -} - -TEST_F(AnalyticsRecorderTest, NewSetVisibility) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::SET_VISIBILITY); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - EXPECT_EQ(log.set_visibility().duration_millis(), 100); - EXPECT_EQ(log.set_visibility().source_visibility(), - ::location::nearby::proto::sharing::Visibility::EVERYONE); - EXPECT_EQ( - log.set_visibility().visibility(), - ::location::nearby::proto::sharing::Visibility::CONTACTS_ONLY); - }); - - analytics_recoder().NewSetVisibility( - DeviceVisibility::DEVICE_VISIBILITY_EVERYONE, - DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, 100); -} - -TEST_F(AnalyticsRecorderTest, NewDeviceSettings) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::DEVICE_SETTINGS); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - EXPECT_EQ(log.device_settings().device_name_size(), 10); - EXPECT_EQ(log.device_settings().visibility(), - ::location::nearby::proto::sharing::Visibility::EVERYONE); - EXPECT_EQ(log.device_settings().data_usage(), - ::location::nearby::proto::sharing::DataUsage::WIFI_ONLY); - EXPECT_EQ(log.device_settings().is_show_notification_enabled(), true); - }); - - AnalyticsDeviceSettings device_settings; - device_settings.device_name_size = 10; - device_settings.data_usage = DataUsage::WIFI_ONLY_DATA_USAGE; - device_settings.is_fast_init_notification_enabled = true; - device_settings.visibility = DeviceVisibility::DEVICE_VISIBILITY_EVERYONE; - analytics_recoder().NewDeviceSettings(device_settings); -} - -TEST_F(AnalyticsRecorderTest, NewSetDataUsage) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::SET_DATA_USAGE); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - EXPECT_EQ(log.set_data_usage().preference(), - ::location::nearby::proto::sharing::DataUsage::OFFLINE); - EXPECT_EQ(log.set_data_usage().original_preference(), - ::location::nearby::proto::sharing::DataUsage::WIFI_ONLY); - }); - - analytics_recoder().NewSetDataUsage(DataUsage::WIFI_ONLY_DATA_USAGE, - DataUsage::OFFLINE_DATA_USAGE); -} - -TEST_F(AnalyticsRecorderTest, NewAddQuickSettingsTile) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::ADD_QUICK_SETTINGS_TILE); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - }); - - analytics_recoder().NewAddQuickSettingsTile(); -} - -TEST_F(AnalyticsRecorderTest, NewRemoveQuickSettingsTile) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::REMOVE_QUICK_SETTINGS_TILE); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - }); - - analytics_recoder().NewRemoveQuickSettingsTile(); -} - -TEST_F(AnalyticsRecorderTest, NewTapQuickSettingsTile) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::TAP_QUICK_SETTINGS_TILE); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - }); - - analytics_recoder().NewTapQuickSettingsTile(); -} - -TEST_F(AnalyticsRecorderTest, NewToggleShowNotification) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::TOGGLE_SHOW_NOTIFICATION); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - EXPECT_EQ( - log.toggle_show_notification().previous_status(), - ::location::nearby::proto::sharing::ShowNotificationStatus::SHOW); - EXPECT_EQ(log.toggle_show_notification().current_status(), - ::location::nearby::proto::sharing::ShowNotificationStatus:: - NOT_SHOW); - }); - - analytics_recoder().NewToggleShowNotification( - ::location::nearby::proto::sharing::ShowNotificationStatus::SHOW, - ::location::nearby::proto::sharing::ShowNotificationStatus::NOT_SHOW); -} - -TEST_F(AnalyticsRecorderTest, NewSetDeviceName) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::SET_DEVICE_NAME); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - EXPECT_EQ(log.set_device_name().device_name_size(), 16); - }); - - analytics_recoder().NewSetDeviceName(16); -} - -TEST_F(AnalyticsRecorderTest, NewRequestSettingPermissions) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::REQUEST_SETTING_PERMISSIONS); - EXPECT_EQ(log.event_category(), EventCategory::SETTINGS_EVENT); - EXPECT_EQ(log.request_setting_permissions().permission_type(), - ::location::nearby::proto::sharing::PermissionRequestType:: - PERMISSION_BLUETOOTH); - EXPECT_EQ( - log.request_setting_permissions().permission_request_result(), - ::location::nearby::proto::sharing::PermissionRequestResult:: - PERMISSION_GRANTED); - }); - - analytics_recoder().NewRequestSettingPermissions( - ::location::nearby::proto::sharing::PermissionRequestType:: - PERMISSION_BLUETOOTH, - ::location::nearby::proto::sharing::PermissionRequestResult:: - PERMISSION_GRANTED); -} - -TEST_F(AnalyticsRecorderTest, NewInstallAPKStatus) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::INSTALL_APK); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - EXPECT_EQ(log.install_apk_status().status(0), - ::location::nearby::proto::sharing::InstallAPKStatus:: - SUCCESS_INSTALLATION); - EXPECT_EQ( - log.install_apk_status().source(0), - ::location::nearby::proto::sharing::ApkSource::APK_FROM_SD_CARD); - }); - - analytics_recoder().NewInstallAPKStatus( - ::location::nearby::proto::sharing::InstallAPKStatus:: - SUCCESS_INSTALLATION, - ::location::nearby::proto::sharing::ApkSource::APK_FROM_SD_CARD); -} - -TEST_F(AnalyticsRecorderTest, NewVerifyAPKStatus) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::VERIFY_APK); - EXPECT_EQ(log.event_category(), EventCategory::RECEIVING_EVENT); - EXPECT_EQ( - log.verify_apk_status().status(0), - ::location::nearby::proto::sharing::VerifyAPKStatus::INSTALLABLE); - EXPECT_EQ( - log.verify_apk_status().source(0), - ::location::nearby::proto::sharing::ApkSource::APK_FROM_SD_CARD); - }); - - analytics_recoder().NewVerifyAPKStatus( - ::location::nearby::proto::sharing::VerifyAPKStatus::INSTALLABLE, - ::location::nearby::proto::sharing::ApkSource::APK_FROM_SD_CARD); -} - -TEST_F(AnalyticsRecorderTest, NewRpcCallStatus) { - EXPECT_CALL(event_logger(), Log(An())) - .WillOnce([](const SharingLog& log) { - EXPECT_EQ(log.event_type(), EventType::RPC_CALL_STATUS); - EXPECT_EQ(log.event_category(), EventCategory::RPC_EVENT); - EXPECT_EQ(log.rpc_call_status().rpc_name(), "service.rpc_name"); - EXPECT_EQ(log.rpc_call_status().direction(), - SharingLog::RpcCallStatus::OUTGOING); - EXPECT_EQ(log.rpc_call_status().error_code(), 123); - EXPECT_EQ(log.rpc_call_status().latency_millis(), 456); - }); - - analytics_recoder().NewRpcCallStatus( - "service.rpc_name", SharingLog::RpcCallStatus::OUTGOING, 123, - absl::Milliseconds(456)); -} - -TEST_F(AnalyticsRecorderTest, GenerateID) { - int64_t id = analytics_recoder().GenerateNextId(); - EXPECT_GT(id, 0); - int64_t id2 = analytics_recoder().GenerateNextId(); - EXPECT_NE(id2, id); -} - -} // namespace -} // namespace nearby::sharing::analytics diff --git a/sharing/certificates/BUILD b/sharing/certificates/BUILD index a0f085b6..b0073e37 100644 --- a/sharing/certificates/BUILD +++ b/sharing/certificates/BUILD @@ -42,23 +42,28 @@ cc_library( ], visibility = ["//visibility:public"], deps = [ + "//google/nearby/identity/v1:resources_cc_proto", + "//google/nearby/identity/v1:rpcs_cc_proto", + "//google/protobuf:timestamp_cc_proto", "//internal/base", "//internal/base:file_path", "//internal/crypto_cros", + "//internal/flags:nearby_flags", "//internal/platform:mac_address", "//internal/platform:types", - "//internal/platform/implementation:account_manager", + "//location/nearby/sharing/lib/account:account_manager", + "//location/nearby/sharing/lib/rpc:sharing_rpc_client", + "//sharing/flags/generated:generated_flags", "//sharing/internal/api:platform", "//sharing/internal/base", "//sharing/internal/public:logging", "//sharing/internal/public:pref_names", "//sharing/internal/public:types", - "//sharing/linux/stubs:highway_fingerprint", - "//sharing/linux/stubs:rpc", "//sharing/local_device_data", "//sharing/proto:enums_cc_proto", "//sharing/proto:share_cc_proto", "//sharing/scheduling", + "//util/hash:highway_fingerprint", "@com_google_absl//absl/algorithm", "@com_google_absl//absl/base:nullability", "@com_google_absl//absl/container:btree", @@ -72,7 +77,6 @@ cc_library( "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", "@com_google_absl//absl/types:span", - "@com_google_protobuf//:protobuf", ], ) @@ -95,10 +99,10 @@ cc_library( "//internal/base:bluetooth_address", "//internal/base:file_path", "//internal/crypto_cros", + "//location/nearby/sharing/lib/rpc:sharing_rpc_client", "//sharing/common:enum", "//sharing/internal/api:platform", "//sharing/internal/public:types", - "//sharing/linux/stubs:rpc", "//sharing/local_device_data", "//sharing/proto:enums_cc_proto", "//sharing/proto:share_cc_proto", @@ -122,12 +126,16 @@ cc_test( deps = [ ":certificates", ":test_support", + "//google/nearby/identity/v1:resources_cc_proto", + "//google/nearby/identity/v1:rpcs_cc_proto", + "//internal/flags:nearby_flags", "//internal/platform:mac_address", - "//internal/platform/implementation:account_manager", "//internal/platform/implementation:platform_impl", - "//internal/test", - "//sharing/linux/stubs:rpc", + "//location/nearby/sharing/lib/account:account_manager", + "//location/nearby/sharing/lib/account:fake_account_manager", + "//location/nearby/sharing/lib/rpc:fake_nearby_share_client", "//sharing/common:enum", + "//sharing/flags/generated:generated_flags", "//sharing/internal/api:mock_sharing_platform", "//sharing/internal/api:platform", "//sharing/internal/public:pref_names", diff --git a/sharing/certificates/nearby_share_certificate_manager_impl.cc b/sharing/certificates/nearby_share_certificate_manager_impl.cc index 5b68afe8..56526182 100644 --- a/sharing/certificates/nearby_share_certificate_manager_impl.cc +++ b/sharing/certificates/nearby_share_certificate_manager_impl.cc @@ -28,7 +28,11 @@ #include #include +#include "google/nearby/identity/v1/resources.pb.h" +#include "google/nearby/identity/v1/rpcs.pb.h" #include "google/protobuf/timestamp.pb.h" +#include "location/nearby/sharing/lib/account/account_manager.h" +#include "location/nearby/sharing/lib/rpc/sharing_rpc_client.h" #include "absl/algorithm/algorithm.h" #include "absl/base/nullability.h" #include "absl/container/flat_hash_map.h" @@ -41,7 +45,7 @@ #include "absl/time/time.h" #include "absl/types/span.h" #include "internal/base/file_path.h" -#include "internal/platform/implementation/account_manager.h" +#include "internal/flags/nearby_flags.h" #include "internal/platform/mac_address.h" #include "sharing/certificates/common.h" #include "sharing/certificates/constants.h" @@ -51,6 +55,7 @@ #include "sharing/certificates/nearby_share_decrypted_public_certificate.h" #include "sharing/certificates/nearby_share_encrypted_metadata_key.h" #include "sharing/certificates/nearby_share_private_certificate.h" +#include "sharing/flags/generated/nearby_sharing_feature_flags.h" #include "sharing/internal/api/bluetooth_adapter.h" #include "sharing/internal/api/preference_manager.h" #include "sharing/internal/api/public_certificate_database.h" @@ -60,16 +65,6 @@ #include "sharing/internal/public/logging.h" #include "sharing/internal/public/pref_names.h" #include "sharing/local_device_data/nearby_share_local_device_data_manager.h" -#if defined(__linux__) -#include "sharing/linux/stubs/highway_fingerprint.h" -#include "sharing/linux/stubs/identity_rpc_types.h" -#include "sharing/linux/stubs/sharing_rpc_client.h" -#else -#include "google/nearby/identity/v1/resources.pb.h" -#include "google/nearby/identity/v1/rpcs.pb.h" -#include "location/nearby/sharing/lib/rpc/sharing_rpc_client.h" -#include "util/hash/highway_fingerprint.h" -#endif #include "sharing/proto/certificate_rpc.pb.h" #include "sharing/proto/encrypted_metadata.pb.h" #include "sharing/proto/enums.pb.h" @@ -77,12 +72,11 @@ #include "sharing/proto/timestamp.pb.h" #include "sharing/scheduling/nearby_share_scheduler.h" #include "sharing/scheduling/nearby_share_scheduler_factory.h" +#include "util/hash/highway_fingerprint.h" -namespace nearby { -namespace sharing { +namespace nearby::sharing { namespace { -using ::google::nearby::identity::v1::AccountInfo; using ::google::nearby::identity::v1::GetAccountInfoRequest; using ::google::nearby::identity::v1::GetAccountInfoResponse; using ::google::nearby::identity::v1::PerVisibilitySharedCredentials; @@ -90,6 +84,10 @@ using ::google::nearby::identity::v1::PublishDeviceRequest; using ::google::nearby::identity::v1::PublishDeviceResponse; using ::google::nearby::identity::v1::QuerySharedCredentialsRequest; using ::google::nearby::identity::v1::QuerySharedCredentialsResponse; +using ::google::nearby::identity::v1:: + QuerySharedCredentialsWithBindingIdsRequest; +using ::google::nearby::identity::v1:: + QuerySharedCredentialsWithBindingIdsResponse; using ::google::nearby::identity::v1::SharedCredential; using ::nearby::sharing::api::PreferenceManager; using ::nearby::sharing::api::PublicCertificateDatabase; @@ -188,11 +186,7 @@ void DumpCertificateId(std::stringstream& sstream, absl::string_view cert_id, } else { sstream << " Private certificates:["; } - for (int i = 0; i < cert_id.size() - 1; ++i) { - sstream << static_cast(static_cast(cert_id[i])) << ", "; - } - sstream << static_cast(static_cast(cert_id[cert_id.size() - 1])) - << "]" << std::endl; + sstream << absl::BytesToHexString(cert_id) << "]" << std::endl; } } // namespace @@ -344,7 +338,7 @@ void NearbyShareCertificateManagerImpl::CertificateDownloadContext:: request.set_page_token(*next_page_token_); } nearby_identity_client_->QuerySharedCredentials( - std::move(request), + std::move(request), api::IdentityRpcClient::kTimeout, [this](const absl::StatusOr& response) mutable { if (!response.ok()) { @@ -380,6 +374,55 @@ void NearbyShareCertificateManagerImpl::CertificateDownloadContext:: }); } + +void NearbyShareCertificateManagerImpl::CertificateDownloadContext:: + QuerySharedCredentialsWithBindingIdsFetchNextPage() { + LOG(INFO) << __func__ + << ": Downloading public certificates with binding ids page=" + << page_number_; + page_number_++; + QuerySharedCredentialsWithBindingIdsRequest request; + request.set_name(absl::StrCat("devices/", device_id_)); + if (next_page_token_.has_value()) { + request.set_page_token(*next_page_token_); + } + nearby_identity_client_->QuerySharedCredentialsWithBindingIds( + std::move(request), api::IdentityRpcClient::kTimeout, + [this](const absl::StatusOr& + response) mutable { + if (!response.ok()) { + LOG(WARNING) << "Failed to download public certificates: " + << response.status(); + std::move(download_callback_)(response.status()); + return; + } + for (const auto& credential : response->shared_credentials()) { + if (credential.data_type() != + SharedCredential::DATA_TYPE_PUBLIC_CERTIFICATE) { + continue; + } + PublicCertificate certificate; + if (!certificate.ParseFromString(credential.data())) { + LOG(ERROR) << "Failed parsing to PublicCertificate, credential.id: " + << credential.id() << " data: " + << absl::BytesToHexString(credential.data()); + continue; + } + VLOG(1) << "Successfully parsed credential: " << credential.id(); + certificates_.push_back(certificate); + } + + if (response->next_page_token().empty()) { + LOG(INFO) << "Completed download of " << certificates_.size() + << " certificates"; + std::move(download_callback_)(std::move(certificates_)); + return; + } + next_page_token_ = response->next_page_token(); + QuerySharedCredentialsWithBindingIdsFetchNextPage(); + }); +} + bool NearbyShareCertificateManagerImpl::UpdatePublicCertificates( const std::vector& certificates) { // Save certificates to store. @@ -445,7 +488,12 @@ bool NearbyShareCertificateManagerImpl::DownloadPublicCertificatesInExecutor() { } notification.Notify(); }); - context->QuerySharedCredentialsFetchNextPage(); + if (NearbyFlags::GetInstance().GetBoolFlag( + config_package_nearby::nearby_sharing_feature::kEnableFileSync)) { + context->QuerySharedCredentialsWithBindingIdsFetchNextPage(); + } else { + context->QuerySharedCredentialsFetchNextPage(); + } // Wait for all pages of certificates to be downloaded. // MUST not terminate early, otherwise notification will go out of scope, and // the callback will call Notify on a destroyed object. @@ -553,7 +601,7 @@ bool NearbyShareCertificateManagerImpl::UploadDeviceCertificatesInExecutor( bool regenerate_certificates = false; absl::Notification notification; nearby_identity_client_->PublishDevice( - std::move(request), + std::move(request), api::IdentityRpcClient::kTimeout, [&upload_certificates_succeeded, ®enerate_certificates, ¬ification](const absl::StatusOr& response) { upload_certificates_succeeded = response.ok(); @@ -703,7 +751,7 @@ std::string NearbyShareCertificateManagerImpl::Dump() const { certificate_storage_->GetPublicCertificateIds(); sstream << " Total count:" << ids.size() << std::endl; for (const auto& id : ids) { - DumpCertificateId(sstream, id, true); + DumpCertificateId(sstream, id, /*is_public_cert=*/true); } sstream << std::endl; @@ -716,7 +764,7 @@ std::string NearbyShareCertificateManagerImpl::Dump() const { sstream << " Total count:" << private_certs.size() << std::endl; for (const auto& cert : private_certs) { std::string id(cert.id().begin(), cert.id().end()); - DumpCertificateId(sstream, id, false); + DumpCertificateId(sstream, id, /*is_public_cert=*/false); } } @@ -885,7 +933,7 @@ bool NearbyShareCertificateManagerImpl::UpdateAccountInfoInExecutor() { bool get_account_info_succeeded = false; absl::Notification notification; nearby_identity_client_->GetAccountInfo( - std::move(request), + std::move(request), api::IdentityRpcClient::kTimeout, [this, &get_account_info_succeeded, ¬ification]( const absl::StatusOr& response) mutable { if (!response.ok()) { @@ -895,8 +943,8 @@ bool NearbyShareCertificateManagerImpl::UpdateAccountInfoInExecutor() { const auto& capabilities = response->account_info().capabilities(); bool has_titanium_capability = (std::find(capabilities.begin(), capabilities.end(), - AccountInfo::CAPABILITY_TITANIUM) != - capabilities.end()); + google::nearby::identity::v1::AccountInfo:: + CAPABILITY_TITANIUM) != capabilities.end()); preference_manager_.SetBoolean(PrefNames::kAdvancedProtectionEnabled, has_titanium_capability); LOG(INFO) << "GetAccountInfo succeeded, advanced protection enabled: " @@ -910,5 +958,4 @@ bool NearbyShareCertificateManagerImpl::UpdateAccountInfoInExecutor() { return get_account_info_succeeded; } -} // namespace sharing -} // namespace nearby +} // namespace nearby::sharing diff --git a/sharing/certificates/nearby_share_certificate_manager_impl.h b/sharing/certificates/nearby_share_certificate_manager_impl.h index 2de41713..5f93cad6 100644 --- a/sharing/certificates/nearby_share_certificate_manager_impl.h +++ b/sharing/certificates/nearby_share_certificate_manager_impl.h @@ -23,12 +23,13 @@ #include #include +#include "location/nearby/sharing/lib/account/account_manager.h" +#include "location/nearby/sharing/lib/rpc/sharing_rpc_client.h" #include "absl/base/nullability.h" #include "absl/functional/any_invocable.h" #include "absl/status/statusor.h" #include "absl/time/time.h" #include "internal/base/file_path.h" -#include "internal/platform/implementation/account_manager.h" #include "internal/platform/task_runner.h" #include "sharing/certificates/nearby_share_certificate_manager.h" #include "sharing/certificates/nearby_share_certificate_storage.h" @@ -42,15 +43,7 @@ #include "sharing/proto/enums.pb.h" #include "sharing/proto/rpc_resources.pb.h" -namespace google::nearby::identity::v1 { -class PublishDeviceRequest; -} // namespace google::nearby::identity::v1 - -namespace nearby { -namespace sharing { -namespace api { -class IdentityRpcClient; -} // namespace api +namespace nearby::sharing { class NearbyShareScheduler; @@ -125,6 +118,7 @@ class NearbyShareCertificateManagerImpl // On successful download, if page token in the response is empty, the // |download_success_callback_| is invoked with all downloaded certificates. void QuerySharedCredentialsFetchNextPage(); + void QuerySharedCredentialsWithBindingIdsFetchNextPage(); private: nearby::sharing::api::IdentityRpcClient* absl_nonnull const @@ -227,7 +221,6 @@ class NearbyShareCertificateManagerImpl std::unique_ptr executor_; }; -} // namespace sharing -} // namespace nearby +} // namespace nearby::sharing #endif // THIRD_PARTY_NEARBY_SHARING_CERTIFICATES_NEARBY_SHARE_CERTIFICATE_MANAGER_IMPL_H_ diff --git a/sharing/certificates/nearby_share_certificate_manager_impl_test.cc b/sharing/certificates/nearby_share_certificate_manager_impl_test.cc index 796c91e0..ebd994a6 100644 --- a/sharing/certificates/nearby_share_certificate_manager_impl_test.cc +++ b/sharing/certificates/nearby_share_certificate_manager_impl_test.cc @@ -27,6 +27,8 @@ #include "google/nearby/identity/v1/resources.pb.h" #include "google/nearby/identity/v1/rpcs.pb.h" +#include "location/nearby/sharing/lib/account/account_manager.h" +#include "location/nearby/sharing/lib/account/fake_account_manager.h" #include "location/nearby/sharing/lib/rpc/fake_nearby_share_client.h" #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" @@ -37,9 +39,8 @@ #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "absl/types/span.h" -#include "internal/platform/implementation/account_manager.h" +#include "internal/flags/nearby_flags.h" #include "internal/platform/mac_address.h" -#include "internal/test/fake_account_manager.h" #include "sharing/certificates/constants.h" #include "sharing/certificates/fake_nearby_share_certificate_storage.h" #include "sharing/certificates/nearby_share_certificate_manager.h" @@ -48,6 +49,7 @@ #include "sharing/certificates/nearby_share_encrypted_metadata_key.h" #include "sharing/certificates/nearby_share_private_certificate.h" #include "sharing/certificates/test_util.h" +#include "sharing/flags/generated/nearby_sharing_feature_flags.h" #include "sharing/internal/api/mock_sharing_platform.h" #include "sharing/internal/public/pref_names.h" #include "sharing/internal/test/fake_bluetooth_adapter.h" @@ -62,16 +64,18 @@ #include "sharing/scheduling/fake_nearby_share_scheduler_factory.h" #include "sharing/scheduling/nearby_share_scheduler_factory.h" -namespace nearby { -namespace sharing { +namespace nearby::sharing { namespace { -using ::google::nearby::identity::v1::AccountInfo; using ::google::nearby::identity::v1::Device; using ::google::nearby::identity::v1::GetAccountInfoResponse; using ::google::nearby::identity::v1::PublishDeviceRequest; using ::google::nearby::identity::v1::PublishDeviceResponse; using ::google::nearby::identity::v1::QuerySharedCredentialsRequest; using ::google::nearby::identity::v1::QuerySharedCredentialsResponse; +using ::google::nearby::identity::v1:: + QuerySharedCredentialsWithBindingIdsRequest; +using ::google::nearby::identity::v1:: + QuerySharedCredentialsWithBindingIdsResponse; using ::nearby::sharing::proto::DeviceVisibility; using ::nearby::sharing::proto::PublicCertificate; using ::testing::Not; @@ -102,6 +106,7 @@ class NearbyShareCertificateManagerImplTest ~NearbyShareCertificateManagerImplTest() override = default; void SetUp() override { + NearbyFlags::GetInstance().ResetOverridedValues(); ON_CALL(mock_sharing_platform_, GetPreferenceManager) .WillByDefault(ReturnRef(preference_manager_)); ON_CALL(mock_sharing_platform_, GetAccountManager) @@ -306,7 +311,7 @@ class NearbyShareCertificateManagerImplTest std::max(max_not_after_self_share, cert.not_after()); break; default: - DCHECK(false); + FAIL() << "Unexpected visibility: " << cert.visibility(); break; } @@ -425,6 +430,78 @@ class NearbyShareCertificateManagerImplTest return response; } + void QuerySharedCredentialsWithBindingIdsFlow( + size_t num_pages, DownloadPublicCertificatesResult result) { + size_t prev_num_results = download_scheduler_->handled_results().size(); + cert_store_->SetPublicCertificateIds(kPublicCertificateIds); + + size_t initial_num_notifications = + num_public_certs_downloaded_notifications_; + size_t initial_num_public_cert_exp_reschedules = + public_cert_exp_scheduler_->num_reschedule_calls(); + + std::vector> + responses; + std::string page_token; + for (size_t page_number = 0; page_number < num_pages; ++page_number) { + bool last_page = page_number == num_pages - 1; + if (last_page && result == DownloadPublicCertificatesResult::kHttpError) { + responses.push_back(absl::InternalError("")); + break; + } + page_token = last_page ? std::string() + : absl::StrCat(kPageTokenPrefix, page_number); + responses.push_back(BuildQuerySharedCredentialsWithBindingIdsResponse( + page_number, page_token)); + } + + identity_client_.SetQuerySharedCredentialsWithBindingIdsResponses( + responses); + cert_store_->SetAddPublicCertificatesResult( + result != DownloadPublicCertificatesResult::kStorageError); + download_scheduler_->InvokeRequestCallback(); + Sync(); + + std::vector requests = + identity_client_.query_shared_credentials_with_binding_ids_requests(); + EXPECT_EQ(requests.size(), num_pages); + EXPECT_EQ(requests.back().name(), absl::StrCat("devices/", kDeviceId)); + ASSERT_EQ(download_scheduler_->handled_results().size(), + prev_num_results + 1); + + bool success = result == DownloadPublicCertificatesResult::kSuccess; + EXPECT_EQ(download_scheduler_->handled_results().back(), success); + EXPECT_EQ(num_public_certs_downloaded_notifications_, + initial_num_notifications + (success ? 1u : 0u)); + EXPECT_EQ(public_cert_exp_scheduler_->num_reschedule_calls(), + initial_num_public_cert_exp_reschedules + (success ? 1u : 0u)); + } + + QuerySharedCredentialsWithBindingIdsResponse + BuildQuerySharedCredentialsWithBindingIdsResponse( + size_t page_number, absl::string_view page_token) { + QuerySharedCredentialsWithBindingIdsResponse response; + int i = 0; + for (auto public_certificate : public_certificates_) { + auto* shared_credential = response.add_shared_credentials(); + shared_credential->set_id(page_number * 100 + i); + if (i % 2 == 0) { + shared_credential->set_data_type( + google::nearby::identity::v1::SharedCredential:: + DATA_TYPE_PUBLIC_CERTIFICATE); + } else { + shared_credential->set_data_type( + google::nearby::identity::v1::SharedCredential:: + DATA_TYPE_SHARED_CREDENTIAL); + } + *shared_credential->mutable_data() = + public_certificate.SerializeAsString(); + i++; + } + response.set_next_page_token(page_token); + return response; + } + void CheckStorageAddCertificates( const FakeNearbyShareCertificateStorage::AddPublicCertificatesCall& add_cert_call) { @@ -702,6 +779,24 @@ TEST_F(NearbyShareCertificateManagerImplTest, /*num_pages=*/2, DownloadPublicCertificatesResult::kHttpError)); } +TEST_F(NearbyShareCertificateManagerImplTest, + QuerySharedCredentialsWithBindingIdsSuccess) { + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_sharing_feature::kEnableFileSync, true); + Initialize(); + ASSERT_NO_FATAL_FAILURE(QuerySharedCredentialsWithBindingIdsFlow( + /*num_pages=*/2, DownloadPublicCertificatesResult::kSuccess)); +} + +TEST_F(NearbyShareCertificateManagerImplTest, + QuerySharedCredentialsWithBindingIdsRPCFailure) { + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_sharing_feature::kEnableFileSync, true); + Initialize(); + ASSERT_NO_FATAL_FAILURE(QuerySharedCredentialsWithBindingIdsFlow( + /*num_pages=*/2, DownloadPublicCertificatesResult::kHttpError)); +} + TEST_F(NearbyShareCertificateManagerImplTest, ClearPublicCertificates) { Initialize(); cert_manager_->ClearPublicCertificates([&](bool result) {}); @@ -958,7 +1053,7 @@ TEST_F(NearbyShareCertificateManagerImplTest, Initialize(); GetAccountInfoResponse response; response.mutable_account_info()->mutable_capabilities()->Add( - AccountInfo::CAPABILITY_TITANIUM); + google::nearby::identity::v1::AccountInfo::CAPABILITY_TITANIUM); identity_client_.SetGetAccountInfoResponse(response); account_info_update_scheduler_->InvokeRequestCallback(); @@ -990,7 +1085,7 @@ TEST_F(NearbyShareCertificateManagerImplTest, preference_manager_.SetBoolean(PrefNames::kAdvancedProtectionEnabled, true); GetAccountInfoResponse response; response.mutable_account_info()->mutable_capabilities()->Add( - AccountInfo::CAPABILITY_UNSPECIFIED); + google::nearby::identity::v1::AccountInfo::CAPABILITY_UNSPECIFIED); identity_client_.SetGetAccountInfoResponse(response); account_info_update_scheduler_->InvokeRequestCallback(); @@ -1015,5 +1110,4 @@ TEST_F(NearbyShareCertificateManagerImplTest, PrefNames::kAdvancedProtectionEnabled, /*default_value=*/false)); } -} // namespace sharing -} // namespace nearby +} // namespace nearby::sharing diff --git a/sharing/common/nearby_share_prefs.cc b/sharing/common/nearby_share_prefs.cc index 279b352b..9d988ba5 100644 --- a/sharing/common/nearby_share_prefs.cc +++ b/sharing/common/nearby_share_prefs.cc @@ -67,7 +67,6 @@ void RegisterNearbySharingPrefs(PreferenceManager& preference_manager, preference_manager.Remove(PrefNames::kUsers); preference_manager.SetBoolean(PrefNames::kAdvancedProtectionEnabled, false); - preference_manager.RemoveAllSyncConfigs(); preference_manager.RemoveAllBindingConfigs(); } diff --git a/sharing/contacts/BUILD b/sharing/contacts/BUILD deleted file mode 100644 index 04f9d8a8..00000000 --- a/sharing/contacts/BUILD +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright 2024 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. - -load("@rules_cc//cc:cc_library.bzl", "cc_library") -load("@rules_cc//cc:cc_test.bzl", "cc_test") - -licenses(["notice"]) - -cc_library( - name = "contacts_interface", - hdrs = [ - "nearby_share_contact_manager.h", - ], - visibility = ["//visibility:public"], - deps = [ - "//sharing/proto:share_cc_proto", - "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/status:statusor", - ], -) - -cc_library( - name = "contacts", - srcs = [ - "nearby_share_contact_manager_impl.cc", - ], - hdrs = [ - "nearby_share_contact_manager_impl.h", - ], - visibility = ["//visibility:public"], - deps = [ - ":contacts_interface", - "//internal/platform:types", - "//internal/platform/implementation:account_manager", - "//sharing/internal/public:logging", - "//sharing/internal/public:types", - "//sharing/linux/stubs:rpc", - "//sharing/proto:share_cc_proto", - "@com_google_absl//absl/base:nullability", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/synchronization", - ], -) - -cc_library( - name = "test_support", - testonly = True, - hdrs = [ - "fake_nearby_share_contact_manager.h", - ], - visibility = ["//visibility:public"], - deps = [":contacts_interface"], -) - -cc_test( - name = "contacts_test", - srcs = [ - "nearby_share_contact_manager_impl_test.cc", - ], - deps = [ - ":contacts", - "//internal/platform/implementation:account_manager", - "//internal/platform/implementation:platform_impl", - "//internal/test", - "//sharing/linux/stubs:rpc", - "//sharing/internal/test:nearby_test", - "//sharing/local_device_data:test_support", - "//sharing/proto:share_cc_proto", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/time", - "@com_google_googletest//:gtest_main", - ], -) diff --git a/sharing/contacts/fake_nearby_share_contact_manager.h b/sharing/contacts/fake_nearby_share_contact_manager.h deleted file mode 100644 index d52d0820..00000000 --- a/sharing/contacts/fake_nearby_share_contact_manager.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2021-2023 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 THIRD_PARTY_NEARBY_SHARING_CONTACTS_FAKE_NEARBY_SHARE_CONTACT_MANAGER_H_ -#define THIRD_PARTY_NEARBY_SHARING_CONTACTS_FAKE_NEARBY_SHARE_CONTACT_MANAGER_H_ - -#include "sharing/contacts/nearby_share_contact_manager.h" - -namespace nearby { -namespace sharing { - -// A fake implementation of NearbyShareContactManager. -class FakeNearbyShareContactManager : public NearbyShareContactManager { - public: - FakeNearbyShareContactManager() = default; - ~FakeNearbyShareContactManager() override = default; - - private: - void GetContacts(ContactsCallback callback) override {}; -}; - -} // namespace sharing -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_SHARING_CONTACTS_FAKE_NEARBY_SHARE_CONTACT_MANAGER_H_ diff --git a/sharing/contacts/nearby_share_contact_manager.h b/sharing/contacts/nearby_share_contact_manager.h deleted file mode 100644 index 81bf5f17..00000000 --- a/sharing/contacts/nearby_share_contact_manager.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2021-2023 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 THIRD_PARTY_NEARBY_SHARING_CONTACTS_NEARBY_SHARE_CONTACT_MANAGER_H_ -#define THIRD_PARTY_NEARBY_SHARING_CONTACTS_NEARBY_SHARE_CONTACT_MANAGER_H_ - -#include - -#include - -#include "absl/functional/any_invocable.h" -#include "absl/status/statusor.h" -#include "sharing/proto/rpc_resources.pb.h" - -namespace nearby { -namespace sharing { - -// The Nearby Share contacts manager retrieves the user's contact list from the -// server. -class NearbyShareContactManager { - public: - using ContactsCallback = absl::AnyInvocable< - void(absl::StatusOr>, - uint32_t num_unreachable_contacts_filtered_out) &&>; - - virtual ~NearbyShareContactManager() = default; - - // Retrieves the user's contact list from the server. - virtual void GetContacts(ContactsCallback callback) = 0; -}; - -} // namespace sharing -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_SHARING_CONTACTS_NEARBY_SHARE_CONTACT_MANAGER_H_ diff --git a/sharing/contacts/nearby_share_contact_manager_impl.cc b/sharing/contacts/nearby_share_contact_manager_impl.cc deleted file mode 100644 index d142e277..00000000 --- a/sharing/contacts/nearby_share_contact_manager_impl.cc +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2023 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 "sharing/contacts/nearby_share_contact_manager_impl.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include "absl/base/nullability.h" -#include "absl/status/statusor.h" -#include "absl/synchronization/notification.h" -#include "internal/platform/implementation/account_manager.h" -#include "sharing/contacts/nearby_share_contact_manager.h" -#include "sharing/internal/public/context.h" -#include "sharing/internal/public/logging.h" -#if defined(__linux__) -#include "sharing/linux/stubs/sharing_rpc_client.h" -#else -#include "location/nearby/sharing/lib/rpc/sharing_rpc_client.h" -#endif -#include "sharing/proto/contact_rpc.pb.h" -#include "sharing/proto/rpc_resources.pb.h" - -namespace nearby { -namespace sharing { -namespace { - -using ::nearby::sharing::proto::ContactRecord; -using ::nearby::sharing::proto::ListContactPeopleRequest; -using ::nearby::sharing::proto::ListContactPeopleResponse; - -// Class for maintaining a single instance of contacts download request. It -// is responsible for downloading all available pages and making the results -// or error available. -class ContactDownloadContext { - public: - ContactDownloadContext( - nearby::sharing::api::SharingRpcClient* nearby_share_client, - NearbyShareContactManager::ContactsCallback download_callback) - : nearby_share_client_(nearby_share_client), - download_callback_(std::move(download_callback)) {} - - // Fetches the next page of contacts. - // If |next_page_token_| is empty, it fetches the first page. - // On successful download, if page token in the response is empty, the - // |download_callback_| is invoked with all downloaded contacts. - void FetchNextPage(); - - private: - nearby::sharing::api::SharingRpcClient* const nearby_share_client_; - std::optional next_page_token_; - int page_number_ = 1; - std::vector contacts_; - NearbyShareContactManager::ContactsCallback download_callback_; -}; - -void ContactDownloadContext::FetchNextPage() { - LOG(INFO) << "Downloading contacts page=" << page_number_++; - ListContactPeopleRequest request; - if (next_page_token_.has_value()) { - request.set_page_token(*next_page_token_); - } - nearby_share_client_->ListContactPeople( - std::move(request), - [this]( - const absl::StatusOr& response) mutable { - if (!response.ok()) { - LOG(WARNING) << "Failed to download contacts: " << response.status(); - std::move(download_callback_)( - response.status(), /*num_unreachable_contacts_filtered_out=*/0); - return; - } - - contacts_.insert(contacts_.end(), response->contact_records().begin(), - response->contact_records().end()); - - if (response->next_page_token().empty()) { - // We should filter here because we only care about contacts that we - // can share with. - uint32_t contacts_size = contacts_.size(); - // Filter out unreachable contacts. - contacts_.erase(std::remove_if(contacts_.begin(), contacts_.end(), - [](const ContactRecord& contact) { - return !contact.is_reachable(); - }), - contacts_.end()); - uint32_t num_unreachable_contacts_filtered_out = - contacts_size - contacts_.size(); - std::move(download_callback_)(std::move(contacts_), - num_unreachable_contacts_filtered_out); - return; - } - // Continue with next page. - next_page_token_ = response->next_page_token(); - FetchNextPage(); - }); -} - -} // namespace - -NearbyShareContactManagerImpl::NearbyShareContactManagerImpl( - Context* absl_nonnull context, AccountManager& account_manager, - nearby::sharing::api::SharingRpcClient* absl_nonnull nearby_client) - : account_manager_(account_manager), - nearby_share_client_(*nearby_client), - executor_(context->CreateSequencedTaskRunner()) {} - -void NearbyShareContactManagerImpl::GetContacts(ContactsCallback callback) { - executor_->PostTask([this, callback = std::move(callback)]() mutable { - LOG(INFO) << "Start downloading contacts"; - std::vector contacts; - if (!account_manager_.GetCurrentAccount().has_value()) { - LOG(WARNING) << "Ignore contacts download, no logged in account."; - std::move(callback)(contacts, - /*num_unreachable_contacts_filtered_out=*/0); - return; - } - - absl::Notification notification; - auto context = std::make_unique( - &nearby_share_client_, - [¬ification, callback = std::move(callback)]( - absl::StatusOr> - contacts, - uint32_t num_unreachable_contacts_filtered_out) mutable { - std::move(callback)(std::move(contacts), - num_unreachable_contacts_filtered_out); - notification.Notify(); - }); - context->FetchNextPage(); - // Wait for all pages of contacts to be downloaded. - // MUST not terminate early, otherwise notification will go out of scope, - // and the callback will call Notify on a destroyed object. - notification.WaitForNotification(); - }); -} - -} // namespace sharing -} // namespace nearby diff --git a/sharing/contacts/nearby_share_contact_manager_impl.h b/sharing/contacts/nearby_share_contact_manager_impl.h deleted file mode 100644 index 06e31cbc..00000000 --- a/sharing/contacts/nearby_share_contact_manager_impl.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2023 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 THIRD_PARTY_NEARBY_SHARING_CONTACTS_NEARBY_SHARE_CONTACT_MANAGER_IMPL_H_ -#define THIRD_PARTY_NEARBY_SHARING_CONTACTS_NEARBY_SHARE_CONTACT_MANAGER_IMPL_H_ - -#include - -#include "absl/base/nullability.h" -#include "internal/platform/implementation/account_manager.h" -#include "internal/platform/task_runner.h" -#include "sharing/contacts/nearby_share_contact_manager.h" -#include "sharing/internal/public/context.h" - -namespace nearby { -namespace sharing { -namespace api { -class SharingRpcClient; -} // namespace api - -class NearbyShareContactManagerImpl : public NearbyShareContactManager { - public: - NearbyShareContactManagerImpl( - Context* absl_nonnull context, AccountManager& account_manager, - api::SharingRpcClient* absl_nonnull nearby_client); - - ~NearbyShareContactManagerImpl() override = default; - - private: - // NearbyShareContactsManager: - void GetContacts(ContactsCallback callback) override; - - AccountManager& account_manager_; - api::SharingRpcClient& nearby_share_client_; - - std::unique_ptr executor_ = nullptr; -}; - -} // namespace sharing -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_SHARING_CONTACTS_NEARBY_SHARE_CONTACT_MANAGER_IMPL_H_ diff --git a/sharing/contacts/nearby_share_contact_manager_impl_test.cc b/sharing/contacts/nearby_share_contact_manager_impl_test.cc deleted file mode 100644 index b2c7da8f..00000000 --- a/sharing/contacts/nearby_share_contact_manager_impl_test.cc +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2023 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 "sharing/contacts/nearby_share_contact_manager_impl.h" - -#include -#include - -#include -#include -#include - -#include "location/nearby/sharing/lib/rpc/fake_nearby_share_client.h" -#include "gtest/gtest.h" -#include "absl/time/time.h" -#include "internal/platform/implementation/account_manager.h" -#include "internal/test/fake_account_manager.h" -#include "sharing/internal/test/fake_context.h" -#include "sharing/local_device_data/fake_nearby_share_local_device_data_manager.h" -#include "sharing/proto/contact_rpc.pb.h" -#include "sharing/proto/rpc_resources.pb.h" - -namespace nearby::sharing { -namespace { - -using ::nearby::sharing::proto::ContactRecord; - -constexpr char kTestDefaultDeviceName[] = "Josh's Chromebook"; -constexpr char kTestProfileUserName[] = "test@google.com"; -constexpr char kTestAccountId[] = "test_account_id"; - -class NearbyShareContactManagerImplTest - : public ::testing::Test { - protected: - struct ContactsDownloadedNotification { - std::vector contacts; - uint32_t num_unreachable_contacts_filtered_out; - }; - struct ContactsUploadedNotification { - bool did_contacts_change_since_last_upload; - }; - - NearbyShareContactManagerImplTest() - : local_device_data_manager_(kTestDefaultDeviceName) {} - - ~NearbyShareContactManagerImplTest() override = default; - - void SetUp() override { - AccountManager::Account account; - account.id = kTestAccountId; - account.email = kTestProfileUserName; - fake_account_manager_.SetAccount(account); - - manager_ = std::make_unique( - &fake_context_, fake_account_manager_, &nearby_client_); - } - - void TearDown() override { - manager_.reset(); - } - - void Sync() { - EXPECT_TRUE(fake_context_.last_sequenced_task_runner()->SyncWithTimeout( - absl::Milliseconds(1000))); - } - - std::vector& - contacts_downloaded_notifications() { - return contacts_downloaded_notifications_; - } - - FakeContext& fake_context() { return fake_context_; } - - private: - FakeAccountManager fake_account_manager_; - FakeContext fake_context_; - std::vector - contacts_downloaded_notifications_; - std::vector contacts_uploaded_notifications_; - FakeNearbyShareClient nearby_client_; - FakeNearbyShareLocalDeviceDataManager local_device_data_manager_; - std::unique_ptr account_manager_; - std::unique_ptr manager_; -}; - -} // namespace -} // namespace nearby::sharing diff --git a/sharing/fake_nearby_connections_manager.cc b/sharing/fake_nearby_connections_manager.cc index e69fbb7d..4fff1842 100644 --- a/sharing/fake_nearby_connections_manager.cc +++ b/sharing/fake_nearby_connections_manager.cc @@ -32,6 +32,7 @@ #include "internal/base/file_path.h" #include "sharing/common/nearby_share_enums.h" #include "sharing/internal/public/logging.h" +#include "sharing/nearby_connection.h" #include "sharing/nearby_connections_manager.h" #include "sharing/nearby_connections_types.h" #include "sharing/proto/enums.pb.h" @@ -204,6 +205,12 @@ void FakeNearbyConnectionsManager::UpgradeBandwidth( upgrade_bandwidth_endpoint_ids_.insert(std::string(endpoint_id)); } +void FakeNearbyConnectionsManager::OverrideSavePath( + absl::string_view endpoint_id, const FilePath& custom_save_path) { + absl::MutexLock lock(endpoints_mutex_); + custom_save_paths_[endpoint_id] = custom_save_path; +} + void FakeNearbyConnectionsManager::OnEndpointFound( absl::string_view endpoint_id, std::unique_ptr info) { diff --git a/sharing/fake_nearby_connections_manager.h b/sharing/fake_nearby_connections_manager.h index 94b3dddb..eabde28b 100644 --- a/sharing/fake_nearby_connections_manager.h +++ b/sharing/fake_nearby_connections_manager.h @@ -27,6 +27,7 @@ #include #include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" @@ -76,7 +77,7 @@ class FakeNearbyConnectionsManager : public NearbyConnectionsManager { void UpgradeBandwidth(absl::string_view endpoint_id) override; void SetCustomSavePath(absl::string_view custom_save_path) override {} void OverrideSavePath(absl::string_view endpoint_id, - const FilePath& custom_save_path) override {} + const FilePath& custom_save_path) override; absl::flat_hash_set GetAndClearUnknownFilePathsToDelete() override; // Testing methods @@ -131,6 +132,14 @@ class FakeNearbyConnectionsManager : public NearbyConnectionsManager { return it->second; } + std::optional custom_save_path(absl::string_view endpoint_id) { + absl::MutexLock lock(endpoints_mutex_); + auto it = custom_save_paths_.find(endpoint_id); + if (it == custom_save_paths_.end()) return std::nullopt; + + return it->second; + } + bool has_incoming_payloads() { absl::MutexLock lock(incoming_payloads_mutex_); return !incoming_payloads_.empty(); @@ -177,6 +186,9 @@ class FakeNearbyConnectionsManager : public NearbyConnectionsManager { // Maps endpoint_id to endpoint_info. std::map> connection_endpoint_infos_ ABSL_GUARDED_BY(endpoints_mutex_); + // Maps endpoint_id to custom_save_path. + absl::flat_hash_map custom_save_paths_ + ABSL_GUARDED_BY(endpoints_mutex_); std::map> payload_status_listeners_; diff --git a/sharing/fake_nearby_sharing_service.cc b/sharing/fake_nearby_sharing_service.cc index 2fcb7932..e3c90021 100644 --- a/sharing/fake_nearby_sharing_service.cc +++ b/sharing/fake_nearby_sharing_service.cc @@ -18,13 +18,22 @@ #include #include #include +#include +#include "location/nearby/sharing/lib/sync/sync_manager.h" +#include "absl/functional/any_invocable.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "internal/base/file_path.h" #include "internal/base/observer_list.h" +#include "internal/platform/clock.h" #include "sharing/advertisement.h" #include "sharing/attachment_container.h" -#include "sharing/local_device_data/nearby_share_local_device_data_manager.h" +#include "sharing/certificates/nearby_share_certificate_manager.h" #include "sharing/nearby_sharing_service.h" #include "sharing/nearby_sharing_settings.h" +#include "sharing/outgoing_share_session.h" +#include "sharing/outgoing_targets_manager.h" #include "sharing/share_target.h" #include "sharing/share_target_discovered_callback.h" #include "sharing/transfer_metadata.h" @@ -34,6 +43,22 @@ namespace nearby { namespace sharing { +FakeNearbySharingService::FakeNearbySharingService() + : service_thread_(&clock_, /*count=*/1), + analytics_recorder_(/*vendor_id=*/0, /*event_logger=*/nullptr), + sync_manager_(std::make_unique(&identity_rpc_client_, + &preference_manager_)), + outgoing_targets_manager_(std::make_unique( + &clock_, &service_thread_, &connections_manager_, + &analytics_recorder_, + [this](const ShareTarget& target) { + FireShareTargetDiscovered(target); + }, + [this](const ShareTarget& target) { FireShareTargetUpdated(target); }, + [this](const ShareTarget& target) { FireShareTargetLost(target); }, + /*transfer_update_callback=*/ + [](OutgoingShareSession&, const TransferMetadata&) {})) {} + void FakeNearbySharingService::AddObserver(Observer* observer) { observers_.AddObserver(observer); } @@ -54,7 +79,7 @@ void FakeNearbySharingService::RegisterSendSurface( TransferUpdateCallback* transfer_callback, ShareTargetDiscoveredCallback* discovery_callback, SendSurfaceState state, Advertisement::BlockedVendorId blocked_vendor_id, bool disable_wifi_hotspot, - std::function status_codes_callback) { + absl::AnyInvocable status_codes_callback) { if (state == SendSurfaceState::kForeground) { foreground_send_surface_map_.insert( {transfer_callback, @@ -73,7 +98,7 @@ void FakeNearbySharingService::RegisterSendSurface( // Unregisters the current send surface. void FakeNearbySharingService::UnregisterSendSurface( TransferUpdateCallback* transfer_callback, - std::function status_codes_callback) { + absl::AnyInvocable status_codes_callback) { foreground_send_surface_map_.erase(transfer_callback); background_send_surface_map_.erase(transfer_callback); @@ -84,7 +109,7 @@ void FakeNearbySharingService::UnregisterSendSurface( void FakeNearbySharingService::RegisterReceiveSurface( TransferUpdateCallback* transfer_callback, ReceiveSurfaceState state, Advertisement::BlockedVendorId vendor_id, - std::function status_codes_callback) { + absl::AnyInvocable status_codes_callback) { if (state == ReceiveSurfaceState::kForeground) { foreground_receive_transfer_callbacks_.AddObserver(transfer_callback); } else { @@ -97,7 +122,7 @@ void FakeNearbySharingService::RegisterReceiveSurface( // Unregisters the current receive surface. void FakeNearbySharingService::UnregisterReceiveSurface( TransferUpdateCallback* transfer_callback, - std::function status_codes_callback) { + absl::AnyInvocable status_codes_callback) { foreground_receive_transfer_callbacks_.RemoveObserver(transfer_callback); background_receive_transfer_callbacks_.RemoveObserver(transfer_callback); status_codes_callback(StatusCodes::kOk); @@ -105,7 +130,7 @@ void FakeNearbySharingService::UnregisterReceiveSurface( // Unregisters all foreground receive surfaces. void FakeNearbySharingService::ClearForegroundReceiveSurfaces( - std::function status_codes_callback) { + absl::AnyInvocable status_codes_callback) { status_codes_callback(StatusCodes::kOk); } @@ -144,19 +169,18 @@ void FakeNearbySharingService::Cancel( status_codes_callback(StatusCodes::kOk); } +void FakeNearbySharingService::InitiatePairing( + int64_t share_target_id, service::proto::BindingRequest::Type binding_type, + absl::AnyInvocable + status_codes_callback) { + initiate_pairing_callbacks_[share_target_id] = + std::move(status_codes_callback); +} + std::string FakeNearbySharingService::Dump() const { return ""; } NearbyShareSettings* FakeNearbySharingService::GetSettings() { return nullptr; } -NearbyShareLocalDeviceDataManager* -FakeNearbySharingService::GetLocalDeviceDataManager() { - return nullptr; -} - -NearbyShareContactManager* FakeNearbySharingService::GetContactManager() { - return nullptr; -} - NearbyShareCertificateManager* FakeNearbySharingService::GetCertificateManager() { return nullptr; @@ -166,6 +190,14 @@ AccountManager* FakeNearbySharingService::GetAccountManager() { return nullptr; } +Clock& FakeNearbySharingService::GetClock() { return clock_; } + +SyncManager& FakeNearbySharingService::sync_manager() { return *sync_manager_; } + +OutgoingTargetsManager& FakeNearbySharingService::outgoing_targets_manager() { + return *outgoing_targets_manager_; +} + void FakeNearbySharingService::FireHighVisibilityChangeRequested() { for (auto& observer : observers_.GetObservers()) { observer->OnHighVisibilityChangeRequested(); @@ -229,29 +261,53 @@ void FakeNearbySharingService::FireReceiveTransferUpdate( // Fire discovery events. void FakeNearbySharingService::FireShareTargetDiscovered( - SendSurfaceState state, ShareTarget share_target) { - if (state == SendSurfaceState::kForeground) { - for (auto& entry : foreground_send_surface_map_) { - entry.second.OnShareTargetDiscovered(share_target); - } - } else { - for (auto& entry : background_send_surface_map_) { - entry.second.OnShareTargetDiscovered(share_target); - } + ShareTarget share_target) { + for (auto& entry : foreground_send_surface_map_) { + entry.second.OnShareTargetDiscovered(share_target); + } + for (auto& entry : background_send_surface_map_) { + entry.second.OnShareTargetDiscovered(share_target); } } -void FakeNearbySharingService::FireShareTargetLost(SendSurfaceState state, - ShareTarget share_target) { - if (state == SendSurfaceState::kForeground) { - for (auto& entry : foreground_send_surface_map_) { - entry.second.OnShareTargetLost(share_target); - } - } else { - for (auto& entry : background_send_surface_map_) { - entry.second.OnShareTargetLost(share_target); - } +void FakeNearbySharingService::FireShareTargetUpdated( + ShareTarget share_target) { + for (auto& entry : foreground_send_surface_map_) { + entry.second.OnShareTargetUpdated(share_target); } + for (auto& entry : background_send_surface_map_) { + entry.second.OnShareTargetUpdated(share_target); + } +} + +void FakeNearbySharingService::FireShareTargetLost(ShareTarget share_target) { + for (auto& entry : foreground_send_surface_map_) { + entry.second.OnShareTargetLost(share_target); + } + for (auto& entry : background_send_surface_map_) { + entry.second.OnShareTargetLost(share_target); + } +} + +void FakeNearbySharingService::FireInitiatePairingResult( + int64_t share_target_id, StatusCodes status) { + auto it = initiate_pairing_callbacks_.find(share_target_id); + if (it == initiate_pairing_callbacks_.end()) { + return; + } + auto callback = std::move(it->second); + initiate_pairing_callbacks_.erase(it); + std::move(callback)(status); +} + +void FakeNearbySharingService::UpdateBackupSavePath( + absl::string_view binding_id, absl::string_view save_path, + absl::AnyInvocable + status_codes_callback) { + absl::StatusOr status = + sync_manager_->UpdateSyncBindingDestinationDirectory(binding_id, + FilePath(save_path)); + status_codes_callback(status.ok() ? StatusCodes::kOk : StatusCodes::kError); } } // namespace sharing diff --git a/sharing/fake_nearby_sharing_service.h b/sharing/fake_nearby_sharing_service.h index 9aaf0376..e68644af 100644 --- a/sharing/fake_nearby_sharing_service.h +++ b/sharing/fake_nearby_sharing_service.h @@ -20,13 +20,27 @@ #include #include +#include "location/nearby/sharing/lib/analytics/analytics_recorder_impl.h" +#include "location/nearby/sharing/lib/rpc/fake_nearby_share_client.h" +#include "location/nearby/sharing/lib/rpc/sharing_rpc_client.h" +#include "location/nearby/sharing/lib/sync/sync_manager.h" #include "absl/container/flat_hash_map.h" +#include "absl/functional/any_invocable.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" #include "internal/base/observer_list.h" +#include "internal/platform/clock.h" +#include "internal/test/fake_clock.h" +#include "internal/test/fake_task_runner.h" #include "sharing/advertisement.h" #include "sharing/attachment_container.h" -#include "sharing/local_device_data/nearby_share_local_device_data_manager.h" +#include "sharing/certificates/nearby_share_certificate_manager.h" +#include "sharing/fake_nearby_connections_manager.h" +#include "sharing/internal/api/preference_manager.h" +#include "sharing/internal/test/fake_preference_manager.h" #include "sharing/nearby_sharing_service.h" #include "sharing/nearby_sharing_settings.h" +#include "sharing/outgoing_targets_manager.h" #include "sharing/share_target.h" #include "sharing/share_target_discovered_callback.h" #include "sharing/transfer_metadata.h" @@ -38,6 +52,7 @@ namespace sharing { class FakeNearbySharingService : public NearbySharingService { public: + FakeNearbySharingService(); ~FakeNearbySharingService() override = default; void AddObserver(Observer* observer) override; @@ -54,27 +69,27 @@ class FakeNearbySharingService : public NearbySharingService { ShareTargetDiscoveredCallback* discovery_callback, SendSurfaceState state, Advertisement::BlockedVendorId blocked_vendor_id, bool disable_wifi_hotspot, - std::function status_codes_callback) override; + absl::AnyInvocable status_codes_callback) override; // Unregisters the current send surface. void UnregisterSendSurface( TransferUpdateCallback* transfer_callback, - std::function status_codes_callback) override; + absl::AnyInvocable status_codes_callback) override; // Registers a receiver surface for handling payload transfer status. void RegisterReceiveSurface( TransferUpdateCallback* transfer_callback, ReceiveSurfaceState state, Advertisement::BlockedVendorId vendor_id, - std::function status_codes_callback) override; + absl::AnyInvocable status_codes_callback) override; // Unregisters the current receive surface. void UnregisterReceiveSurface( TransferUpdateCallback* transfer_callback, - std::function status_codes_callback) override; + absl::AnyInvocable status_codes_callback) override; // Unregisters all foreground receive surfaces. void ClearForegroundReceiveSurfaces( - std::function status_codes_callback) override; + absl::AnyInvocable status_codes_callback) override; // Returns true if there is an ongoing file transfer. bool IsTransferring() const override; @@ -103,13 +118,49 @@ class FakeNearbySharingService : public NearbySharingService { std::function status_codes_callback) override; + void InitiatePairing( + int64_t share_target_id, + service::proto::BindingRequest::Type binding_type, + absl::AnyInvocable + status_codes_callback) override; + std::string Dump() const override; + bool IsBluetoothPresent() const override { return true; } + bool IsBluetoothPowered() const override { return true; } + bool IsExtendedAdvertisingSupported() const override { return true; } + bool IsLanConnected() const override { return true; } + std::string GetQrCodeUrl() const override { return ""; } + void SetVisibility( + proto::DeviceVisibility visibility, absl::Duration expiration, + absl::AnyInvocable callback) override {} + void UpdateFilePathsInProgress(bool update_file_paths) override {} NearbyShareSettings* GetSettings() override; - NearbyShareLocalDeviceDataManager* GetLocalDeviceDataManager() override; - NearbyShareContactManager* GetContactManager() override; NearbyShareCertificateManager* GetCertificateManager() override; AccountManager* GetAccountManager() override; + Clock& GetClock() override; + void SetAlternateServiceUuidForDiscovery( + uint16_t alternate_service_uuid) override {} + SyncManager& sync_manager() override; + OutgoingTargetsManager& outgoing_targets_manager() override; + void UpdateBackupSavePath( + absl::string_view binding_id, absl::string_view save_path, + absl::AnyInvocable + status_codes_callback) override; + + nearby::sharing::api::IdentityRpcClient& fake_identity_rpc_client() { + return identity_rpc_client_; + } + nearby::sharing::api::PreferenceManager& fake_preference_manager() { + return preference_manager_; + } + FakeNearbyConnectionsManager& fake_nearby_connections_manager() { + return connections_manager_; + } + FakeTaskRunner& fake_task_runner() { return service_thread_; } + analytics::AnalyticsRecorderImpl& analytics_recorder() { + return analytics_recorder_; + } // Fake methods to support test scenarios. @@ -130,9 +181,10 @@ class FakeNearbySharingService : public NearbySharingService { TransferMetadata transfer_metadata); // Fire discovery events. - void FireShareTargetDiscovered(SendSurfaceState state, - ShareTarget share_target); - void FireShareTargetLost(SendSurfaceState state, ShareTarget share_target); + void FireShareTargetDiscovered(ShareTarget share_target); + void FireShareTargetUpdated(ShareTarget share_target); + void FireShareTargetLost(ShareTarget share_target); + void FireInitiatePairingResult(int64_t share_target_id, StatusCodes status); private: ObserverList observers_; @@ -147,6 +199,17 @@ class FakeNearbySharingService : public NearbySharingService { background_send_surface_map_; ObserverList foreground_receive_transfer_callbacks_; ObserverList background_receive_transfer_callbacks_; + FakeClock clock_; + FakeTaskRunner service_thread_; + FakeNearbyConnectionsManager connections_manager_; + analytics::AnalyticsRecorderImpl analytics_recorder_; + FakePreferenceManager preference_manager_; + FakeNearbyIdentityClient identity_rpc_client_; + std::unique_ptr sync_manager_; + std::unique_ptr outgoing_targets_manager_; + absl::flat_hash_map> + initiate_pairing_callbacks_; }; } // namespace sharing diff --git a/sharing/fast_initiation/BUILD b/sharing/fast_initiation/BUILD index 8a38dcd7..d45d85b4 100644 --- a/sharing/fast_initiation/BUILD +++ b/sharing/fast_initiation/BUILD @@ -14,9 +14,24 @@ load("@rules_cc//cc:cc_library.bzl", "cc_library") load("@rules_cc//cc:cc_test.bzl", "cc_test") +load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") licenses(["notice"]) +refresh_compile_commands( + name = "refresh_compile_commands_fast_initiation", + + # Specify the targets of interest. + # For example, specify a dict of targets and any flags required to build. + targets = { + ":nearby_fast_initiation": "", + }, + # No need to add flags already in .bazelrc. They're automatically picked up. + # If you don't need flags, a list of targets is also okay, as is a single target string. + # Wildcard patterns, like //... for everything, *are* allowed here, just like a build. + # As are additional targets (+) and subtractions (-), like in bazel query https://docs.bazel.build/versions/main/query.html#expressions + # And if you're working on a header-only library, specify a test or binary target that compiles it. +) cc_library( name = "nearby_fast_initiation", srcs = [ diff --git a/sharing/flags/generated/README.md b/sharing/flags/generated/README.md deleted file mode 100644 index a7682807..00000000 --- a/sharing/flags/generated/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Feature flags generation - -This directory contains the generated Nearby Share feature flags definition -file. - -## Adding flags - -New flags need to be added to google3/googledata/experiments/mobile/nearby/features/nearby_sharing_feature.gcl. - -To generate code for the new flags, run: - -``` -blaze build //third_party/nearby/sharing/flags:nearby_sharing_feature_flags_cpp_consts -``` - -The creates the generated file in *blaze-genfiles/third_party/nearby/sharing/flags/nearby_sharing_feature_flags.h*. Copy this file to google3/third_party/nearby/sharing/flags/generated/nearby_sharing_feature_flags.h -and include in your CL for submission. diff --git a/sharing/flags/generated/nearby_sharing_feature_flags.h b/sharing/flags/generated/nearby_sharing_feature_flags.h index 71b1295a..6e61e601 100755 --- a/sharing/flags/generated/nearby_sharing_feature_flags.h +++ b/sharing/flags/generated/nearby_sharing_feature_flags.h @@ -47,9 +47,6 @@ constexpr auto kEnableMediumWifiLan = // Enable/disable retry/resume transfer for partial files. constexpr auto kEnableRetryResumeTransfer = flags::Flag(kConfigPackage, "45411589", false); -// Enable/disable self share UI in Nearby Share -constexpr auto kEnableSelfShareUi = - flags::Flag(kConfigPackage, "45418908", false); // Enable/disable sending desktop events constexpr auto kEnableSendingDesktopEvents = flags::Flag(kConfigPackage, "45459748", false); @@ -83,6 +80,9 @@ constexpr auto kUpdateTrack = // Timeout between displays of the conflict banner. constexpr auto kConflictBannerTimeout = flags::Flag(kConfigPackage, "45668886", 604800); +// When true, enables the backup feature. +constexpr auto kEnableBackup = + flags::Flag(kConfigPackage, "45776229", false); // Enable a persistent BETA label. constexpr auto kEnableBetaLabel = flags::Flag(kConfigPackage, "45662570", true); @@ -98,9 +98,6 @@ constexpr auto kEnableMiniPulse = // When true, enables notifications implemented in native code. constexpr auto kEnableNativeNotifications = flags::Flag(kConfigPackage, "45743135", false); -// When true, enables responsive UI. -constexpr auto kEnableResponsiveUi = - flags::Flag(kConfigPackage, "45727212", false); inline absl::btree_map&> GetBoolFlags() { return { @@ -109,18 +106,17 @@ inline absl::btree_map&> GetBoolFlags() { {45418905, kEnableMediumWebRtc}, {45418906, kEnableMediumWifiLan}, {45411589, kEnableRetryResumeTransfer}, - {45418908, kEnableSelfShareUi}, {45459748, kEnableSendingDesktopEvents}, {45409033, kShowAutoUpdateSetting}, {45762616, kEnableFileSync}, {45673628, kEnableWifiHotspotForHpRealtekDevices}, {45683539, kUseAlternateServiceUuidForDiscovery}, + {45776229, kEnableBackup}, {45662570, kEnableBetaLabel}, {45661130, kEnableConflictBanner}, {45720206, kEnableFlutterHooks}, {45724244, kEnableMiniPulse}, {45743135, kEnableNativeNotifications}, - {45727212, kEnableResponsiveUi}, }; } diff --git a/sharing/incoming_frames_reader.cc b/sharing/incoming_frames_reader.cc index 72be3530..73c3cb43 100644 --- a/sharing/incoming_frames_reader.cc +++ b/sharing/incoming_frames_reader.cc @@ -24,6 +24,7 @@ #include #include +#include "absl/functional/any_invocable.h" #include "absl/memory/memory.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" @@ -66,21 +67,21 @@ IncomingFramesReader::~IncomingFramesReader() { } void IncomingFramesReader::ReadFrame( - std::function)> callback, + absl::AnyInvocable)> callback, absl::Duration timeout) { ProcessReadRequest(std::nullopt, std::move(callback), timeout); } void IncomingFramesReader::ReadFrame( FrameType frame_type, - std::function)> callback, + absl::AnyInvocable)> callback, absl::Duration timeout) { ProcessReadRequest(frame_type, std::move(callback), timeout); } void IncomingFramesReader::ProcessReadRequest( std::optional frame_type, - std::function)> callback, + absl::AnyInvocable)> callback, absl::Duration timeout) { std::unique_ptr cached_frame; { diff --git a/sharing/incoming_frames_reader.h b/sharing/incoming_frames_reader.h index cd3ba339..b03845d4 100644 --- a/sharing/incoming_frames_reader.h +++ b/sharing/incoming_frames_reader.h @@ -25,6 +25,7 @@ #include #include "absl/base/thread_annotations.h" +#include "absl/functional/any_invocable.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" #include "internal/platform/task_runner.h" @@ -54,7 +55,7 @@ class IncomingFramesReader // Note: Callers are expected wait for `callback` to be run before scheduling // subsequent calls to ReadFrame(..). virtual void ReadFrame( - std::function< + absl::AnyInvocable< void(bool is_timeout, std::optional)> callback, @@ -70,7 +71,7 @@ class IncomingFramesReader // subsequent calls to ReadFrame(..). virtual void ReadFrame( nearby::sharing::service::proto::V1Frame::FrameType frame_type, - std::function< + absl::AnyInvocable< void(bool is_timeout, std::optional)> callback, @@ -84,8 +85,9 @@ class IncomingFramesReader struct ReadFrameInfo { std::optional frame_type = std::nullopt; - std::function)> + absl::AnyInvocable)> callback = nullptr; absl::Duration timeout = absl::ZeroDuration(); }; @@ -93,7 +95,7 @@ class IncomingFramesReader void ProcessReadRequest( std::optional frame_type, - std::function< + absl::AnyInvocable< void(bool is_timeout, std::optional)> callback, diff --git a/sharing/incoming_share_session.cc b/sharing/incoming_share_session.cc index 669cff8f..a029c4d3 100644 --- a/sharing/incoming_share_session.cc +++ b/sharing/incoming_share_session.cc @@ -34,11 +34,7 @@ #include "sharing/attachment_container.h" #include "sharing/constants.h" #include "sharing/file_attachment.h" -#if defined(__linux__) -#include "sharing/linux/stubs/sync_manager.h" -#else #include "location/nearby/sharing/lib/sync/sync_manager.h" -#endif #include "sharing/internal/public/logging.h" #include "sharing/nearby_connection.h" #include "sharing/nearby_connections_manager.h" @@ -46,6 +42,7 @@ #include "sharing/payload_tracker.h" #include "sharing/proto/wire_format.pb.h" #include "sharing/share_session.h" +#include "sharing/share_session_usage.h" #include "sharing/share_target.h" #include "sharing/text_attachment.h" #include "sharing/thread_timer.h" @@ -56,16 +53,12 @@ namespace nearby::sharing { namespace { -using ::location::nearby::proto::sharing::OSType; using ::location::nearby::proto::sharing::ResponseToIntroduction; using ::nearby::sharing::service::proto::AppMetadata; using ::nearby::sharing::service::proto::ConnectionResponseFrame; -using ::nearby::sharing::service::proto::Frame; using ::nearby::sharing::service::proto::IntroductionFrame; -using ::nearby::sharing::service::proto::SyncConfig; using ::nearby::sharing::service::proto::V1Frame; using ::nearby::sharing::service::proto::WifiCredentials; -using ::nearby::sharing::sync::SyncConfigPrefs; } // namespace @@ -78,7 +71,9 @@ IncomingShareSession::IncomingShareSession( transfer_update_callback) : ShareSession(clock, service_thread, connections_manager, analytics_recorder, std::move(endpoint_id), share_target), - transfer_update_callback_(std::move(transfer_update_callback)) {} + transfer_update_callback_(std::move(transfer_update_callback)) { + set_session_usage(ShareSessionUsage::kSharing); +} IncomingShareSession::IncomingShareSession(IncomingShareSession&&) = default; @@ -144,8 +139,20 @@ IncomingShareSession::ProcessIntroduction( "64 bit integer."; return TransferMetadata::Status::kNotEnoughSpace; } + if (apk.file_name_size() != apk.file_size_size() || + apk.file_name_size() != apk.payload_id_size()) { + LOG(WARNING) + << __func__ + << ": Ignore introduction, AppMetadata array length mismatch"; + return TransferMetadata::Status::kUnsupportedAttachmentType; + } // Map each apk file to a file attachment. for (int index = 0; index < apk.file_name_size(); ++index) { + if (apk.file_size(index) <= 0) { + LOG(WARNING) << __func__ + << ": Ignore introduction, due to invalid apk file size"; + return TransferMetadata::Status::kUnsupportedAttachmentType; + } // Locally generate an attachment id for each apk file, and map it to the // payload id. FileAttachment apk_file( @@ -219,6 +226,7 @@ bool IncomingShareSession::ReadyForTransfer( if (!self_share()) { TransferMetadataBuilder transfer_metadata_builder; + transfer_metadata_builder.set_usage(session_usage()); transfer_metadata_builder.set_status( TransferMetadata::Status::kAwaitingLocalConfirmation); transfer_metadata_builder.set_token(token()); @@ -258,6 +266,7 @@ bool IncomingShareSession::AcceptTransfer( UpdateTransferMetadata( TransferMetadataBuilder() + .set_usage(session_usage()) .set_status(TransferMetadata::Status::kAwaitingRemoteAcceptance) .set_token(token()) .build()); @@ -443,7 +452,10 @@ void IncomingShareSession::SendFailureResponse( WriteResponseFrame(response_status); DCHECK(TransferMetadata::IsFinalStatus(status)) << "SendFailureResponse should only be called with a final status"; - UpdateTransferMetadata(TransferMetadataBuilder().set_status(status).build()); + UpdateTransferMetadata(TransferMetadataBuilder() + .set_usage(session_usage()) + .set_status(status) + .build()); } std::optional @@ -458,19 +470,21 @@ IncomingShareSession::ProcessPayloadTransferUpdates( // Cancel acceptance timer when payload transfer update is received. // This mean sender has begun sending payload. mutual_acceptance_timeout_ = nullptr; - std::optional metadata; + std::optional metadata_builder; // If there is a batch of updates in the queue, only return the latest // TransferMetadata. for (; !updates.empty(); updates.pop()) { - metadata = + metadata_builder = get_payload_tracker()->ProcessPayloadUpdate(std::move(updates.front())); - if (!metadata.has_value()) { + if (!metadata_builder.has_value()) { continue; } - - if (metadata->status() == TransferMetadata::Status::kComplete) { + TransferMetadata metadata = + metadata_builder->set_usage(session_usage()).build(); + if (metadata.status() == TransferMetadata::Status::kComplete) { if (!FinalizePayloads()) { return TransferMetadataBuilder() + .set_usage(session_usage()) .set_status(TransferMetadata::Status::kIncompletePayloads) .build(); } @@ -483,13 +497,15 @@ IncomingShareSession::ProcessPayloadTransferUpdates( if (update_file_paths_in_progress) { UpdateFilePayloadPaths(); } else { - if (metadata->status() == TransferMetadata::Status::kCancelled) { + if (metadata.status() == TransferMetadata::Status::kCancelled) { VLOG(1) << __func__ << ": Update file paths for cancelled transfer"; UpdateFilePayloadPaths(); } } } - return metadata; + return metadata_builder.has_value() + ? std::make_optional(metadata_builder->build()) + : std::nullopt; } void IncomingShareSession::OnConnected(NearbyConnection* connection) { @@ -502,43 +518,4 @@ void IncomingShareSession::PushPayloadTransferUpdateForTest( payload_updates_queue()->Queue(std::move(update)); } -void IncomingShareSession::ProcessSyncFrame( - SyncManager& sync_manager, - const nearby::sharing::service::proto::SyncFrame& sync_frame) { - if (session_phase_ != SessionPhase::kUninitialized) { - LOG(WARNING) << "Ignore SyncFrame received in unexpected session phase: " - << static_cast(session_phase_); - return; - } - // TODO: b/485304482 - Check that the connected device is authenticated and is - // part of a sync pairing. - if (!certificate().has_value()) { - LOG(WARNING) << "Ignore SyncFrame received from unauthenticated device."; - return; - } - if (false && - !sync_manager.IsFileSyncBinding(certificate()->binding_id())) { - LOG(WARNING) << "Ignore SyncFrame received in unexpected binding id: " - << certificate()->binding_id(); - return; - } - session_phase_ = SessionPhase::kSync; - if (sync_frame.has_handshake()) { - VLOG(1) << __func__ << ": Received FileSync Handshake"; - WriteSyncConfigFrame( - sync_manager.GetSyncConfig(certificate()->binding_id()) - .value_or(SyncConfigPrefs()) - .sync_config()); - } -} - -void IncomingShareSession::WriteSyncConfigFrame(const SyncConfig& config) { - Frame frame; - frame.set_version(Frame::V1); - V1Frame* v1_frame = frame.mutable_v1(); - v1_frame->set_type(V1Frame::FILE_SYNC); - *v1_frame->mutable_file_sync()->mutable_config() = config; - WriteFrame(frame); -} - } // namespace nearby::sharing diff --git a/sharing/incoming_share_session.h b/sharing/incoming_share_session.h index 73a7ce14..ec4a55f4 100644 --- a/sharing/incoming_share_session.h +++ b/sharing/incoming_share_session.h @@ -105,9 +105,6 @@ class IncomingShareSession : public ShareSession { // Called when an incoming connection is established. void OnConnected(NearbyConnection* connection); - void ProcessSyncFrame(nearby::sharing::SyncManager& sync_manager, - const nearby::sharing::service::proto::SyncFrame& sync_frame); - protected: void InvokeTransferUpdateCallback(const TransferMetadata& metadata) override; @@ -129,9 +126,6 @@ class IncomingShareSession : public ShareSession { // Returns true if all payloads were successfully finalized. bool FinalizePayloads(); - void WriteSyncConfigFrame( - const nearby::sharing::service::proto::SyncConfig& config); - std::function transfer_update_callback_; diff --git a/sharing/incoming_share_session_test.cc b/sharing/incoming_share_session_test.cc index b926f65b..1c77ae79 100644 --- a/sharing/incoming_share_session_test.cc +++ b/sharing/incoming_share_session_test.cc @@ -24,28 +24,28 @@ #include #include +#include "location/nearby/analytics/cpp/logging/mock_event_logger.h" +#include "location/nearby/analytics/cpp/logging/sharing_log_matchers.h" +#include "location/nearby/analytics/cpp/proto/nearby_sharing_log.pb.h" +#include "location/nearby/sharing/lib/analytics/analytics_recorder_impl.h" #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" -#include "internal/analytics/mock_event_logger.h" -#include "internal/analytics/sharing_log_matchers.h" #include "internal/base/file_path.h" #include "internal/test/fake_clock.h" #include "internal/test/fake_device_info.h" #include "internal/test/fake_task_runner.h" #include "proto/sharing_enums.pb.h" -#include "sharing/analytics/analytics_recorder.h" #include "sharing/attachment_compare.h" // IWYU pragma: keep #include "sharing/fake_nearby_connections_manager.h" #include "sharing/file_attachment.h" #include "sharing/internal/public/logging.h" #include "sharing/nearby_connection_impl.h" #include "sharing/nearby_connections_types.h" -#include "sharing/paired_key_verification_runner.h" -#include "sharing/proto/analytics/nearby_sharing_log.pb.h" #include "sharing/proto/wire_format.pb.h" +#include "sharing/share_session_usage.h" #include "sharing/share_target.h" #include "sharing/text_attachment.h" #include "sharing/transfer_metadata.h" @@ -59,7 +59,6 @@ namespace { using ::absl::Seconds; using ::location::nearby::proto::sharing::EventCategory; using ::location::nearby::proto::sharing::EventType; -using ::location::nearby::proto::sharing::OSType; using ::location::nearby::proto::sharing::ResponseToIntroduction; using ::nearby::analytics::HasAction; using ::nearby::analytics::HasCategory; @@ -187,8 +186,8 @@ class IncomingShareSessionTest : public ::testing::Test { FakeClock clock_; FakeTaskRunner task_runner_{&clock_, 1}; nearby::analytics::MockEventLogger mock_event_logger_; - analytics::AnalyticsRecorder analytics_recorder_{/*vendor_id=*/0, - &mock_event_logger_}; + analytics::AnalyticsRecorderImpl analytics_recorder_{/*vendor_id=*/0, + &mock_event_logger_}; ShareTarget share_target_; MockFunction transfer_metadata_callback_; @@ -355,6 +354,51 @@ TEST_F(IncomingShareSessionTest, ProcessIntroductionWithApkSuccess) { UnorderedElementsAre(file1, file2, file3)); } +TEST_F(IncomingShareSessionTest, ProcessIntroductionWithApkLengthMismatch) { + IntroductionFrame introduction_frame; + CHECK( + proto2::TextFormat::ParseFromString(R"pb( + app_metadata { + app_name: "MyApp" + size: 300 + payload_id: 9876 + id: 1234 + file_name: "MyApp.apk" + file_name: "MyApp2.apk" + file_size: 100 + file_size: 100 + file_size: 100 + package_name: "com.example.myapp" + } + )pb", + &introduction_frame)); + session_.OnConnected(&connection_); + + EXPECT_THAT(session_.ProcessIntroduction(introduction_frame), + Eq(TransferMetadata::Status::kUnsupportedAttachmentType)); +} + +TEST_F(IncomingShareSessionTest, ProcessIntroductionWithApkInvalidSize) { + IntroductionFrame introduction_frame; + CHECK( + proto2::TextFormat::ParseFromString(R"pb( + app_metadata { + app_name: "MyApp" + size: 300 + payload_id: 9876 + id: 1234 + file_name: "MyApp.apk" + file_size: 0 + package_name: "com.example.myapp" + } + )pb", + &introduction_frame)); + session_.OnConnected(&connection_); + + EXPECT_THAT(session_.ProcessIntroduction(introduction_frame), + Eq(TransferMetadata::Status::kUnsupportedAttachmentType)); +} + TEST_F(IncomingShareSessionTest, PayloadTransferUpdateCompleteWithWrongPayloadType) { connections_manager_.AcceptConnection( @@ -1085,7 +1129,9 @@ TEST_F(IncomingShareSessionTest, ReadyForTransferNotSelfShare) { session_.OnConnected(&connection_); EXPECT_CALL( transfer_metadata_callback_, - Call(_, HasStatus(TransferMetadata::Status::kAwaitingLocalConfirmation))); + Call(_, AllOf(HasStatus( + TransferMetadata::Status::kAwaitingLocalConfirmation), + HasUsage(ShareSessionUsage::kSharing)))); EXPECT_THAT( session_.ReadyForTransfer( @@ -1104,7 +1150,9 @@ TEST_F(IncomingShareSessionTest, ReadyForTransferSelfShare) { session.OnConnected(&connection_); EXPECT_CALL( transfer_metadata_callback_, - Call(_, HasStatus(TransferMetadata::Status::kAwaitingLocalConfirmation))) + Call(_, AllOf(HasStatus( + TransferMetadata::Status::kAwaitingLocalConfirmation), + HasUsage(ShareSessionUsage::kSharing)))) .Times(0); EXPECT_THAT( @@ -1117,7 +1165,9 @@ TEST_F(IncomingShareSessionTest, ReadyForTransferTimeout) { session_.OnConnected(&connection_); EXPECT_CALL( transfer_metadata_callback_, - Call(_, HasStatus(TransferMetadata::Status::kAwaitingLocalConfirmation))); + Call(_, AllOf(HasStatus( + TransferMetadata::Status::kAwaitingLocalConfirmation), + HasUsage(ShareSessionUsage::kSharing)))); bool accept_timeout_called = false; EXPECT_THAT(session_.ReadyForTransfer( @@ -1195,7 +1245,9 @@ TEST_F(IncomingShareSessionTest, AcceptTransferSuccess) { IsFalse()); EXPECT_CALL( transfer_metadata_callback_, - Call(_, HasStatus(TransferMetadata::Status::kAwaitingRemoteAcceptance))); + Call(_, + AllOf(HasStatus(TransferMetadata::Status::kAwaitingRemoteAcceptance), + HasUsage(ShareSessionUsage::kSharing)))); EXPECT_CALL( mock_event_logger_, Log(Matcher(AllOf( @@ -1274,8 +1326,10 @@ TEST_F(IncomingShareSessionTest, TryUpgradeBandwidthNeeded) { } TEST_F(IncomingShareSessionTest, SendFailureResponseNotConnected) { - EXPECT_CALL(transfer_metadata_callback_, - Call(_, HasStatus(TransferMetadata::Status::kNotEnoughSpace))); + EXPECT_CALL( + transfer_metadata_callback_, + Call(_, AllOf(HasStatus(TransferMetadata::Status::kNotEnoughSpace), + HasUsage(ShareSessionUsage::kSharing)))); session_.SendFailureResponse(TransferMetadata::Status::kNotEnoughSpace); } @@ -1284,8 +1338,10 @@ TEST_F(IncomingShareSessionTest, SendFailureResponseConnected) { connections_manager_.AcceptConnection( /*endpoint_info=*/{}, kEndpointId, &connection_); session_.OnConnected(&connection_); - EXPECT_CALL(transfer_metadata_callback_, - Call(_, HasStatus(TransferMetadata::Status::kNotEnoughSpace))); + EXPECT_CALL( + transfer_metadata_callback_, + Call(_, AllOf(HasStatus(TransferMetadata::Status::kNotEnoughSpace), + HasUsage(ShareSessionUsage::kSharing)))); std::queue> frames_data; connections_manager_.set_send_payload_callback( [&](std::unique_ptr payload, diff --git a/sharing/internal/api/BUILD b/sharing/internal/api/BUILD index 897f6c6f..aaa5a5a3 100644 --- a/sharing/internal/api/BUILD +++ b/sharing/internal/api/BUILD @@ -13,9 +13,27 @@ # limitations under the License. load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:defs.bzl", "cc_binary") licenses(["notice"]) +load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") + +refresh_compile_commands( + name = "refresh_compile_commands_sharing_platform", + + # Specify the targets of interest. + # For example, specify a dict of targets and any flags required to build. + targets = { + ":platform": "", + }, + # No need to add flags already in .bazelrc. They're automatically picked up. + # If you don't need flags, a list of targets is also okay, as is a single target string. + # Wildcard patterns, like //... for everything, *are* allowed here, just like a build. + # As are additional targets (+) and subtractions (-), like in bazel query https://docs.bazel.build/versions/main/query.html#expressions + # And if you're working on a header-only library, specify a test or binary target that compiles it. +) + cc_library( name = "platform", hdrs = [ @@ -31,8 +49,10 @@ cc_library( "system_info.h", ], visibility = [ + "//internal/account:__pkg__", "//location/nearby/analytics/cpp/logging:__pkg__", "//location/nearby/cpp/sharing:__subpackages__", + "//location/nearby/sharing/lib:__subpackages__", "//location/nearby/sharing/sdk/test_client:__pkg__", "//sharing:__subpackages__", ], @@ -40,9 +60,10 @@ cc_library( "//internal/base:file_path", "//internal/platform:mac_address", "//internal/platform:types", - "//internal/platform/implementation:account_manager", + "//internal/platform/implementation:types", + "//location/nearby/sharing/lib/account:account_manager", + "//location/nearby/sharing/lib/sync:sync_binding_prefs_cc_proto", "//sharing/proto:share_cc_proto", - "//sharing/proto:wire_format_cc_proto", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/strings:string_view", "@com_google_absl//absl/time", @@ -69,16 +90,10 @@ cc_library( "//internal/base:file_path", "//internal/platform:mac_address", "//internal/platform:types", - "//internal/platform/implementation:account_manager", - "//sharing/analytics", - "//sharing/internal/public:logging", - "//sharing/proto:share_cc_proto", - "@com_google_absl//absl/base:core_headers", + "//internal/platform/implementation:types", + "//location/nearby/sharing/lib/account:account_manager", "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", - "@com_google_absl//absl/synchronization", "@com_google_absl//absl/types:span", "@com_google_googletest//:gtest_for_library_testonly", ], diff --git a/sharing/internal/api/mock_sharing_platform.h b/sharing/internal/api/mock_sharing_platform.h index b5b64e18..c19adcc8 100644 --- a/sharing/internal/api/mock_sharing_platform.h +++ b/sharing/internal/api/mock_sharing_platform.h @@ -19,12 +19,11 @@ #include #include +#include "location/nearby/sharing/lib/account/account_manager.h" #include "gmock/gmock.h" #include "absl/strings/string_view.h" #include "internal/base/file_path.h" -#include "internal/platform/device_info.h" -#include "internal/platform/implementation/account_manager.h" -#include "internal/platform/task_runner.h" +#include "internal/platform/implementation/device_info.h" #include "sharing/internal/api/app_info.h" #include "sharing/internal/api/bluetooth_adapter.h" #include "sharing/internal/api/fast_init_ble_beacon.h" @@ -71,8 +70,7 @@ class MockSharingPlatform : public SharingPlatform { MOCK_METHOD(PreferenceManager&, GetPreferenceManager, (), (override)); MOCK_METHOD(AccountManager&, GetAccountManager, (), (override)); - MOCK_METHOD(TaskRunner&, GetDefaultTaskRunner, (), (override)); - MOCK_METHOD(nearby::DeviceInfo&, GetDeviceInfo, (), (override)); + MOCK_METHOD(nearby::api::DeviceInfo&, GetDeviceInfo, (), (override)); MOCK_METHOD(std::unique_ptr, CreatePublicCertificateDatabase, (const FilePath& database_path), (override)); diff --git a/sharing/internal/api/preference_manager.h b/sharing/internal/api/preference_manager.h index 1cd38bb7..58fba5aa 100644 --- a/sharing/internal/api/preference_manager.h +++ b/sharing/internal/api/preference_manager.h @@ -22,55 +22,12 @@ #include #include -#if defined(__has_include) -#if __has_include("location/nearby/sharing/lib/sync/sync_binding_prefs.pb.h") #include "location/nearby/sharing/lib/sync/sync_binding_prefs.pb.h" -#define NEARBY_HAS_SYNC_BINDING_PREFS_PROTO 1 -#endif // __has_include("location/nearby/sharing/lib/sync/sync_binding_prefs.pb.h") -#if __has_include("location/nearby/sharing/lib/sync/sync_config_prefs.pb.h") -#include "location/nearby/sharing/lib/sync/sync_config_prefs.pb.h" -#define NEARBY_HAS_SYNC_CONFIG_PREFS_PROTO 1 -#endif // __has_include("location/nearby/sharing/lib/sync/sync_config_prefs.pb.h") -#endif // defined(__has_include) -#include "sharing/proto/wire_format.pb.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "absl/types/span.h" #include "sharing/internal/api/private_certificate_data.h" -#ifndef NEARBY_HAS_SYNC_CONFIG_PREFS_PROTO -namespace nearby::sharing::sync { -class SyncConfigPrefs { - public: - bool ParseFromString(const std::string& serialized) { - return sync_config_.ParseFromString(serialized); - } - std::string SerializeAsString() const { - return sync_config_.SerializeAsString(); - } - const nearby::sharing::service::proto::SyncConfig& sync_config() const { - return sync_config_; - } - nearby::sharing::service::proto::SyncConfig* mutable_sync_config() { - return &sync_config_; - } - - private: - nearby::sharing::service::proto::SyncConfig sync_config_; -}; -} // namespace nearby::sharing::sync -#endif // NEARBY_HAS_SYNC_CONFIG_PREFS_PROTO - -#ifndef NEARBY_HAS_SYNC_BINDING_PREFS_PROTO -namespace nearby::sharing::sync { -class SyncBindingPrefs { - public: - bool ParseFromString(const std::string&) { return false; } - std::string SerializeAsString() const { return {}; } -}; -} // namespace nearby::sharing::sync -#endif // NEARBY_HAS_SYNC_BINDING_PREFS_PROTO - namespace nearby::sharing::api { class PreferenceManager { @@ -123,10 +80,6 @@ class PreferenceManager { virtual void RemoveDictionaryItem(absl::string_view key, absl::string_view dictionary_item) = 0; - virtual void SetSyncConfigValue( - absl::string_view binding_id, - const nearby::sharing::sync::SyncConfigPrefs& value) = 0; - virtual void SetSyncBindingValue( const nearby::sharing::sync::SyncBindingPrefs& value) = 0; @@ -169,17 +122,11 @@ class PreferenceManager { virtual std::optional GetDictionaryStringValue( absl::string_view key, absl::string_view dictionary_item) const = 0; - virtual std::optional - GetSyncConfigValue(absl::string_view binding_id) const = 0; - virtual std::optional GetSyncBindingValue() const = 0; // Removes preferences virtual void Remove(absl::string_view key) = 0; - // Removes all sync configs. - // Observers are not notified for each removed config. - virtual void RemoveAllSyncConfigs() = 0; // Removes all binding configs. // Observers are not notified for each removed config. virtual void RemoveAllBindingConfigs() = 0; diff --git a/sharing/internal/api/sharing_platform.h b/sharing/internal/api/sharing_platform.h index d5bab76a..9aefd967 100644 --- a/sharing/internal/api/sharing_platform.h +++ b/sharing/internal/api/sharing_platform.h @@ -19,11 +19,10 @@ #include #include +#include "location/nearby/sharing/lib/account/account_manager.h" #include "absl/strings/string_view.h" #include "internal/base/file_path.h" -#include "internal/platform/device_info.h" -#include "internal/platform/implementation/account_manager.h" -#include "internal/platform/task_runner.h" +#include "internal/platform/implementation/device_info.h" #include "sharing/internal/api/app_info.h" #include "sharing/internal/api/bluetooth_adapter.h" #include "sharing/internal/api/fast_init_ble_beacon.h" @@ -64,8 +63,7 @@ class SharingPlatform { virtual PreferenceManager& GetPreferenceManager() = 0; virtual AccountManager& GetAccountManager() = 0; - virtual TaskRunner& GetDefaultTaskRunner() = 0; - virtual nearby::DeviceInfo& GetDeviceInfo() = 0; + virtual nearby::api::DeviceInfo& GetDeviceInfo() = 0; virtual std::unique_ptr CreatePublicCertificateDatabase(const FilePath& database_path) = 0; diff --git a/sharing/internal/public/context.h b/sharing/internal/public/context.h index 23c8de03..05b3d25f 100644 --- a/sharing/internal/public/context.h +++ b/sharing/internal/public/context.h @@ -49,9 +49,6 @@ class Context { // count of tasks running at the same time. virtual std::unique_ptr CreateConcurrentTaskRunner( uint32_t concurrent_count) const = 0; - - // Provides the API to retrieve TaskRunner to run a task globally. - virtual TaskRunner* GetTaskRunner() = 0; }; } // namespace nearby diff --git a/sharing/internal/public/context_impl.cc b/sharing/internal/public/context_impl.cc index b439cefa..7aefa05c 100644 --- a/sharing/internal/public/context_impl.cc +++ b/sharing/internal/public/context_impl.cc @@ -70,8 +70,4 @@ std::unique_ptr ContextImpl::CreateConcurrentTaskRunner( return task_runner; } -TaskRunner* ContextImpl::GetTaskRunner() { - return &platform_.GetDefaultTaskRunner(); -} - } // namespace nearby diff --git a/sharing/internal/public/context_impl.h b/sharing/internal/public/context_impl.h index 9775228d..a9078a6a 100644 --- a/sharing/internal/public/context_impl.h +++ b/sharing/internal/public/context_impl.h @@ -43,7 +43,6 @@ class ContextImpl : public Context { std::unique_ptr CreateSequencedTaskRunner() const override; std::unique_ptr CreateConcurrentTaskRunner( uint32_t concurrent_count) const override; - TaskRunner* GetTaskRunner() override; private: nearby::sharing::api::SharingPlatform& platform_; diff --git a/sharing/internal/public/pref_names.h b/sharing/internal/public/pref_names.h index 83ced43c..6daef139 100644 --- a/sharing/internal/public/pref_names.h +++ b/sharing/internal/public/pref_names.h @@ -61,13 +61,6 @@ class PrefNames { // TODO: b/485304482 - define data format for binding configs. static constexpr absl::string_view kBindingConfigPrefix = "nearby_sharing.binding_config."; - - // Sync configs preferences are stored in pref keys: - // kSyncConfigPrefix + - // Example: "nearby_sharing.sync_config.01243347-2343-4324-3423-432432432432" - // Data stored in sync config prefs is a SyncConfig proto. - static constexpr absl::string_view kSyncConfigPrefix = - "nearby_sharing.sync_config."; }; } // namespace nearby::sharing diff --git a/sharing/internal/test/BUILD b/sharing/internal/test/BUILD index b377f056..a6069b4c 100644 --- a/sharing/internal/test/BUILD +++ b/sharing/internal/test/BUILD @@ -40,6 +40,7 @@ cc_library( "//internal/platform:mac_address", "//internal/platform:types", "//internal/test", + "//location/nearby/sharing/lib/sync:sync_binding_prefs_cc_proto", "//sharing/internal/api:platform", "//sharing/internal/public:pref_names", "//sharing/internal/public:types", diff --git a/sharing/internal/test/fake_context.cc b/sharing/internal/test/fake_context.cc index 35605bca..37557c15 100644 --- a/sharing/internal/test/fake_context.cc +++ b/sharing/internal/test/fake_context.cc @@ -38,8 +38,7 @@ FakeContext::FakeContext() fake_connectivity_manager_(std::make_unique()), fake_bluetooth_adapter_(std::make_unique()), fake_fast_initiation_manager_( - std::make_unique()), - executor_(std::make_unique(fake_clock_.get(), 5)) {} + std::make_unique()) {} Clock* FakeContext::GetClock() const { return fake_clock_.get(); } @@ -70,6 +69,4 @@ std::unique_ptr FakeContext::CreateConcurrentTaskRunner( return std::make_unique(fake_clock_.get(), concurrent_count); } -TaskRunner* FakeContext::GetTaskRunner() { return executor_.get(); } - } // namespace nearby diff --git a/sharing/internal/test/fake_context.h b/sharing/internal/test/fake_context.h index c637dbf2..5a59879a 100644 --- a/sharing/internal/test/fake_context.h +++ b/sharing/internal/test/fake_context.h @@ -47,7 +47,6 @@ class FakeContext : public Context { std::unique_ptr CreateSequencedTaskRunner() const override; std::unique_ptr CreateConcurrentTaskRunner( uint32_t concurrent_count) const override; - TaskRunner* GetTaskRunner() override; FakeClock* fake_clock() const { return fake_clock_.get(); } FakeConnectivityManager* fake_connectivity_manager() const { @@ -59,9 +58,6 @@ class FakeContext : public Context { FakeFastInitiationManager* fake_fast_initiation_manager() const { return fake_fast_initiation_manager_.get(); } - FakeTaskRunner* fake_task_runner() const { - return executor_.get(); - } FakeTaskRunner* last_sequenced_task_runner() const { return last_sequenced_task_runner_; @@ -72,7 +68,6 @@ class FakeContext : public Context { std::unique_ptr fake_connectivity_manager_; std::unique_ptr fake_bluetooth_adapter_; std::unique_ptr fake_fast_initiation_manager_; - std::unique_ptr executor_; mutable FakeTaskRunner* last_sequenced_task_runner_ = nullptr; }; diff --git a/sharing/internal/test/fake_context_test.cc b/sharing/internal/test/fake_context_test.cc index 0b0c3152..03179926 100644 --- a/sharing/internal/test/fake_context_test.cc +++ b/sharing/internal/test/fake_context_test.cc @@ -15,9 +15,6 @@ #include "sharing/internal/test/fake_context.h" #include "gtest/gtest.h" -#include "absl/synchronization/notification.h" -#include "absl/time/time.h" -#include "internal/platform/task_runner.h" namespace nearby { namespace { @@ -31,18 +28,5 @@ TEST(FakeContext, TestAccessMockContext) { EXPECT_NE(context.CreateConcurrentTaskRunner(5), nullptr); } -TEST(FakeContext, ExecuteTask) { - FakeContext context; - absl::Notification notification; - bool is_called = false; - context.GetTaskRunner()->PostTask([&]() { - is_called = true; - notification.Notify(); - }); - - EXPECT_TRUE(notification.WaitForNotificationWithTimeout(absl::Seconds(1))); - EXPECT_TRUE(is_called); -} - } // namespace } // namespace nearby diff --git a/sharing/internal/test/fake_preference_manager.cc b/sharing/internal/test/fake_preference_manager.cc index 84955eb8..27066249 100644 --- a/sharing/internal/test/fake_preference_manager.cc +++ b/sharing/internal/test/fake_preference_manager.cc @@ -22,6 +22,7 @@ #include #include +#include "location/nearby/sharing/lib/sync/sync_binding_prefs.pb.h" #include "absl/container/flat_hash_map.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" @@ -35,7 +36,6 @@ namespace nearby { using ::nearby::sharing::PrefNames; using ::nearby::sharing::api::PrivateCertificateData; using ::nearby::sharing::sync::SyncBindingPrefs; -using ::nearby::sharing::sync::SyncConfigPrefs; // Preference suffix for the sync binding information. constexpr absl::string_view kFileSyncBindingName = "FileSync"; @@ -243,12 +243,6 @@ void FakePreferenceManager::RemoveDictionaryItem( NotifyPreferenceChanged(key); } -void FakePreferenceManager::SetSyncConfigValue(absl::string_view binding_id, - const SyncConfigPrefs& value) { - SetValue(absl::StrCat(PrefNames::kSyncConfigPrefix, binding_id), - value.SerializeAsString()); -} - void FakePreferenceManager::SetSyncBindingValue( const SyncBindingPrefs& value) { SetValue(absl::StrCat(PrefNames::kBindingConfigPrefix, kFileSyncBindingName), @@ -339,21 +333,6 @@ std::optional FakePreferenceManager::GetDictionaryStringValue( return GetDictionaryValue(key, dictionary_item); } -std::optional FakePreferenceManager::GetSyncConfigValue( - absl::string_view binding_id) const { - std::string serialized_sync_config; - serialized_sync_config = - GetString(absl::StrCat(PrefNames::kSyncConfigPrefix, binding_id), ""); - if (serialized_sync_config.empty()) { - return std::nullopt; - } - SyncConfigPrefs sync_config; - if (!sync_config.ParseFromString(serialized_sync_config)) { - return std::nullopt; - } - return sync_config; -} - std::optional FakePreferenceManager::GetSyncBindingValue() const { std::string serialized_sync_binding; @@ -379,13 +358,6 @@ void FakePreferenceManager::Remove(absl::string_view key) { NotifyPreferenceChanged(key); } -void FakePreferenceManager::RemoveAllSyncConfigs() { - absl::MutexLock lock(mutex_); - absl::erase_if(values_, [](const auto& item) { - return item.first.starts_with(PrefNames::kSyncConfigPrefix); - }); -} - void FakePreferenceManager::RemoveAllBindingConfigs() { absl::MutexLock lock(mutex_); absl::erase_if(values_, [](const auto& item) { diff --git a/sharing/internal/test/fake_preference_manager.h b/sharing/internal/test/fake_preference_manager.h index 4b0181f9..1fb023cf 100644 --- a/sharing/internal/test/fake_preference_manager.h +++ b/sharing/internal/test/fake_preference_manager.h @@ -23,6 +23,7 @@ #include #include +#include "location/nearby/sharing/lib/sync/sync_binding_prefs.pb.h" #include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" @@ -74,9 +75,6 @@ class FakePreferenceManager : public nearby::sharing::api::PreferenceManager { void RemoveDictionaryItem(absl::string_view key, absl::string_view dictionary_item) override; - void SetSyncConfigValue( - absl::string_view binding_id, - const nearby::sharing::sync::SyncConfigPrefs& value) override; void SetSyncBindingValue( const nearby::sharing::sync::SyncBindingPrefs& value) override; @@ -113,13 +111,10 @@ class FakePreferenceManager : public nearby::sharing::api::PreferenceManager { absl::string_view key, absl::string_view dictionary_item) const override; std::optional GetDictionaryStringValue( absl::string_view key, absl::string_view dictionary_item) const override; - std::optional - GetSyncConfigValue(absl::string_view binding_id) const override; std::optional GetSyncBindingValue() const override; void Remove(absl::string_view key) override; - void RemoveAllSyncConfigs() override; void RemoveAllBindingConfigs() override; void AddObserver( diff --git a/sharing/linux/BUILD b/sharing/linux/BUILD index 29f06fc4..c530bd6b 100644 --- a/sharing/linux/BUILD +++ b/sharing/linux/BUILD @@ -19,12 +19,14 @@ load("@rules_cc//cc/private/rules_impl:cc_static_library.bzl", "cc_static_librar load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") refresh_compile_commands( - name = "refresh_compile_commands_sharing", + name = "refresh_compile_commands", # Specify the targets of interest. # For example, specify a dict of targets and any flags required to build. targets = { - ":nearby_sharing_api_shared": "-s --check_visibility=false --spawn_strategy=standalone --verbose_failures --cxxopt=-std=c++20 --host_cxxopt=-std=c++20 --cxxopt='-fvisibility-inlines-hidden'", + ":fast_init": "", + ":nearby_sharing_cli": "", + "//sharing/linux/tui:tui": "", }, # No need to add flags already in .bazelrc. They're automatically picked up. # If you don't need flags, a list of targets is also okay, as is a single target string. @@ -33,23 +35,83 @@ refresh_compile_commands( # And if you're working on a header-only library, specify a test or binary target that compiles it. ) + +cc_library( + name = "fast_init", + hdrs = [ + "nearby_fast_init_ble_beacon.h", + "nearby_fast_init_manager.h", + ], + srcs = [ + "nearby_fast_init_manager.cc", + ], + deps = [ + "//sharing/internal/api:platform", + "//connections:core", + "//internal/platform/implementation/linux" + ] +) + + +cc_binary( + name = "fast_init_test", + srcs = ["nearby_fast_init.cc"], + deps = [":fast_init"], +) + + +cc_binary( + name = "nearby_sharing_cli", + srcs = ["nearby_sharing_cli.cc"], + deps = [ + ":linux_sharing_platform", + "//connections/implementation/flags:connections_flags", + "//internal/base:file_path", + "//internal/flags:nearby_flags", + "//proto:sharing_enums_cc_proto", + "//sharing:attachments", + "//sharing/flags/generated:generated_flags", + "//sharing:nearby_sharing_service", + "//sharing:transfer_metadata", + "//sharing:types", + "//sharing/analytics", + "//sharing/common:enum", + "//sharing/proto:enums_cc_proto", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/time", + ], +) + cc_library( name = "linux_sharing_platform", - srcs = ["platform/linux_sharing_platform.cc"], - hdrs = ["platform/linux_sharing_platform.h"], - visibility = ["//visibility:private"], + srcs = [ + "platform/linux_account_manager.cc", + "platform/linux_account_manager.h", + "platform/linux_platform_components.cc", + "platform/linux_platform_components.h", + "platform/linux_preference_manager.cc", + "platform/linux_preference_manager.h", + "platform/linux_sharing_platform.cc", + "platform/platform_util.cc", + "platform/platform_util.h", + ], + hdrs = ["platform/linux_sharing_platform.h", "nearby_noop_analytics_recorder.h"], + visibility = ["//visibility:public"], deps = [ + ":fast_init", "//internal/base:file_path", "//internal/base:files", "//internal/platform:base", + "//internal/platform:logging", "//internal/platform:mac_address", "//internal/platform:types", "//internal/platform:uuid", - "//internal/platform/implementation:account_manager", "//internal/platform/implementation:platform", - "//internal/platform/implementation:signin_attempt", "//internal/platform/implementation:types", "//internal/platform/implementation/linux", + "//location/nearby/sharing/lib/account:account_manager", + "//location/nearby/sharing/lib/sync:sync_binding_prefs_cc_proto", "//sharing/internal/api:platform", "//sharing/internal/public:pref_names", "//sharing/proto:share_cc_proto", @@ -63,95 +125,3 @@ cc_library( "@nlohmann_json//:json", ], ) - -cc_library( - name = "nearby_sharing_api", - srcs = ["nearby_sharing_api.cc"], - hdrs = ["nearby_sharing_api.h"], - alwayslink = True, - visibility = ["//visibility:public"], - deps = [ - ":linux_sharing_platform", - "//internal/base:file_path", - "//internal/platform/implementation/linux:system_clock", - "//sharing:attachments", - "//sharing:nearby_sharing_service", - "//sharing/analytics", - "//sharing/local_device_data", - ], -) - -cc_library( - name = "nearby_connections_api", - srcs = ["nearby_connections_api.cc"], - hdrs = ["nearby_connections_api.h"], - alwayslink = True, - visibility = ["//visibility:public"], - deps = [ - "//internal/platform:base", - ":linux_sharing_platform", - "//internal/base:file_path", - "//sharing/internal/public:nearby_context", - "//sharing:nearby_sharing_service", - ], -) - -cc_static_library( - name = "nearby_sharing_api_static", - deps = [":nearby_sharing_api"], -) - -cc_binary( - name = "nearby_sharing_api_shared", - linkshared = True, - srcs = [ - "nearby_sharing_api.cc", - "nearby_sharing_api.h", - ], - visibility = ["//visibility:public"], - linkopts = [ - "-Wl,--exclude-libs,ALL", - ], - deps = [ - "//internal/platform:base", - ":linux_sharing_platform", - "//internal/base:file_path", - "//internal/platform/implementation:platform", - "//internal/platform/implementation/linux", - "//internal/platform/implementation/linux:system_clock", - "//sharing:attachments", - "//sharing:nearby_sharing_service", - "//sharing/analytics", - "//sharing/local_device_data", - ], -) - -cc_binary( - name = "nearby_connections_api_shared", - linkshared = True, - srcs = [ - "nearby_connections_api.cc", - "nearby_connections_api.h", - ], - visibility = ["//visibility:public"], - linkopts = [ - "-Wl,--exclude-libs,ALL", - ], - deps = [ - ":linux_sharing_platform", - "//internal/base:file_path", - "//internal/platform/implementation:platform", - "//internal/platform/implementation/linux", - "//sharing/internal/public:nearby_context", - "//sharing:nearby_sharing_service", - ], -) - -cc_test( - name = "nearby_connections_api_test", - srcs = ["nearby_connections_api_test.cc"], - deps = [ - ":nearby_connections_api", - "@com_google_googletest//:gtest_main", - ], -) diff --git a/sharing/linux/IMPLEMENTATION_GUIDE.md b/sharing/linux/IMPLEMENTATION_GUIDE.md deleted file mode 100644 index 02b69089..00000000 --- a/sharing/linux/IMPLEMENTATION_GUIDE.md +++ /dev/null @@ -1,592 +0,0 @@ -# Nearby Sharing Service Linux - Architecture & Implementation Guide - -## Table of Contents -1. [Architecture Overview](#architecture-overview) -2. [How It Works](#how-it-works) -3. [Implementation Guide](#implementation-guide) -4. [Code Examples](#code-examples) -5. [Best Practices](#best-practices) - -## Architecture Overview - -### Component Hierarchy - -``` -NearbySharingServiceLinux -├── Connections Core (nearby connections layer) -│ ├── ServiceControllerRouter -│ └── Medium Management (BLE, WiFi) -├── Observers (UI/App notifications) -├── Send Surfaces (outgoing transfers) -│ ├── Transfer Callbacks -│ └── Discovery Callbacks -├── Receive Surfaces (incoming transfers) -│ └── Transfer Callbacks -└── Active Transfers - ├── Endpoint Mapping - ├── Transfer State - └── Attachment Container -``` - -### Key Classes - -**NearbySharingServiceLinux**: Main service class -- Manages discovery, advertising, and transfers -- Built on top of Nearby Connections Core -- Handles lifecycle of send/receive surfaces - -**TransferUpdateCallback**: Interface for transfer notifications -- Called on status changes (connecting, in-progress, complete) -- Provides progress information -- Reports errors and completion - -**ShareTargetDiscoveredCallback**: Interface for discovery notifications -- Called when devices are found -- Called when devices are lost -- Called when device info updates - -**AttachmentContainer**: Container for files and text -- Manages multiple attachments -- Supports files, text, and WiFi credentials -- Handles attachment lifecycle - -## How It Works - -### 1. Discovery & Advertising Flow - -#### Sender (Discovers devices): -``` -RegisterSendSurface (Foreground) - ↓ -StartDiscoveryIfNeeded() - ↓ -core_->StartDiscovery() - ↓ -[BLE Scanning Starts] - ↓ -endpoint_found_cb → ParseAdvertisement() - ↓ -ShareTarget created - ↓ -OnShareTargetDiscovered() callback -``` - -#### Receiver (Advertises availability): -``` -RegisterReceiveSurface (Foreground) - ↓ -StartAdvertisingIfNeeded() - ↓ -BuildAdvertisement() - ↓ -core_->StartAdvertising() - ↓ -[BLE Advertising Starts] - ↓ -[Visible to nearby senders] -``` - -### 2. Connection Establishment - -``` -Sender Receiver - | | - | RequestConnection() | - |------------------------------->| - | | connection_initiated_cb - | | (auto or manual accept) - | connection_initiated_cb | - |<-------------------------------| - | | - | AcceptConnection() | AcceptConnection() - |------------------------------->| - |<-------------------------------| - | | - | connection_accepted_cb | connection_accepted_cb - | | - [Connected - Ready for transfer] -``` - -### 3. File Transfer Flow - -``` -Sender Receiver - | | - | SendAttachments() | - | - Create AttachmentContainer | - | - Add FileAttachment | - | | - | RequestConnection() | - |------------------------------->| - | Status: kAwaitingLocalConfirmation - | | - | | Accept() - | | - | AcceptConnection() | AcceptConnection() - | + PayloadListener | + PayloadListener - | | - | Status: kConnecting | - | | - | Send Payloads | - |=============================> | - | (File data chunks) | - | | - | Status: kInProgress | - | Progress: 0% → 100% | - | | - | payload_progress_cb | payload_progress_cb - | | - | Status: kComplete | - | | -``` - -### 4. Advertisement Format - -The service creates custom BLE advertisements with device information: - -``` -Byte Layout: -[0] Header Byte - - Bits 7-5: Version (3 bits) - - Bit 4: Visibility (0=visible, 1=hidden) - - Bits 3-1: Device Type (3 bits) - - Bit 0: Reserved - -[1-2] Salt (2 random bytes) - -[3-16] Metadata Key (14 bytes - for encryption) - -[17+] TLV Fields (Type-Length-Value) - - Vendor ID (1 byte) - - QR Code data (variable) - - Other metadata - -[N+] Device Name (optional, UTF-8) -``` - -**Device Types:** -- 0: Unknown -- 1: Phone -- 2: Tablet -- 3: Laptop -- 4: Unknown - -### 5. State Management - -```cpp -struct TransferState { - AttachmentContainer attachments; // Files/text being transferred - TransferUpdateCallback* callback; // Where to send updates - bool is_incoming; // Direction of transfer -}; - -// Mappings -endpoint_to_target_ // endpoint_id → ShareTarget -target_id_to_endpoint_ // share_target_id → endpoint_id -active_transfers_ // endpoint_id → TransferState -``` - -## Implementation Guide - -### Step 1: Create Service Instance - -```cpp -#include "sharing/linux/nearby_sharing_service_linux.h" - -// Create service with custom device name -NearbySharingServiceLinux service("MyLinuxDevice"); - -// Or let it auto-detect from system -NearbySharingServiceLinux service; -``` - -### Step 2: Implement Callbacks - -```cpp -class MyTransferCallback : public TransferUpdateCallback { - public: - void OnTransferUpdate(const ShareTarget& share_target, - const AttachmentContainer& attachment_container, - const TransferMetadata& transfer_metadata) override { - // Handle transfer status changes - switch (transfer_metadata.status()) { - case TransferMetadata::Status::kAwaitingLocalConfirmation: - // Incoming transfer - need to accept/reject - HandleIncomingRequest(share_target); - break; - - case TransferMetadata::Status::kInProgress: - // Show progress - UpdateProgress(transfer_metadata.progress()); - break; - - case TransferMetadata::Status::kComplete: - // Transfer done - access attachments - HandleCompletedTransfer(attachment_container); - break; - - case TransferMetadata::Status::kFailed: - // Handle error - HandleError(); - break; - } - } -}; - -class MyDiscoveryCallback : public ShareTargetDiscoveredCallback { - public: - void OnShareTargetDiscovered(const ShareTarget& share_target) override { - // New device found - devices_.push_back(share_target); - NotifyUI(); - } - - void OnShareTargetLost(const ShareTarget& share_target) override { - // Device went away - RemoveDevice(share_target.id); - } - - void OnShareTargetUpdated(const ShareTarget& share_target) override { - // Device info changed - UpdateDevice(share_target); - } -}; -``` - -### Step 3: Register Surfaces - -```cpp -MyTransferCallback transfer_callback; -MyDiscoveryCallback discovery_callback; - -// To receive files -service.RegisterReceiveSurface( - &transfer_callback, - NearbySharingService::ReceiveSurfaceState::kForeground, - Advertisement::BlockedVendorId::kNone, - [](auto status) { - if (status == NearbySharingService::StatusCodes::kOk) { - std::cout << "Now advertising to nearby devices" << std::endl; - } - }); - -// To send files -service.RegisterSendSurface( - &transfer_callback, - &discovery_callback, - NearbySharingService::SendSurfaceState::kForeground, - Advertisement::BlockedVendorId::kNone, - false, // don't disable wifi hotspot - [](auto status) { - if (status == NearbySharingService::StatusCodes::kOk) { - std::cout << "Now scanning for nearby devices" << std::endl; - } - }); -``` - -### Step 4: Send Content - -```cpp -// Send a file -void SendFile(int64_t target_id, const std::string& file_path) { - auto container = std::make_unique(); - - FileAttachment attachment(FilePath(file_path)); - container->AddFileAttachment(std::move(attachment)); - - service.SendAttachments(target_id, std::move(container), - [](auto status) { - std::cout << "Send status: " - << NearbySharingService::StatusCodeToString(status) - << std::endl; - }); -} - -// Send text -void SendText(int64_t target_id, const std::string& text) { - auto container = std::make_unique(); - - TextAttachment attachment( - TextAttachment::Type::TEXT, - text, - std::nullopt, // no title - std::nullopt // no mime type - ); - container->AddTextAttachment(std::move(attachment)); - - service.SendAttachments(target_id, std::move(container), - [](auto status) { /* ... */ }); -} -``` - -### Step 5: Handle Incoming Transfers - -```cpp -void HandleIncomingRequest(const ShareTarget& target) { - // Show confirmation dialog to user - std::cout << "Accept file from " << target.device_name << "? (y/n): "; - char choice; - std::cin >> choice; - - if (choice == 'y') { - service.Accept(target.id, [](auto status) { - std::cout << "Accepted!" << std::endl; - }); - } else { - service.Reject(target.id, [](auto status) { - std::cout << "Rejected!" << std::endl; - }); - } -} - -void HandleCompletedTransfer(const AttachmentContainer& container) { - // Process received files - for (const auto& file : container.GetFileAttachments()) { - std::cout << "Received: " << file.file_name() << std::endl; - if (file.file_path().has_value()) { - std::cout << "Saved to: " << file.file_path()->string() << std::endl; - } - } - - // Process received text - for (const auto& text : container.GetTextAttachments()) { - std::cout << "Received text: " << text.text_body() << std::endl; - } -} -``` - -## Code Examples - -### Example 1: Simple File Sender - -```cpp -#include "sharing/linux/nearby_sharing_service_linux.h" -#include "sharing/file_attachment.h" -#include - -int main() { - NearbySharingServiceLinux service("FileSender"); - - // Setup callbacks - class SimpleCallback : public TransferUpdateCallback { - void OnTransferUpdate(...) override { - std::cout << "Progress: " << transfer_metadata.progress() * 100 << "%" << std::endl; - } - } transfer_cb; - - class SimpleDiscovery : public ShareTargetDiscoveredCallback { - int64_t target_id = -1; - void OnShareTargetDiscovered(const ShareTarget& t) override { - target_id = t.id; - std::cout << "Found: " << t.device_name << std::endl; - } - void OnShareTargetLost(...) override {} - void OnShareTargetUpdated(...) override {} - } discovery_cb; - - // Start scanning - service.RegisterSendSurface(&transfer_cb, &discovery_cb, - NearbySharingService::SendSurfaceState::kForeground, - Advertisement::BlockedVendorId::kNone, false, [](auto) {}); - - // Wait for discovery - std::this_thread::sleep_for(std::chrono::seconds(5)); - - if (discovery_cb.target_id != -1) { - // Send file - auto container = std::make_unique(); - container->AddFileAttachment(FileAttachment(FilePath("/path/to/file.txt"))); - service.SendAttachments(discovery_cb.target_id, std::move(container), [](auto) {}); - - // Wait for completion - std::this_thread::sleep_for(std::chrono::seconds(10)); - } - - return 0; -} -``` - -### Example 2: Auto-Accepting Receiver - -```cpp -class AutoAcceptCallback : public TransferUpdateCallback { - public: - AutoAcceptCallback(NearbySharingServiceLinux* service) : service_(service) {} - - void OnTransferUpdate(const ShareTarget& share_target, - const AttachmentContainer& attachment_container, - const TransferMetadata& transfer_metadata) override { - // Auto-accept all incoming transfers - if (transfer_metadata.status() == TransferMetadata::Status::kAwaitingLocalConfirmation) { - service_->Accept(share_target.id, [](auto) {}); - } - - // Save received files - if (transfer_metadata.status() == TransferMetadata::Status::kComplete) { - for (const auto& file : attachment_container.GetFileAttachments()) { - std::cout << "Saved: " << file.file_name() << std::endl; - } - } - } - - private: - NearbySharingServiceLinux* service_; -}; - -int main() { - NearbySharingServiceLinux service("AutoReceiver"); - AutoAcceptCallback callback(&service); - - service.RegisterReceiveSurface(&callback, - NearbySharingService::ReceiveSurfaceState::kForeground, - Advertisement::BlockedVendorId::kNone, [](auto) {}); - - // Keep running - while (true) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } -} -``` - -## Best Practices - -### 1. Callback Lifetime Management - -```cpp -// DON'T: Callbacks going out of scope -void BadExample() { - MyTransferCallback callback; // Stack allocated - service.RegisterSendSurface(&callback, ...); - // callback destroyed when function exits! -} - -// DO: Keep callbacks alive -class App { - MyTransferCallback callback_; // Member variable - - void Setup() { - service.RegisterSendSurface(&callback_, ...); - } -}; -``` - -### 2. Error Handling - -```cpp -service.SendAttachments(target_id, container, - [this](NearbySharingService::StatusCodes status) { - switch (status) { - case StatusCodes::kOk: - // Success - break; - case StatusCodes::kInvalidArgument: - // Bad target_id or empty container - LogError("Invalid arguments"); - break; - case StatusCodes::kNoAvailableConnectionMedium: - // Bluetooth/WiFi not available - NotifyUserToEnableBluetooth(); - break; - default: - LogError("Transfer failed"); - break; - } - }); -``` - -### 3. Resource Cleanup - -```cpp -class ProperCleanup { - public: - ~ProperCleanup() { - // Unregister surfaces before destroying callbacks - service_.UnregisterSendSurface(&transfer_callback_, [](auto) {}); - service_.UnregisterReceiveSurface(&transfer_callback_, [](auto) {}); - - // Shutdown service - service_.Shutdown([](auto) {}); - } - - private: - NearbySharingServiceLinux service_; - MyTransferCallback transfer_callback_; -}; -``` - -### 4. Thread Safety - -```cpp -// The service is NOT thread-safe -// All calls should be from the same thread or synchronized - -class ThreadSafeApp { - public: - void SendFromAnyThread(int64_t target_id, const std::string& file) { - task_runner_.PostTask([this, target_id, file]() { - // All service calls happen on same thread - auto container = std::make_unique(); - container->AddFileAttachment(FileAttachment(FilePath(file))); - service_.SendAttachments(target_id, std::move(container), [](auto) {}); - }); - } - - private: - NearbySharingServiceLinux service_; - TaskRunner task_runner_; // Your threading implementation -}; -``` - -### 5. State Tracking - -```cpp -class StatefulApp { - public: - void OnTransferUpdate(...) override { - current_state_ = transfer_metadata.status(); - - // Track progress - if (transfer_metadata.status() == Status::kInProgress) { - progress_map_[share_target.id] = transfer_metadata.progress(); - } - - // Cleanup on completion - if (TransferMetadata::IsFinalStatus(transfer_metadata.status())) { - progress_map_.erase(share_target.id); - } - } - - private: - TransferMetadata::Status current_state_; - std::unordered_map progress_map_; -}; -``` - -## Troubleshooting - -### Discovery Not Working -- Check Bluetooth is enabled: `IsBluetoothPowered()` -- Verify sender is in foreground state -- Ensure receiver is advertising -- Check for permission issues - -### Transfers Failing -- Verify file paths are valid and accessible -- Check available disk space on receiver -- Ensure stable Bluetooth connection -- Monitor transfer callbacks for specific error status - -### Connection Issues -- Devices must be within Bluetooth range (~10m) -- Minimize interference from other BLE devices -- Ensure both devices support required BLE features -- Check firewall settings for WiFi Direct - -## Performance Tips - -1. **Use appropriate surface states**: Background mode when not actively transferring -2. **Unregister when not needed**: Stop scanning/advertising to save battery -3. **Batch small files**: Combine into zip for better efficiency -4. **Monitor transfer progress**: Cancel stalled transfers -5. **Handle errors gracefully**: Retry with exponential backoff - diff --git a/sharing/linux/QUICK_REFERENCE.md b/sharing/linux/QUICK_REFERENCE.md deleted file mode 100644 index 22e17efa..00000000 --- a/sharing/linux/QUICK_REFERENCE.md +++ /dev/null @@ -1,300 +0,0 @@ -# Nearby Sharing Linux - Quick Reference - -## Quick Start - -### Build -```bash -bazel build //sharing/linux:simple_example -bazel build //sharing/linux:nearby_sharing_app -``` - -### Run Simple Example -```bash -# Terminal 1 (Receiver) -./bazel-bin/sharing/linux/simple_example receiver - -# Terminal 2 (Sender) -./bazel-bin/sharing/linux/simple_example sender "Hello World!" -``` - -### Run Full App -```bash -./bazel-bin/sharing/linux/nearby_sharing_app [device_name] -``` - -## API Cheat Sheet - -### Include Headers -```cpp -#include "sharing/linux/nearby_sharing_service_linux.h" -#include "sharing/attachment_container.h" -#include "sharing/file_attachment.h" -#include "sharing/text_attachment.h" -#include "sharing/share_target.h" -#include "sharing/transfer_metadata.h" -``` - -### Create Service -```cpp -using namespace nearby::sharing::linux; -NearbySharingServiceLinux service("DeviceName"); -``` - -### Implement Callbacks -```cpp -// Transfer updates -class MyCallback : public TransferUpdateCallback { - void OnTransferUpdate(const ShareTarget& target, - const AttachmentContainer& attachments, - const TransferMetadata& metadata) override { - // Handle status changes - } -}; - -// Device discovery -class MyDiscovery : public ShareTargetDiscoveredCallback { - void OnShareTargetDiscovered(const ShareTarget& target) override { } - void OnShareTargetLost(const ShareTarget& target) override { } - void OnShareTargetUpdated(const ShareTarget& target) override { } -}; -``` - -### Register to Receive -```cpp -service.RegisterReceiveSurface( - &transfer_callback, - NearbySharingService::ReceiveSurfaceState::kForeground, - Advertisement::BlockedVendorId::kNone, - [](auto status) { /* callback */ }); -``` - -### Register to Send -```cpp -service.RegisterSendSurface( - &transfer_callback, - &discovery_callback, - NearbySharingService::SendSurfaceState::kForeground, - Advertisement::BlockedVendorId::kNone, - false, // disable_wifi_hotspot - [](auto status) { /* callback */ }); -``` - -### Send File -```cpp -auto container = std::make_unique(); -container->AddFileAttachment(FileAttachment(FilePath("/path/to/file"))); -service.SendAttachments(target_id, std::move(container), [](auto) {}); -``` - -### Send Text -```cpp -auto container = std::make_unique(); -container->AddTextAttachment(TextAttachment( - TextAttachment::Type::TEXT, "message", std::nullopt, std::nullopt)); -service.SendAttachments(target_id, std::move(container), [](auto) {}); -``` - -### Accept/Reject/Cancel -```cpp -service.Accept(target_id, [](auto status) {}); -service.Reject(target_id, [](auto status) {}); -service.Cancel(target_id, [](auto status) {}); -``` - -### Check Status -```cpp -bool scanning = service.IsScanning(); -bool transferring = service.IsTransferring(); -bool bt_present = service.IsBluetoothPresent(); -bool bt_powered = service.IsBluetoothPowered(); -``` - -### Shutdown -```cpp -service.Shutdown([](auto status) {}); -``` - -## Transfer Statuses - -| Status | Meaning | Action | -|--------|---------|--------| -| `kConnecting` | Establishing connection | Wait | -| `kAwaitingLocalConfirmation` | Need to accept/reject | Call Accept() or Reject() | -| `kAwaitingRemoteAcceptance` | Waiting for remote | Wait | -| `kInProgress` | Transferring data | Show progress | -| `kComplete` | Success | Access attachments | -| `kFailed` | Error occurred | Check logs | -| `kRejected` | User rejected | Retry or cancel | -| `kCancelled` | Transfer cancelled | Cleanup | -| `kTimedOut` | Connection timeout | Retry | - -## Status Codes - -| Code | Meaning | -|------|---------| -| `kOk` | Success | -| `kError` | General error | -| `kOutOfOrderApiCall` | API called incorrectly | -| `kTransferAlreadyInProgress` | Can't start new transfer | -| `kNoAvailableConnectionMedium` | No Bluetooth/WiFi | -| `kInvalidArgument` | Bad parameters | - -## Common Patterns - -### Auto-Accept Pattern -```cpp -class AutoAccept : public TransferUpdateCallback { - void OnTransferUpdate(...) override { - if (metadata.status() == Status::kAwaitingLocalConfirmation) { - service_->Accept(target.id, [](auto) {}); - } - } -}; -``` - -### Progress Tracking Pattern -```cpp -void OnTransferUpdate(...) override { - if (metadata.status() == Status::kInProgress) { - int percent = metadata.progress() * 100; - uint64_t bytes = metadata.transferred_bytes(); - std::cout << percent << "% (" << bytes << " bytes)" << std::endl; - } -} -``` - -### Device Selection Pattern -```cpp -std::vector devices; - -void OnShareTargetDiscovered(const ShareTarget& target) override { - devices.push_back(target); - std::cout << devices.size() << ". " << target.device_name << std::endl; -} - -void SendToDevice(size_t index) { - if (index < devices.size()) { - SendFile(devices[index].id, file_path); - } -} -``` - -### Error Handling Pattern -```cpp -service.SendAttachments(target_id, container, - [](NearbySharingService::StatusCodes status) { - if (status != StatusCodes::kOk) { - std::cerr << "Error: " - << NearbySharingService::StatusCodeToString(status) - << std::endl; - return; - } - std::cout << "Transfer initiated" << std::endl; - }); -``` - -## Debugging Tips - -### Enable Verbose Logging -```cpp -// Set environment variable -export NEARBY_LOGS=VERBOSE -``` - -### Check Bluetooth -```cpp -if (!service.IsBluetoothPresent()) { - std::cout << "No Bluetooth adapter found" << std::endl; -} -if (!service.IsBluetoothPowered()) { - std::cout << "Bluetooth is off" << std::endl; -} -``` - -### Dump Service State -```cpp -std::cout << service.Dump() << std::endl; -// Output: "NearbySharingServiceLinux advertising=true scanning=false ..." -``` - -### Monitor Callbacks -```cpp -void OnTransferUpdate(...) override { - std::cout << "[Transfer] " << target.device_name - << " - " << TransferMetadata::StatusToString(metadata.status()) - << " - " << (metadata.progress() * 100) << "%" << std::endl; -} -``` - -## File Locations - -- **Service**: `sharing/linux/nearby_sharing_service_linux.{h,cc}` -- **Simple Example**: `sharing/linux/simple_example.cc` -- **Full App**: `sharing/linux/nearby_sharing_app.cc` -- **README**: `sharing/linux/README.md` -- **Implementation Guide**: `sharing/linux/IMPLEMENTATION_GUIDE.md` -- **BUILD**: `sharing/linux/BUILD` - -## Common Issues - -### "No devices found" -- Ensure receiver is running and advertising -- Check Bluetooth is enabled on both devices -- Verify devices are within range (~10m) -- Try restarting Bluetooth - -### "Transfer failed" -- Check file permissions -- Verify disk space -- Ensure stable connection -- Check firewall settings - -### "Invalid argument" -- Verify target_id is valid -- Ensure container has attachments -- Check surface is registered - -### Callback not called -- Verify callback lifetime (must outlive service) -- Check registration was successful -- Ensure main thread/event loop is running - -## Example Workflows - -### Send File Workflow -``` -1. Create service -2. Create callbacks -3. RegisterSendSurface (foreground) -4. Wait for OnShareTargetDiscovered -5. Create AttachmentContainer -6. Add FileAttachment -7. SendAttachments(target_id, container) -8. Wait for kComplete in OnTransferUpdate -``` - -### Receive File Workflow -``` -1. Create service -2. Create callback -3. RegisterReceiveSurface (foreground) -4. Wait for kAwaitingLocalConfirmation -5. Call Accept(target_id) -6. Wait for kInProgress updates -7. Wait for kComplete -8. Access files from AttachmentContainer -``` - -## Performance Notes - -- **Scanning**: Consumes battery, stop when not needed -- **Advertising**: Minimal impact -- **Transfer**: WiFi Direct faster than Bluetooth -- **File Size**: Large files (>100MB) benefit from WiFi -- **Small Files**: Bluetooth sufficient for <10MB - -## Links - -- [README.md](README.md) - Overview and features -- [IMPLEMENTATION_GUIDE.md](IMPLEMENTATION_GUIDE.md) - Detailed architecture -- [nearby_sharing_service.h](../nearby_sharing_service.h) - Base interface diff --git a/sharing/linux/README.md b/sharing/linux/README.md deleted file mode 100644 index 81eaeecc..00000000 --- a/sharing/linux/README.md +++ /dev/null @@ -1,104 +0,0 @@ -# Nearby Sharing for Linux - -This directory contains: -- A Linux-facing Nearby Sharing library (`NearbySharingApi`) -- A sample CLI app (`nearby_sharing_app`) -- A Qt/QML tray sample app (`qml_tray_app`) - -## Scope and Intended Use - -This project is primarily intended to be used as a reusable library/API. - -The sample applications are still supported and will continue to be supported because they are used in real day-to-day device sharing workflows. - -## Current Status - -The current implementation works with the reverse-engineered certificate manager currently used in this project. - -Compatibility can still break if Google changes certificate manager behavior/protocol details. That component is closed source, so upstream changes can be difficult to inspect and adapt to quickly. - -## Test Coverage and Session Notes - -- Verified: single-file sharing flow. -- Not fully verified: multiple transfers in one app lifetime. -- Current practical testing pattern: restart the application before each new transfer. - -For this README, a "session" means one process lifetime (app start to app exit). - -After one transfer, some endpoints may close and internal state can reset/change. Multi-transfer handling in one live session is still under investigation. - -## Known Issues - -- Linux hotspot startup can be slow. -- Connecting to a hotspot started on another device can be slow on Linux. -- Android-initiated connection formation can be very slow. - -The Android/Linux connection latency issue still needs deeper investigation. One possible cause is connection/negotiation behavior that Linux does not currently handle well. - -## Wi-Fi Direct Status - -Wi-Fi Direct is theoretically possible but not implemented yet. - -Reason: NetworkManager does not natively support creating Wi-Fi Direct Group Owners in the way this project needs. - -## Installation - -### 1. Install the shared library - -From the repository root: - -```bash -./sharing/linux/install_nearby_sharing_service.sh -``` - -This installs: -- `libnearby_sharing_api_shared.so` -- `sharing/linux/nearby_sharing_api.h` - -### 2. Build and run the CLI sample app (optional) - -From the repository root: - -```bash -bazel build //sharing/linux:nearby_sharing_app -./bazel-bin/sharing/linux/nearby_sharing_app -# Optional custom device name -./bazel-bin/sharing/linux/nearby_sharing_app "MyDeviceName" -``` - -### 3. Build and run the tray sample app (optional) - -From `sharing/linux/qml_tray_app`: - -```bash -cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DNEARBY_PREFIX=/usr/local -cmake --build build -j -./build/nearby_qml_file_tray_app -``` - -### 4. Install launcher entry (`.desktop`) for the tray app - -From `sharing/linux/qml_tray_app`: - -```bash -mkdir -p "$HOME/.local/share/applications" -install -m 0644 nearby-file-share.desktop "$HOME/.local/share/applications/nearby-file-share.desktop" -sed -i "s|^Exec=.*|Exec=$(pwd)/build/nearby_qml_file_tray_app|" "$HOME/.local/share/applications/nearby-file-share.desktop" -sed -i "s|^Icon=.*|Icon=$(pwd)/nearby-linux-desktop.png|" "$HOME/.local/share/applications/nearby-file-share.desktop" -update-desktop-database "$HOME/.local/share/applications" 2>/dev/null || true -``` - -After this, search for `Nearby File Share` in your desktop launcher. - -## Documentation - -Technical deep dives are being moved from README content to the wiki. - -- Wiki: https://github.com/kidfromjupiter/nearby/wiki - -This README stays focused on status, installation, and known limitations. - -## Demo Assets (Planned) - -- Video demo of end-to-end sharing flow. -- GIF showing the sharing process. diff --git a/sharing/linux/app/AppContent.qml b/sharing/linux/app/AppContent.qml new file mode 100644 index 00000000..fbd1238c --- /dev/null +++ b/sharing/linux/app/AppContent.qml @@ -0,0 +1,57 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Shapes + +RowLayout { + id: top + anchors.fill: parent + spacing: 0 + + property string pendingPath: "" + property bool pendingTransfer: false + + Connections { + target: backend + + function onStatusTextChanged(){ + console.log(backend.statusText) + } + } + //Component.onCompleted: { + // backend.startReceive(); + //} + //Component.onDestruction: { + // backend.stopReceive(); + //} + + + Sidebar {} + StackLayout { + id: contentStack + Layout.fillWidth: true + Layout.fillHeight: true + currentIndex: 0 + + Drop { + } + + Rectangle { + color: "transparent" + Layout.fillWidth: true + Layout.fillHeight: true + + Targets {} + } + + IncomingShare { + filename: "VacationPhoto_2026.jpg" + targetname: "Lasan's A55" + progressValue: 0.64 + statusText: "Receiving 1 of 1 items" + transferring: true + } + + + } +} diff --git a/sharing/linux/app/BUILD b/sharing/linux/app/BUILD new file mode 100644 index 00000000..d5b1957e --- /dev/null +++ b/sharing/linux/app/BUILD @@ -0,0 +1,82 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_qt//:qt.bzl", "qt_cc_binary", "qt_cc_library", "qt_resource_via_qrc") + +load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") + +refresh_compile_commands( + name = "refresh_compile_commands", + + # Specify the targets of interest. + # For example, specify a dict of targets and any flags required to build. + targets = { + ":app" : "", + }, + # No need to add flags already in .bazelrc. They're automatically picked up. + # If you don't need flags, a list of targets is also okay, as is a single target string. + # Wildcard patterns, like //... for everything, *are* allowed here, just like a build. + # As are additional targets (+) and subtractions (-), like in bazel query https://docs.bazel.build/versions/main/query.html#expressions + # And if you're working on a header-only library, specify a test or binary target that compiles it. +) + +qt_resource_via_qrc( + name = "app_resources", + qrc_file = "resources.qrc", + files = [ + "main.qml", + "AppContent.qml", + "Drop.qml", + "Sidebar.qml", + "ShareTarget.qml", + "Targets.qml", + "IncomingShare.qml", + "googlesans_var.ttf", + "icons/file.svg", + "icons/laptop.svg", + "icons/smartphone.svg", + "icons/tablet.svg", + "icons/up_file.svg", + ], +) + +qt_cc_library( + name = "backend", + srcs = ["backend.cc"], + hdrs = ["backend.h"], + deps = [ + ":nearby_sharing_dbus_client", + "@rules_qt//:qt_core", + "@rules_qt//:qt_hdrs", + "@rules_qt//:qt_qml", + "@rules_qt//:qt_qml_meta", + ], +) + +qt_cc_binary( + name = "app", + srcs = ["main.cc"], + deps = [ + ":app_resources", + ":backend", + "@rules_qt//:qt_core", + "@rules_qt//:qt_gui", + "@rules_qt//:qt_hdrs", + "@rules_qt//:qt_opengl", + "@rules_qt//:qt_qml", + "@rules_qt//:qt_qml_meta", + "@rules_qt//:qt_qml_workerscript", + "@rules_qt//:qt_quick", + "@rules_qt//:qt_quick_controls2", + "@rules_qt//:qt_quick_layouts", + "@rules_qt//:qt_quick_shapes", + ], +) + +cc_library( + name = "nearby_sharing_dbus_client", + srcs = ["nearby_sharing_dbus_client.cc"], + hdrs = ["nearby_sharing_dbus_client.h"], + deps = [ + "//sharing/linux/daemon:nearby_sharing_dbus_client_glue", + "@sdbus_cpp", + ], +) diff --git a/sharing/linux/app/Drop.qml b/sharing/linux/app/Drop.qml new file mode 100644 index 00000000..4f1fc7a5 --- /dev/null +++ b/sharing/linux/app/Drop.qml @@ -0,0 +1,74 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Shapes + +Rectangle { + id: dropZone + color: "transparent" + + //signal fileDropped(string path) + DropArea { + anchors.fill: parent + + onExited: { + dropText.text = "Drag and drop files to share"; + dropZone.color = "transparent"; + } + + // The action: What happens when the user releases the item here + + onDropped: drop => { + + drop.acceptProposedAction(); + if (drop.hasUrls) { + console.log(drop.urls[0].toString()); + for (let i of drop.formats){ + console.log(i) + } + + } + } + } + ColumnLayout { + + anchors.centerIn: parent + Button { + icon.source: "qrc:icons/up_file.svg" + icon.height: 80 + icon.width: 80 + icon.color: "#195871" + Layout.fillWidth: true + background: Rectangle { + color: "transparent" + } + } + + Text { + id: dropText + text: "Drag and drop files to share" + font.pointSize: 18 + color: "#333333" + } + } + Canvas { + id: dashedBorderCanvas + anchors.fill: parent + + onPaint: { + var ctx = getContext("2d"); + ctx.clearRect(0, 0, width, height); + + // Setup styling parameters + ctx.strokeStyle = "#91C8DE"; + ctx.lineWidth = 3; + + // Set the dash pattern array: [length of dash, length of space] + ctx.setLineDash([8, 6]); + + // Draw a rectangle border (X, Y, Width, Height) + // Offset by half line-width (1px) so the line doesn't get clipped on the edges + ctx.strokeRect(80, 80, width - 160, height - 160); + } + } +} diff --git a/sharing/linux/app/IncomingShare.qml b/sharing/linux/app/IncomingShare.qml new file mode 100644 index 00000000..67ded9e7 --- /dev/null +++ b/sharing/linux/app/IncomingShare.qml @@ -0,0 +1,264 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Shapes + +Rectangle { + id: root + color: "#DCF5FF" + Layout.fillWidth: true + Layout.fillHeight: true + + property string filename: "ThisIsAnImage.jpg" + property string targetname: "Lasan's A55" + property bool transferring: true + property real progressValue: 0.64 + property string statusText: "Receiving file" + + Item { + visible: root.transferring + anchors.centerIn: parent + width: Math.min(parent.width * 0.9, 700) + height: 212 + + Rectangle { + anchors.fill: parent + radius: 24 + color: "#FFFFFF" + border.color: "#BCE5F5" + border.width: 1 + } + + RowLayout { + anchors.fill: parent + anchors.margins: 28 + spacing: 24 + + Rectangle { + Layout.preferredWidth: 96 + Layout.preferredHeight: 96 + radius: 24 + color: "#E8F7FC" + + Image { + anchors.centerIn: parent + width: 60 + height: 60 + source: "qrc:/icons/file.svg" + fillMode: Image.PreserveAspectFit + sourceSize.width: width + sourceSize.height: height + smooth: true + antialiasing: true + } + } + + ColumnLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + spacing: 10 + + Text { + text: root.targetname + font.pointSize: 17 + font.weight: 700 + color: "#1A1C1E" + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + text: root.filename + color: "#57707A" + font.pointSize: 12 + elide: Text.ElideMiddle + Layout.fillWidth: true + } + + ProgressBar { + id: transferProgress + Layout.fillWidth: true + from: 0 + to: 1 + value: Math.max(0, Math.min(1, root.progressValue)) + + background: Rectangle { + implicitHeight: 10 + radius: 5 + color: "#DDEEF5" + } + + contentItem: Item { + Rectangle { + width: transferProgress.visualPosition * parent.width + height: 10 + radius: 5 + color: "#0D6D90" + } + } + } + + RowLayout { + Layout.fillWidth: true + + Text { + text: root.statusText + color: "#57707A" + font.pointSize: 12 + Layout.fillWidth: true + elide: Text.ElideRight + } + + Text { + text: Math.round(Math.max(0, Math.min(1, root.progressValue)) * 100) + "%" + color: "#0D6D90" + font.pointSize: 13 + font.weight: 700 + } + } + + RowLayout { + Layout.fillWidth: true + + Item { + Layout.fillWidth: true + } + + Button { + id: cancelButton + text: "Cancel" + + contentItem: Text { + text: cancelButton.text + font.pointSize: 14 + font.weight: 600 + color: "white" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + background: Rectangle { + implicitWidth: 118 + implicitHeight: 46 + radius: 12 + color: cancelButton.hovered ? "#B62828" : "#D94C4C" + } + } + } + } + } + } + + Item { + visible: !root.transferring + anchors.centerIn: parent + width: Math.min(parent.width * 0.85, 360) + height: 460 + + Rectangle { + id: cardBackground + anchors.fill: parent + radius: 24 + color: "#FFFFFF" + border.color: "#BCE5F5" + border.width: 1 + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 28 + spacing: 16 + + Text { + Layout.fillWidth: true + font.pointSize: 18 + font.weight: 700 + color: "#377B95" + text: "Incoming share" + horizontalAlignment: Text.AlignHCenter + } + + Image { + source: "qrc:/icons/file.svg" + Layout.fillHeight: true + Layout.fillWidth: true + fillMode: Image.PreserveAspectCrop + sourceSize.height: height + sourceSize.width: width + smooth: true + antialiasing: true + } + + Text { + Layout.fillWidth: true + color: "#57707A" + font.pointSize: 11 + wrapMode: Text.WrapAnywhere + text: root.filename + horizontalAlignment: Text.AlignHCenter + maximumLineCount: 2 + elide: Text.ElideMiddle + } + + Text { + Layout.fillWidth: true + color: "#57707A" + font.pointSize: 15 + font.weight: 700 + wrapMode: Text.WrapAnywhere + text: root.targetname + horizontalAlignment: Text.AlignHCenter + maximumLineCount: 2 + elide: Text.ElideMiddle + } + + RowLayout { + Button { + id: cancelbutton + Layout.fillWidth: true + text: "Cancel" + + contentItem: Text { + text: cancelbutton.text + font.pointSize: 14 + font.weight: 600 + color: "white" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + background: Rectangle { + implicitHeight: 46 + radius: 12 + color: cancelbutton.hovered ? "#db1e1e" : "#6EA7B6" + border.color: "#91C8DE" + border.width: 1 + } + } + + Button { + id: acceptButton + Layout.fillWidth: true + text: "Accept" + + contentItem: Text { + text: acceptButton.text + font.pointSize: 14 + font.weight: 600 + color: "white" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + background: Rectangle { + implicitHeight: 46 + radius: 12 + color: acceptButton.hovered ? "#195871" : "#06384C" + border.color: "#91C8DE" + border.width: 1 + } + } + } + } + } +} diff --git a/sharing/linux/app/ShareTarget.qml b/sharing/linux/app/ShareTarget.qml new file mode 100644 index 00000000..734e4efa --- /dev/null +++ b/sharing/linux/app/ShareTarget.qml @@ -0,0 +1,119 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Shapes + +Rectangle { + id: rootItem + // Explicit sizes so parent containers know how to space them + width: 100 + height: 130 + color: "transparent" + + // --- CUSTOM ARGUMENTS (PROPERTIES) --- + property string deviceName: "Unknown Device" + property real progressValue: 0.0 // Value between 0.0 and 1.0 + property string iconSource: "qrc:icons/smartphone.svg" + + ColumnLayout { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + spacing: 5 + + // --- ICON CONTAINER WITH PROGRESS RINGS --- + Item { + id: iconContainer + implicitWidth: 72 + implicitHeight: 72 + Layout.alignment: Qt.AlignHCenter + + // Target angle calculated using the custom dynamic argument + property real targetAngle: 360 * rootItem.progressValue + + Behavior on targetAngle { + NumberAnimation { + duration: 300 + easing.type: Easing.OutQuad + } + } + + // Background Gray Track Ring + Shape { + anchors.fill: parent + layer.enabled: true + layer.samples: 8 + antialiasing: true + + ShapePath { + strokeColor: "#E0E4E8" + strokeWidth: 4 + fillColor: "transparent" + + PathAngleArc { + centerX: iconContainer.width / 2 + centerY: iconContainer.height / 2 + radiusX: 32 + radiusY: 32 + startAngle: -90 + sweepAngle: 360 + } + } + } + + // Active Progress Ring + Shape { + anchors.fill: parent + layer.enabled: true + layer.samples: 8 + antialiasing: true + + ShapePath { + strokeColor: "#00658F" + strokeWidth: 4 + fillColor: "transparent" + capStyle: ShapePath.RoundCap + + PathAngleArc { + centerX: iconContainer.width / 2 + centerY: iconContainer.height / 2 + radiusX: 32 + radiusY: 32 + startAngle: -90 + sweepAngle: iconContainer.targetAngle + } + } + } + + // Icon Circle + Rectangle { + width: 60 + height: 60 + anchors.centerIn: parent + color: "#6EA7B6" + radius: 30 + + Button { + icon.source: rootItem.iconSource + anchors.fill: parent + anchors.margins: 5 + icon.color: "white" + icon.height: height + icon.width: width + background: Rectangle { + color: "transparent" + } + } + } + } + + // --- TEXT COMPONENT --- + Text { + text: rootItem.deviceName + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + color: "#1A1C1E" // Using the text charcoal color from your palette + } + } +} diff --git a/sharing/linux/app/Sidebar.qml b/sharing/linux/app/Sidebar.qml new file mode 100644 index 00000000..ff265fff --- /dev/null +++ b/sharing/linux/app/Sidebar.qml @@ -0,0 +1,128 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Shapes + +Rectangle { + id: sidebar + Layout.fillHeight: true + Layout.preferredWidth: 300 + + color: "#CBF0FF" + + ColumnLayout { + anchors.margins: 20 + anchors.fill: parent + + Rectangle { + Layout.preferredHeight: innerColumn.implicitHeight + 20 + Layout.fillWidth: true + color: "transparent" + + ColumnLayout { + id: innerColumn + Text { + text: "Device name" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Text { + text: "lasans-laptop" + font.weight: 500 + font.pointSize: 17 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: "transparent" + + ColumnLayout { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + //anchors.fill: parent + + Text { + text: "Visible to Everyone" + font.weight: 700 + color: "#377B95" + } + + Text { + Layout.preferredWidth: parent.width + wrapMode: Text.WordWrap + text: "This cannot be changed due to limitations in QuickShare on Linux" + color: "gray" + } + } + } + + Rectangle { + Layout.preferredHeight: 350 + Layout.fillWidth: true + border.color: "#91C8DE" + border.width: 2 + radius: 20 + color: "#DCF5FF" + ColumnLayout { + anchors.fill: parent + anchors.margins: 15 + + Text { + Layout.fillWidth: true + font.pointSize: 15 + + font.weight: 700 + color: "#377B95" + text: "Sharing" + } + Image { + source: "qrc:icons/file.svg" + Layout.fillHeight: true + Layout.fillWidth: true + fillMode: Image.PreserveAspectCrop + sourceSize.height: height + sourceSize.width: width + smooth: true + antialiasing: true + } + + Text { + Layout.fillWidth: true + color: "gray" + wrapMode: Text.WordWrap + text: "/home/lasan/test/this/is/a/very/long/file/path/file.pp" + horizontalAlignment: Text.AlignHCenter + } + Button { + id: cancelbutton + Layout.fillWidth: true + text: "Cancel" + contentItem: Text { + text: cancelbutton.text + font.pointSize: 14 + font.weight: 500 + color: "white" // Dims text when pressed + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + radius: 10 + color: cancelbutton.hovered ? "#195871" : "#06384C" + border.color: "#91C8DE" + border.width: 2 + } + } + } + } + } +} diff --git a/sharing/linux/app/Targets.qml b/sharing/linux/app/Targets.qml new file mode 100644 index 00000000..5b555b51 --- /dev/null +++ b/sharing/linux/app/Targets.qml @@ -0,0 +1,36 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +Row { + id: shareTargetsRow + spacing: 10 + + Repeater { + model: backend.targets + + Rectangle { + width: 100 + height: 130 + color: "transparent" + + ColumnLayout { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + spacing: 5 + + ShareTarget { + deviceName: model.deviceName + iconSource: { + if (model.type === 2) + return "qrc:icons/laptop.svg" + if (model.type === 3) + return "qrc:icons/tablet.svg" + return "qrc:icons/smartphone.svg" + } + } + } + } + } +} diff --git a/sharing/linux/app/backend.cc b/sharing/linux/app/backend.cc new file mode 100644 index 00000000..cea2b667 --- /dev/null +++ b/sharing/linux/app/backend.cc @@ -0,0 +1,265 @@ +#include "sharing/linux/app/backend.h" + +#include +#include + +#include +#include + +namespace { + +QString ToQString(const std::string& value) { + return QString::fromStdString(value); +} + +} // namespace + +ShareTargetModel::ShareTargetModel(QObject* parent) + : QAbstractListModel(parent) {} + +int ShareTargetModel::rowCount(const QModelIndex& parent) const { + if (parent.isValid()) { + return 0; + } + return static_cast(targets_.size()); +} + +QVariant ShareTargetModel::data(const QModelIndex& index, int role) const { + if (!index.isValid() || index.row() < 0 || + index.row() >= static_cast(targets_.size())) { + return {}; + } + + const ShareTarget& target = targets_[index.row()]; + switch (role) { + case IdRole: + return QVariant::fromValue(target.id); + case DeviceNameRole: + return ToQString(target.device_name); + case TypeRole: + return target.type; + case IsIncomingRole: + return target.is_incoming; + case IsKnownRole: + return target.is_known; + case DeviceIdRole: + return ToQString(target.device_id); + case ForSelfShareRole: + return target.for_self_share; + case VendorIdRole: + return target.vendor_id; + case ReceiveDisabledRole: + return target.receive_disabled; + default: + return {}; + } +} + +QHash ShareTargetModel::roleNames() const { + return { + {IdRole, "id"}, + {DeviceNameRole, "deviceName"}, + {TypeRole, "type"}, + {IsIncomingRole, "isIncoming"}, + {IsKnownRole, "isKnown"}, + {DeviceIdRole, "deviceId"}, + {ForSelfShareRole, "forSelfShare"}, + {VendorIdRole, "vendorId"}, + {ReceiveDisabledRole, "receiveDisabled"}, + }; +} + +void ShareTargetModel::ApplyTarget(const ShareTarget& target) { + const int row = IndexOf(target.id); + if (row < 0) { + const int insert_row = static_cast(targets_.size()); + beginInsertRows(QModelIndex(), insert_row, insert_row); + targets_.push_back(target); + endInsertRows(); + return; + } + + targets_[row] = target; + const QModelIndex changed_index = index(row); + emit dataChanged(changed_index, changed_index, + {IdRole, DeviceNameRole, TypeRole, IsIncomingRole, + IsKnownRole, DeviceIdRole, ForSelfShareRole, VendorIdRole, + ReceiveDisabledRole}); +} + +void ShareTargetModel::RemoveTarget(int64_t target_id) { + const int row = IndexOf(target_id); + if (row < 0) { + return; + } + + beginRemoveRows(QModelIndex(), row, row); + targets_.erase(targets_.begin() + row); + endRemoveRows(); +} + +void ShareTargetModel::ResetTargets(const std::vector& targets) { + beginResetModel(); + targets_ = targets; + endResetModel(); +} + +int ShareTargetModel::IndexOf(int64_t target_id) const { + for (int i = 0; i < static_cast(targets_.size()); ++i) { + if (targets_[i].id == target_id) { + return i; + } + } + return -1; +} + +Backend::Backend(QObject* parent) : QObject(parent), targets_(this) { + try { + client_ = std::make_unique(this); + SetStatusText(QStringLiteral("Connected to nearby sharing daemon")); + } catch (const sdbus::Error& error) { + SetStatusText(QStringLiteral("D-Bus connection failed: %1") + .arg(QString::fromStdString(error.getMessage()))); + } +} + +Backend::~Backend() = default; + +void Backend::startReceive() { + RunCommand(QStringLiteral("Start receive"), + [this]() { return client_->StartReceive(); }); +} + +void Backend::stopReceive() { + RunCommand(QStringLiteral("Stop receive"), + [this]() { return client_->StopReceive(); }); +} + +void Backend::startDiscovery() { + RunCommand(QStringLiteral("Start discovery"), + [this]() { return client_->StartDiscovery(); }); +} + +void Backend::stopDiscovery() { + RunCommand(QStringLiteral("Stop discovery"), + [this]() { return client_->StopDiscovery(); }); +} + +void Backend::sendFile(qint64 share_target_id, const QString& path) { + RunCommand(QStringLiteral("Send file"), [this, share_target_id, path]() { + return client_->SendFile(share_target_id, path.toStdString()); + }); +} + +void Backend::accept(qint64 share_target_id) { + RunCommand(QStringLiteral("Accept transfer"), [this, share_target_id]() { + return client_->Accept(share_target_id); + }); +} + +void Backend::reject(qint64 share_target_id) { + RunCommand(QStringLiteral("Reject transfer"), [this, share_target_id]() { + return client_->Reject(share_target_id); + }); +} + +void Backend::cancel(qint64 share_target_id) { + RunCommand(QStringLiteral("Cancel transfer"), [this, share_target_id]() { + return client_->Cancel(share_target_id); + }); +} + +void Backend::OnTargetDiscovered(const ShareTarget& target) { + QMetaObject::invokeMethod( + this, [this, target]() { ApplyTarget(target); }, Qt::QueuedConnection); +} + +void Backend::OnTargetUpdated(const ShareTarget& target) { + QMetaObject::invokeMethod( + this, [this, target]() { ApplyTarget(target); }, Qt::QueuedConnection); +} + +void Backend::OnTargetLost(const ShareTarget& target) { + QMetaObject::invokeMethod( + this, [this, target]() { RemoveTarget(target); }, Qt::QueuedConnection); +} + +void Backend::OnIncomingTransfer(const std::string& direction, + const ShareTarget& target, + const Transfer& transfer) { + QMetaObject::invokeMethod( + this, + [this, target, transfer]() { + ApplyTarget(target); + emit incomingTransfer(target.id, ToQString(target.device_name), + ToQString(transfer.status)); + }, + Qt::QueuedConnection); +} + +void Backend::OnTransferUpdate(const std::string& direction, + const ShareTarget& target, + const Transfer& transfer) { + QMetaObject::invokeMethod( + this, + [this, target, transfer]() { + ApplyTarget(target); + emit transferUpdate(target.id, ToQString(target.device_name), + ToQString(transfer.status), transfer.progress); + }, + Qt::QueuedConnection); +} + +void Backend::OnStatusChanged(const Status& status) { + QMetaObject::invokeMethod( + this, [this, status]() { ApplyStatus(status); }, Qt::QueuedConnection); +} + +void Backend::ApplyTarget(const ShareTarget& target) { + targets_.ApplyTarget(target); +} + +void Backend::RemoveTarget(const ShareTarget& target) { + targets_.RemoveTarget(target.id); +} + +void Backend::ApplyStatus(const Status& status) { + status_ = status; + targets_.ResetTargets(status.targets); + emit statusChanged(); +} + +void Backend::ApplyCommandResult(const QString& command, + const std::tuple& result) { + const auto& [ok, message] = result; + const QString detail = + message.empty() ? (ok ? QStringLiteral("ok") : QStringLiteral("failed")) + : ToQString(message); + SetStatusText(QStringLiteral("%1: %2").arg(command, detail)); +} + +void Backend::RunCommand( + const QString& command, + const std::function()>& operation) { + if (!client_) { + SetStatusText(QStringLiteral("%1 failed: not connected").arg(command)); + return; + } + + try { + ApplyCommandResult(command, operation()); + } catch (const sdbus::Error& error) { + SetStatusText( + QStringLiteral("%1 failed: %2") + .arg(command, QString::fromStdString(error.getMessage()))); + } +} + + +void Backend::SetStatusText(const QString& text) { + if (status_text_ == text) { + return; + } + status_text_ = text; + emit statusTextChanged(); +} diff --git a/sharing/linux/app/backend.h b/sharing/linux/app/backend.h new file mode 100644 index 00000000..330ccd86 --- /dev/null +++ b/sharing/linux/app/backend.h @@ -0,0 +1,124 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "QtQmlIntegration/qqmlintegration.h" +#include "qtmetamacros.h" +#include "sharing/linux/app/nearby_sharing_dbus_client.h" + +class ShareTargetModel : public QAbstractListModel { + Q_OBJECT + + public: + using ShareTarget = nearby::sharing::linux::app::ShareTarget; + + enum Role { + IdRole = Qt::UserRole + 1, + DeviceNameRole, + TypeRole, + IsIncomingRole, + IsKnownRole, + DeviceIdRole, + ForSelfShareRole, + VendorIdRole, + ReceiveDisabledRole, + }; + + explicit ShareTargetModel(QObject* parent = nullptr); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role) const override; + QHash roleNames() const override; + + void ApplyTarget(const ShareTarget& target); + void RemoveTarget(int64_t target_id); + void ResetTargets(const std::vector& targets); + + private: + int IndexOf(int64_t target_id) const; + + std::vector targets_; +}; + +class Backend + : public QObject, + public nearby::sharing::linux::app::NearbySharingDbusClient::Observer { + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged) + Q_PROPERTY(bool receiveRegistered READ receiveRegistered NOTIFY statusChanged) + Q_PROPERTY( + bool discoveryRegistered READ discoveryRegistered NOTIFY statusChanged) + Q_PROPERTY(bool scanning READ scanning NOTIFY statusChanged) + Q_PROPERTY(bool transferring READ transferring NOTIFY statusChanged) + Q_PROPERTY(QAbstractListModel* targets READ targets CONSTANT) + + public: + explicit Backend(QObject* parent = nullptr); + ~Backend() override; + + QString statusText() const { return status_text_; } + bool receiveRegistered() const { return status_.receive_registered; } + bool discoveryRegistered() const { return status_.discovery_registered; } + bool scanning() const { return status_.is_scanning; } + bool transferring() const { return status_.is_transferring; } + QAbstractListModel* targets() { return &targets_; } + + Q_INVOKABLE void startReceive(); + Q_INVOKABLE void stopReceive(); + Q_INVOKABLE void startDiscovery(); + Q_INVOKABLE void stopDiscovery(); + Q_INVOKABLE void sendFile(qint64 share_target_id, const QString& path); + Q_INVOKABLE void accept(qint64 share_target_id); + Q_INVOKABLE void reject(qint64 share_target_id); + Q_INVOKABLE void cancel(qint64 share_target_id); + + signals: + void statusTextChanged(); + void statusChanged(); + void incomingTransfer(qint64 share_target_id, QString device_name, + QString status); + void transferUpdate(qint64 share_target_id, QString device_name, + QString status, double progress); + + private: + using Client = nearby::sharing::linux::app::NearbySharingDbusClient; + using ShareTarget = nearby::sharing::linux::app::ShareTarget; + using Status = nearby::sharing::linux::app::Status; + using Transfer = nearby::sharing::linux::app::Transfer; + + void OnTargetDiscovered(const ShareTarget& target) override; + void OnTargetUpdated(const ShareTarget& target) override; + void OnTargetLost(const ShareTarget& target) override; + void OnIncomingTransfer(const std::string& direction, + const ShareTarget& target, + const Transfer& transfer) override; + void OnTransferUpdate(const std::string& direction, const ShareTarget& target, + const Transfer& transfer) override; + void OnStatusChanged(const Status& status) override; + + void ApplyTarget(const ShareTarget& target); + void RemoveTarget(const ShareTarget& target); + void ApplyStatus(const Status& status); + void ApplyCommandResult(const QString& command, + const std::tuple& result); + void RunCommand( + const QString& command, + const std::function()>& operation); + void SetStatusText(const QString& text); + + QString status_text_; + Status status_; + ShareTargetModel targets_; + std::unique_ptr client_; + bool is_incoming_transfer_ = false; +}; diff --git a/sharing/linux/app/googlesans_var.ttf b/sharing/linux/app/googlesans_var.ttf new file mode 100644 index 00000000..6df533e0 Binary files /dev/null and b/sharing/linux/app/googlesans_var.ttf differ diff --git a/sharing/linux/app/icons/AdobeColor-My Color Theme.jpeg b/sharing/linux/app/icons/AdobeColor-My Color Theme.jpeg new file mode 100644 index 00000000..b0c0ab29 Binary files /dev/null and b/sharing/linux/app/icons/AdobeColor-My Color Theme.jpeg differ diff --git a/sharing/linux/app/icons/file.svg b/sharing/linux/app/icons/file.svg new file mode 100644 index 00000000..71d24d20 --- /dev/null +++ b/sharing/linux/app/icons/file.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/sharing/linux/app/icons/laptop.svg b/sharing/linux/app/icons/laptop.svg new file mode 100644 index 00000000..51edde5f --- /dev/null +++ b/sharing/linux/app/icons/laptop.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/sharing/linux/app/icons/smartphone.svg b/sharing/linux/app/icons/smartphone.svg new file mode 100644 index 00000000..11eb5b84 --- /dev/null +++ b/sharing/linux/app/icons/smartphone.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/sharing/linux/app/icons/tablet.svg b/sharing/linux/app/icons/tablet.svg new file mode 100644 index 00000000..e1f66e16 --- /dev/null +++ b/sharing/linux/app/icons/tablet.svg @@ -0,0 +1,12 @@ + + + + ic_fluent_tablet_24_regular + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/sharing/linux/app/icons/up_file.svg b/sharing/linux/app/icons/up_file.svg new file mode 100644 index 00000000..5ea1b168 --- /dev/null +++ b/sharing/linux/app/icons/up_file.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/sharing/linux/app/main.cc b/sharing/linux/app/main.cc new file mode 100644 index 00000000..eb586aa8 --- /dev/null +++ b/sharing/linux/app/main.cc @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "backend.h" + +namespace { + +constexpr char kQmlHotReloadEnv[] = "NEARBY_QML_HOT_RELOAD"; +constexpr char kQmlSourceDirEnv[] = "NEARBY_QML_SOURCE_DIR"; +constexpr char kDefaultQmlSourceDir[] = + "/home/lasan/Dev/nearby_latest/sharing/linux/app"; + +bool IsHotReloadEnabled() { + const QByteArray value = qgetenv(kQmlHotReloadEnv); + return value == "1" || value.toLower() == "true"; +} + +QDir QmlSourceDir() { + const QByteArray configured_dir = qgetenv(kQmlSourceDirEnv); + if (!configured_dir.isEmpty()) { + return QDir(QString::fromLocal8Bit(configured_dir)); + } + return QDir(QString::fromUtf8(kDefaultQmlSourceDir)); +} + +void WatchQmlFiles(QFileSystemWatcher& watcher, const QDir& source_dir) { + const QStringList existing_files = watcher.files(); + if (!existing_files.isEmpty()) { + watcher.removePaths(existing_files); + } + + const QStringList files = + source_dir.entryList(QStringList() << "*.qml", QDir::Files, QDir::Name); + for (const QString& file : files) { + watcher.addPath(source_dir.absoluteFilePath(file)); + } +} + +void DestroyRootObjects(QQmlApplicationEngine& engine) { + const QList root_objects = engine.rootObjects(); + for (QObject* object : root_objects) { + object->deleteLater(); + } +} + +bool ReloadInnerContent(QQmlApplicationEngine& engine) { + const QList root_objects = engine.rootObjects(); + if (root_objects.isEmpty()) { + return false; + } + + QObject* root = root_objects.constFirst(); + return QMetaObject::invokeMethod(root, "reloadInnerContent"); +} + +bool RequiresFullReload(const QFileInfo& changed_file) { + return changed_file.fileName() == QStringLiteral("main.qml"); +} + +} // namespace + +int main(int argc, char* argv[]) { + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + const int fontId = QFontDatabase::addApplicationFont(":/googlesans_var.ttf"); + + if (fontId != -1) { + const QStringList fontFamilies = + QFontDatabase::applicationFontFamilies(fontId); + if (!fontFamilies.isEmpty()) { + const QFont defaultFont(fontFamilies.at(0)); + app.setFont(defaultFont); + } + } + + if (IsHotReloadEnabled()) { + const QDir qml_source_dir = QmlSourceDir(); + const QUrl source_url = + QUrl::fromLocalFile(qml_source_dir.absoluteFilePath("main.qml")); + QFileSystemWatcher watcher; + QTimer reload_timer; + reload_timer.setSingleShot(true); + reload_timer.setInterval(75); + QString changed_path; + + const auto fullReload = [&engine, &watcher, qml_source_dir, source_url]() { + WatchQmlFiles(watcher, qml_source_dir); + DestroyRootObjects(engine); + engine.clearComponentCache(); + engine.load(source_url); + }; + + const auto reload = [&engine, &watcher, qml_source_dir, source_url, + &changed_path, &fullReload]() { + WatchQmlFiles(watcher, qml_source_dir); + const QFileInfo changed_file(changed_path); + + if (RequiresFullReload(changed_file) || engine.rootObjects().isEmpty()) { + fullReload(); + return; + } + + engine.clearComponentCache(); + if (!ReloadInnerContent(engine)) { + fullReload(); + } + }; + + QObject::connect(&reload_timer, &QTimer::timeout, &app, reload); + QObject::connect(&watcher, &QFileSystemWatcher::fileChanged, &app, + [&reload_timer, &changed_path](const QString& path) { + changed_path = path; + reload_timer.start(); + }); + QObject::connect(&watcher, &QFileSystemWatcher::directoryChanged, &app, + [&reload_timer, &changed_path](const QString& path) { + changed_path = path; + reload_timer.start(); + }); + + watcher.addPath(qml_source_dir.absolutePath()); + fullReload(); + + return app.exec(); + } + // 1. Create the backend instance in C++ + Backend backend; + + // 2. Inject it into the QML root context + engine.rootContext()->setContextProperty("backend", &backend); + // Loaded via the qrc scheme since it's compiled into the binary + const QUrl url(QStringLiteral("qrc:/main.qml")); + + QObject::connect( + &engine, &QQmlApplicationEngine::objectCreated, &app, + [url](QObject* obj, const QUrl& objUrl) { + if (!obj && url == objUrl) QCoreApplication::exit(-1); + }, + Qt::QueuedConnection); + + engine.load(url); + + return app.exec(); +} diff --git a/sharing/linux/app/main.qml b/sharing/linux/app/main.qml new file mode 100644 index 00000000..6874b0c1 --- /dev/null +++ b/sharing/linux/app/main.qml @@ -0,0 +1,31 @@ +import QtQuick +import QtQuick.Window + +Window { + id: root + width: 640 + height: 480 + visible: true + title: qsTr("QuickShare") + color: "#DCF5FF" + + + FontLoader { + id: googlesans + source: "qrc:/googlesans_var.ttf" + } + + Loader { + id: contentLoader + anchors.fill: parent + source: "AppContent.qml" + } + + function reloadInnerContent() { + const nextSource = "AppContent.qml?rev=" + Date.now() + contentLoader.active = false + contentLoader.source = "" + contentLoader.active = true + contentLoader.source = nextSource + } +} diff --git a/sharing/linux/app/nearby_sharing_dbus_client.cc b/sharing/linux/app/nearby_sharing_dbus_client.cc new file mode 100644 index 00000000..d84e3570 --- /dev/null +++ b/sharing/linux/app/nearby_sharing_dbus_client.cc @@ -0,0 +1,214 @@ +#include "sharing/linux/app/nearby_sharing_dbus_client.h" + +#include + +namespace nearby::sharing::linux::app { +namespace { + +template +T GetField(const DbusDictionary& map, const std::string& key, + T default_value = T{}) { + auto it = map.find(key); + if (it == map.end()) { + return default_value; + } + + try { + return it->second.get(); + } catch (const sdbus::Error&) { + return default_value; + } +} + +template +std::optional GetOptionalField(const DbusDictionary& map, + const std::string& key) { + auto it = map.find(key); + if (it == map.end()) { + return std::nullopt; + } + + try { + return it->second.get(); + } catch (const sdbus::Error&) { + return std::nullopt; + } +} + +double GetProgress(const DbusDictionary& map) { + if (auto progress = GetOptionalField(map, "progress")) { + return *progress; + } + if (auto progress = GetOptionalField(map, "progress")) { + return *progress; + } + if (auto progress = GetOptionalField(map, "progress")) { + return *progress; + } + return 0; +} + +} // namespace + +NearbySharingDbusClient::NearbySharingDbusClient(Observer* observer) + : sdbus::ProxyInterfaces( + sdbus::ServiceName("com.google.nearby.sharing"), + sdbus::ObjectPath("/com/google/nearby/sharing")), + observer_(observer) { + registerProxy(); +} + +NearbySharingDbusClient::~NearbySharingDbusClient() { + unregisterProxy(); +} + +std::tuple NearbySharingDbusClient::StartReceive() { + return com::google::nearby::sharing_proxy::StartReceive(); +} + +std::tuple NearbySharingDbusClient::StopReceive() { + return com::google::nearby::sharing_proxy::StopReceive(); +} + +std::tuple NearbySharingDbusClient::StartDiscovery() { + return com::google::nearby::sharing_proxy::StartDiscovery(); +} + +std::tuple NearbySharingDbusClient::StopDiscovery() { + return com::google::nearby::sharing_proxy::StopDiscovery(); +} + +std::tuple NearbySharingDbusClient::SendFile( + int64_t share_target_id, const std::string& path) { + return com::google::nearby::sharing_proxy::SendFile(share_target_id, path); +} + +std::tuple NearbySharingDbusClient::Accept( + int64_t share_target_id) { + return com::google::nearby::sharing_proxy::Accept(share_target_id); +} + +std::tuple NearbySharingDbusClient::Reject( + int64_t share_target_id) { + return com::google::nearby::sharing_proxy::Reject(share_target_id); +} + +std::tuple NearbySharingDbusClient::Cancel( + int64_t share_target_id) { + return com::google::nearby::sharing_proxy::Cancel(share_target_id); +} + +ShareTarget NearbySharingDbusClient::ConvertToShareTarget( + const DbusDictionary& map) { + ShareTarget target; + target.id = GetField(map, "id"); + target.device_name = GetField(map, "device_name"); + target.type = GetField(map, "type"); + target.is_incoming = GetField(map, "is_incoming"); + target.is_known = GetField(map, "is_known"); + target.device_id = GetField(map, "device_id"); + target.for_self_share = GetField(map, "for_self_share"); + target.vendor_id = GetField(map, "vendor_id"); + target.receive_disabled = GetField(map, "receive_disabled"); + return target; +} + +Transfer NearbySharingDbusClient::ConvertToTransfer(const DbusDictionary& map) { + Transfer transfer; + transfer.status = GetField(map, "status"); + transfer.progress = GetProgress(map); + transfer.transferred_bytes = GetField(map, "transferred_bytes"); + transfer.total_bytes = GetField(map, "total_bytes"); + transfer.transfer_speed = GetField(map, "transfer_speed"); + transfer.estimated_time_remaining = + GetField(map, "estimated_time_remaining"); + transfer.total_attachments_count = + GetField(map, "total_attachments_count"); + transfer.transferred_attachments_count = + GetField(map, "transferred_attachments_count"); + transfer.is_final_status = GetField(map, "is_final_status"); + transfer.is_self_share = GetField(map, "is_self_share"); + transfer.binding_id = GetField(map, "binding_id"); + transfer.token = GetOptionalField(map, "token"); + transfer.in_progress_attachment_id = + GetOptionalField(map, "in_progress_attachment_id"); + transfer.in_progress_attachment_transferred_bytes = GetOptionalField( + map, "in_progress_attachment_transferred_bytes"); + transfer.in_progress_attachment_total_bytes = + GetOptionalField(map, "in_progress_attachment_total_bytes"); + return transfer; +} + +Status NearbySharingDbusClient::ConvertToStatus(const DbusDictionary& map) { + Status status; + status.receive_registered = GetField(map, "receive_registered"); + status.discovery_registered = GetField(map, "discovery_registered"); + status.is_transferring = GetField(map, "is_transferring"); + status.is_scanning = GetField(map, "is_scanning"); + status.bluetooth_present = GetField(map, "bluetooth_present"); + status.bluetooth_powered = GetField(map, "bluetooth_powered"); + status.lan_connected = GetField(map, "lan_connected"); + + auto raw_targets = + GetOptionalField>(map, "targets"); + if (raw_targets.has_value()) { + status.targets.reserve(raw_targets->size()); + for (const auto& target : *raw_targets) { + status.targets.push_back(ConvertToShareTarget(target)); + } + } + + return status; +} + +void NearbySharingDbusClient::onTargetDiscovered( + const DbusDictionary& share_target) { + if (observer_ == nullptr) { + return; + } + observer_->OnTargetDiscovered(ConvertToShareTarget(share_target)); +} + +void NearbySharingDbusClient::onTargetUpdated( + const DbusDictionary& share_target) { + if (observer_ == nullptr) { + return; + } + observer_->OnTargetUpdated(ConvertToShareTarget(share_target)); +} + +void NearbySharingDbusClient::onTargetLost(const DbusDictionary& share_target) { + if (observer_ == nullptr) { + return; + } + observer_->OnTargetLost(ConvertToShareTarget(share_target)); +} + +void NearbySharingDbusClient::onIncomingTransfer( + const std::string& direction, const DbusDictionary& share_target, + const DbusDictionary& transfer) { + if (observer_ == nullptr) { + return; + } + observer_->OnIncomingTransfer(direction, ConvertToShareTarget(share_target), + ConvertToTransfer(transfer)); +} + +void NearbySharingDbusClient::onTransferUpdate( + const std::string& direction, const DbusDictionary& share_target, + const DbusDictionary& transfer) { + if (observer_ == nullptr) { + return; + } + observer_->OnTransferUpdate(direction, ConvertToShareTarget(share_target), + ConvertToTransfer(transfer)); +} + +void NearbySharingDbusClient::onStatusChanged(const DbusDictionary& status) { + if (observer_ == nullptr) { + return; + } + observer_->OnStatusChanged(ConvertToStatus(status)); +} + +} // namespace nearby::sharing::linux::app diff --git a/sharing/linux/app/nearby_sharing_dbus_client.h b/sharing/linux/app/nearby_sharing_dbus_client.h new file mode 100644 index 00000000..65fca9ed --- /dev/null +++ b/sharing/linux/app/nearby_sharing_dbus_client.h @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +#include "sharing/linux/daemon/nearby_sharing_client.h" + +namespace nearby::sharing::linux::app { + +using DbusDictionary = std::map; + +struct ShareTarget { + int64_t id = 0; + std::string device_name; + int32_t type = 0; + bool is_incoming = false; + bool is_known = false; + std::string device_id; + bool for_self_share = false; + int32_t vendor_id = 0; + bool receive_disabled = false; +}; + +struct Transfer { + std::string status; + double progress = 0; + int64_t transferred_bytes = 0; + int64_t total_bytes = 0; + int64_t transfer_speed = 0; + int64_t estimated_time_remaining = 0; + int32_t total_attachments_count = 0; + int32_t transferred_attachments_count = 0; + bool is_final_status = false; + bool is_self_share = false; + std::string binding_id; + std::optional token; + std::optional in_progress_attachment_id; + std::optional in_progress_attachment_transferred_bytes; + std::optional in_progress_attachment_total_bytes; +}; + +struct Status { + bool receive_registered = false; + bool discovery_registered = false; + bool is_transferring = false; + bool is_scanning = false; + bool bluetooth_present = false; + bool bluetooth_powered = false; + bool lan_connected = false; + std::vector targets; +}; + +class NearbySharingDbusClient + : public sdbus::ProxyInterfaces { + public: + struct Observer { + virtual ~Observer() = default; + virtual void OnTargetDiscovered(const ShareTarget& target) {} + virtual void OnTargetUpdated(const ShareTarget& target) {} + virtual void OnTargetLost(const ShareTarget& target) {} + virtual void OnIncomingTransfer(const std::string& direction, + const ShareTarget& target, + const Transfer& transfer) {} + virtual void OnTransferUpdate(const std::string& direction, + const ShareTarget& target, + const Transfer& transfer) {} + virtual void OnStatusChanged(const Status& status) {} + }; + + explicit NearbySharingDbusClient(Observer* observer); + ~NearbySharingDbusClient(); + + NearbySharingDbusClient(const NearbySharingDbusClient&) = delete; + NearbySharingDbusClient& operator=(const NearbySharingDbusClient&) = delete; + + std::tuple StartReceive(); + std::tuple StopReceive(); + std::tuple StartDiscovery(); + std::tuple StopDiscovery(); + std::tuple SendFile(int64_t share_target_id, + const std::string& path); + std::tuple Accept(int64_t share_target_id); + std::tuple Reject(int64_t share_target_id); + std::tuple Cancel(int64_t share_target_id); + + static ShareTarget ConvertToShareTarget(const DbusDictionary& map); + static Transfer ConvertToTransfer(const DbusDictionary& map); + static Status ConvertToStatus(const DbusDictionary& map); + + private: + void onTargetDiscovered(const DbusDictionary& share_target) override; + void onTargetUpdated(const DbusDictionary& share_target) override; + void onTargetLost(const DbusDictionary& share_target) override; + void onIncomingTransfer(const std::string& direction, + const DbusDictionary& share_target, + const DbusDictionary& transfer) override; + void onTransferUpdate(const std::string& direction, + const DbusDictionary& share_target, + const DbusDictionary& transfer) override; + void onStatusChanged(const DbusDictionary& status) override; + + Observer* observer_ = nullptr; +}; + +} // namespace nearby::sharing::linux::app diff --git a/sharing/linux/app/resources.qrc b/sharing/linux/app/resources.qrc new file mode 100644 index 00000000..67ce68e3 --- /dev/null +++ b/sharing/linux/app/resources.qrc @@ -0,0 +1,17 @@ + + + main.qml + AppContent.qml + Sidebar.qml + Drop.qml + ShareTarget.qml + IncomingShare.qml + Targets.qml + googlesans_var.ttf + icons/file.svg + icons/up_file.svg + icons/tablet.svg + icons/laptop.svg + icons/smartphone.svg + + diff --git a/sharing/linux/daemon/BUILD b/sharing/linux/daemon/BUILD new file mode 100644 index 00000000..2f48b054 --- /dev/null +++ b/sharing/linux/daemon/BUILD @@ -0,0 +1,59 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:cc_binary.bzl", "cc_binary") +load("@rules_cc//cc:cc_test.bzl", "cc_test") + +cc_binary( + name = "nearby_sharing_daemon", + srcs = ["main.cc"], + deps = [ + ":nearby_sharing_dbus_service", + "//connections/implementation/flags:connections_flags", + "//internal/flags:nearby_flags", + "//sharing:nearby_sharing_service", + "//sharing/flags/generated:generated_flags", + "//sharing/linux:linux_sharing_platform", + "//sharing/proto:enums_cc_proto", + "@com_google_absl//absl/time", + "@sdbus_cpp", + ], +) + +cc_library( + name = "nearby_sharing_dbus_client_glue", + hdrs = ["nearby_sharing_client.h"], + visibility = ["//visibility:public"], + deps = ["@sdbus_cpp"], +) + +cc_library( + name = "nearby_sharing_dbus_service", + srcs = ["nearby_sharing_dbus_service.cc"], + hdrs = [ + "nearby_sharing_dbus_service.h", + "nearby_sharing_server.h", + "nearby_sharing_client.h", + ], + deps = [ + "//internal/base:file_path", + "//sharing:attachments", + "//sharing:nearby_sharing_service", + "//sharing:transfer_metadata", + "//sharing:types", + "@com_google_absl//absl/synchronization", + "@sdbus_cpp", + ], +) + +cc_test( + name = "nearby_sharing_dbus_service_test", + srcs = ["nearby_sharing_dbus_service_test.cc"], + deps = [ + ":nearby_sharing_dbus_service", + "//internal/platform/implementation/linux:linux", + "//sharing:attachments", + "//sharing:transfer_metadata", + "//sharing:types", + "@com_google_googletest//:gtest_main", + "@sdbus_cpp", + ], +) diff --git a/sharing/linux/daemon/main.cc b/sharing/linux/daemon/main.cc new file mode 100644 index 00000000..45efdf16 --- /dev/null +++ b/sharing/linux/daemon/main.cc @@ -0,0 +1,165 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "absl/time/time.h" +#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "internal/flags/nearby_flags.h" +#include "sharing/flags/generated/nearby_sharing_feature_flags.h" +#include "sharing/linux/daemon/nearby_sharing_dbus_service.h" +#include "sharing/linux/nearby_noop_analytics_recorder.h" +#include "sharing/linux/platform/linux_sharing_platform.h" +#include "sharing/nearby_sharing_service.h" +#include "sharing/nearby_sharing_service_factory.h" +#include "sharing/nearby_sharing_settings.h" +#include "sharing/proto/enums.pb.h" + +namespace nearby::sharing::linux { +namespace { + +std::atomic g_interrupted = false; +sdbus::IConnection* g_bus = nullptr; + +void HandleSignal(int signal) { + static_cast(signal); + g_interrupted = true; + if (g_bus != nullptr) { + g_bus->leaveEventLoop(); + } +} + +std::string GetHostname() { + char hostname[256] = {}; + if (gethostname(hostname, sizeof(hostname)) == 0 && hostname[0] != '\0') { + return std::string(hostname); + } + return "LinuxShare"; +} + +std::string GetDeviceName(int argc, char** argv) { + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + if (arg == "--name" && i + 1 < argc) { + return argv[i + 1]; + } + } + const char* env_name = std::getenv("NEARBY_DEVICE_NAME"); + if (env_name != nullptr && *env_name != '\0') { + return env_name; + } + return GetHostname(); +} + +template +NearbySharingService::StatusCodes WaitForStatus(Invoker invoker) { + std::mutex mutex; + std::condition_variable cv; + std::optional status; + invoker([&](NearbySharingService::StatusCodes callback_status) { + { + std::lock_guard lock(mutex); + status = callback_status; + } + cv.notify_one(); + }); + + std::unique_lock lock(mutex); + cv.wait(lock, [&] { return status.has_value(); }); + return *status; +} + +void ConfigureFlags() { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::sharing::config_package_nearby::nearby_sharing_feature:: + kEnableBleForTransfer, + true); + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableBleL2cap, + true); + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kRefactorBleL2cap, + true); +} + +bool ConfigureSharingService(NearbySharingService& service, + const std::string& device_name) { + service.GetSettings()->SetDataUsage(proto::WIFI_ONLY_DATA_USAGE); + service.GetSettings()->SetDeviceName( + device_name, [](DeviceNameValidationResult validation_result) { + static_cast(validation_result); + }); + auto status = WaitForStatus([&](auto callback) { + service.SetVisibility(proto::DEVICE_VISIBILITY_EVERYONE, + absl::ZeroDuration(), std::move(callback)); + }); + if (status != NearbySharingService::StatusCodes::kOk) { + std::cerr << "SetVisibility failed: " + << NearbySharingService::StatusCodeToString(status) << std::endl; + return false; + } + return true; +} + +} // namespace +} // namespace nearby::sharing::linux + +int main(int argc, char** argv) { + signal(SIGINT, nearby::sharing::linux::HandleSignal); + signal(SIGTERM, nearby::sharing::linux::HandleSignal); + + nearby::sharing::linux::ConfigureFlags(); + + const std::string device_name = + nearby::sharing::linux::GetDeviceName(argc, argv); + auto analytics_recorder = nearby::sharing::linux::NoOpAnalyticsRecorder(); + auto linux_platform = + nearby::sharing::linux::LinuxSharingPlatform(device_name); + auto* service = + nearby::sharing::NearbySharingServiceFactory::GetInstance() + ->CreateSharingService(linux_platform, &analytics_recorder, + /*event_logger=*/nullptr, + /*supports_file_sync=*/false); + if (service == nullptr) { + std::cerr << "failed to create NearbySharingService" << std::endl; + return 1; + } + if (!nearby::sharing::linux::ConfigureSharingService(*service, device_name)) { + nearby::sharing::linux::WaitForStatus( + [&](auto callback) { service->Shutdown(std::move(callback)); }); + return 1; + } + + auto bus = sdbus::createSessionBusConnection( + sdbus::ServiceName("com.google.nearby.sharing")); + nearby::sharing::linux::g_bus = bus.get(); + auto object = sdbus::createObject( + *bus, sdbus::ObjectPath("/com/google/nearby/sharing")); + nearby::sharing::linux::NearbySharingDbusService dbus_service( + *object, *service, [&bus]() { bus->leaveEventLoop(); }); + dbus_service.EmitStatusChanged(); + + try { + bus->enterEventLoop(); + } catch (const sdbus::Error& error) { + std::cerr << "D-Bus event loop failed: " << error.getName() << ": " + << error.getMessage() << std::endl; + dbus_service.ShutdownService(); + nearby::sharing::linux::g_bus = nullptr; + return 1; + } + + dbus_service.ShutdownService(); + nearby::sharing::linux::g_bus = nullptr; + return nearby::sharing::linux::g_interrupted ? 130 : 0; +} diff --git a/sharing/linux/daemon/nearby_sharing_client.h b/sharing/linux/daemon/nearby_sharing_client.h new file mode 100644 index 00000000..d67af2c3 --- /dev/null +++ b/sharing/linux/daemon/nearby_sharing_client.h @@ -0,0 +1,174 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__sharing_linux_daemon_nearby_sharing_client_h__proxy__H__ +#define __sdbuscpp__sharing_linux_daemon_nearby_sharing_client_h__proxy__H__ + +#include +#include +#include + +namespace com { +namespace google { +namespace nearby { + +class sharing_proxy { + public: + static constexpr const char* INTERFACE_NAME = "com.google.nearby.sharing"; + + protected: + sharing_proxy(sdbus::IProxy& proxy) : m_proxy(proxy) {} + + sharing_proxy(const sharing_proxy&) = delete; + sharing_proxy& operator=(const sharing_proxy&) = delete; + sharing_proxy(sharing_proxy&&) = delete; + sharing_proxy& operator=(sharing_proxy&&) = delete; + + ~sharing_proxy() = default; + + void registerProxy() { + m_proxy.uponSignal("TargetDiscovered") + .onInterface(INTERFACE_NAME) + .call( + [this](const std::map& share_target) { + this->onTargetDiscovered(share_target); + }); + m_proxy.uponSignal("TargetUpdated") + .onInterface(INTERFACE_NAME) + .call( + [this](const std::map& share_target) { + this->onTargetUpdated(share_target); + }); + m_proxy.uponSignal("TargetLost") + .onInterface(INTERFACE_NAME) + .call( + [this](const std::map& share_target) { + this->onTargetLost(share_target); + }); + m_proxy.uponSignal("IncomingTransfer") + .onInterface(INTERFACE_NAME) + .call([this](const std::string& direction, + const std::map& share_target, + const std::map& transfer) { + this->onIncomingTransfer(direction, share_target, transfer); + }); + m_proxy.uponSignal("TransferUpdate") + .onInterface(INTERFACE_NAME) + .call([this](const std::string& direction, + const std::map& share_target, + const std::map& transfer) { + this->onTransferUpdate(direction, share_target, transfer); + }); + m_proxy.uponSignal("StatusChanged") + .onInterface(INTERFACE_NAME) + .call([this](const std::map& status) { + this->onStatusChanged(status); + }); + } + + virtual void onTargetDiscovered( + const std::map& share_target) = 0; + virtual void onTargetUpdated( + const std::map& share_target) = 0; + virtual void onTargetLost( + const std::map& share_target) = 0; + virtual void onIncomingTransfer( + const std::string& direction, + const std::map& share_target, + const std::map& transfer) = 0; + virtual void onTransferUpdate( + const std::string& direction, + const std::map& share_target, + const std::map& transfer) = 0; + virtual void onStatusChanged( + const std::map& status) = 0; + + public: + std::tuple StartReceive() { + std::tuple result; + m_proxy.callMethod("StartReceive") + .onInterface(INTERFACE_NAME) + .storeResultsTo(result); + return result; + } + + std::tuple StopReceive() { + std::tuple result; + m_proxy.callMethod("StopReceive") + .onInterface(INTERFACE_NAME) + .storeResultsTo(result); + return result; + } + + std::tuple StartDiscovery() { + std::tuple result; + m_proxy.callMethod("StartDiscovery") + .onInterface(INTERFACE_NAME) + .storeResultsTo(result); + return result; + } + + std::tuple StopDiscovery() { + std::tuple result; + m_proxy.callMethod("StopDiscovery") + .onInterface(INTERFACE_NAME) + .storeResultsTo(result); + return result; + } + + std::tuple SendFile(const int64_t& share_target_id, + const std::string& path) { + std::tuple result; + m_proxy.callMethod("SendFile") + .onInterface(INTERFACE_NAME) + .withArguments(share_target_id, path) + .storeResultsTo(result); + return result; + } + + std::tuple Accept(const int64_t& share_target_id) { + std::tuple result; + m_proxy.callMethod("Accept") + .onInterface(INTERFACE_NAME) + .withArguments(share_target_id) + .storeResultsTo(result); + return result; + } + + std::tuple Reject(const int64_t& share_target_id) { + std::tuple result; + m_proxy.callMethod("Reject") + .onInterface(INTERFACE_NAME) + .withArguments(share_target_id) + .storeResultsTo(result); + return result; + } + + std::tuple Cancel(const int64_t& share_target_id) { + std::tuple result; + m_proxy.callMethod("Cancel") + .onInterface(INTERFACE_NAME) + .withArguments(share_target_id) + .storeResultsTo(result); + return result; + } + + std::tuple Shutdown() { + std::tuple result; + m_proxy.callMethod("Shutdown") + .onInterface(INTERFACE_NAME) + .storeResultsTo(result); + return result; + } + + private: + sdbus::IProxy& m_proxy; +}; + +} // namespace nearby +} // namespace google +} // namespace com + +#endif diff --git a/sharing/linux/daemon/nearby_sharing_dbus_interface.xml b/sharing/linux/daemon/nearby_sharing_dbus_interface.xml new file mode 100644 index 00000000..42773eb2 --- /dev/null +++ b/sharing/linux/daemon/nearby_sharing_dbus_interface.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sharing/linux/daemon/nearby_sharing_dbus_service.cc b/sharing/linux/daemon/nearby_sharing_dbus_service.cc new file mode 100644 index 00000000..9c7f3260 --- /dev/null +++ b/sharing/linux/daemon/nearby_sharing_dbus_service.cc @@ -0,0 +1,441 @@ +#include "sharing/linux/daemon/nearby_sharing_dbus_service.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "internal/base/file_path.h" +#include "sharing/advertisement.h" +#include "sharing/file_attachment.h" + +namespace nearby::sharing::linux { +namespace { + +std::string StatusCodeToString(NearbySharingService::StatusCodes status) { + return NearbySharingService::StatusCodeToString(status); +} + +std::tuple StatusResult( + NearbySharingService::StatusCodes status) { + return {status == NearbySharingService::StatusCodes::kOk, + StatusCodeToString(status)}; +} + +std::unique_ptr CreateFileAttachments( + const std::string& file_path) { + AttachmentContainer::Builder builder; + builder.AddFileAttachment(FileAttachment(FilePath(file_path))); + return builder.Build(); +} + +template +NearbySharingService::StatusCodes WaitForStatus(Invoker invoker) { + std::mutex mutex; + std::condition_variable cv; + std::optional status; + invoker([&](NearbySharingService::StatusCodes callback_status) { + { + std::lock_guard lock(mutex); + status = callback_status; + } + cv.notify_one(); + }); + + std::unique_lock lock(mutex); + cv.wait(lock, [&] { return status.has_value(); }); + return *status; +} + +} // namespace + +DbusDictionary ShareTargetToDbus(const ShareTarget& share_target) { + DbusDictionary result; + result.emplace("id", sdbus::Variant(share_target.id)); + result.emplace("device_name", sdbus::Variant(share_target.device_name)); + result.emplace("type", + sdbus::Variant(static_cast(share_target.type))); + result.emplace("is_incoming", sdbus::Variant(share_target.is_incoming)); + result.emplace("is_known", sdbus::Variant(share_target.is_known)); + result.emplace("device_id", sdbus::Variant(share_target.device_id)); + result.emplace("for_self_share", sdbus::Variant(share_target.for_self_share)); + result.emplace("vendor_id", + sdbus::Variant(static_cast(share_target.vendor_id))); + result.emplace("receive_disabled", + sdbus::Variant(share_target.receive_disabled)); + return result; +} + +DbusDictionary TransferMetadataToDbus( + const TransferMetadata& transfer_metadata, + const AttachmentContainer& attachment_container) { + DbusDictionary result; + result.emplace("status", sdbus::Variant(TransferMetadata::StatusToString( + transfer_metadata.status()))); + result.emplace( + "progress", + sdbus::Variant(static_cast(transfer_metadata.progress()))); + result.emplace("transferred_bytes", + sdbus::Variant(static_cast( + transfer_metadata.transferred_bytes()))); + result.emplace( + "total_bytes", + sdbus::Variant(attachment_container.GetTotalAttachmentsSize())); + result.emplace( + "transfer_speed", + sdbus::Variant(static_cast(transfer_metadata.transfer_speed()))); + result.emplace("estimated_time_remaining", + sdbus::Variant(static_cast( + transfer_metadata.estimated_time_remaining()))); + result.emplace("total_attachments_count", + sdbus::Variant(transfer_metadata.total_attachments_count())); + result.emplace( + "transferred_attachments_count", + sdbus::Variant(transfer_metadata.transferred_attachments_count())); + result.emplace("is_final_status", + sdbus::Variant(transfer_metadata.is_final_status())); + result.emplace("is_self_share", + sdbus::Variant(transfer_metadata.is_self_share())); + result.emplace("binding_id", sdbus::Variant(transfer_metadata.binding_id())); + + if (transfer_metadata.token().has_value()) { + result.emplace("token", sdbus::Variant(*transfer_metadata.token())); + } + if (transfer_metadata.in_progress_attachment_id().has_value()) { + result.emplace( + "in_progress_attachment_id", + sdbus::Variant(*transfer_metadata.in_progress_attachment_id())); + } + if (transfer_metadata.in_progress_attachment_transferred_bytes() + .has_value()) { + result.emplace( + "in_progress_attachment_transferred_bytes", + sdbus::Variant(static_cast( + *transfer_metadata.in_progress_attachment_transferred_bytes()))); + } + if (transfer_metadata.in_progress_attachment_total_bytes().has_value()) { + result.emplace( + "in_progress_attachment_total_bytes", + sdbus::Variant(static_cast( + *transfer_metadata.in_progress_attachment_total_bytes()))); + } + return result; +} + +NearbySharingDbusService::TransferCallback::TransferCallback( + NearbySharingDbusService& service, bool receive_mode) + : service_(service), receive_mode_(receive_mode) {} + +void NearbySharingDbusService::TransferCallback::OnTransferUpdate( + const ShareTarget& share_target, + const AttachmentContainer& attachment_container, + const TransferMetadata& transfer_metadata) { + service_.OnTransferUpdate(receive_mode_, share_target, attachment_container, + transfer_metadata); +} + +NearbySharingDbusService::NearbySharingDbusService( + sdbus::IObject& object, NearbySharingService& service, + QuitCallback quit_callback) + : sharing_adaptor(object), + service_(service), + quit_callback_(std::move(quit_callback)), + send_transfer_callback_(*this, /*receive_mode=*/false), + receive_transfer_callback_(*this, /*receive_mode=*/true) { + registerAdaptor(); + service_.AddObserver(this); +} + +NearbySharingDbusService::~NearbySharingDbusService() { + service_.RemoveObserver(this); + ShutdownService(); +} + +void NearbySharingDbusService::EmitStatusChanged() { + emitStatusChanged(StatusToDbus()); +} + +void NearbySharingDbusService::ShutdownService() { + bool stop_receive = false; + bool stop_discovery = false; + { + absl::MutexLock lock(lock_); + if (shutdown_) { + return; + } + shutdown_ = true; + stop_receive = receive_registered_; + stop_discovery = discovery_registered_; + receive_registered_ = false; + discovery_registered_ = false; + targets_.clear(); + } + + if (stop_receive) { + WaitForStatus([&](auto callback) { + service_.UnregisterReceiveSurface(&receive_transfer_callback_, + std::move(callback)); + }); + } + if (stop_discovery) { + WaitForStatus([&](auto callback) { + service_.UnregisterSendSurface(&send_transfer_callback_, + std::move(callback)); + }); + } + WaitForStatus([&](auto callback) { service_.Shutdown(std::move(callback)); }); + EmitStatusChanged(); +} + +std::tuple NearbySharingDbusService::StartReceive() { + { + absl::MutexLock lock(lock_); + if (receive_registered_) { + return {true, "receive already started"}; + } + } + + auto result = InvokeStatusCommand([&](auto callback) { + service_.RegisterReceiveSurface( + &receive_transfer_callback_, + NearbySharingService::ReceiveSurfaceState::kForeground, + Advertisement::BlockedVendorId::kNone, std::move(callback)); + }); + if (std::get<0>(result)) { + { + absl::MutexLock lock(lock_); + receive_registered_ = true; + } + EmitStatusChanged(); + } + return result; +} + +std::tuple NearbySharingDbusService::StopReceive() { + { + absl::MutexLock lock(lock_); + if (!receive_registered_) { + return {true, "receive already stopped"}; + } + } + + auto result = InvokeStatusCommand([&](auto callback) { + service_.UnregisterReceiveSurface(&receive_transfer_callback_, + std::move(callback)); + }); + if (std::get<0>(result)) { + { + absl::MutexLock lock(lock_); + receive_registered_ = false; + } + EmitStatusChanged(); + } + return result; +} + +std::tuple NearbySharingDbusService::StartDiscovery() { + { + absl::MutexLock lock(lock_); + if (discovery_registered_) { + return {true, "discovery already started"}; + } + } + + auto result = InvokeStatusCommand([&](auto callback) { + service_.RegisterSendSurface( + &send_transfer_callback_, this, + NearbySharingService::SendSurfaceState::kForeground, + Advertisement::BlockedVendorId::kNone, + /*disable_wifi_hotspot=*/false, std::move(callback)); + }); + if (std::get<0>(result)) { + { + absl::MutexLock lock(lock_); + discovery_registered_ = true; + } + EmitStatusChanged(); + } + return result; +} + +std::tuple NearbySharingDbusService::StopDiscovery() { + { + absl::MutexLock lock(lock_); + if (!discovery_registered_) { + return {true, "discovery already stopped"}; + } + } + + auto result = InvokeStatusCommand([&](auto callback) { + service_.UnregisterSendSurface(&send_transfer_callback_, + std::move(callback)); + }); + if (std::get<0>(result)) { + { + absl::MutexLock lock(lock_); + discovery_registered_ = false; + targets_.clear(); + } + EmitStatusChanged(); + } + return result; +} + +std::tuple NearbySharingDbusService::SendFile( + const int64_t& share_target_id, const std::string& path) { + std::error_code file_error; + if (!std::filesystem::is_regular_file(path, file_error)) { + return {false, "path is not a regular file"}; + } + + { + absl::MutexLock lock(lock_); + if (targets_.find(share_target_id) == targets_.end()) { + return {false, "unknown share_target_id"}; + } + } + + auto attachments = CreateFileAttachments(path); + return InvokeStatusCommand([&](auto callback) { + service_.SendAttachments(share_target_id, std::move(attachments), + std::move(callback)); + }); +} + +std::tuple NearbySharingDbusService::Accept( + const int64_t& share_target_id) { + return InvokeStatusCommand([&](auto callback) { + service_.Accept(share_target_id, std::move(callback)); + }); +} + +std::tuple NearbySharingDbusService::Reject( + const int64_t& share_target_id) { + return InvokeStatusCommand([&](auto callback) { + service_.Reject(share_target_id, std::move(callback)); + }); +} + +std::tuple NearbySharingDbusService::Cancel( + const int64_t& share_target_id) { + return InvokeStatusCommand([&](auto callback) { + service_.Cancel(share_target_id, std::move(callback)); + }); +} + +std::tuple NearbySharingDbusService::Shutdown() { + ShutdownService(); + if (quit_callback_) { + quit_callback_(); + } + return {true, "daemon service shut down"}; +} + +std::tuple NearbySharingDbusService::InvokeStatusCommand( + std::function)> + invoker) { + return StatusResult(WaitForStatus(std::move(invoker))); +} + +DbusDictionary NearbySharingDbusService::StatusToDbus() const { + std::vector targets; + bool receive_registered = false; + bool discovery_registered = false; + { + absl::MutexLock lock(lock_); + receive_registered = receive_registered_; + discovery_registered = discovery_registered_; + targets.reserve(targets_.size()); + for (const auto& [id, target] : targets_) { + static_cast(id); + targets.push_back(ShareTargetToDbus(target)); + } + } + + DbusDictionary status; + status.emplace("receive_registered", sdbus::Variant(receive_registered)); + status.emplace("discovery_registered", sdbus::Variant(discovery_registered)); + status.emplace("is_transferring", sdbus::Variant(service_.IsTransferring())); + status.emplace("is_scanning", sdbus::Variant(service_.IsScanning())); + status.emplace("bluetooth_present", + sdbus::Variant(service_.IsBluetoothPresent())); + status.emplace("bluetooth_powered", + sdbus::Variant(service_.IsBluetoothPowered())); + status.emplace("lan_connected", sdbus::Variant(service_.IsLanConnected())); + status.emplace("targets", sdbus::Variant(targets)); + return status; +} + +void NearbySharingDbusService::OnTransferUpdate( + bool receive_mode, const ShareTarget& share_target, + const AttachmentContainer& attachment_container, + const TransferMetadata& transfer_metadata) { + const std::string direction = receive_mode ? "receive" : "send"; + const DbusDictionary target = ShareTargetToDbus(share_target); + const DbusDictionary transfer = + TransferMetadataToDbus(transfer_metadata, attachment_container); + + if (receive_mode && + transfer_metadata.status() == + TransferMetadata::Status::kAwaitingLocalConfirmation) { + emitIncomingTransfer(direction, target, transfer); + } else { + emitTransferUpdate(direction, target, transfer); + } + EmitStatusChanged(); +} + +void NearbySharingDbusService::OnShareTargetDiscovered( + const ShareTarget& share_target) { + { + absl::MutexLock lock(lock_); + targets_[share_target.id] = share_target; + } + emitTargetDiscovered(ShareTargetToDbus(share_target)); + EmitStatusChanged(); +} + +void NearbySharingDbusService::OnShareTargetLost( + const ShareTarget& share_target) { + { + absl::MutexLock lock(lock_); + targets_.erase(share_target.id); + } + emitTargetLost(ShareTargetToDbus(share_target)); + EmitStatusChanged(); +} + +void NearbySharingDbusService::OnShareTargetUpdated( + const ShareTarget& share_target) { + { + absl::MutexLock lock(lock_); + targets_[share_target.id] = share_target; + } + emitTargetUpdated(ShareTargetToDbus(share_target)); + EmitStatusChanged(); +} + +void NearbySharingDbusService::OnHighVisibilityChanged( + bool in_high_visibility) { + static_cast(in_high_visibility); + EmitStatusChanged(); +} + +void NearbySharingDbusService::OnBluetoothStatusChanged(AdapterState state) { + static_cast(state); + EmitStatusChanged(); +} + +void NearbySharingDbusService::OnLanStatusChanged(AdapterState state) { + static_cast(state); + EmitStatusChanged(); +} + +void NearbySharingDbusService::OnIrrecoverableHardwareErrorReported() { + EmitStatusChanged(); +} + +} // namespace nearby::sharing::linux diff --git a/sharing/linux/daemon/nearby_sharing_dbus_service.h b/sharing/linux/daemon/nearby_sharing_dbus_service.h new file mode 100644 index 00000000..d7a4be77 --- /dev/null +++ b/sharing/linux/daemon/nearby_sharing_dbus_service.h @@ -0,0 +1,105 @@ +#ifndef SHARING_LINUX_DAEMON_NEARBY_SHARING_DBUS_SERVICE_H_ +#define SHARING_LINUX_DAEMON_NEARBY_SHARING_DBUS_SERVICE_H_ + +#include +#include +#include +#include +#include +#include + +#include + +#include "absl/synchronization/mutex.h" +#include "sharing/attachment_container.h" +#include "sharing/linux/daemon/nearby_sharing_server.h" +#include "sharing/nearby_sharing_service.h" +#include "sharing/share_target.h" +#include "sharing/share_target_discovered_callback.h" +#include "sharing/transfer_metadata.h" +#include "sharing/transfer_update_callback.h" + +namespace nearby::sharing::linux { + +using DbusDictionary = std::map; + +DbusDictionary ShareTargetToDbus(const ShareTarget& share_target); +DbusDictionary TransferMetadataToDbus( + const TransferMetadata& transfer_metadata, + const AttachmentContainer& attachment_container); + +class NearbySharingDbusService final + : public com::google::nearby::sharing_adaptor, + public ShareTargetDiscoveredCallback, + public NearbySharingService::Observer { + public: + using QuitCallback = std::function; + + NearbySharingDbusService(sdbus::IObject& object, + NearbySharingService& service, + QuitCallback quit_callback); + ~NearbySharingDbusService() override; + + NearbySharingDbusService(const NearbySharingDbusService&) = delete; + NearbySharingDbusService& operator=(const NearbySharingDbusService&) = delete; + + void EmitStatusChanged(); + void ShutdownService(); + + void OnShareTargetDiscovered(const ShareTarget& share_target) override; + void OnShareTargetLost(const ShareTarget& share_target) override; + void OnShareTargetUpdated(const ShareTarget& share_target) override; + + void OnHighVisibilityChanged(bool in_high_visibility) override; + void OnBluetoothStatusChanged(AdapterState state) override; + void OnLanStatusChanged(AdapterState state) override; + void OnIrrecoverableHardwareErrorReported() override; + + private: + class TransferCallback final : public TransferUpdateCallback { + public: + TransferCallback(NearbySharingDbusService& service, bool receive_mode); + + void OnTransferUpdate(const ShareTarget& share_target, + const AttachmentContainer& attachment_container, + const TransferMetadata& transfer_metadata) override; + + private: + NearbySharingDbusService& service_; + bool receive_mode_; + }; + + std::tuple StartReceive() override; + std::tuple StopReceive() override; + std::tuple StartDiscovery() override; + std::tuple StopDiscovery() override; + std::tuple SendFile(const int64_t& share_target_id, + const std::string& path) override; + std::tuple Accept(const int64_t& share_target_id) override; + std::tuple Reject(const int64_t& share_target_id) override; + std::tuple Cancel(const int64_t& share_target_id) override; + std::tuple Shutdown() override; + + std::tuple InvokeStatusCommand( + std::function< + void(std::function)> + invoker); + DbusDictionary StatusToDbus() const; + void OnTransferUpdate(bool receive_mode, const ShareTarget& share_target, + const AttachmentContainer& attachment_container, + const TransferMetadata& transfer_metadata); + + NearbySharingService& service_; + QuitCallback quit_callback_; + TransferCallback send_transfer_callback_; + TransferCallback receive_transfer_callback_; + mutable absl::Mutex lock_; + std::unordered_map targets_; + bool receive_registered_ = false; + bool discovery_registered_ = false; + bool shutdown_ = false; +}; + +} // namespace nearby::sharing::linux + +#endif // SHARING_LINUX_DAEMON_NEARBY_SHARING_DBUS_SERVICE_H_ diff --git a/sharing/linux/daemon/nearby_sharing_dbus_service_test.cc b/sharing/linux/daemon/nearby_sharing_dbus_service_test.cc new file mode 100644 index 00000000..342e78b6 --- /dev/null +++ b/sharing/linux/daemon/nearby_sharing_dbus_service_test.cc @@ -0,0 +1,89 @@ +#include "sharing/linux/daemon/nearby_sharing_dbus_service.h" + +#include +#include + +#include + +#include "sharing/attachment_container.h" +#include "sharing/common/nearby_share_enums.h" +#include "sharing/share_target.h" +#include "sharing/transfer_metadata.h" +#include "sharing/transfer_metadata_builder.h" + +namespace nearby::sharing::linux { +namespace { + +ShareTarget MakeShareTarget() { + ShareTarget target; + target.id = 42; + target.device_name = "Pixel"; + target.type = ShareTargetType::kPhone; + target.is_incoming = true; + target.is_known = true; + target.device_id = "device-id"; + target.for_self_share = true; + target.vendor_id = 7; + target.receive_disabled = true; + return target; +} + +TEST(NearbySharingDbusServiceTest, ShareTargetToDbusMapsNearbyFields) { + DbusDictionary result = ShareTargetToDbus(MakeShareTarget()); + + EXPECT_EQ(result.at("id").get(), 42); + EXPECT_EQ(result.at("device_name").get(), "Pixel"); + EXPECT_EQ(result.at("type").get(), + static_cast(ShareTargetType::kPhone)); + EXPECT_TRUE(result.at("is_incoming").get()); + EXPECT_TRUE(result.at("is_known").get()); + EXPECT_EQ(result.at("device_id").get(), "device-id"); + EXPECT_TRUE(result.at("for_self_share").get()); + EXPECT_EQ(result.at("vendor_id").get(), 7); + EXPECT_TRUE(result.at("receive_disabled").get()); +} + +TEST(NearbySharingDbusServiceTest, TransferMetadataToDbusMapsNearbyFields) { + AttachmentContainer::Builder attachments_builder; + auto attachments = attachments_builder.Build(); + TransferMetadata metadata = + TransferMetadataBuilder() + .set_status(TransferMetadata::Status::kInProgress) + .set_progress(25) + .set_token(std::string("1234")) + .set_is_self_share(true) + .set_transferred_bytes(1024) + .set_transfer_speed(256) + .set_estimated_time_remaining(4) + .set_total_attachments_count(2) + .set_transferred_attachments_count(1) + .set_in_progress_attachment_id(99) + .set_in_progress_attachment_transferred_bytes(512) + .set_in_progress_attachment_total_bytes(2048) + .set_binding_id("binding") + .build(); + + DbusDictionary result = TransferMetadataToDbus(metadata, *attachments); + + EXPECT_EQ(result.at("status").get(), "kInProgress"); + EXPECT_EQ(result.at("progress").get(), 25); + EXPECT_EQ(result.at("transferred_bytes").get(), 1024); + EXPECT_EQ(result.at("total_bytes").get(), 0); + EXPECT_EQ(result.at("transfer_speed").get(), 256); + EXPECT_EQ(result.at("estimated_time_remaining").get(), 4); + EXPECT_EQ(result.at("total_attachments_count").get(), 2); + EXPECT_EQ(result.at("transferred_attachments_count").get(), 1); + EXPECT_FALSE(result.at("is_final_status").get()); + EXPECT_TRUE(result.at("is_self_share").get()); + EXPECT_EQ(result.at("binding_id").get(), "binding"); + EXPECT_EQ(result.at("token").get(), "1234"); + EXPECT_EQ(result.at("in_progress_attachment_id").get(), 99); + EXPECT_EQ( + result.at("in_progress_attachment_transferred_bytes").get(), + 512); + EXPECT_EQ(result.at("in_progress_attachment_total_bytes").get(), + 2048); +} + +} // namespace +} // namespace nearby::sharing::linux diff --git a/sharing/linux/daemon/nearby_sharing_server.h b/sharing/linux/daemon/nearby_sharing_server.h new file mode 100644 index 00000000..cb32f0b8 --- /dev/null +++ b/sharing/linux/daemon/nearby_sharing_server.h @@ -0,0 +1,168 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__sharing_linux_daemon_nearby_sharing_server_h__adaptor__H__ +#define __sdbuscpp__sharing_linux_daemon_nearby_sharing_server_h__adaptor__H__ + +#include +#include +#include + +namespace com { +namespace google { +namespace nearby { + +class sharing_adaptor { + public: + static constexpr const char* INTERFACE_NAME = "com.google.nearby.sharing"; + + protected: + sharing_adaptor(sdbus::IObject& object) : m_object(object) {} + + sharing_adaptor(const sharing_adaptor&) = delete; + sharing_adaptor& operator=(const sharing_adaptor&) = delete; + sharing_adaptor(sharing_adaptor&&) = delete; + sharing_adaptor& operator=(sharing_adaptor&&) = delete; + + ~sharing_adaptor() = default; + + void registerAdaptor() { + m_object + .addVTable( + sdbus::registerMethod("StartReceive") + .withOutputParamNames("ok", "message") + .implementedAs([this]() { return this->StartReceive(); }), + sdbus::registerMethod("StopReceive") + .withOutputParamNames("ok", "message") + .implementedAs([this]() { return this->StopReceive(); }), + sdbus::registerMethod("StartDiscovery") + .withOutputParamNames("ok", "message") + .implementedAs([this]() { return this->StartDiscovery(); }), + sdbus::registerMethod("StopDiscovery") + .withOutputParamNames("ok", "message") + .implementedAs([this]() { return this->StopDiscovery(); }), + sdbus::registerMethod("SendFile") + .withInputParamNames("share_target_id", "path") + .withOutputParamNames("ok", "message") + .implementedAs([this](const int64_t& share_target_id, + const std::string& path) { + return this->SendFile(share_target_id, path); + }), + sdbus::registerMethod("Accept") + .withInputParamNames("share_target_id") + .withOutputParamNames("ok", "message") + .implementedAs([this](const int64_t& share_target_id) { + return this->Accept(share_target_id); + }), + sdbus::registerMethod("Reject") + .withInputParamNames("share_target_id") + .withOutputParamNames("ok", "message") + .implementedAs([this](const int64_t& share_target_id) { + return this->Reject(share_target_id); + }), + sdbus::registerMethod("Cancel") + .withInputParamNames("share_target_id") + .withOutputParamNames("ok", "message") + .implementedAs([this](const int64_t& share_target_id) { + return this->Cancel(share_target_id); + }), + sdbus::registerMethod("Shutdown") + .withOutputParamNames("ok", "message") + .implementedAs([this]() { return this->Shutdown(); }), + sdbus::registerSignal("TargetDiscovered") + .withParameters>( + "share_target"), + sdbus::registerSignal("TargetUpdated") + .withParameters>( + "share_target"), + sdbus::registerSignal("TargetLost") + .withParameters>( + "share_target"), + sdbus::registerSignal("IncomingTransfer") + .withParameters, + std::map>( + "direction", "share_target", "transfer"), + sdbus::registerSignal("TransferUpdate") + .withParameters, + std::map>( + "direction", "share_target", "transfer"), + sdbus::registerSignal("StatusChanged") + .withParameters>( + "status")) + .forInterface(INTERFACE_NAME); + } + + public: + void emitTargetDiscovered( + const std::map& share_target) { + m_object.emitSignal("TargetDiscovered") + .onInterface(INTERFACE_NAME) + .withArguments(share_target); + } + + void emitTargetUpdated( + const std::map& share_target) { + m_object.emitSignal("TargetUpdated") + .onInterface(INTERFACE_NAME) + .withArguments(share_target); + } + + void emitTargetLost( + const std::map& share_target) { + m_object.emitSignal("TargetLost") + .onInterface(INTERFACE_NAME) + .withArguments(share_target); + } + + void emitIncomingTransfer( + const std::string& direction, + const std::map& share_target, + const std::map& transfer) { + m_object.emitSignal("IncomingTransfer") + .onInterface(INTERFACE_NAME) + .withArguments(direction, share_target, transfer); + } + + void emitTransferUpdate( + const std::string& direction, + const std::map& share_target, + const std::map& transfer) { + m_object.emitSignal("TransferUpdate") + .onInterface(INTERFACE_NAME) + .withArguments(direction, share_target, transfer); + } + + void emitStatusChanged(const std::map& status) { + m_object.emitSignal("StatusChanged") + .onInterface(INTERFACE_NAME) + .withArguments(status); + } + + private: + virtual std::tuple StartReceive() = 0; + virtual std::tuple StopReceive() = 0; + virtual std::tuple StartDiscovery() = 0; + virtual std::tuple StopDiscovery() = 0; + virtual std::tuple SendFile(const int64_t& share_target_id, + const std::string& path) = 0; + virtual std::tuple Accept( + const int64_t& share_target_id) = 0; + virtual std::tuple Reject( + const int64_t& share_target_id) = 0; + virtual std::tuple Cancel( + const int64_t& share_target_id) = 0; + virtual std::tuple Shutdown() = 0; + + private: + sdbus::IObject& m_object; +}; + +} // namespace nearby +} // namespace google +} // namespace com + +#endif diff --git a/sharing/linux/install_nearby_connections_service.sh b/sharing/linux/install_nearby_connections_service.sh deleted file mode 100755 index e2ab5094..00000000 --- a/sharing/linux/install_nearby_connections_service.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -DELEGATE_SCRIPT="${SCRIPT_DIR}/install_nearby_sharing_service.sh" - -if [[ ! -x "${DELEGATE_SCRIPT}" ]]; then - echo "Missing installer: ${DELEGATE_SCRIPT}" >&2 - exit 1 -fi - -cat <<'MSG' -install_nearby_connections_service.sh is deprecated. -Installing Nearby Sharing API artifacts instead. -MSG - -exec "${DELEGATE_SCRIPT}" "$@" diff --git a/sharing/linux/install_nearby_sharing_service.sh b/sharing/linux/install_nearby_sharing_service.sh deleted file mode 100755 index b8520a68..00000000 --- a/sharing/linux/install_nearby_sharing_service.sh +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -TARGET="//sharing/linux:nearby_sharing_api_shared" -HEADER_SRC="sharing/linux/nearby_sharing_api.h" -BAZEL_CMD="${BAZEL:-bazel}" -PREFIX="/usr/local" -LIBDIR="" -INCLUDEDIR="" -SKIP_BUILD=0 -NEEDS_ELEVATION=0 -INSTALL_PREFIX=() - -usage() { - cat </lib) - --includedir DIR Include root directory (default: /include) - --bazel CMD Bazel command (default: bazel or env BAZEL) - --skip-build Skip bazel build step and only install from bazel-bin - -h, --help Show this help - -Examples: - $0 - sudo $0 - sudo $0 --prefix /usr - sudo $0 --bazel /usr/bin/bazel -USAGE -} - -run_bazel() { - if [[ "$(id -u)" -eq 0 && -n "${SUDO_USER:-}" ]]; then - local caller_home - caller_home="$(getent passwd "$SUDO_USER" | cut -d: -f6)" - if [[ -z "$caller_home" ]]; then - echo "Failed to resolve home directory for SUDO_USER=$SUDO_USER" >&2 - exit 1 - fi - sudo -u "$SUDO_USER" -H env \ - HOME="$caller_home" \ - BAZELISK_HOME="${BAZELISK_HOME:-$caller_home/.cache/bazelisk}" \ - "$BAZEL_CMD" "$@" - else - "$BAZEL_CMD" "$@" - fi -} - -nearest_existing_parent() { - local p="$1" - while [[ ! -e "$p" ]]; do - p="$(dirname "$p")" - done - printf '%s\n' "$p" -} - -while [[ $# -gt 0 ]]; do - case "$1" in - --prefix) - PREFIX="$2" - shift 2 - ;; - --libdir) - LIBDIR="$2" - shift 2 - ;; - --includedir) - INCLUDEDIR="$2" - shift 2 - ;; - --bazel) - BAZEL_CMD="$2" - shift 2 - ;; - --skip-build) - SKIP_BUILD=1 - shift - ;; - -h|--help) - usage - exit 0 - ;; - *) - echo "Unknown argument: $1" >&2 - usage - exit 1 - ;; - esac -done - -if [[ -z "$LIBDIR" ]]; then - LIBDIR="${PREFIX}/lib" -fi - -if [[ -z "$INCLUDEDIR" ]]; then - INCLUDEDIR="${PREFIX}/include" -fi - -LIB_PARENT="$(nearest_existing_parent "$LIBDIR")" -INCLUDE_PARENT="$(nearest_existing_parent "${INCLUDEDIR}/sharing/linux")" - -if [[ ! -w "$LIB_PARENT" || ! -w "$INCLUDE_PARENT" ]]; then - NEEDS_ELEVATION=1 -fi - -if [[ "$NEEDS_ELEVATION" -eq 1 && "$(id -u)" -ne 0 ]]; then - if ! command -v sudo >/dev/null 2>&1; then - echo "Install requires elevated privileges, but sudo is not available." >&2 - exit 1 - fi - INSTALL_PREFIX=(sudo) -fi - -if [[ ! -f "$HEADER_SRC" ]]; then - echo "Header not found: $HEADER_SRC" >&2 - echo "Run this script from the workspace root." >&2 - exit 1 -fi - -if [[ "$SKIP_BUILD" -eq 0 ]]; then - echo "[1/4] Building $TARGET" - run_bazel build "$TARGET" -else - echo "[1/4] Skipping build (--skip-build)" -fi - -echo "[2/4] Resolving bazel-bin path" -BAZEL_BIN="$(run_bazel info bazel-bin)" -LIB_SRC="${BAZEL_BIN}/sharing/linux/libnearby_sharing_api_shared.so" - -if [[ ! -f "$LIB_SRC" ]]; then - echo "Shared library not found: $LIB_SRC" >&2 - echo "Expected Bazel output for $TARGET is missing." >&2 - exit 1 -fi - -echo "[3/4] Installing library and header" -"${INSTALL_PREFIX[@]}" install -d "$LIBDIR" -"${INSTALL_PREFIX[@]}" install -d "${INCLUDEDIR}/sharing/linux" -"${INSTALL_PREFIX[@]}" install -m 0755 "$LIB_SRC" "$LIBDIR/" -"${INSTALL_PREFIX[@]}" install -m 0644 "$HEADER_SRC" "${INCLUDEDIR}/sharing/linux/" - -if command -v ldconfig >/dev/null 2>&1; then - echo "[4/4] Refreshing dynamic linker cache" - "${INSTALL_PREFIX[@]}" ldconfig -else - echo "[4/4] ldconfig not found; skipping linker cache refresh" -fi - -echo "Installed:" -echo " library: $LIBDIR/$(basename "$LIB_SRC")" -echo " header : ${INCLUDEDIR}/sharing/linux/$(basename "$HEADER_SRC")" diff --git a/sharing/linux/nearby_connections_adapter.cc b/sharing/linux/nearby_connections_adapter.cc deleted file mode 100644 index a911ef5e..00000000 --- a/sharing/linux/nearby_connections_adapter.cc +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2025 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 "sharing/linux/nearby_connections_adapter.h" - -#include -#include - -namespace nearby::sharing::linux { - -CoreNearbyConnectionsAdapter::CoreNearbyConnectionsAdapter() - : router_(std::make_unique()), - core_(std::make_unique(router_.get())) {} - -CoreNearbyConnectionsAdapter::~CoreNearbyConnectionsAdapter() = default; - -void CoreNearbyConnectionsAdapter::StartAdvertising( - absl::string_view service_id, - connections::AdvertisingOptions advertising_options, - connections::ConnectionRequestInfo request_info, - std::function callback) { - core_->StartAdvertising(service_id, std::move(advertising_options), - std::move(request_info), std::move(callback)); -} - -void CoreNearbyConnectionsAdapter::StopAdvertising( - std::function callback) { - core_->StopAdvertising(std::move(callback)); -} - -void CoreNearbyConnectionsAdapter::StartDiscovery( - absl::string_view service_id, - connections::DiscoveryOptions discovery_options, - connections::DiscoveryListener discovery_listener, - std::function callback) { - core_->StartDiscovery(service_id, std::move(discovery_options), - std::move(discovery_listener), std::move(callback)); -} - -void CoreNearbyConnectionsAdapter::StopDiscovery( - std::function callback) { - core_->StopDiscovery(std::move(callback)); -} - -void CoreNearbyConnectionsAdapter::RequestConnection( - absl::string_view endpoint_id, - connections::ConnectionRequestInfo request_info, - connections::ConnectionOptions connection_options, - std::function callback) { - core_->RequestConnection(endpoint_id, std::move(request_info), - std::move(connection_options), std::move(callback)); -} - -void CoreNearbyConnectionsAdapter::AcceptConnection( - absl::string_view endpoint_id, connections::PayloadListener listener, - std::function callback) { - core_->AcceptConnection(endpoint_id, std::move(listener), std::move(callback)); -} - -void CoreNearbyConnectionsAdapter::RejectConnection( - absl::string_view endpoint_id, - std::function callback) { - core_->RejectConnection(endpoint_id, std::move(callback)); -} - -void CoreNearbyConnectionsAdapter::SendPayload( - absl::Span endpoint_ids, connections::Payload payload, - std::function callback) { - core_->SendPayload(endpoint_ids, std::move(payload), std::move(callback)); -} - -void CoreNearbyConnectionsAdapter::DisconnectFromEndpoint( - absl::string_view endpoint_id, - std::function callback) { - core_->DisconnectFromEndpoint(endpoint_id, std::move(callback)); -} - -} // namespace nearby::sharing::linux diff --git a/sharing/linux/nearby_connections_adapter.h b/sharing/linux/nearby_connections_adapter.h deleted file mode 100644 index c478442b..00000000 --- a/sharing/linux/nearby_connections_adapter.h +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2025 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 THIRD_PARTY_NEARBY_SHARING_LINUX_NEARBY_CONNECTIONS_ADAPTER_H_ -#define THIRD_PARTY_NEARBY_SHARING_LINUX_NEARBY_CONNECTIONS_ADAPTER_H_ - -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "absl/types/span.h" -#include "connections/advertising_options.h" -#include "connections/connection_options.h" -#include "connections/core.h" -#include "connections/discovery_options.h" -#include "connections/implementation/service_controller_router.h" -#include "connections/listeners.h" -#include "connections/payload.h" -#include "connections/status.h" - -namespace nearby::sharing::linux { - -class NearbyConnectionsAdapter { - public: - virtual ~NearbyConnectionsAdapter() = default; - - virtual void StartAdvertising( - absl::string_view service_id, - connections::AdvertisingOptions advertising_options, - connections::ConnectionRequestInfo request_info, - std::function callback) = 0; - virtual void StopAdvertising( - std::function callback) = 0; - virtual void StartDiscovery( - absl::string_view service_id, - connections::DiscoveryOptions discovery_options, - connections::DiscoveryListener discovery_listener, - std::function callback) = 0; - virtual void StopDiscovery( - std::function callback) = 0; - virtual void RequestConnection( - absl::string_view endpoint_id, - connections::ConnectionRequestInfo request_info, - connections::ConnectionOptions connection_options, - std::function callback) = 0; - virtual void AcceptConnection( - absl::string_view endpoint_id, connections::PayloadListener listener, - std::function callback) = 0; - virtual void RejectConnection( - absl::string_view endpoint_id, - std::function callback) = 0; - virtual void SendPayload( - absl::Span endpoint_ids, connections::Payload payload, - std::function callback) = 0; - virtual void DisconnectFromEndpoint( - absl::string_view endpoint_id, - std::function callback) = 0; -}; - -class CoreNearbyConnectionsAdapter : public NearbyConnectionsAdapter { - public: - CoreNearbyConnectionsAdapter(); - ~CoreNearbyConnectionsAdapter() override; - - void StartAdvertising( - absl::string_view service_id, - connections::AdvertisingOptions advertising_options, - connections::ConnectionRequestInfo request_info, - std::function callback) override; - void StopAdvertising( - std::function callback) override; - void StartDiscovery( - absl::string_view service_id, - connections::DiscoveryOptions discovery_options, - connections::DiscoveryListener discovery_listener, - std::function callback) override; - void StopDiscovery( - std::function callback) override; - void RequestConnection( - absl::string_view endpoint_id, - connections::ConnectionRequestInfo request_info, - connections::ConnectionOptions connection_options, - std::function callback) override; - void AcceptConnection( - absl::string_view endpoint_id, connections::PayloadListener listener, - std::function callback) override; - void RejectConnection( - absl::string_view endpoint_id, - std::function callback) override; - void SendPayload( - absl::Span endpoint_ids, connections::Payload payload, - std::function callback) override; - void DisconnectFromEndpoint( - absl::string_view endpoint_id, - std::function callback) override; - - private: - std::unique_ptr router_; - std::unique_ptr core_; -}; - -} // namespace nearby::sharing::linux - -#endif // THIRD_PARTY_NEARBY_SHARING_LINUX_NEARBY_CONNECTIONS_ADAPTER_H_ diff --git a/sharing/linux/nearby_connections_api.cc b/sharing/linux/nearby_connections_api.cc deleted file mode 100644 index 4eaa04a4..00000000 --- a/sharing/linux/nearby_connections_api.cc +++ /dev/null @@ -1,659 +0,0 @@ -#include "sharing/linux/nearby_connections_api.h" - -#include -#include -#include -#include -#include -#include - -#include "absl/time/time.h" -#include "internal/base/file_path.h" -#include "internal/interop/authentication_status.h" -#include "sharing/internal/public/context_impl.h" -#include "sharing/linux/platform/linux_sharing_platform.h" -#include "sharing/nearby_connections_service.h" -#include "sharing/nearby_connections_service_impl.h" - -namespace nearby::sharing { - -namespace { - -using NativeService = nearby::sharing::NearbyConnectionsService; - -NearbyConnectionsApi::StatusCode ToFacadeStatus(Status status) { - switch (status) { - case Status::kSuccess: - return NearbyConnectionsApi::StatusCode::kSuccess; - case Status::kError: - return NearbyConnectionsApi::StatusCode::kError; - case Status::kOutOfOrderApiCall: - return NearbyConnectionsApi::StatusCode::kOutOfOrderApiCall; - case Status::kAlreadyHaveActiveStrategy: - return NearbyConnectionsApi::StatusCode::kAlreadyHaveActiveStrategy; - case Status::kAlreadyAdvertising: - return NearbyConnectionsApi::StatusCode::kAlreadyAdvertising; - case Status::kAlreadyDiscovering: - return NearbyConnectionsApi::StatusCode::kAlreadyDiscovering; - case Status::kAlreadyListening: - return NearbyConnectionsApi::StatusCode::kAlreadyListening; - case Status::kEndpointIOError: - return NearbyConnectionsApi::StatusCode::kEndpointIOError; - case Status::kEndpointUnknown: - return NearbyConnectionsApi::StatusCode::kEndpointUnknown; - case Status::kConnectionRejected: - return NearbyConnectionsApi::StatusCode::kConnectionRejected; - case Status::kAlreadyConnectedToEndpoint: - return NearbyConnectionsApi::StatusCode::kAlreadyConnectedToEndpoint; - case Status::kNotConnectedToEndpoint: - return NearbyConnectionsApi::StatusCode::kNotConnectedToEndpoint; - case Status::kBluetoothError: - return NearbyConnectionsApi::StatusCode::kBluetoothError; - case Status::kBleError: - return NearbyConnectionsApi::StatusCode::kBleError; - case Status::kWifiLanError: - return NearbyConnectionsApi::StatusCode::kWifiLanError; - case Status::kPayloadUnknown: - return NearbyConnectionsApi::StatusCode::kPayloadUnknown; - case Status::kReset: - return NearbyConnectionsApi::StatusCode::kReset; - case Status::kTimeout: - return NearbyConnectionsApi::StatusCode::kTimeout; - case Status::kUnknown: - return NearbyConnectionsApi::StatusCode::kUnknown; - case Status::kNextValue: - break; - } - return NearbyConnectionsApi::StatusCode::kUnknown; -} - -NearbyConnectionsApi::AuthenticationStatus ToFacadeAuthenticationStatus( - ::nearby::AuthenticationStatus status) { - switch (status) { - case ::nearby::AuthenticationStatus::kUnknown: - return NearbyConnectionsApi::AuthenticationStatus::kUnknown; - case ::nearby::AuthenticationStatus::kSuccess: - return NearbyConnectionsApi::AuthenticationStatus::kSuccess; - case ::nearby::AuthenticationStatus::kFailure: - return NearbyConnectionsApi::AuthenticationStatus::kFailure; - } - return NearbyConnectionsApi::AuthenticationStatus::kUnknown; -} - -NearbyConnectionsApi::DistanceInfo ToFacadeDistanceInfo( - nearby::sharing::DistanceInfo distance_info) { - switch (distance_info) { - case nearby::sharing::DistanceInfo::kUnknown: - return NearbyConnectionsApi::DistanceInfo::kUnknown; - case nearby::sharing::DistanceInfo::kVeryClose: - return NearbyConnectionsApi::DistanceInfo::kVeryClose; - case nearby::sharing::DistanceInfo::kClose: - return NearbyConnectionsApi::DistanceInfo::kClose; - case nearby::sharing::DistanceInfo::kFar: - return NearbyConnectionsApi::DistanceInfo::kFar; - } - return NearbyConnectionsApi::DistanceInfo::kUnknown; -} - -NearbyConnectionsApi::Medium ToFacadeMedium(nearby::sharing::Medium medium) { - switch (medium) { - case nearby::sharing::Medium::kUnknown: - return NearbyConnectionsApi::Medium::kUnknown; - case nearby::sharing::Medium::kMdns: - return NearbyConnectionsApi::Medium::kMdns; - case nearby::sharing::Medium::kBluetooth: - return NearbyConnectionsApi::Medium::kBluetooth; - case nearby::sharing::Medium::kWifiHotspot: - return NearbyConnectionsApi::Medium::kWifiHotspot; - case nearby::sharing::Medium::kBle: - return NearbyConnectionsApi::Medium::kBle; - case nearby::sharing::Medium::kWifiLan: - return NearbyConnectionsApi::Medium::kWifiLan; - case nearby::sharing::Medium::kWifiAware: - return NearbyConnectionsApi::Medium::kWifiAware; - case nearby::sharing::Medium::kNfc: - return NearbyConnectionsApi::Medium::kNfc; - case nearby::sharing::Medium::kWifiDirect: - return NearbyConnectionsApi::Medium::kWifiDirect; - case nearby::sharing::Medium::kWebRtc: - return NearbyConnectionsApi::Medium::kWebRtc; - case nearby::sharing::Medium::kBleL2Cap: - return NearbyConnectionsApi::Medium::kBleL2Cap; - } - return NearbyConnectionsApi::Medium::kUnknown; -} - -nearby::sharing::Strategy ToNativeStrategy( - NearbyConnectionsApi::Strategy strategy) { - switch (strategy) { - case NearbyConnectionsApi::Strategy::kP2pCluster: - return nearby::sharing::Strategy::kP2pCluster; - case NearbyConnectionsApi::Strategy::kP2pStar: - return nearby::sharing::Strategy::kP2pStar; - case NearbyConnectionsApi::Strategy::kP2pPointToPoint: - return nearby::sharing::Strategy::kP2pPointToPoint; - } - return nearby::sharing::Strategy::kP2pCluster; -} - -nearby::sharing::MediumSelection ToNativeMediumSelection( - const NearbyConnectionsApi::MediumSelection& selection) { - nearby::sharing::MediumSelection native_selection; - native_selection.bluetooth = selection.bluetooth; - native_selection.ble = selection.ble; - native_selection.web_rtc = selection.web_rtc; - native_selection.wifi_lan = selection.wifi_lan; - native_selection.wifi_hotspot = selection.wifi_hotspot; - return native_selection; -} - -nearby::sharing::AdvertisingOptions ToNativeAdvertisingOptions( - const NearbyConnectionsApi::AdvertisingOptions& options) { - nearby::sharing::AdvertisingOptions native_options; - native_options.strategy = ToNativeStrategy(options.strategy); - native_options.allowed_mediums = - ToNativeMediumSelection(options.allowed_mediums); - native_options.auto_upgrade_bandwidth = options.auto_upgrade_bandwidth; - native_options.enforce_topology_constraints = - options.enforce_topology_constraints; - native_options.enable_bluetooth_listening = - options.enable_bluetooth_listening; - native_options.enable_webrtc_listening = options.enable_webrtc_listening; - native_options.use_stable_endpoint_id = options.use_stable_endpoint_id; - native_options.force_new_endpoint_id = options.force_new_endpoint_id; - native_options.fast_advertisement_service_uuid = - nearby::sharing::Uuid(options.fast_advertisement_service_uuid); - return native_options; -} - -nearby::sharing::DiscoveryOptions ToNativeDiscoveryOptions( - const NearbyConnectionsApi::DiscoveryOptions& options) { - nearby::sharing::DiscoveryOptions native_options; - native_options.strategy = ToNativeStrategy(options.strategy); - native_options.allowed_mediums = - ToNativeMediumSelection(options.allowed_mediums); - if (options.has_fast_advertisement_service_uuid) { - native_options.fast_advertisement_service_uuid = - nearby::sharing::Uuid(options.fast_advertisement_service_uuid.uuid); - } - native_options.is_out_of_band_connection = - options.is_out_of_band_connection; - if (options.has_alternate_service_uuid) { - native_options.alternate_service_uuid = options.alternate_service_uuid; - } - return native_options; -} - -nearby::sharing::ConnectionOptions ToNativeConnectionOptions( - const NearbyConnectionsApi::ConnectionOptions& options) { - nearby::sharing::ConnectionOptions native_options; - native_options.allowed_mediums = - ToNativeMediumSelection(options.allowed_mediums); - if (!options.remote_bluetooth_mac_address.empty()) { - native_options.remote_bluetooth_mac_address = - options.remote_bluetooth_mac_address; - } - if (options.has_keep_alive_interval_millis && - options.keep_alive_interval_millis >= 0) { - native_options.keep_alive_interval = - absl::Milliseconds(options.keep_alive_interval_millis); - } - if (options.has_keep_alive_timeout_millis && - options.keep_alive_timeout_millis >= 0) { - native_options.keep_alive_timeout = - absl::Milliseconds(options.keep_alive_timeout_millis); - } - native_options.non_disruptive_hotspot_mode = - options.non_disruptive_hotspot_mode; - return native_options; -} - -NearbyConnectionsApi::ConnectionInfo ToFacadeConnectionInfo( - const nearby::sharing::ConnectionInfo& info) { - NearbyConnectionsApi::ConnectionInfo facade_info; - facade_info.authentication_token = info.authentication_token; - facade_info.raw_authentication_token = info.raw_authentication_token; - facade_info.endpoint_info = info.endpoint_info; - facade_info.is_incoming_connection = info.is_incoming_connection; - facade_info.connection_layer_status = - ToFacadeStatus(info.connection_layer_status); - facade_info.authentication_status = - ToFacadeAuthenticationStatus(info.authentication_status); - return facade_info; -} - -NearbyConnectionsApi::DiscoveredEndpointInfo ToFacadeDiscoveredEndpointInfo( - const nearby::sharing::DiscoveredEndpointInfo& info) { - NearbyConnectionsApi::DiscoveredEndpointInfo facade_info; - facade_info.endpoint_info = info.endpoint_info; - facade_info.service_id = info.service_id; - return facade_info; -} - -NearbyConnectionsApi::PayloadStatus ToFacadePayloadStatus( - nearby::sharing::PayloadStatus status) { - switch (status) { - case nearby::sharing::PayloadStatus::kSuccess: - return NearbyConnectionsApi::PayloadStatus::kSuccess; - case nearby::sharing::PayloadStatus::kFailure: - return NearbyConnectionsApi::PayloadStatus::kFailure; - case nearby::sharing::PayloadStatus::kInProgress: - return NearbyConnectionsApi::PayloadStatus::kInProgress; - case nearby::sharing::PayloadStatus::kCanceled: - return NearbyConnectionsApi::PayloadStatus::kCanceled; - } - return NearbyConnectionsApi::PayloadStatus::kFailure; -} - -NearbyConnectionsApi::Payload ToFacadePayload( - const nearby::sharing::Payload& payload) { - NearbyConnectionsApi::Payload facade_payload; - facade_payload.id = payload.id; - switch (payload.content.type) { - case nearby::sharing::PayloadContent::Type::kBytes: - facade_payload.type = NearbyConnectionsApi::PayloadType::kBytes; - facade_payload.bytes = payload.content.bytes_payload.bytes; - break; - case nearby::sharing::PayloadContent::Type::kStream: - facade_payload.type = NearbyConnectionsApi::PayloadType::kStream; - facade_payload.stream_bytes = payload.content.stream_payload.bytes; - break; - case nearby::sharing::PayloadContent::Type::kFile: - facade_payload.type = NearbyConnectionsApi::PayloadType::kFile; - facade_payload.file_path = - payload.content.file_payload.file_path.ToString(); - facade_payload.parent_folder = payload.content.file_payload.parent_folder; - break; - case nearby::sharing::PayloadContent::Type::kUnknown: - facade_payload.type = NearbyConnectionsApi::PayloadType::kUnknown; - break; - } - return facade_payload; -} - -std::unique_ptr ToNativePayload( - NearbyConnectionsApi::Payload payload) { - switch (payload.type) { - case NearbyConnectionsApi::PayloadType::kBytes: - return std::make_unique( - payload.id, std::move(payload.bytes)); - case NearbyConnectionsApi::PayloadType::kFile: - return std::make_unique( - payload.id, FilePath(payload.file_path), payload.parent_folder); - case NearbyConnectionsApi::PayloadType::kStream: { - nearby::sharing::StreamPayload stream_payload; - stream_payload.bytes = std::move(payload.stream_bytes); - stream_payload.input_stream = std::move(payload.stream_input); - return std::make_unique(payload.id, - std::move(stream_payload)); - } - case NearbyConnectionsApi::PayloadType::kUnknown: - return nullptr; - } - return nullptr; -} - -NearbyConnectionsApi::PayloadTransferUpdate ToFacadePayloadTransferUpdate( - const nearby::sharing::PayloadTransferUpdate& update) { - NearbyConnectionsApi::PayloadTransferUpdate facade_update; - facade_update.payload_id = update.payload_id; - facade_update.status = ToFacadePayloadStatus(update.status); - facade_update.total_bytes = update.total_bytes; - facade_update.bytes_transferred = update.bytes_transferred; - return facade_update; -} - -} // namespace - -class NearbyConnectionsApi::Impl { - public: - struct ListenerState { - std::mutex mutex; - NearbyConnectionsApi::Listener listener; - }; - - Impl() - : platform(), - context(platform), - service(std::make_unique( - context.GetConnectivityManager(), /*event_logger=*/nullptr)), - listener_state(std::make_shared()) {} - - static NearbyConnectionsApi::Listener CopyListener( - const std::shared_ptr& listener_state) { - std::scoped_lock lock(listener_state->mutex); - return listener_state->listener; - } - - static NativeService::ConnectionListener BuildConnectionListener( - const std::shared_ptr& listener_state) { - NativeService::ConnectionListener listener; - listener.initiated_cb = - [listener_state](const std::string& endpoint_id, - const nearby::sharing::ConnectionInfo& info) { - NearbyConnectionsApi::Listener listener_copy = - CopyListener(listener_state); - if (!listener_copy.connection_initiated_cb) { - return; - } - listener_copy.connection_initiated_cb(endpoint_id, - ToFacadeConnectionInfo(info)); - }; - listener.accepted_cb = [listener_state](const std::string& endpoint_id) { - NearbyConnectionsApi::Listener listener_copy = - CopyListener(listener_state); - if (listener_copy.connection_accepted_cb) { - listener_copy.connection_accepted_cb(endpoint_id); - } - }; - listener.rejected_cb = [listener_state](const std::string& endpoint_id, - Status status) { - NearbyConnectionsApi::Listener listener_copy = - CopyListener(listener_state); - if (listener_copy.connection_rejected_cb) { - listener_copy.connection_rejected_cb(endpoint_id, - ToFacadeStatus(status)); - } - }; - listener.disconnected_cb = [listener_state](const std::string& endpoint_id) { - NearbyConnectionsApi::Listener listener_copy = - CopyListener(listener_state); - if (listener_copy.disconnected_cb) { - listener_copy.disconnected_cb(endpoint_id); - } - }; - listener.bandwidth_changed_cb = - [listener_state](const std::string& endpoint_id, - nearby::sharing::Medium medium) { - NearbyConnectionsApi::Listener listener_copy = - CopyListener(listener_state); - if (listener_copy.bandwidth_changed_cb) { - listener_copy.bandwidth_changed_cb(endpoint_id, - ToFacadeMedium(medium)); - } - }; - return listener; - } - - static NativeService::DiscoveryListener BuildDiscoveryListener( - const std::shared_ptr& listener_state) { - NativeService::DiscoveryListener listener; - listener.endpoint_found_cb = - [listener_state](const std::string& endpoint_id, - const nearby::sharing::DiscoveredEndpointInfo& info) { - NearbyConnectionsApi::Listener listener_copy = - CopyListener(listener_state); - if (listener_copy.endpoint_found_cb) { - listener_copy.endpoint_found_cb( - endpoint_id, ToFacadeDiscoveredEndpointInfo(info)); - } - }; - listener.endpoint_lost_cb = [listener_state](const std::string& endpoint_id) { - NearbyConnectionsApi::Listener listener_copy = - CopyListener(listener_state); - if (listener_copy.endpoint_lost_cb) { - listener_copy.endpoint_lost_cb(endpoint_id); - } - }; - listener.endpoint_distance_changed_cb = - [listener_state](const std::string& endpoint_id, - nearby::sharing::DistanceInfo distance_info) { - NearbyConnectionsApi::Listener listener_copy = - CopyListener(listener_state); - if (listener_copy.endpoint_distance_changed_cb) { - listener_copy.endpoint_distance_changed_cb( - endpoint_id, ToFacadeDistanceInfo(distance_info)); - } - }; - return listener; - } - - static NativeService::PayloadListener BuildPayloadListener( - const std::shared_ptr& listener_state) { - NativeService::PayloadListener listener; - listener.payload_cb = [listener_state](absl::string_view endpoint_id, - nearby::sharing::Payload payload) { - NearbyConnectionsApi::Listener listener_copy = - CopyListener(listener_state); - if (listener_copy.payload_received_cb) { - listener_copy.payload_received_cb(std::string(endpoint_id), - ToFacadePayload(payload)); - } - }; - listener.payload_progress_cb = - [listener_state]( - absl::string_view endpoint_id, - const nearby::sharing::PayloadTransferUpdate& update) { - NearbyConnectionsApi::Listener listener_copy = - CopyListener(listener_state); - if (listener_copy.payload_transfer_update_cb) { - listener_copy.payload_transfer_update_cb( - std::string(endpoint_id), ToFacadePayloadTransferUpdate(update)); - } - }; - return listener; - } - - LinuxSharingPlatform platform; - ::nearby::ContextImpl context; - std::unique_ptr service; - std::shared_ptr listener_state; -}; - -NearbyConnectionsApi::NearbyConnectionsApi() : impl_(std::make_unique()) {} - -NearbyConnectionsApi::~NearbyConnectionsApi() = default; - -NearbyConnectionsApi::NearbyConnectionsApi(NearbyConnectionsApi&&) noexcept = - default; - -NearbyConnectionsApi& NearbyConnectionsApi::operator=( - NearbyConnectionsApi&&) noexcept = default; - -void NearbyConnectionsApi::SetListener(Listener listener) { - std::scoped_lock lock(impl_->listener_state->mutex); - impl_->listener_state->listener = std::move(listener); -} - -void NearbyConnectionsApi::StartAdvertising( - const std::string& service_id, const std::vector& endpoint_info, - const AdvertisingOptions& options, - std::function callback) { - impl_->service->StartAdvertising( - service_id, endpoint_info, ToNativeAdvertisingOptions(options), - Impl::BuildConnectionListener(impl_->listener_state), - [callback = std::move(callback)](Status status) mutable { - if (callback) { - callback(ToFacadeStatus(status)); - } - }); -} - -void NearbyConnectionsApi::StopAdvertising( - const std::string& service_id, std::function callback) { - impl_->service->StopAdvertising( - service_id, [callback = std::move(callback)](Status status) mutable { - if (callback) { - callback(ToFacadeStatus(status)); - } - }); -} - -void NearbyConnectionsApi::StartDiscovery( - const std::string& service_id, const DiscoveryOptions& options, - std::function callback) { - impl_->service->StartDiscovery( - service_id, ToNativeDiscoveryOptions(options), - Impl::BuildDiscoveryListener(impl_->listener_state), - [callback = std::move(callback)](Status status) mutable { - if (callback) { - callback(ToFacadeStatus(status)); - } - }); -} - -void NearbyConnectionsApi::StopDiscovery( - const std::string& service_id, std::function callback) { - impl_->service->StopDiscovery( - service_id, [callback = std::move(callback)](Status status) mutable { - if (callback) { - callback(ToFacadeStatus(status)); - } - }); -} - -void NearbyConnectionsApi::RequestConnection( - const std::string& service_id, const std::vector& endpoint_info, - const std::string& endpoint_id, const ConnectionOptions& options, - std::function callback) { - impl_->service->RequestConnection( - service_id, endpoint_info, endpoint_id, ToNativeConnectionOptions(options), - Impl::BuildConnectionListener(impl_->listener_state), - [callback = std::move(callback)](Status status) mutable { - if (callback) { - callback(ToFacadeStatus(status)); - } - }); -} - -void NearbyConnectionsApi::DisconnectFromEndpoint( - const std::string& service_id, const std::string& endpoint_id, - std::function callback) { - impl_->service->DisconnectFromEndpoint( - service_id, endpoint_id, - [callback = std::move(callback)](Status status) mutable { - if (callback) { - callback(ToFacadeStatus(status)); - } - }); -} - -void NearbyConnectionsApi::SendPayload( - const std::string& service_id, const std::vector& endpoint_ids, - Payload payload, std::function callback) { - std::unique_ptr native_payload = - ToNativePayload(std::move(payload)); - if (native_payload == nullptr) { - if (callback) { - callback(StatusCode::kError); - } - return; - } - - impl_->service->SendPayload( - service_id, endpoint_ids, std::move(native_payload), - [callback = std::move(callback)](Status status) mutable { - if (callback) { - callback(ToFacadeStatus(status)); - } - }); -} - -void NearbyConnectionsApi::CancelPayload( - const std::string& service_id, int64_t payload_id, - std::function callback) { - impl_->service->CancelPayload( - service_id, payload_id, - [callback = std::move(callback)](Status status) mutable { - if (callback) { - callback(ToFacadeStatus(status)); - } - }); -} - -void NearbyConnectionsApi::InitiateBandwidthUpgrade( - const std::string& service_id, const std::string& endpoint_id, - std::function callback) { - impl_->service->InitiateBandwidthUpgrade( - service_id, endpoint_id, - [callback = std::move(callback)](Status status) mutable { - if (callback) { - callback(ToFacadeStatus(status)); - } - }); -} - -void NearbyConnectionsApi::AcceptConnection( - const std::string& service_id, const std::string& endpoint_id, - std::function callback) { - impl_->service->AcceptConnection( - service_id, endpoint_id, Impl::BuildPayloadListener(impl_->listener_state), - [callback = std::move(callback)](Status status) mutable { - if (callback) { - callback(ToFacadeStatus(status)); - } - }); -} - -void NearbyConnectionsApi::StopAllEndpoints( - std::function callback) { - impl_->service->StopAllEndpoints( - [callback = std::move(callback)](Status status) mutable { - if (callback) { - callback(ToFacadeStatus(status)); - } - }); -} - -void NearbyConnectionsApi::SetCustomSavePath( - const std::string& path, std::function callback) { - impl_->service->SetCustomSavePath( - path, [callback = std::move(callback)](Status status) mutable { - if (callback) { - callback(ToFacadeStatus(status)); - } - }); -} - -void NearbyConnectionsApi::OverrideSavePath(const std::string& endpoint_id, - const std::string& path) { - impl_->service->OverrideSavePath(endpoint_id, path); -} - -std::string NearbyConnectionsApi::Dump() const { return impl_->service->Dump(); } - -std::string NearbyConnectionsApi::StatusCodeToString(StatusCode status) { - switch (status) { - case StatusCode::kSuccess: - return "Success"; - case StatusCode::kError: - return "Error"; - case StatusCode::kOutOfOrderApiCall: - return "OutOfOrderApiCall"; - case StatusCode::kAlreadyHaveActiveStrategy: - return "AlreadyHaveActiveStrategy"; - case StatusCode::kAlreadyAdvertising: - return "AlreadyAdvertising"; - case StatusCode::kAlreadyDiscovering: - return "AlreadyDiscovering"; - case StatusCode::kAlreadyListening: - return "AlreadyListening"; - case StatusCode::kEndpointIOError: - return "EndpointIOError"; - case StatusCode::kEndpointUnknown: - return "EndpointUnknown"; - case StatusCode::kConnectionRejected: - return "ConnectionRejected"; - case StatusCode::kAlreadyConnectedToEndpoint: - return "AlreadyConnectedToEndpoint"; - case StatusCode::kNotConnectedToEndpoint: - return "NotConnectedToEndpoint"; - case StatusCode::kBluetoothError: - return "BluetoothError"; - case StatusCode::kBleError: - return "BleError"; - case StatusCode::kWifiLanError: - return "WifiLanError"; - case StatusCode::kPayloadUnknown: - return "PayloadUnknown"; - case StatusCode::kReset: - return "Reset"; - case StatusCode::kTimeout: - return "Timeout"; - case StatusCode::kUnknown: - return "Unknown"; - } - return "Unknown"; -} - -} // namespace nearby::sharing diff --git a/sharing/linux/nearby_connections_api.h b/sharing/linux/nearby_connections_api.h deleted file mode 100644 index 5be0975b..00000000 --- a/sharing/linux/nearby_connections_api.h +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2026 -// -// Thin app-facing API for NearbyConnectionsService on Linux that avoids -// exposing internal Nearby headers to external consumers. - -#ifndef SHARING_LINUX_NEARBY_CONNECTIONS_API_H_ -#define SHARING_LINUX_NEARBY_CONNECTIONS_API_H_ - -#include - -#include -#include -#include -#include - -#include "internal/platform/input_stream.h" - -namespace nearby { -namespace sharing { - -class __attribute__((visibility("default"))) NearbyConnectionsApi { - public: - enum class StatusCode { - kSuccess = 0, - kError = 1, - kOutOfOrderApiCall = 2, - kAlreadyHaveActiveStrategy = 3, - kAlreadyAdvertising = 4, - kAlreadyDiscovering = 5, - kAlreadyListening = 6, - kEndpointIOError = 7, - kEndpointUnknown = 8, - kConnectionRejected = 9, - kAlreadyConnectedToEndpoint = 10, - kNotConnectedToEndpoint = 11, - kBluetoothError = 12, - kBleError = 13, - kWifiLanError = 14, - kPayloadUnknown = 15, - kReset = 16, - kTimeout = 17, - kUnknown = 18, - }; - - enum class Strategy { - kP2pCluster = 0, - kP2pStar = 1, - kP2pPointToPoint = 2, - }; - - enum class Medium { - kUnknown = 0, - kMdns = 1, - kBluetooth = 2, - kWifiHotspot = 3, - kBle = 4, - kWifiLan = 5, - kWifiAware = 6, - kNfc = 7, - kWifiDirect = 8, - kWebRtc = 9, - kBleL2Cap = 10, - }; - - enum class DistanceInfo { - kUnknown = 1, - kVeryClose = 2, - kClose = 3, - kFar = 4, - }; - - enum class AuthenticationStatus { - kUnknown = 0, - kSuccess = 1, - kFailure = 2, - }; - - enum class PayloadType { - kUnknown = 0, - kBytes = 1, - kFile = 2, - kStream = 3, - }; - - enum class PayloadStatus { - kSuccess = 0, - kFailure = 1, - kInProgress = 2, - kCanceled = 3, - }; - - struct Uuid { - std::string uuid; - }; - - struct MediumSelection { - bool bluetooth = true; - bool ble = true; - bool web_rtc = true; - bool wifi_lan = true; - bool wifi_hotspot = true; - }; - - struct AdvertisingOptions { - Strategy strategy = Strategy::kP2pCluster; - MediumSelection allowed_mediums; - bool auto_upgrade_bandwidth = true; - bool enforce_topology_constraints = true; - bool enable_bluetooth_listening = false; - bool enable_webrtc_listening = false; - bool use_stable_endpoint_id = false; - bool force_new_endpoint_id = false; - std::string fast_advertisement_service_uuid; - }; - - struct DiscoveryOptions { - Strategy strategy = Strategy::kP2pCluster; - MediumSelection allowed_mediums; - bool has_fast_advertisement_service_uuid = false; - Uuid fast_advertisement_service_uuid; - bool is_out_of_band_connection = false; - bool has_alternate_service_uuid = false; - uint16_t alternate_service_uuid = 0; - }; - - struct ConnectionOptions { - MediumSelection allowed_mediums; - std::vector remote_bluetooth_mac_address; - bool has_keep_alive_interval_millis = false; - int64_t keep_alive_interval_millis = 0; - bool has_keep_alive_timeout_millis = false; - int64_t keep_alive_timeout_millis = 0; - bool non_disruptive_hotspot_mode = false; - }; - - struct ConnectionInfo { - std::string authentication_token; - std::vector raw_authentication_token; - std::vector endpoint_info; - bool is_incoming_connection = false; - StatusCode connection_layer_status = StatusCode::kUnknown; - AuthenticationStatus authentication_status = - AuthenticationStatus::kUnknown; - }; - - struct DiscoveredEndpointInfo { - std::vector endpoint_info; - std::string service_id; - }; - - struct PayloadTransferUpdate { - int64_t payload_id = 0; - PayloadStatus status = PayloadStatus::kInProgress; - uint64_t total_bytes = 0; - uint64_t bytes_transferred = 0; - }; - - struct Payload { - int64_t id = 0; - PayloadType type = PayloadType::kUnknown; - std::vector bytes; - std::vector stream_bytes; - std::shared_ptr stream_input; - std::string file_path; - std::string parent_folder; - - static Payload FromBytes(int64_t id, std::vector bytes) { - Payload payload; - payload.id = id; - payload.type = PayloadType::kBytes; - payload.bytes = std::move(bytes); - return payload; - } - - static Payload FromFile(int64_t id, std::string file_path, - std::string parent_folder = {}) { - Payload payload; - payload.id = id; - payload.type = PayloadType::kFile; - payload.file_path = std::move(file_path); - payload.parent_folder = std::move(parent_folder); - return payload; - } - - static Payload FromStream(int64_t id, std::vector stream_bytes) { - Payload payload; - payload.id = id; - payload.type = PayloadType::kStream; - payload.stream_bytes = std::move(stream_bytes); - return payload; - } - - static Payload FromInputStream(int64_t id, - std::shared_ptr stream_input) { - Payload payload; - payload.id = id; - payload.type = PayloadType::kStream; - payload.stream_input = std::move(stream_input); - return payload; - } - }; - - struct Listener { - std::function - endpoint_found_cb; - std::function endpoint_lost_cb; - std::function - endpoint_distance_changed_cb; - - std::function - connection_initiated_cb; - std::function connection_accepted_cb; - std::function connection_rejected_cb; - std::function disconnected_cb; - std::function bandwidth_changed_cb; - - std::function - payload_received_cb; - std::function - payload_transfer_update_cb; - }; - - NearbyConnectionsApi(); - ~NearbyConnectionsApi(); - - NearbyConnectionsApi(const NearbyConnectionsApi&) = delete; - NearbyConnectionsApi& operator=(const NearbyConnectionsApi&) = delete; - NearbyConnectionsApi(NearbyConnectionsApi&&) noexcept; - NearbyConnectionsApi& operator=(NearbyConnectionsApi&&) noexcept; - - void SetListener(Listener listener); - - void StartAdvertising(const std::string& service_id, - const std::vector& endpoint_info, - const AdvertisingOptions& options, - std::function callback); - void StopAdvertising(const std::string& service_id, - std::function callback); - - void StartDiscovery(const std::string& service_id, - const DiscoveryOptions& options, - std::function callback); - void StopDiscovery(const std::string& service_id, - std::function callback); - - void RequestConnection(const std::string& service_id, - const std::vector& endpoint_info, - const std::string& endpoint_id, - const ConnectionOptions& options, - std::function callback); - - void DisconnectFromEndpoint(const std::string& service_id, - const std::string& endpoint_id, - std::function callback); - - void SendPayload(const std::string& service_id, - const std::vector& endpoint_ids, - Payload payload, - std::function callback); - void CancelPayload(const std::string& service_id, int64_t payload_id, - std::function callback); - - void InitiateBandwidthUpgrade(const std::string& service_id, - const std::string& endpoint_id, - std::function callback); - - void AcceptConnection(const std::string& service_id, - const std::string& endpoint_id, - std::function callback); - - void StopAllEndpoints(std::function callback); - - void SetCustomSavePath(const std::string& path, - std::function callback); - void OverrideSavePath(const std::string& endpoint_id, - const std::string& path); - - std::string Dump() const; - - static std::string StatusCodeToString(StatusCode status); - - private: - class Impl; - std::unique_ptr impl_; -}; - -} // namespace sharing -} // namespace nearby - -#endif // SHARING_LINUX_NEARBY_CONNECTIONS_API_H_ diff --git a/sharing/linux/nearby_connections_api_test.cc b/sharing/linux/nearby_connections_api_test.cc deleted file mode 100644 index c34eefaa..00000000 --- a/sharing/linux/nearby_connections_api_test.cc +++ /dev/null @@ -1,75 +0,0 @@ -#include "sharing/linux/nearby_connections_api.h" - -#include -#include - -#include "gtest/gtest.h" -#include "internal/platform/pipe.h" - -namespace nearby::sharing { -namespace { - -TEST(NearbyConnectionsApiTest, PayloadFromBytesSetsFields) { - NearbyConnectionsApi::Payload payload = - NearbyConnectionsApi::Payload::FromBytes(42, {1, 2, 3}); - - EXPECT_EQ(payload.id, 42); - EXPECT_EQ(payload.type, NearbyConnectionsApi::PayloadType::kBytes); - EXPECT_EQ(payload.bytes, (std::vector{1, 2, 3})); - EXPECT_TRUE(payload.stream_bytes.empty()); - EXPECT_TRUE(payload.file_path.empty()); -} - -TEST(NearbyConnectionsApiTest, PayloadFromFileSetsFields) { - NearbyConnectionsApi::Payload payload = - NearbyConnectionsApi::Payload::FromFile(7, "/tmp/test.txt", "tmp"); - - EXPECT_EQ(payload.id, 7); - EXPECT_EQ(payload.type, NearbyConnectionsApi::PayloadType::kFile); - EXPECT_EQ(payload.file_path, "/tmp/test.txt"); - EXPECT_EQ(payload.parent_folder, "tmp"); - EXPECT_TRUE(payload.bytes.empty()); - EXPECT_TRUE(payload.stream_bytes.empty()); -} - -TEST(NearbyConnectionsApiTest, PayloadFromStreamSetsFields) { - NearbyConnectionsApi::Payload payload = - NearbyConnectionsApi::Payload::FromStream(9, {4, 5, 6}); - - EXPECT_EQ(payload.id, 9); - EXPECT_EQ(payload.type, NearbyConnectionsApi::PayloadType::kStream); - EXPECT_EQ(payload.stream_bytes, (std::vector{4, 5, 6})); - EXPECT_TRUE(payload.bytes.empty()); - EXPECT_TRUE(payload.file_path.empty()); -} - -TEST(NearbyConnectionsApiTest, PayloadFromInputStreamSetsFields) { - auto [input, output] = CreatePipe(); - (void)output; - - NearbyConnectionsApi::Payload payload = - NearbyConnectionsApi::Payload::FromInputStream( - 10, std::shared_ptr(std::move(input))); - - EXPECT_EQ(payload.id, 10); - EXPECT_EQ(payload.type, NearbyConnectionsApi::PayloadType::kStream); - EXPECT_TRUE(payload.stream_bytes.empty()); - EXPECT_NE(payload.stream_input, nullptr); - EXPECT_TRUE(payload.bytes.empty()); - EXPECT_TRUE(payload.file_path.empty()); -} - -TEST(NearbyConnectionsApiTest, StatusCodeToStringCoversRepresentativeValues) { - EXPECT_EQ(NearbyConnectionsApi::StatusCodeToString( - NearbyConnectionsApi::StatusCode::kSuccess), - "Success"); - EXPECT_EQ(NearbyConnectionsApi::StatusCodeToString( - NearbyConnectionsApi::StatusCode::kConnectionRejected), - "ConnectionRejected"); - EXPECT_EQ(NearbyConnectionsApi::StatusCodeToString( - NearbyConnectionsApi::StatusCode::kUnknown), - "Unknown"); -} - -} // namespace -} // namespace nearby::sharing diff --git a/sharing/linux/nearby_fast_init.cc b/sharing/linux/nearby_fast_init.cc new file mode 100644 index 00000000..b51995fb --- /dev/null +++ b/sharing/linux/nearby_fast_init.cc @@ -0,0 +1,50 @@ +#include "internal/platform/implementation/linux/bluetooth_adapter.h" +#include "internal/platform/implementation/linux/bluez.h" +#include "internal/platform/implementation/linux/dbus.h" +#include "sharing/linux/nearby_fast_init_ble_beacon.h" +#include "sharing/linux/nearby_fast_init_manager.h" +#include +#include + +// For local testing only. Android logs show that our fast init beacon is +// properly detected and they start advertising. + +int main() { + auto system_bus = ::nearby::linux::getSystemBusConnection(); + auto manager = ::nearby::linux::bluez::BluezObjectManager(*system_bus); + std::shared_ptr adapter; + auto interfaces = manager.GetManagedObjects(); + for (auto& [object, properties] : interfaces) { + if (properties.count(sdbus::InterfaceName( + org::bluez::Adapter1_proxy::INTERFACE_NAME)) == 1) { + LOG(INFO) << __func__ << ": found bluetooth adapter " << object; + adapter = std::make_shared<::nearby::linux::BluetoothAdapter>(system_bus, + object); + } + } + auto beacon = ::nearby::sharing::linux::LinuxFastInitBleBeacon(); + + try { + auto interfaces = manager.GetManagedObjects(); + for (auto& [object, properties] : interfaces) { + if (properties.count(sdbus::InterfaceName( + org::bluez::Adapter1_proxy::INTERFACE_NAME)) == 1) { + LOG(INFO) << __func__ << ": found bluetooth adapter " << object; + adapter = std::make_shared<::nearby::linux::BluetoothAdapter>( + system_bus, object); + } + } + } catch (const sdbus::Error& e) { + DBUS_LOG_METHOD_CALL_ERROR(&manager, "GetManagedObjects", e); + } + + auto fast_init_manager = + ::nearby::sharing::linux::LinuxFastInitiationManager(beacon, adapter); + + fast_init_manager.StartAdvertising( + nearby::api::FastInitBleBeacon::FastInitType::kNotify, []() { return; }, + [](nearby::api::FastInitiationManager::Error e) { return; }); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + return 0; +} diff --git a/sharing/linux/nearby_fast_init_ble_beacon.h b/sharing/linux/nearby_fast_init_ble_beacon.h new file mode 100644 index 00000000..14c083cc --- /dev/null +++ b/sharing/linux/nearby_fast_init_ble_beacon.h @@ -0,0 +1,87 @@ +#include "sharing/internal/api/fast_init_ble_beacon.h" + +namespace nearby { +namespace sharing { +namespace linux { + +class LinuxFastInitBleBeacon final : public nearby::api::FastInitBleBeacon { + public: + void SerializeToByteArray() override { + std::array + data{}; + std::size_t offset = 0; + + data[offset++] = nearby::api::FastInitBleBeacon::kFastInitServiceUuid[0]; + data[offset++] = nearby::api::FastInitBleBeacon::kFastInitServiceUuid[1]; + + for (uint8_t byte : nearby::api::FastInitBleBeacon::kFastInitModelId) { + data[offset++] = byte; + } + + const uint8_t metadata = (static_cast(this->GetVersion()) << 5) | + (static_cast(this->GetType()) << 2) | + (this->GetUwbSupported() ? 0x02 : 0x00) | + (this->GetSenderCertSupported() ? 0x01 : 0x00); + data[offset++] = metadata; + data[offset++] = static_cast(this->GetAdjustedTxPower()); + + for (uint8_t byte : this->GetUwbMetadata()) { + data[offset++] = byte; + } + for (uint8_t byte : this->GetUwbAddress()) { + data[offset++] = byte; + } + for (uint8_t byte : this->GetSalt()) { + data[offset++] = byte; + } + for (uint8_t byte : this->GetSecretIdHash()) { + data[offset++] = byte; + } + + data[offset++] = (this->GetRequireBtAdvertising() ? 0x80 : 0x00) | + (this->GetSelfOnlyAdvertising() ? 0x40 : 0x00); + SetAdDataByteArray(data); + } + + void ParseFromByteArray() override { + const auto data = GetAdDataByteArray(); + if (data[0] != nearby::api::FastInitBleBeacon::kFastInitServiceUuid[0] || + data[1] != nearby::api::FastInitBleBeacon::kFastInitServiceUuid[1]) { + return; + } + + const uint8_t metadata = data[5]; + SetVersion(static_cast((metadata >> 5) & 0x07)); + SetType(static_cast((metadata >> 2) & 0x07)); + SetUwbSupported((metadata & 0x02) != 0); + SetSenderCertSupported((metadata & 0x01) != 0); + SetAdjustedTxPower(static_cast(data[6])); + + std::array uwb_metadata{}; + uwb_metadata[0] = data[7]; + SetUwbMetadata(uwb_metadata); + + std::array uwb_address{}; + for (std::size_t i = 0; i < uwb_address.size(); ++i) { + uwb_address[i] = data[8 + i]; + } + SetUwbAddress(uwb_address); + + std::array salt{}; + salt[0] = data[16]; + SetSalt(salt); + + std::array secret_id_hash{}; + for (std::size_t i = 0; i < secret_id_hash.size(); ++i) { + secret_id_hash[i] = data[17 + i]; + } + SetSecretIdHash(secret_id_hash); + + const uint8_t flags = data[25]; + SetRequireBtAdvertising((flags & 0x80) != 0); + SetSelfOnlyAdvertising((flags & 0x40) != 0); + } +}; +} // namespace linux +} // namespace sharing +} // namespace nearby diff --git a/sharing/linux/nearby_fast_init_manager.cc b/sharing/linux/nearby_fast_init_manager.cc new file mode 100644 index 00000000..a0a47c03 --- /dev/null +++ b/sharing/linux/nearby_fast_init_manager.cc @@ -0,0 +1,142 @@ +#include + +#include "sharing/internal/api/fast_init_ble_beacon.h" +#include "sharing/internal/api/fast_initiation_manager.h" + +#include "internal/platform/implementation/linux/bluetooth_adapter.h" +#include "sharing/linux/nearby_fast_init_manager.h" +#include "internal/platform/implementation/linux/bluez.h" +#include "internal/platform/implementation/linux/bluez_le_advertisement.h" + +namespace nearby { +namespace sharing { +namespace linux { + +void LinuxFastInitiationManager::StartAdvertising( + nearby::api::FastInitBleBeacon::FastInitType type, + std::function callback, + std::function + error_callback) { + absl::MutexLock lock(&mutex_); + if (advertisement_ != nullptr) { + if (error_callback) { + error_callback(nearby::api::FastInitiationManager::Error::kResourceInUse); + } + return; + } + + if (adapter_ == nullptr || !adapter_->IsEnabled()) { + if (error_callback) { + error_callback(nearby::api::FastInitiationManager::Error:: + kBluetoothRadioUnavailable); + } + return; + } + + if (adv_manager_ == nullptr) { + if (error_callback) { + error_callback( + nearby::api::FastInitiationManager::Error::kHardwareNotSupported); + } + return; + } + + auto fast_init_uuid = Uuid::FromString(kFastInitServiceUuid); + if (!fast_init_uuid.has_value()) { + if (error_callback) { + error_callback(nearby::api::FastInitiationManager::Error::kUnknown); + } + return; + } + + beacon_.SetVersion(nearby::api::FastInitBleBeacon::FastInitVersion::kV1); + beacon_.SetType(type); + beacon_.SetUwbSupported(false); + beacon_.SetSenderCertSupported(false); + beacon_.SetAdjustedTxPower( + 45); // TODO: make this set the real value from adapter + beacon_.SetUwbMetadata({}); + beacon_.SetUwbAddress({}); + + beacon_.SetSalt({}); + beacon_.SetSecretIdHash({}); + beacon_.SetRequireBtAdvertising(true); + beacon_.SetSelfOnlyAdvertising(false); + beacon_.SerializeToByteArray(); + + const auto ad_data = beacon_.GetAdDataByteArray(); + nearby::api::ble::BleAdvertisementData advertising_data; + advertising_data.is_extended_advertisement = false; + advertising_data.service_data.insert( + {*fast_init_uuid, + nearby::ByteArray(reinterpret_cast(ad_data.data() + 2), + ad_data.size() - 2)}); + + nearby::api::ble::AdvertiseParameters advertising_parameters{ + .tx_power_level = nearby::api::ble::TxPowerLevel::kHigh, + .is_connectable = false, + }; + advertisement_ = + ::nearby::linux::bluez::LEAdvertisement::CreateLEAdvertisement( + *adapter_->GetConnection(), advertising_data, advertising_parameters); + + try { + adv_manager_->RegisterAdvertisementSync( + advertisement_->getObject().getObjectPath(), {}); + } catch (const sdbus::Error& e) { + advertisement_.reset(); + if (error_callback) { + if (e.getName() == "org.bluez.Error.AlreadyExists") { + error_callback( + nearby::api::FastInitiationManager::Error::kResourceInUse); + } else if (e.getName() == "org.bluez.Error.NotPermitted") { + error_callback( + nearby::api::FastInitiationManager::Error::kDisabledByUser); + } else { + error_callback(nearby::api::FastInitiationManager::Error::kUnknown); + } + } + return; + } + + if (callback) { + callback(); + } +} + +void LinuxFastInitiationManager::StopAdvertising( + std::function callback) { + absl::MutexLock lock(&mutex_); + if (advertisement_ != nullptr && adv_manager_ != nullptr) { + try { + adv_manager_->UnregisterAdvertisementSync( + advertisement_->getObject().getObjectPath()); + } catch (const sdbus::Error& e) { + DBUS_LOG_METHOD_CALL_ERROR(adv_manager_.get(), + "UnregisterAdvertisementSync", e); + } + advertisement_.reset(); + } + if (callback) { + callback(); + } +} +void StopScanning(std::function callback) { + if (callback) { + callback(); + } +} +void LinuxFastInitiationManager::StartScanning( + std::function devices_discovered_callback, + std::function devices_not_discovered_callback, + std::function + error_callback) { + if (error_callback) { + error_callback( + nearby::api::FastInitiationManager::Error::kHardwareNotSupported); + } +} +} // namespace linux +} // namespace sharing +} // namespace nearby + diff --git a/sharing/linux/nearby_fast_init_manager.h b/sharing/linux/nearby_fast_init_manager.h new file mode 100644 index 00000000..b2c35573 --- /dev/null +++ b/sharing/linux/nearby_fast_init_manager.h @@ -0,0 +1,87 @@ + +// 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 LINUX_NEARBY_SHARING_INTERNAL_API_FAST_INITIATION_MANAGER_H_ +#define LINUX_NEARBY_SHARING_INTERNAL_API_FAST_INITIATION_MANAGER_H_ + +#include + +#include "sharing/internal/api/fast_init_ble_beacon.h" +#include "sharing/internal/api/fast_initiation_manager.h" + +#include "internal/platform/implementation/linux/bluetooth_adapter.h" +#include "internal/platform/implementation/linux/bluez.h" +#include "internal/platform/implementation/linux/bluez_le_advertisement.h" + +constexpr char kFastInitServiceUuid[] = "0000fe2c-0000-1000-8000-00805f9b34fb"; + +namespace nearby { +namespace sharing { +namespace linux { + +class LinuxFastInitiationManager final + : public nearby::api::FastInitiationManager { + public: + explicit LinuxFastInitiationManager( + nearby::api::FastInitBleBeacon& beacon, + std::shared_ptr<::nearby::linux::BluetoothAdapter> bluetooth_adapter) + : beacon_(beacon), adapter_(std::move(bluetooth_adapter)) { + if (adapter_ != nullptr) { + adv_manager_ = + std::make_unique( + *adapter_->GetConnection(), *adapter_); + } + } + + void StartAdvertising( + nearby::api::FastInitBleBeacon::FastInitType type, + std::function callback, + std::function + error_callback) override ; + + void StopAdvertising(std::function callback) override; + + void StartScanning( + std::function devices_discovered_callback, + std::function devices_not_discovered_callback, + std::function + error_callback) override ; + + void StopScanning(std::function callback) override { + if (callback) { + callback(); + } + } + + bool IsAdvertising() override { + absl::MutexLock lock(&mutex_); + return advertisement_ != nullptr; + } + bool IsScanning() override { return false; } + + private: + nearby::api::FastInitBleBeacon& beacon_; + std::shared_ptr<::nearby::linux::BluetoothAdapter> adapter_; + std::unique_ptr<::nearby::linux::bluez::LEAdvertisementManager> adv_manager_; + absl::Mutex mutex_; + std::unique_ptr<::nearby::linux::bluez::LEAdvertisement> advertisement_ + ABSL_GUARDED_BY(mutex_); +}; + +} // namespace linux +} // namespace sharing +} // namespace nearby + +#endif // LINUX_NEARBY_SHARING_INTERNAL_API_FAST_INITIATION_MANAGER_H_ diff --git a/sharing/linux/nearby_noop_analytics_recorder.h b/sharing/linux/nearby_noop_analytics_recorder.h new file mode 100644 index 00000000..c396385d --- /dev/null +++ b/sharing/linux/nearby_noop_analytics_recorder.h @@ -0,0 +1,142 @@ +#include "sharing/analytics/analytics_device_settings.h" +#include "sharing/analytics/analytics_information.h" +#include +#include + +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "proto/sharing_enums.pb.h" +#include "sharing/analytics/analytics_recorder.h" +#include "sharing/attachment_container.h" +#include "sharing/common/nearby_share_enums.h" +#include "sharing/share_target.h" + +namespace nearby::sharing::linux { +class NoOpAnalyticsRecorder final : public analytics::AnalyticsRecorder { + public: + void NewEstablishConnection( + int64_t session_id, + location::nearby::proto::sharing::EstablishConnectionStatus + connection_status, + const ShareTarget& share_target, int transfer_position, + int concurrent_connections, int64_t duration_millis, + std::optional referrer_package) override {} + void NewAcceptAgreements() override {} + void NewDeclineAgreements() override {} + void NewAddContact() override {} + void NewRemoveContact() override {} + void NewTapFeedback() override {} + void NewTapHelp() override {} + void NewLaunchDeviceContactConsent( + location::nearby::proto::sharing::ConsentAcceptanceStatus status) + override {} + void NewAdvertiseDevicePresenceEnd(int64_t session_id) override {} + void NewAdvertiseDevicePresenceStart( + int64_t session_id, proto::DeviceVisibility visibility, + location::nearby::proto::sharing::SessionStatus status, + proto::DataUsage data_usage, + std::optional referrer_package) override {} + void NewDescribeAttachments(const AttachmentContainer& attachments) override {} + void NewDiscoverShareTarget( + const ShareTarget& share_target, int64_t session_id, + int64_t latency_since_scanning_start_millis, int64_t flow_id, + std::optional referrer_package, + int64_t latency_since_send_surface_registered_millis) override {} + void NewEnableNearbySharing( + location::nearby::proto::sharing::NearbySharingStatus status) override {} + void NewOpenReceivedAttachments(const AttachmentContainer& attachments, + int64_t session_id) override {} + void NewProcessReceivedAttachmentsEnd( + int64_t session_id, + location::nearby::proto::sharing::ProcessReceivedAttachmentsStatus status) + override {} + void NewReceiveAttachmentsEnd( + int64_t session_id, int64_t received_bytes, + location::nearby::proto::sharing::AttachmentTransmissionStatus status, + std::optional referrer_package) override {} + void NewReceiveAttachmentsStart( + int64_t session_id, const AttachmentContainer& attachments) override {} + void NewReceiveFastInitialization( + int64_t timeElapseSinceScreenUnlockMillis) override {} + void NewAcceptFastInitialization() override {} + void NewDismissFastInitialization() override {} + void NewReceiveIntroduction( + int64_t session_id, const ShareTarget& share_target, + std::optional referrer_package, + location::nearby::proto::sharing::OSType share_target_os_type) override {} + void NewRespondToIntroduction( + location::nearby::proto::sharing::ResponseToIntroduction action, + int64_t session_id) override {} + void NewTapPrivacyNotification() override {} + void NewDismissPrivacyNotification() override {} + void NewScanForShareTargetsEnd(int64_t session_id) override {} + void NewScanForShareTargetsStart( + int64_t session_id, + location::nearby::proto::sharing::SessionStatus status, + analytics::AnalyticsInformation analytics_information, int64_t flow_id, + std::optional referrer_package) override {} + void NewSendAttachmentsEnd( + int64_t session_id, int64_t sent_bytes, const ShareTarget& share_target, + location::nearby::proto::sharing::AttachmentTransmissionStatus status, + int transfer_position, int concurrent_connections, + int64_t duration_millis, std::optional referrer_package, + location::nearby::proto::sharing::ConnectionLayerStatus + connection_layer_status, + location::nearby::proto::sharing::OSType share_target_os_type) override {} + void NewSendAttachmentsStart(int64_t session_id, + const AttachmentContainer& attachments, + int transfer_position, + int concurrent_connections, + bool advanced_protection_enabled, + bool advanced_protection_mismatch) override {} + void NewSendFastInitialization() override {} + void NewSendStart(int64_t session_id, int transfer_position, + int concurrent_connections, + const ShareTarget& share_target) override {} + void NewSendIntroduction( + ShareTargetType target_type, int64_t session_id, + location::nearby::proto::sharing::DeviceRelationship relationship, + location::nearby::proto::sharing::OSType share_target_os_type) override {} + void NewSendIntroduction( + int64_t session_id, const ShareTarget& share_target, + int transfer_position, int concurrent_connections, + location::nearby::proto::sharing::OSType share_target_os_type) override {} + void NewSetVisibility(proto::DeviceVisibility src_visibility, + proto::DeviceVisibility dst_visibility, + int64_t duration_millis) override {} + void NewDeviceSettings(analytics::AnalyticsDeviceSettings settings) override { + } + void NewSetDataUsage(proto::DataUsage original_preference, + proto::DataUsage preference) override {} + void NewAddQuickSettingsTile() override {} + void NewRemoveQuickSettingsTile() override {} + void NewTapQuickSettingsTile() override {} + void NewToggleShowNotification( + location::nearby::proto::sharing::ShowNotificationStatus prev_status, + location::nearby::proto::sharing::ShowNotificationStatus current_status) + override {} + void NewSetDeviceName(int device_name_size) override {} + void NewRequestSettingPermissions( + location::nearby::proto::sharing::PermissionRequestType type, + location::nearby::proto::sharing::PermissionRequestResult result) + override {} + void NewInstallAPKStatus( + location::nearby::proto::sharing::InstallAPKStatus status, + location::nearby::proto::sharing::ApkSource source) override {} + void NewVerifyAPKStatus( + location::nearby::proto::sharing::VerifyAPKStatus status, + location::nearby::proto::sharing::ApkSource source) override {} + void NewRpcCallStatus(absl::string_view rpc_name, RpcDirection direction, + int error_code, absl::Duration latency) override {} + int64_t GenerateNextId() override { return next_id_++; } + + private: + std::atomic next_id_{1}; +}; +} diff --git a/sharing/linux/nearby_sharing_api.cc b/sharing/linux/nearby_sharing_api.cc deleted file mode 100644 index febeac11..00000000 --- a/sharing/linux/nearby_sharing_api.cc +++ /dev/null @@ -1,680 +0,0 @@ -#include "sharing/linux/nearby_sharing_api.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "absl/strings/escaping.h" -#include "absl/time/time.h" -#include "connections/implementation/flags/nearby_connections_feature_flags.h" -#include "internal/base/file_path.h" -#include "internal/base/files.h" -#include "internal/crypto_cros/ec_private_key.h" -#include "internal/flags/nearby_flags.h" -#include "internal/platform/implementation/linux/linux_flags.h" -#include "sharing/analytics/analytics_recorder.h" -#include "sharing/attachment_container.h" -#include "sharing/file_attachment.h" -#include "sharing/flags/generated/nearby_sharing_feature_flags.h" -#include "sharing/linux/platform/linux_sharing_platform.h" -#include "sharing/local_device_data/nearby_share_local_device_data_manager.h" -#include "sharing/nearby_sharing_service_factory.h" -#include "sharing/proto/enums.pb.h" -#include "sharing/share_target_discovered_callback.h" -#include "sharing/transfer_metadata.h" -#include "sharing/transfer_update_callback.h" - -namespace nearby::sharing { - -namespace { - -using NativeService = nearby::sharing::NearbySharingService; - -void EnableBleL2capDefaults() { - nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( - nearby::connections::config_package_nearby::nearby_connections_feature:: - kEnableBleL2cap, - true); - nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( - nearby::sharing::config_package_nearby::nearby_sharing_feature:: - kEnableBleForTransfer, - true); - //nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( - // nearby::connections::config_package_nearby::nearby_connections_feature:: - // kRefactorBleL2cap, - // false); -} - -NearbySharingApi::StatusCode ToFacadeStatus( - nearby::sharing::NearbySharingService::StatusCodes status) { - switch (status) { - case nearby::sharing::NearbySharingService::StatusCodes::kOk: - return NearbySharingApi::StatusCode::kOk; - case nearby::sharing::NearbySharingService::StatusCodes::kError: - return NearbySharingApi::StatusCode::kError; - case nearby::sharing::NearbySharingService::StatusCodes::kOutOfOrderApiCall: - return NearbySharingApi::StatusCode::kOutOfOrderApiCall; - case nearby::sharing::NearbySharingService::StatusCodes::kStatusAlreadyStopped: - return NearbySharingApi::StatusCode::kStatusAlreadyStopped; - case nearby::sharing::NearbySharingService::StatusCodes::kTransferAlreadyInProgress: - return NearbySharingApi::StatusCode::kTransferAlreadyInProgress; - case nearby::sharing::NearbySharingService::StatusCodes::kNoAvailableConnectionMedium: - return NearbySharingApi::StatusCode::kNoAvailableConnectionMedium; - case nearby::sharing::NearbySharingService::StatusCodes::kIrrecoverableHardwareError: - return NearbySharingApi::StatusCode::kIrrecoverableHardwareError; - case nearby::sharing::NearbySharingService::StatusCodes::kInvalidArgument: - return NearbySharingApi::StatusCode::kInvalidArgument; - } - return NearbySharingApi::StatusCode::kError; -} - -NearbySharingApi::TransferStatus ToFacadeTransferStatus( - nearby::sharing::TransferMetadata::Status status) { - using NativeStatus = nearby::sharing::TransferMetadata::Status; - using FacadeStatus = NearbySharingApi::TransferStatus; - switch (status) { - case NativeStatus::kUnknown: - return FacadeStatus::kUnknown; - case NativeStatus::kConnecting: - return FacadeStatus::kConnecting; - case NativeStatus::kAwaitingLocalConfirmation: - return FacadeStatus::kAwaitingLocalConfirmation; - case NativeStatus::kAwaitingRemoteAcceptance: - return FacadeStatus::kAwaitingRemoteAcceptance; - case NativeStatus::kInProgress: - return FacadeStatus::kInProgress; - case NativeStatus::kComplete: - return FacadeStatus::kComplete; - case NativeStatus::kFailed: - return FacadeStatus::kFailed; - case NativeStatus::kRejected: - return FacadeStatus::kRejected; - case NativeStatus::kCancelled: - return FacadeStatus::kCancelled; - case NativeStatus::kTimedOut: - return FacadeStatus::kTimedOut; - case NativeStatus::kMediaUnavailable: - return FacadeStatus::kMediaUnavailable; - case NativeStatus::kNotEnoughSpace: - return FacadeStatus::kNotEnoughSpace; - case NativeStatus::kUnsupportedAttachmentType: - return FacadeStatus::kUnsupportedAttachmentType; - case NativeStatus::kDeviceAuthenticationFailed: - return FacadeStatus::kDeviceAuthenticationFailed; - case NativeStatus::kIncompletePayloads: - return FacadeStatus::kIncompletePayloads; - } - return FacadeStatus::kUnknown; -} - -NearbySharingApi::TextAttachmentType ToFacadeTextAttachmentType( - nearby::sharing::TextAttachment::Type type) { - using FacadeType = NearbySharingApi::TextAttachmentType; - switch (type) { - case nearby::sharing::service::proto::TextMetadata::TEXT: - return FacadeType::kText; - case nearby::sharing::service::proto::TextMetadata::URL: - return FacadeType::kUrl; - case nearby::sharing::service::proto::TextMetadata::PHONE_NUMBER: - return FacadeType::kPhoneNumber; - case nearby::sharing::service::proto::TextMetadata::ADDRESS: - return FacadeType::kAddress; - case nearby::sharing::service::proto::TextMetadata::UNKNOWN: - return FacadeType::kUnknown; - } - return FacadeType::kUnknown; -} - -float NormalizeFacadeProgress(float progress) { - if (progress <= 0.0f) { - return 0.0f; - } - if (progress >= 100.0f) { - return 1.0f; - } - return progress / 100.0f; -} - -std::string GenerateQrCodeUrl() { - auto ec_key = nearby::crypto::ECPrivateKey::Create(); - if (!ec_key) { - return {}; - } - - const EC_KEY* raw_ec_key = EVP_PKEY_get0_EC_KEY(ec_key->key()); - if (!raw_ec_key) { - return {}; - } - - const EC_GROUP* group = EC_KEY_get0_group(raw_ec_key); - const EC_POINT* public_key = EC_KEY_get0_public_key(raw_ec_key); - if (!group || !public_key) { - return {}; - } - - BIGNUM* x = BN_new(); - BIGNUM* y = BN_new(); - if (!x || !y) { - BN_free(x); - BN_free(y); - return {}; - } - - if (!EC_POINT_get_affine_coordinates_GFp(group, public_key, x, y, nullptr)) { - BN_free(x); - BN_free(y); - return {}; - } - - std::vector x_bytes(32, 0); - const int x_len = BN_num_bytes(x); - if (x_len > static_cast(x_bytes.size())) { - BN_free(x); - BN_free(y); - return {}; - } - BN_bn2bin(x, x_bytes.data() + (x_bytes.size() - x_len)); - - const uint8_t prefix = BN_is_odd(y) ? 0x03 : 0x02; - BN_free(x); - BN_free(y); - - std::vector key_data; - key_data.reserve(35); - key_data.push_back(0x00); - key_data.push_back(0x00); - key_data.push_back(prefix); - key_data.insert(key_data.end(), x_bytes.begin(), x_bytes.end()); - - std::string encoded; - absl::WebSafeBase64Escape( - std::string(reinterpret_cast(key_data.data()), - key_data.size()), - &encoded); - return "https://quickshare.google/qrcode#key=" + encoded; -} - -} // namespace - -class NearbySharingApi::Impl : public nearby::sharing::ShareTargetDiscoveredCallback, - public nearby::sharing::TransferUpdateCallback { - public: - Impl() - : analytics_recorder(0, nullptr), - platform(), - service(NearbySharingServiceFactory::GetInstance()->CreateSharingService( - platform, &analytics_recorder, /*event_logger=*/nullptr, - /*supports_file_sync=*/false)) {} - - explicit Impl(std::string device_name_override) - : analytics_recorder(0, nullptr), - device_name_override(device_name_override), - platform(device_name_override), - service(NearbySharingServiceFactory::GetInstance()->CreateSharingService( - platform, &analytics_recorder, /*event_logger=*/nullptr, - /*supports_file_sync=*/false)) { - if (service != nullptr && !device_name_override.empty() && - service->GetLocalDeviceDataManager() != nullptr) { - service->GetLocalDeviceDataManager()->SetDeviceName(device_name_override); - } - } - - void OnShareTargetDiscovered(const nearby::sharing::ShareTarget& share_target) - override { - Listener listener_copy; - { - std::scoped_lock lock(listener_mutex); - listener_copy = listener; - } - if (!listener_copy.target_discovered_cb) { - return; - } - listener_copy.target_discovered_cb(ToShareTargetInfo(share_target)); - } - - void OnShareTargetLost(const nearby::sharing::ShareTarget& share_target) - override { - Listener listener_copy; - { - std::scoped_lock lock(listener_mutex); - listener_copy = listener; - } - if (!listener_copy.target_lost_cb) { - return; - } - listener_copy.target_lost_cb(share_target.id); - } - - void OnShareTargetUpdated(const nearby::sharing::ShareTarget& share_target) - override { - Listener listener_copy; - { - std::scoped_lock lock(listener_mutex); - listener_copy = listener; - } - if (!listener_copy.target_updated_cb) { - return; - } - listener_copy.target_updated_cb(ToShareTargetInfo(share_target)); - } - - void OnTransferUpdate( - const nearby::sharing::ShareTarget& share_target, - const nearby::sharing::AttachmentContainer& attachment_container, - const nearby::sharing::TransferMetadata& transfer_metadata) override { - Listener listener_copy; - { - std::scoped_lock lock(listener_mutex); - listener_copy = listener; - } - if (!listener_copy.transfer_update_cb) { - return; - } - - NearbySharingApi::TransferUpdateInfo info; - info.share_target_id = share_target.id; - info.device_name = share_target.device_name; - info.is_incoming = share_target.is_incoming; - info.status = ToFacadeTransferStatus(transfer_metadata.status()); - info.progress = NormalizeFacadeProgress(transfer_metadata.progress()); - info.transferred_bytes = transfer_metadata.transferred_bytes(); - info.total_attachments = transfer_metadata.total_attachments_count(); - info.transferred_attachments = transfer_metadata.transferred_attachments_count(); - if (!attachment_container.GetFileAttachments().empty()) { - const nearby::sharing::FileAttachment& file = - attachment_container.GetFileAttachments().front(); - info.first_file_name = std::string(file.file_name()); - if (file.file_path().has_value()) { - info.first_file_path = file.file_path()->ToString(); - } - } - info.text_attachments.reserve( - attachment_container.GetTextAttachments().size()); - for (const nearby::sharing::TextAttachment& text : - attachment_container.GetTextAttachments()) { - NearbySharingApi::TextAttachmentInfo text_info; - text_info.type = ToFacadeTextAttachmentType(text.type()); - text_info.text_title = std::string(text.text_title()); - text_info.text_body = std::string(text.text_body()); - info.text_attachments.push_back(std::move(text_info)); - } - listener_copy.transfer_update_cb(info); - } - - NearbySharingApi::ShareTargetInfo ToShareTargetInfo( - const nearby::sharing::ShareTarget& share_target) { - NearbySharingApi::ShareTargetInfo info; - info.id = share_target.id; - info.device_name = share_target.device_name; - info.is_incoming = share_target.is_incoming; - info.device_type = static_cast(share_target.type); - return info; - } - - nearby::sharing::analytics::AnalyticsRecorder analytics_recorder; - std::string device_name_override; - LinuxSharingPlatform platform; - NativeService* service = nullptr; - bool send_mode_started = false; - bool receive_mode_started = false; - std::string qr_code_url; - std::mutex listener_mutex; - NearbySharingApi::Listener listener; -}; - -NearbySharingApi::NearbySharingApi() { - EnableBleL2capDefaults(); - impl_ = std::make_unique(); -} - -NearbySharingApi::NearbySharingApi(std::string device_name_override) - : impl_(nullptr) { - EnableBleL2capDefaults(); - impl_ = std::make_unique(std::move(device_name_override)); -} - -NearbySharingApi::~NearbySharingApi() = default; - -NearbySharingApi::NearbySharingApi(NearbySharingApi&&) noexcept = default; - -NearbySharingApi& NearbySharingApi::operator=(NearbySharingApi&&) noexcept = default; - -void NearbySharingApi::SetListener(Listener listener) { - std::scoped_lock lock(impl_->listener_mutex); - impl_->listener = std::move(listener); -} - -void NearbySharingApi::StartSendMode(std::function callback) { - if (impl_->service == nullptr) { - if (callback) { - callback(StatusCode::kError); - } - return; - } - if (impl_->send_mode_started) { - if (callback) { - callback(StatusCode::kOk); - } - return; - } - impl_->service->RegisterSendSurface( - impl_.get(), impl_.get(), - nearby::sharing::NearbySharingService::SendSurfaceState::kForeground, - nearby::sharing::Advertisement::BlockedVendorId::kNone, - /*disable_wifi_hotspot=*/false, - [this, cb = std::move(callback)]( - nearby::sharing::NearbySharingService::StatusCodes status) mutable { - if (status == nearby::sharing::NearbySharingService::StatusCodes::kOk) { - impl_->send_mode_started = true; - } - if (cb) { - cb(ToFacadeStatus(status)); - } - }); -} - -void NearbySharingApi::StopSendMode(std::function callback) { - if (impl_->service == nullptr) { - if (callback) { - callback(StatusCode::kError); - } - return; - } - if (!impl_->send_mode_started) { - if (callback) { - callback(StatusCode::kStatusAlreadyStopped); - } - return; - } - impl_->service->UnregisterSendSurface( - impl_.get(), - [this, cb = std::move(callback)]( - nearby::sharing::NearbySharingService::StatusCodes status) mutable { - if (status == nearby::sharing::NearbySharingService::StatusCodes::kOk) { - impl_->send_mode_started = false; - } - if (cb) { - cb(ToFacadeStatus(status)); - } - }); -} - -void NearbySharingApi::StartReceiveMode(std::function callback) { - if (impl_->service == nullptr) { - if (callback) { - callback(StatusCode::kError); - } - return; - } - if (impl_->receive_mode_started) { - if (callback) { - callback(StatusCode::kOk); - } - return; - } - impl_->service->SetVisibility( - nearby::sharing::proto::DeviceVisibility::DEVICE_VISIBILITY_EVERYONE, - absl::Minutes(10), - [this, cb = std::move(callback)]( - nearby::sharing::NearbySharingService::StatusCodes status) mutable { - if (status != nearby::sharing::NearbySharingService::StatusCodes::kOk) { - if (cb) { - cb(ToFacadeStatus(status)); - } - return; - } - impl_->service->RegisterReceiveSurface( - impl_.get(), - nearby::sharing::NearbySharingService::ReceiveSurfaceState:: - kForeground, - nearby::sharing::Advertisement::BlockedVendorId::kNone, - [this, cb = std::move(cb)]( - nearby::sharing::NearbySharingService::StatusCodes status) - mutable { - if (status == - nearby::sharing::NearbySharingService::StatusCodes::kOk) { - impl_->receive_mode_started = true; - } - if (cb) { - cb(ToFacadeStatus(status)); - } - }); - }); -} - -void NearbySharingApi::StopReceiveMode(std::function callback) { - if (impl_->service == nullptr) { - if (callback) { - callback(StatusCode::kError); - } - return; - } - if (!impl_->receive_mode_started) { - if (callback) { - callback(StatusCode::kStatusAlreadyStopped); - } - return; - } - impl_->service->UnregisterReceiveSurface( - impl_.get(), - [this, cb = std::move(callback)]( - nearby::sharing::NearbySharingService::StatusCodes status) mutable { - if (status == nearby::sharing::NearbySharingService::StatusCodes::kOk) { - impl_->receive_mode_started = false; - } - if (cb) { - cb(ToFacadeStatus(status)); - } - }); -} - -void NearbySharingApi::SendFile(int64_t share_target_id, - const std::string& file_path, - std::function callback) { - if (impl_->service == nullptr) { - if (callback) { - callback(StatusCode::kError); - } - return; - } - if (file_path.empty()) { - if (callback) { - callback(StatusCode::kInvalidArgument); - } - return; - } - - FilePath path(file_path); - std::optional file_size = nearby::Files::GetFileSize(path); - if (!file_size.has_value() || *file_size == 0 || - *file_size > - static_cast(std::numeric_limits::max())) { - if (callback) { - callback(StatusCode::kInvalidArgument); - } - return; - } - - nearby::sharing::AttachmentContainer::Builder builder; - nearby::sharing::FileAttachment attachment(path); - attachment.set_size(static_cast(*file_size)); - builder.AddFileAttachment(std::move(attachment)); - std::unique_ptr attachments = - builder.Build(); - if (!attachments || !attachments->HasAttachments()) { - if (callback) { - callback(StatusCode::kInvalidArgument); - } - return; - } - - impl_->service->SendAttachments( - share_target_id, std::move(attachments), - [cb = std::move(callback)]( - nearby::sharing::NearbySharingService::StatusCodes status) mutable { - if (cb) { - cb(ToFacadeStatus(status)); - } - }); -} - -void NearbySharingApi::Accept(int64_t share_target_id, - std::function callback) { - if (impl_->service == nullptr) { - if (callback) { - callback(StatusCode::kError); - } - return; - } - impl_->service->Accept( - share_target_id, - [cb = std::move(callback)]( - nearby::sharing::NearbySharingService::StatusCodes status) mutable { - if (cb) { - cb(ToFacadeStatus(status)); - } - }); -} - -void NearbySharingApi::Reject(int64_t share_target_id, - std::function callback) { - if (impl_->service == nullptr) { - if (callback) { - callback(StatusCode::kError); - } - return; - } - impl_->service->Reject( - share_target_id, - [cb = std::move(callback)]( - nearby::sharing::NearbySharingService::StatusCodes status) mutable { - if (cb) { - cb(ToFacadeStatus(status)); - } - }); -} - -void NearbySharingApi::Cancel(int64_t share_target_id, - std::function callback) { - if (impl_->service == nullptr) { - if (callback) { - callback(StatusCode::kError); - } - return; - } - impl_->service->Cancel( - share_target_id, - [cb = std::move(callback)]( - nearby::sharing::NearbySharingService::StatusCodes status) mutable { - if (cb) { - cb(ToFacadeStatus(status)); - } - }); -} - -void NearbySharingApi::Set5GhzHotspotEnabled(bool enabled) { - nearby::linux::Set5GhzHotspotEnabled(enabled); -} - -void NearbySharingApi::SetDeviceName(const std::string& device_name) { - if (impl_->service == nullptr || device_name.empty()) { - return; - } - if (impl_->service->GetLocalDeviceDataManager() == nullptr) { - return; - } - impl_->service->GetLocalDeviceDataManager()->SetDeviceName(device_name); -} - -void NearbySharingApi::Shutdown(std::function callback) { - if (impl_->service == nullptr) { - if (callback) { - callback(StatusCode::kError); - } - return; - } - impl_->service->Shutdown( - [this, cb = std::move(callback)]( - nearby::sharing::NearbySharingService::StatusCodes status) mutable { - if (status == nearby::sharing::NearbySharingService::StatusCodes::kOk) { - impl_->send_mode_started = false; - impl_->receive_mode_started = false; - } - if (cb) { - cb(ToFacadeStatus(status)); - } - }); -} - -std::string NearbySharingApi::GetQrCodeUrl() const { - if (impl_->qr_code_url.empty()) { - impl_->qr_code_url = GenerateQrCodeUrl(); - } - return impl_->qr_code_url; -} - -std::string NearbySharingApi::StatusCodeToString(StatusCode status) { - switch (status) { - case StatusCode::kOk: - return "Ok"; - case StatusCode::kError: - return "Error"; - case StatusCode::kOutOfOrderApiCall: - return "OutOfOrderApiCall"; - case StatusCode::kStatusAlreadyStopped: - return "StatusAlreadyStopped"; - case StatusCode::kTransferAlreadyInProgress: - return "TransferAlreadyInProgress"; - case StatusCode::kNoAvailableConnectionMedium: - return "NoAvailableConnectionMedium"; - case StatusCode::kIrrecoverableHardwareError: - return "IrrecoverableHardwareError"; - case StatusCode::kInvalidArgument: - return "InvalidArgument"; - } - return "Error"; -} - -std::string NearbySharingApi::TransferStatusToString(TransferStatus status) { - switch (status) { - case TransferStatus::kUnknown: - return "Unknown"; - case TransferStatus::kConnecting: - return "Connecting"; - case TransferStatus::kAwaitingLocalConfirmation: - return "AwaitingLocalConfirmation"; - case TransferStatus::kAwaitingRemoteAcceptance: - return "AwaitingRemoteAcceptance"; - case TransferStatus::kInProgress: - return "InProgress"; - case TransferStatus::kComplete: - return "Complete"; - case TransferStatus::kFailed: - return "Failed"; - case TransferStatus::kRejected: - return "Rejected"; - case TransferStatus::kCancelled: - return "Cancelled"; - case TransferStatus::kTimedOut: - return "TimedOut"; - case TransferStatus::kMediaUnavailable: - return "MediaUnavailable"; - case TransferStatus::kNotEnoughSpace: - return "NotEnoughSpace"; - case TransferStatus::kUnsupportedAttachmentType: - return "UnsupportedAttachmentType"; - case TransferStatus::kDeviceAuthenticationFailed: - return "DeviceAuthenticationFailed"; - case TransferStatus::kIncompletePayloads: - return "IncompletePayloads"; - } - return "Unknown"; -} - -} // namespace nearby::sharing diff --git a/sharing/linux/nearby_sharing_api.h b/sharing/linux/nearby_sharing_api.h deleted file mode 100644 index dd8baee5..00000000 --- a/sharing/linux/nearby_sharing_api.h +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2026 -// -// Thin app-facing API for NearbySharingServiceLinux that avoids exposing -// internal Nearby headers to external consumers. - -#ifndef SHARING_LINUX_NEARBY_SHARING_API_H_ -#define SHARING_LINUX_NEARBY_SHARING_API_H_ - -#include - -#include -#include -#include -#include - -namespace nearby { -namespace sharing { - -class __attribute__((visibility("default"))) NearbySharingApi { - public: - enum class StatusCode { - kOk = 0, - kError = 1, - kOutOfOrderApiCall = 2, - kStatusAlreadyStopped = 3, - kTransferAlreadyInProgress = 4, - kNoAvailableConnectionMedium = 5, - kIrrecoverableHardwareError = 6, - kInvalidArgument = 7, - }; - - enum class TransferStatus { - kUnknown = 0, - kConnecting = 1, - kAwaitingLocalConfirmation = 2, - kAwaitingRemoteAcceptance = 3, - kInProgress = 4, - kComplete = 5, - kFailed = 6, - kRejected = 7, - kCancelled = 8, - kTimedOut = 9, - kMediaUnavailable = 10, - kNotEnoughSpace = 11, - kUnsupportedAttachmentType = 12, - kDeviceAuthenticationFailed = 13, - kIncompletePayloads = 14, - }; - - enum class TextAttachmentType { - kUnknown = 0, - kText = 1, - kUrl = 2, - kPhoneNumber = 3, - kAddress = 4, - }; - - struct ShareTargetInfo { - int64_t id = 0; - std::string device_name; - bool is_incoming = false; - int device_type = 0; - }; - - struct TextAttachmentInfo { - TextAttachmentType type = TextAttachmentType::kUnknown; - std::string text_title; - std::string text_body; - }; - - struct TransferUpdateInfo { - int64_t share_target_id = 0; - std::string device_name; - bool is_incoming = false; - TransferStatus status = TransferStatus::kUnknown; - float progress = 0.0f; - uint64_t transferred_bytes = 0; - int total_attachments = 0; - int transferred_attachments = 0; - std::string first_file_name; - std::string first_file_path; - std::vector text_attachments; - }; - - struct Listener { - std::function target_discovered_cb; - std::function target_updated_cb; - std::function target_lost_cb; - std::function transfer_update_cb; - }; - - NearbySharingApi(); - explicit NearbySharingApi(std::string device_name_override); - ~NearbySharingApi(); - - NearbySharingApi(const NearbySharingApi&) = delete; - NearbySharingApi& operator=(const NearbySharingApi&) = delete; - NearbySharingApi(NearbySharingApi&&) noexcept; - NearbySharingApi& operator=(NearbySharingApi&&) noexcept; - - void SetListener(Listener listener); - - void StartSendMode(std::function callback); - void StopSendMode(std::function callback); - - void StartReceiveMode(std::function callback); - void StopReceiveMode(std::function callback); - - void SendFile(int64_t share_target_id, const std::string& file_path, - std::function callback); - void Accept(int64_t share_target_id, std::function callback); - void Reject(int64_t share_target_id, std::function callback); - void Cancel(int64_t share_target_id, std::function callback); - void Set5GhzHotspotEnabled(bool enabled); - void SetDeviceName(const std::string& device_name); - - void Shutdown(std::function callback); - std::string GetQrCodeUrl() const; - - static std::string StatusCodeToString(StatusCode status); - static std::string TransferStatusToString(TransferStatus status); - - private: - class Impl; - std::unique_ptr impl_; -}; - -} // namespace sharing -} // namespace nearby - -#endif // SHARING_LINUX_NEARBY_SHARING_API_H_ diff --git a/sharing/linux/nearby_sharing_cli.cc b/sharing/linux/nearby_sharing_cli.cc new file mode 100644 index 00000000..1895b9a5 --- /dev/null +++ b/sharing/linux/nearby_sharing_cli.cc @@ -0,0 +1,468 @@ +// Copyright 2026 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "absl/time/time.h" +#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "internal/base/file_path.h" +#include "internal/flags/nearby_flags.h" +#include "sharing/advertisement.h" +#include "sharing/attachment_container.h" +#include "sharing/common/nearby_share_enums.h" +#include "sharing/file_attachment.h" +#include "sharing/linux/platform/linux_sharing_platform.h" +#include "sharing/linux/nearby_noop_analytics_recorder.h" +#include "sharing/flags/generated/nearby_sharing_feature_flags.h" +#include "sharing/nearby_sharing_service.h" +#include "sharing/nearby_sharing_service_factory.h" +#include "sharing/nearby_sharing_settings.h" +#include "sharing/share_target.h" +#include "sharing/share_target_discovered_callback.h" +#include "sharing/transfer_metadata.h" +#include "sharing/transfer_update_callback.h" +#include "sharing/proto/enums.pb.h" + +namespace nearby::sharing::linux { +namespace { + +std::atomic g_interrupted = false; + +void HandleSignal(int signal) { + static_cast(signal); + g_interrupted = true; +} + +std::string GetHostname() { + char hostname[256] = {}; + if (gethostname(hostname, sizeof(hostname)) == 0 && hostname[0] != '\0') { + return std::string(hostname); + } + return "Linux"; +} + +void PrintUsage(const char* argv0) { + std::cerr << "Usage:\n" + << " " << argv0 + << " receive [--name NAME] [--timeout SECONDS]\n" + << " " << argv0 + << " send FILE [--name NAME] [--timeout SECONDS]\n"; +} + +struct Options { + enum class Mode { kReceive, kSend }; + + Mode mode; + std::string file_path; + std::string device_name = GetHostname(); + int timeout_seconds = 120; +}; + +std::optional ParseArgs(int argc, char** argv) { + if (argc < 2) { + return std::nullopt; + } + + Options options{.mode = Options::Mode::kReceive}; + std::string command = argv[1]; + int index = 2; + if (command == "receive") { + options.mode = Options::Mode::kReceive; + } else if (command == "send") { + options.mode = Options::Mode::kSend; + if (index >= argc) { + return std::nullopt; + } + options.file_path = argv[index++]; + } else { + return std::nullopt; + } + + while (index < argc) { + std::string arg = argv[index++]; + if (arg == "--name") { + if (index >= argc) { + return std::nullopt; + } + options.device_name = argv[index++]; + } else if (arg == "--timeout") { + if (index >= argc) { + return std::nullopt; + } + options.timeout_seconds = std::atoi(argv[index++]); + if (options.timeout_seconds < 0) { + return std::nullopt; + } + } else { + return std::nullopt; + } + } + + return options; +} + +std::string StatusCodeToString(NearbySharingService::StatusCodes status) { + return NearbySharingService::StatusCodeToString(status); +} + +std::unique_ptr CreateFileAttachments( + const std::string& file_path) { + AttachmentContainer::Builder builder; + builder.AddFileAttachment(FileAttachment(FilePath(file_path))); + return builder.Build(); +} + +template +NearbySharingService::StatusCodes WaitForStatus(Invoker invoker) { + std::mutex mutex; + std::condition_variable cv; + std::optional status; + invoker([&](NearbySharingService::StatusCodes callback_status) { + { + std::lock_guard lock(mutex); + status = callback_status; + } + cv.notify_one(); + }); + + std::unique_lock lock(mutex); + cv.wait(lock, [&] { return status.has_value(); }); + return *status; +} + + +struct CliState { + std::mutex mutex; + std::condition_variable cv; + bool done = false; + bool send_requested = false; + bool accept_requested = false; + int exit_code = 1; + std::optional selected_target; + std::optional accept_target_id; +}; + +void PrintTransferUpdate(const ShareTarget& share_target, + const AttachmentContainer& attachment_container, + const TransferMetadata& metadata) { + std::cout << "transfer target=\"" << share_target.device_name << "\" id=" + << share_target.id << " status=" + << TransferMetadata::StatusToString(metadata.status()) + << " progress=" << metadata.progress() + << "% bytes=" << metadata.transferred_bytes() << "/" + << attachment_container.GetTotalAttachmentsSize() << std::endl; + if (metadata.token().has_value()) { + std::cout << "confirmation token: " << *metadata.token() << std::endl; + } +} + +class CliTransferCallback final : public TransferUpdateCallback { + public: + CliTransferCallback(CliState& state, bool receive_mode) + : state_(state), receive_mode_(receive_mode) {} + + void OnTransferUpdate(const ShareTarget& share_target, + const AttachmentContainer& attachment_container, + const TransferMetadata& transfer_metadata) override { + PrintTransferUpdate(share_target, attachment_container, transfer_metadata); + std::lock_guard lock(state_.mutex); + if (receive_mode_ && + transfer_metadata.status() == + TransferMetadata::Status::kAwaitingLocalConfirmation && + !state_.accept_requested) { + state_.accept_requested = true; + state_.accept_target_id = share_target.id; + } + if (TransferMetadata::IsFinalStatus(transfer_metadata.status())) { + state_.done = true; + state_.exit_code = + transfer_metadata.status() == TransferMetadata::Status::kComplete ? 0 + : 1; + } + state_.cv.notify_all(); + } + + private: + CliState& state_; + bool receive_mode_; +}; + +class CliDiscoveryCallback final : public ShareTargetDiscoveredCallback { + public: + explicit CliDiscoveryCallback(CliState& state) : state_(state) {} + + void OnShareTargetDiscovered(const ShareTarget& share_target) override { + std::cout << "discovered target=\"" << share_target.device_name + << "\" id=" << share_target.id << std::endl; + if (share_target.receive_disabled) { + return; + } + std::lock_guard lock(state_.mutex); + if (!state_.selected_target.has_value() && !state_.send_requested) { + state_.selected_target = share_target; + } + state_.cv.notify_all(); + } + + void OnShareTargetLost(const ShareTarget& share_target) override { + std::cout << "lost target=\"" << share_target.device_name + << "\" id=" << share_target.id << std::endl; + } + + void OnShareTargetUpdated(const ShareTarget& share_target) override { + std::cout << "updated target=\"" << share_target.device_name + << "\" id=" << share_target.id << std::endl; + OnShareTargetDiscovered(share_target); + } + + private: + CliState& state_; +}; + +class CliApp { + public: + explicit CliApp(const Options& options) + : options_(options), platform_(options.device_name) {} + + int Run() { + service_ = NearbySharingServiceFactory::GetInstance()->CreateSharingService( + platform_, &analytics_recorder_, /*event_logger=*/nullptr, + /*supports_file_sync=*/false); + if (service_ == nullptr) { + std::cerr << "failed to create NearbySharingService" << std::endl; + return 1; + } + + if (!ConfigureService()) { + Shutdown(); + return 1; + } + int result = options_.mode == Options::Mode::kReceive ? RunReceive() + : RunSend(); + Shutdown(); + return result; + } + + private: + bool ConfigureService() { + std::cout << "device name: " << options_.device_name << std::endl; + service_->GetSettings()->SetDataUsage(proto::WIFI_ONLY_DATA_USAGE); + service_->GetSettings()->SetDeviceName( + options_.device_name, + [](DeviceNameValidationResult validation_result) { + static_cast(validation_result); + }); + auto status = WaitForStatus([&](auto callback) { + service_->SetVisibility(proto::DEVICE_VISIBILITY_EVERYONE, + absl::ZeroDuration(), std::move(callback)); + }); + std::cout << "SetVisibility: " << StatusCodeToString(status) << std::endl; + return status == NearbySharingService::StatusCodes::kOk; + } + + int RunReceive() { + CliState state; + CliTransferCallback transfer_callback(state, /*receive_mode=*/true); + + auto status = WaitForStatus([&](auto callback) { + service_->RegisterReceiveSurface( + &transfer_callback, NearbySharingService::ReceiveSurfaceState:: + kForeground, + Advertisement::BlockedVendorId::kNone, std::move(callback)); + }); + std::cout << "RegisterReceiveSurface: " << StatusCodeToString(status) + << std::endl; + if (status != NearbySharingService::StatusCodes::kOk) { + return 1; + } + + std::cout << "receiving; waiting for incoming share" << std::endl; + int result = WaitForTransferOrActions(state); + WaitForStatus([&](auto callback) { + service_->UnregisterReceiveSurface(&transfer_callback, + std::move(callback)); + }); + return result; + } + + int RunSend() { + std::error_code error; + if (!std::filesystem::is_regular_file(options_.file_path, error)) { + std::cerr << "not a regular file: " << options_.file_path << std::endl; + return 1; + } + + CliState state; + CliTransferCallback transfer_callback(state, /*receive_mode=*/false); + CliDiscoveryCallback discovery_callback(state); + + auto status = WaitForStatus([&](auto callback) { + service_->RegisterSendSurface( + &transfer_callback, &discovery_callback, + NearbySharingService::SendSurfaceState::kForeground, + Advertisement::BlockedVendorId::kNone, + /*disable_wifi_hotspot=*/false, std::move(callback)); + }); + std::cout << "RegisterSendSurface: " << StatusCodeToString(status) + << std::endl; + if (status != NearbySharingService::StatusCodes::kOk) { + return 1; + } + + std::cout << "scanning; waiting for first target" << std::endl; + int result = WaitForTransferOrActions(state); + WaitForStatus([&](auto callback) { + service_->UnregisterSendSurface(&transfer_callback, std::move(callback)); + }); + return result; + } + + int WaitForTransferOrActions(CliState& state) { + const auto start = std::chrono::steady_clock::now(); + while (!g_interrupted) { + std::optional accept_target_id; + std::optional send_target; + { + std::unique_lock lock(state.mutex); + if (options_.timeout_seconds == 0) { + state.cv.wait_for(lock, std::chrono::seconds(1), [&] { + return state.done || state.accept_target_id.has_value() || + state.selected_target.has_value() || g_interrupted.load(); + }); + } else { + auto elapsed = std::chrono::duration_cast( + std::chrono::steady_clock::now() - start); + if (elapsed.count() >= options_.timeout_seconds) { + std::cerr << "timed out after " << options_.timeout_seconds + << " seconds" << std::endl; + return 1; + } + state.cv.wait_for(lock, std::chrono::seconds(1), [&] { + return state.done || state.accept_target_id.has_value() || + state.selected_target.has_value() || g_interrupted.load(); + }); + } + if (state.done) { + return state.exit_code; + } + if (state.accept_target_id.has_value()) { + accept_target_id = state.accept_target_id; + state.accept_target_id.reset(); + } + if (state.selected_target.has_value() && !state.send_requested) { + send_target = state.selected_target; + state.send_requested = true; + } + } + + if (accept_target_id.has_value()) { + std::cout << "accepting incoming share id=" << *accept_target_id + << std::endl; + auto status = WaitForStatus([&](auto callback) { + service_->Accept(*accept_target_id, std::move(callback)); + }); + std::cout << "Accept: " << StatusCodeToString(status) << std::endl; + if (status != NearbySharingService::StatusCodes::kOk) { + return 1; + } + } + + if (send_target.has_value()) { + std::cout << "sending " << options_.file_path << " to \"" + << send_target->device_name << "\" id=" << send_target->id + << std::endl; + auto attachments = CreateFileAttachments(options_.file_path); + auto status = WaitForStatus([&](auto callback) { + service_->SendAttachments(send_target->id, std::move(attachments), + std::move(callback)); + }); + std::cout << "SendAttachments: " << StatusCodeToString(status) + << std::endl; + if (status != NearbySharingService::StatusCodes::kOk) { + return 1; + } + } + } + + std::cerr << "interrupted" << std::endl; + return 1; + } + + void Shutdown() { + if (service_ == nullptr) { + return; + } + auto status = WaitForStatus( + [&](auto callback) { service_->Shutdown(std::move(callback)); }); + std::cout << "Shutdown: " << StatusCodeToString(status) << std::endl; + } + + Options options_; + LinuxSharingPlatform platform_; + NoOpAnalyticsRecorder analytics_recorder_; + NearbySharingService* service_ = nullptr; +}; + +} // namespace +} // namespace nearby::sharing::linux + +int main(int argc, char** argv) { + signal(SIGINT, nearby::sharing::linux::HandleSignal); + signal(SIGTERM, nearby::sharing::linux::HandleSignal); + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::sharing::config_package_nearby::nearby_sharing_feature:: + kEnableBleForTransfer, + true); + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableBleL2cap, + true); + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kRefactorBleL2cap, + true); + + std::optional options = + nearby::sharing::linux::ParseArgs(argc, argv); + if (!options.has_value()) { + nearby::sharing::linux::PrintUsage(argv[0]); + return 2; + } + + if (options->mode == nearby::sharing::linux::Options::Mode::kSend) { + std::error_code error; + if (!std::filesystem::is_regular_file(options->file_path, error)) { + std::cerr << "not a regular file: " << options->file_path << std::endl; + return 1; + } + } + + nearby::sharing::linux::CliApp app(*options); + return app.Run(); +} diff --git a/sharing/linux/platform/linux_account_manager.cc b/sharing/linux/platform/linux_account_manager.cc new file mode 100644 index 00000000..bd791abd --- /dev/null +++ b/sharing/linux/platform/linux_account_manager.cc @@ -0,0 +1,102 @@ +// Copyright 2026 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 "sharing/linux/platform/linux_account_manager.h" + +#include +#include +#include +#include + +#include "absl/container/flat_hash_set.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" + +namespace nearby::sharing::linux::internal { +namespace { + +class FailedSigninAttempt final : public SigninAttempt { + public: + std::string Start( + absl::AnyInvocable + callback) override { + if (callback) { + std::move(callback)(UNSUPPORTED, "", "", AccountInfo{}); + } + return {}; + } + + void Close() override {} +}; + +class LinuxAccountManager final : public AccountManager { + public: + std::optional GetCurrentAccount() override { return std::nullopt; } + + std::unique_ptr Login(absl::string_view client_id, + absl::string_view client_secret) + override { + last_client_id_ = std::string(client_id); + last_client_secret_ = std::string(client_secret); + return std::make_unique(); + } + + void Logout(absl::AnyInvocable logout_callback) override { + if (logout_callback) { + std::move(logout_callback)(absl::OkStatus()); + } + } + + bool GetAccessToken( + absl::AnyInvocable)> callback) + override { + if (!callback) { + return false; + } + std::move(callback)( + absl::UnavailableError("Linux account integration is not available")); + return true; + } + + std::pair GetOAuthClientCredential() override { + return {last_client_id_, last_client_secret_}; + } + + void AddObserver(Observer* observer) override { observers_.insert(observer); } + void RemoveObserver(Observer* observer) override { + observers_.erase(observer); + } + + void SaveAccountPrefs(absl::string_view user_id, absl::string_view client_id, + absl::string_view client_secret) override { + static_cast(user_id); + last_client_id_ = std::string(client_id); + last_client_secret_ = std::string(client_secret); + } + + private: + absl::flat_hash_set observers_; + std::string last_client_id_; + std::string last_client_secret_; +}; + +} // namespace + +std::unique_ptr CreateLinuxAccountManager() { + return std::make_unique(); +} + +} // namespace nearby::sharing::linux::internal diff --git a/sharing/linux/platform/linux_account_manager.h b/sharing/linux/platform/linux_account_manager.h new file mode 100644 index 00000000..7126e036 --- /dev/null +++ b/sharing/linux/platform/linux_account_manager.h @@ -0,0 +1,28 @@ +// Copyright 2026 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 SHARING_LINUX_PLATFORM_LINUX_ACCOUNT_MANAGER_H_ +#define SHARING_LINUX_PLATFORM_LINUX_ACCOUNT_MANAGER_H_ + +#include + +#include "location/nearby/sharing/lib/account/account_manager.h" + +namespace nearby::sharing::linux::internal { + +std::unique_ptr CreateLinuxAccountManager(); + +} // namespace nearby::sharing::linux::internal + +#endif // SHARING_LINUX_PLATFORM_LINUX_ACCOUNT_MANAGER_H_ diff --git a/sharing/linux/platform/linux_platform_components.cc b/sharing/linux/platform/linux_platform_components.cc new file mode 100644 index 00000000..7ff9d117 --- /dev/null +++ b/sharing/linux/platform/linux_platform_components.cc @@ -0,0 +1,381 @@ +// Copyright 2026 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 "sharing/linux/platform/linux_platform_components.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "internal/base/file_path.h" +#include "internal/platform/mac_address.h" +#include "sharing/internal/api/private_certificate_data.h" +#include "sharing/linux/platform/platform_util.h" +#include "sharing/proto/rpc_resources.pb.h" + +namespace nearby::sharing::linux::internal { +namespace { + +using ::nearby::sharing::api::PreferenceManager; +using ::nearby::sharing::api::PublicCertificateDatabase; +using ::nearby::sharing::proto::PublicCertificate; + +constexpr absl::string_view kAppFirstRunPref = "nearby_sharing.app.first_run"; +constexpr absl::string_view kAppActivePref = "nearby_sharing.app.active"; + +class LinuxNetworkMonitor final : public nearby::api::NetworkMonitor { + public: + LinuxNetworkMonitor(std::function lan_connected_callback, + std::function internet_connected_callback) + : nearby::api::NetworkMonitor(std::move(lan_connected_callback), + std::move(internet_connected_callback)) { + if (lan_connected_callback_) { + lan_connected_callback_(IsLanConnected()); + } + if (internet_connected_callback_) { + internet_connected_callback_(IsInternetConnected()); + } + } + + bool IsLanConnected() override { return HasNonLoopbackInterface(); } + bool IsInternetConnected() override { return HasNonLoopbackInterface(); } +}; + +class LinuxSystemInfo final : public nearby::api::SystemInfo { + public: + std::string GetComputerManufacturer() override { return "Unknown"; } + std::string GetComputerModel() override { return "Unknown"; } + int64_t GetComputerPhysicalMemory() override { + struct sysinfo info; + if (sysinfo(&info) != 0) { + return 0; + } + return static_cast(info.totalram) * info.mem_unit; + } + int GetComputerProcessorCount() override { + long processors = sysconf(_SC_NPROCESSORS_CONF); + return processors > 0 ? static_cast(processors) : 1; + } + int GetComputerLogicProcessorCount() override { + long processors = sysconf(_SC_NPROCESSORS_ONLN); + return processors > 0 ? static_cast(processors) : 1; + } + int GetProcessorMemoryInfo() override { return 0; } + BatteryChargeStatus QueryBatteryInfo(int& seconds, int& percent, + bool& battery_saver) override { + seconds = 0; + percent = 0; + battery_saver = false; + return BatteryChargeStatus::UNKNOWN; + } + std::string GetOsManufacturer() override { return "Linux"; } + std::string GetOsName() override { return "Linux"; } + std::string GetOsVersion() override { + struct utsname info; + return uname(&info) == 0 ? std::string(info.release) : std::string(); + } + std::string GetOsArchitecture() override { + struct utsname info; + return uname(&info) == 0 ? std::string(info.machine) : std::string(); + } + std::string GetOsLanguage() override { + return GetLanguageCode().value_or("en"); + } + std::string GetProcessorManufacturer() override { return "Unknown"; } + std::string GetProcessorName() override { return "Unknown"; } + std::list GetBluetoothDriverInfos() override { return {}; } + std::list GetNetworkDriverInfos() override { return {}; } + void GetBatteryUsageReport(const FilePath& save_path) override { + static_cast(save_path); + } +}; + +class LinuxAppInfo final : public nearby::api::AppInfo { + public: + explicit LinuxAppInfo(PreferenceManager& preference_manager) + : preference_manager_(preference_manager) {} + + std::optional GetAppVersion() override { + return std::string("linux"); + } + std::optional GetAppLanguage() override { + return GetLanguageCode(); + } + std::optional GetUpdateTrack() override { return std::nullopt; } + std::optional GetAppInstallSource() override { + return std::string("manual"); + } + bool GetFirstRunDone() override { + return preference_manager_.GetBoolean(kAppFirstRunPref, false); + } + bool SetFirstRunDone(bool value) override { + preference_manager_.SetBoolean(kAppFirstRunPref, value); + return true; + } + bool SetActiveFlag() override { + preference_manager_.SetBoolean(kAppActivePref, true); + return true; + } + + private: + PreferenceManager& preference_manager_; +}; + +class LinuxDeviceInfo final : public nearby::api::DeviceInfo { + public: + std::optional GetOsDeviceName() const override { + char hostname[256] = {}; + if (gethostname(hostname, sizeof(hostname)) == 0 && hostname[0] != '\0') { + return std::string(hostname); + } + return std::string("Linux"); + } + DeviceType GetDeviceType() const override { return DeviceType::kLaptop; } + OsType GetOsType() const override { return OsType::kUnknown; } + FilePath GetDownloadPath() const override { + const char* xdg_download_dir = std::getenv("XDG_DOWNLOAD_DIR"); + if (xdg_download_dir != nullptr && *xdg_download_dir != '\0') { + return FilePath(std::string(xdg_download_dir)); + } + return BuildPathFromBase(GetHomeDirectory(), {"Downloads"}); + } + FilePath GetLocalAppDataPath(FilePath sub_path) const override { + std::string config_home = GetEnvOrDefault( + "XDG_CONFIG_HOME", + BuildPathFromBase(GetHomeDirectory(), {".config"}).ToString()); + FilePath path = BuildPathFromBase(config_home, {"Google Nearby"}); + if (!sub_path.IsEmpty()) { + path.append(sub_path); + } + return path; + } + FilePath GetTemporaryPath() const override { + const char* runtime_dir = std::getenv("XDG_RUNTIME_DIR"); + if (runtime_dir != nullptr && *runtime_dir != '\0') { + return BuildPathFromBase(runtime_dir, {"Google Nearby"}); + } + return BuildPathFromBase("/tmp", {"Google Nearby"}); + } + FilePath GetLogPath() const override { + return GetLocalAppDataPath(FilePath("logs")); + } + bool IsScreenLocked() const override { return false; } + void RegisterScreenLockedListener( + absl::string_view listener_name, + std::function callback) override { + screen_locked_listeners_[std::string(listener_name)] = std::move(callback); + } + void UnregisterScreenLockedListener( + absl::string_view listener_name) override { + screen_locked_listeners_.erase(std::string(listener_name)); + } + bool PreventSleep() override { return true; } + bool AllowSleep() override { return true; } + + private: + mutable absl::flat_hash_map> + screen_locked_listeners_; +}; + +class LinuxBluetoothAdapter final : public api::BluetoothAdapter { + public: + explicit LinuxBluetoothAdapter( + std::shared_ptr<::nearby::linux::BluetoothAdapter> adapter) + : adapter_(std::move(adapter)) {} + + bool IsPresent() const override { return GetAddress().IsSet(); } + bool IsPowered() const override { + return adapter_ != nullptr && adapter_->IsEnabled(); + } + bool IsLowEnergySupported() const override { return adapter_ != nullptr; } + bool IsScanOffloadSupported() const override { return false; } + bool IsAdvertisementOffloadSupported() const override { return false; } + bool IsExtendedAdvertisingSupported() const override { return false; } + bool IsPeripheralRoleSupported() const override { return adapter_ != nullptr; } + PermissionStatus GetOsPermissionStatus() const override { + return adapter_ != nullptr ? PermissionStatus::kAllowed + : PermissionStatus::kSystemDenied; + } + void SetPowered(bool powered, std::function success_callback, + std::function error_callback) override { + if (adapter_ == nullptr) { + if (error_callback) { + error_callback(); + } + return; + } + bool success = adapter_->SetStatus( + powered ? nearby::api::BluetoothAdapter::Status::kEnabled + : nearby::api::BluetoothAdapter::Status::kDisabled); + if (success) { + if (success_callback) { + success_callback(); + } + return; + } + if (error_callback) { + error_callback(); + } + } + std::optional GetAdapterId() const override { + if (adapter_ == nullptr) { + return std::nullopt; + } + std::string name = adapter_->GetName(); + return name.empty() ? std::nullopt : std::make_optional(name); + } + MacAddress GetAddress() const override { + return adapter_ != nullptr ? adapter_->GetMacAddress() : MacAddress(); + } + void AddObserver(Observer* observer) override { observers_.insert(observer); } + void RemoveObserver(Observer* observer) override { + observers_.erase(observer); + } + bool HasObserver(Observer* observer) override { + return observers_.contains(observer); + } + + private: + std::shared_ptr<::nearby::linux::BluetoothAdapter> adapter_; + absl::flat_hash_set observers_; +}; + +class LinuxPublicCertificateDatabase final : public PublicCertificateDatabase { + public: + void Initialize(absl::AnyInvocable callback) override { + if (callback) { + std::move(callback)(InitStatus::kOk); + } + } + void LoadEntries( + absl::AnyInvocable>) &&> + callback) override { + auto certificates = std::make_unique>(); + { + absl::MutexLock lock(mutex_); + for (const auto& [id, certificate] : entries_) { + static_cast(id); + certificates->push_back(certificate); + } + } + if (callback) { + std::move(callback)(true, std::move(certificates)); + } + } + void LoadCertificate( + absl::string_view id, + absl::AnyInvocable) &&> + callback) override { + auto certificate = std::make_unique(); + bool found = false; + { + absl::MutexLock lock(mutex_); + auto it = entries_.find(std::string(id)); + if (it != entries_.end()) { + *certificate = it->second; + found = true; + } + } + if (callback) { + std::move(callback)(found, found ? std::move(certificate) : nullptr); + } + } + void AddCertificates(absl::Span certificates, + absl::AnyInvocable callback) override { + { + absl::MutexLock lock(mutex_); + for (const PublicCertificate& certificate : certificates) { + entries_[certificate.secret_id()] = certificate; + } + } + if (callback) { + std::move(callback)(true); + } + } + void RemoveCertificatesById( + std::vector ids_to_remove, + absl::AnyInvocable callback) override { + { + absl::MutexLock lock(mutex_); + for (const std::string& id : ids_to_remove) { + entries_.erase(id); + } + } + if (callback) { + std::move(callback)(true); + } + } + void Destroy(absl::AnyInvocable callback) override { + { + absl::MutexLock lock(mutex_); + entries_.clear(); + } + if (callback) { + std::move(callback)(true); + } + } + + private: + absl::Mutex mutex_; + std::map entries_ ABSL_GUARDED_BY(mutex_); +}; + +} // namespace + +std::unique_ptr CreateLinuxNetworkMonitor( + std::function lan_connected_callback, + std::function internet_connected_callback) { + return std::make_unique( + std::move(lan_connected_callback), std::move(internet_connected_callback)); +} + +std::unique_ptr CreateLinuxSystemInfo() { + return std::make_unique(); +} + +std::unique_ptr CreateLinuxAppInfo( + api::PreferenceManager& preference_manager) { + return std::make_unique(preference_manager); +} + +std::unique_ptr CreateLinuxDeviceInfo() { + return std::make_unique(); +} + +std::unique_ptr CreateLinuxBluetoothAdapter( + std::shared_ptr<::nearby::linux::BluetoothAdapter> adapter) { + return std::make_unique(std::move(adapter)); +} + +std::unique_ptr +CreateLinuxPublicCertificateDatabase() { + return std::make_unique(); +} + +} // namespace nearby::sharing::linux::internal diff --git a/sharing/linux/platform/linux_platform_components.h b/sharing/linux/platform/linux_platform_components.h new file mode 100644 index 00000000..6350b4dd --- /dev/null +++ b/sharing/linux/platform/linux_platform_components.h @@ -0,0 +1,46 @@ +// Copyright 2026 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 SHARING_LINUX_PLATFORM_LINUX_PLATFORM_COMPONENTS_H_ +#define SHARING_LINUX_PLATFORM_LINUX_PLATFORM_COMPONENTS_H_ + +#include +#include + +#include "internal/platform/implementation/device_info.h" +#include "internal/platform/implementation/linux/bluetooth_adapter.h" +#include "sharing/internal/api/app_info.h" +#include "sharing/internal/api/bluetooth_adapter.h" +#include "sharing/internal/api/network_monitor.h" +#include "sharing/internal/api/preference_manager.h" +#include "sharing/internal/api/public_certificate_database.h" +#include "sharing/internal/api/system_info.h" + +namespace nearby::sharing::linux::internal { + +std::unique_ptr CreateLinuxNetworkMonitor( + std::function lan_connected_callback, + std::function internet_connected_callback); +std::unique_ptr CreateLinuxSystemInfo(); +std::unique_ptr CreateLinuxAppInfo( + api::PreferenceManager& preference_manager); +std::unique_ptr CreateLinuxDeviceInfo(); +std::unique_ptr CreateLinuxBluetoothAdapter( + std::shared_ptr<::nearby::linux::BluetoothAdapter> adapter); +std::unique_ptr +CreateLinuxPublicCertificateDatabase(); + +} // namespace nearby::sharing::linux::internal + +#endif // SHARING_LINUX_PLATFORM_LINUX_PLATFORM_COMPONENTS_H_ diff --git a/sharing/linux/platform/linux_preference_manager.cc b/sharing/linux/platform/linux_preference_manager.cc new file mode 100644 index 00000000..95c48bd5 --- /dev/null +++ b/sharing/linux/platform/linux_preference_manager.cc @@ -0,0 +1,410 @@ +// Copyright 2026 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 "sharing/linux/platform/linux_preference_manager.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "internal/platform/implementation/platform.h" +#include "location/nearby/sharing/lib/sync/sync_binding_prefs.pb.h" +#include "nlohmann/json.hpp" +#include "sharing/internal/api/private_certificate_data.h" +#include "sharing/internal/public/pref_names.h" + +namespace nearby::sharing::linux::internal { +namespace { + +using Json = nlohmann::json; +using ::nearby::sharing::api::PreferenceManager; +using ::nearby::sharing::api::PrivateCertificateData; + +constexpr absl::string_view kPreferencesFile = "nearby_sharing_linux.json"; +constexpr absl::string_view kFileSyncBindingName = "FileSync"; + +Json ToJson(const PrivateCertificateData& certificate) { + return Json{ + {PrivateCertificateData::kVisibility, certificate.visibility}, + {PrivateCertificateData::kNotBefore, certificate.not_before}, + {PrivateCertificateData::kNotAfter, certificate.not_after}, + {PrivateCertificateData::kKeyPair, certificate.key_pair}, + {PrivateCertificateData::kSecretKey, certificate.secret_key}, + {PrivateCertificateData::kMetadataEncryptionKey, + certificate.metadata_encryption_key}, + {PrivateCertificateData::kId, certificate.id}, + {PrivateCertificateData::kUnencryptedMetadata, + certificate.unencrypted_metadata_proto}, + {PrivateCertificateData::kConsumedSalts, certificate.consumed_salts}, + }; +} + +std::optional FromJson(const Json& value) { + if (!value.is_object()) { + return std::nullopt; + } + + PrivateCertificateData certificate; + try { + certificate.visibility = value.at(PrivateCertificateData::kVisibility); + certificate.not_before = value.at(PrivateCertificateData::kNotBefore); + certificate.not_after = value.at(PrivateCertificateData::kNotAfter); + certificate.key_pair = value.at(PrivateCertificateData::kKeyPair); + certificate.secret_key = value.at(PrivateCertificateData::kSecretKey); + certificate.metadata_encryption_key = + value.at(PrivateCertificateData::kMetadataEncryptionKey); + certificate.id = value.at(PrivateCertificateData::kId); + certificate.unencrypted_metadata_proto = + value.at(PrivateCertificateData::kUnencryptedMetadata); + certificate.consumed_salts = + value.at(PrivateCertificateData::kConsumedSalts); + return certificate; + } catch (const Json::exception&) { + return std::nullopt; + } +} + +class LinuxPreferenceManager final : public PreferenceManager { + public: + LinuxPreferenceManager() + : storage_(nearby::api::ImplementationPlatform::CreatePreferencesManager( + kPreferencesFile)) {} + + void SetBoolean(absl::string_view key, bool value) override { + if (storage_ != nullptr && storage_->SetBoolean(key, value)) { + NotifyPreferenceChanged(key); + } + } + void SetInteger(absl::string_view key, int value) override { + if (storage_ != nullptr && storage_->SetInteger(key, value)) { + NotifyPreferenceChanged(key); + } + } + void SetInt64(absl::string_view key, int64_t value) override { + if (storage_ != nullptr && storage_->SetInt64(key, value)) { + NotifyPreferenceChanged(key); + } + } + void SetString(absl::string_view key, absl::string_view value) override { + if (storage_ != nullptr && storage_->SetString(key, value)) { + NotifyPreferenceChanged(key); + } + } + void SetTime(absl::string_view key, absl::Time value) override { + if (storage_ != nullptr && storage_->SetTime(key, value)) { + NotifyPreferenceChanged(key); + } + } + void SetBooleanArray(absl::string_view key, + absl::Span value) override { + if (storage_ != nullptr && storage_->SetBooleanArray(key, value)) { + NotifyPreferenceChanged(key); + } + } + void SetIntegerArray(absl::string_view key, + absl::Span value) override { + if (storage_ != nullptr && storage_->SetIntegerArray(key, value)) { + NotifyPreferenceChanged(key); + } + } + void SetInt64Array(absl::string_view key, + absl::Span value) override { + if (storage_ != nullptr && storage_->SetInt64Array(key, value)) { + NotifyPreferenceChanged(key); + } + } + void SetStringArray(absl::string_view key, + absl::Span value) override { + if (storage_ != nullptr && storage_->SetStringArray(key, value)) { + NotifyPreferenceChanged(key); + } + } + void SetPrivateCertificateArray( + absl::string_view key, absl::Span value) + override { + Json certificates = Json::array(); + for (const PrivateCertificateData& certificate : value) { + certificates.push_back(ToJson(certificate)); + } + if (storage_ != nullptr && storage_->Set(key, certificates)) { + NotifyPreferenceChanged(key); + } + } + void SetCertificateExpirationArray( + absl::string_view key, + absl::Span> value) override { + Json expirations = Json::array(); + for (const auto& [id, expiration] : value) { + expirations.push_back(Json{{"id", id}, {"expiration", expiration}}); + } + if (storage_ != nullptr && storage_->Set(key, expirations)) { + NotifyPreferenceChanged(key); + } + } + void SetDictionaryBooleanValue(absl::string_view key, + absl::string_view dictionary_item, + bool value) override { + SetDictionaryValue(key, dictionary_item, value); + } + void SetDictionaryIntegerValue(absl::string_view key, + absl::string_view dictionary_item, + int value) override { + SetDictionaryValue(key, dictionary_item, value); + } + void SetDictionaryInt64Value(absl::string_view key, + absl::string_view dictionary_item, + int64_t value) override { + SetDictionaryValue(key, dictionary_item, value); + } + void SetDictionaryStringValue(absl::string_view key, + absl::string_view dictionary_item, + std::string value) override { + SetDictionaryValue(key, dictionary_item, std::move(value)); + } + void RemoveDictionaryItem(absl::string_view key, + absl::string_view dictionary_item) override { + if (storage_ == nullptr) { + return; + } + Json dictionary = storage_->Get(key, Json::object()); + if (!dictionary.is_object()) { + return; + } + dictionary.erase(std::string(dictionary_item)); + if (storage_->Set(key, dictionary)) { + NotifyPreferenceChanged(key); + } + } + void SetSyncBindingValue( + const nearby::sharing::sync::SyncBindingPrefs& value) override { + std::string serialized; + if (value.SerializeToString(&serialized)) { + SetString(absl::StrCat(PrefNames::kBindingConfigPrefix, + kFileSyncBindingName), + serialized); + } + } + + bool GetBoolean(absl::string_view key, bool default_value) const override { + return storage_ != nullptr ? storage_->GetBoolean(key, default_value) + : default_value; + } + int GetInteger(absl::string_view key, int default_value) const override { + return storage_ != nullptr ? storage_->GetInteger(key, default_value) + : default_value; + } + int64_t GetInt64(absl::string_view key, + int64_t default_value) const override { + return storage_ != nullptr ? storage_->GetInt64(key, default_value) + : default_value; + } + std::string GetString(absl::string_view key, + const std::string& default_value) const override { + return storage_ != nullptr ? storage_->GetString(key, default_value) + : default_value; + } + absl::Time GetTime(absl::string_view key, + absl::Time default_value) const override { + return storage_ != nullptr ? storage_->GetTime(key, default_value) + : default_value; + } + std::vector GetBooleanArray( + absl::string_view key, + absl::Span default_value) const override { + return storage_ != nullptr ? storage_->GetBooleanArray(key, default_value) + : std::vector(default_value.begin(), + default_value.end()); + } + std::vector GetIntegerArray( + absl::string_view key, + absl::Span default_value) const override { + return storage_ != nullptr ? storage_->GetIntegerArray(key, default_value) + : std::vector(default_value.begin(), + default_value.end()); + } + std::vector GetInt64Array( + absl::string_view key, + absl::Span default_value) const override { + return storage_ != nullptr ? storage_->GetInt64Array(key, default_value) + : std::vector(default_value.begin(), + default_value.end()); + } + std::vector GetStringArray( + absl::string_view key, + absl::Span default_value) const override { + return storage_ != nullptr ? storage_->GetStringArray(key, default_value) + : std::vector(default_value.begin(), + default_value.end()); + } + std::vector GetPrivateCertificateArray( + absl::string_view key) const override { + std::vector result; + if (storage_ == nullptr) { + return result; + } + Json certificates = storage_->Get(key, Json::array()); + if (!certificates.is_array()) { + return result; + } + for (const Json& certificate_json : certificates) { + std::optional certificate = + FromJson(certificate_json); + if (certificate.has_value()) { + result.push_back(*certificate); + } + } + return result; + } + std::vector> + GetCertificateExpirationArray(absl::string_view key) const override { + std::vector> result; + if (storage_ == nullptr) { + return result; + } + Json expirations = storage_->Get(key, Json::array()); + if (!expirations.is_array()) { + return result; + } + for (const Json& item : expirations) { + if (!item.is_object()) { + continue; + } + try { + result.emplace_back(item.at("id").get(), + item.at("expiration").get()); + } catch (const Json::exception&) { + } + } + return result; + } + std::optional GetDictionaryBooleanValue( + absl::string_view key, absl::string_view dictionary_item) const override { + return GetDictionaryValue(key, dictionary_item); + } + std::optional GetDictionaryIntegerValue( + absl::string_view key, absl::string_view dictionary_item) const override { + return GetDictionaryValue(key, dictionary_item); + } + std::optional GetDictionaryInt64Value( + absl::string_view key, absl::string_view dictionary_item) const override { + return GetDictionaryValue(key, dictionary_item); + } + std::optional GetDictionaryStringValue( + absl::string_view key, absl::string_view dictionary_item) const override { + return GetDictionaryValue(key, dictionary_item); + } + std::optional GetSyncBindingValue() + const override { + std::string serialized = + GetString(absl::StrCat(PrefNames::kBindingConfigPrefix, + kFileSyncBindingName), + ""); + if (serialized.empty()) { + return std::nullopt; + } + nearby::sharing::sync::SyncBindingPrefs value; + if (!value.ParseFromString(serialized)) { + return std::nullopt; + } + return value; + } + void Remove(absl::string_view key) override { + if (storage_ != nullptr) { + storage_->Remove(key); + NotifyPreferenceChanged(key); + } + } + void RemoveAllBindingConfigs() override { + if (storage_ != nullptr && + storage_->RemoveKeyPrefix(PrefNames::kBindingConfigPrefix)) { + NotifyPreferenceChanged(PrefNames::kBindingConfigPrefix); + } + } + void AddObserver( + absl::string_view name, + std::function observer) override { + observers_[std::string(name)] = std::move(observer); + } + void RemoveObserver(absl::string_view name) override { + observers_.erase(std::string(name)); + } + + private: + template + void SetDictionaryValue(absl::string_view key, + absl::string_view dictionary_item, T value) { + if (storage_ == nullptr) { + return; + } + Json dictionary = storage_->Get(key, Json::object()); + if (!dictionary.is_object()) { + dictionary = Json::object(); + } + dictionary[std::string(dictionary_item)] = std::move(value); + if (storage_->Set(key, dictionary)) { + NotifyPreferenceChanged(key); + } + } + + template + std::optional GetDictionaryValue( + absl::string_view key, absl::string_view dictionary_item) const { + if (storage_ == nullptr) { + return std::nullopt; + } + Json dictionary = storage_->Get(key, Json::object()); + if (!dictionary.is_object()) { + return std::nullopt; + } + auto it = dictionary.find(std::string(dictionary_item)); + if (it == dictionary.end()) { + return std::nullopt; + } + try { + return it->get(); + } catch (const Json::exception&) { + return std::nullopt; + } + } + + void NotifyPreferenceChanged(absl::string_view key) { + for (const auto& [name, observer] : observers_) { + static_cast(name); + if (observer) { + observer(key); + } + } + } + + std::unique_ptr storage_; + absl::flat_hash_map> + observers_; +}; + +} // namespace + +std::unique_ptr CreateLinuxPreferenceManager() { + return std::make_unique(); +} + +} // namespace nearby::sharing::linux::internal diff --git a/sharing/linux/platform/linux_preference_manager.h b/sharing/linux/platform/linux_preference_manager.h new file mode 100644 index 00000000..cd4086d7 --- /dev/null +++ b/sharing/linux/platform/linux_preference_manager.h @@ -0,0 +1,28 @@ +// Copyright 2026 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 SHARING_LINUX_PLATFORM_LINUX_PREFERENCE_MANAGER_H_ +#define SHARING_LINUX_PLATFORM_LINUX_PREFERENCE_MANAGER_H_ + +#include + +#include "sharing/internal/api/preference_manager.h" + +namespace nearby::sharing::linux::internal { + +std::unique_ptr CreateLinuxPreferenceManager(); + +} // namespace nearby::sharing::linux::internal + +#endif // SHARING_LINUX_PLATFORM_LINUX_PREFERENCE_MANAGER_H_ diff --git a/sharing/linux/platform/linux_sharing_platform.cc b/sharing/linux/platform/linux_sharing_platform.cc index eeb76612..6f9030ca 100644 --- a/sharing/linux/platform/linux_sharing_platform.cc +++ b/sharing/linux/platform/linux_sharing_platform.cc @@ -1,1295 +1,36 @@ -// Copyright 2026 +// Copyright 2026 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 "sharing/linux/platform/linux_sharing_platform.h" -#include -#include -#include -#include -#include - -#include -#include -#include -#include #include -#include -#include #include -#include #include #include #include -#include - -#include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" -#include "absl/synchronization/mutex.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" #include "internal/base/file_path.h" -#include "internal/base/files.h" -#include "internal/platform/bluetooth_adapter.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/implementation/account_info.h" -#include "internal/platform/implementation/auth_status.h" -#include "internal/platform/implementation/ble.h" -#include "internal/platform/implementation/linux/bluetooth_adapter.h" -#include "internal/platform/implementation/linux/bluez.h" -#include "internal/platform/implementation/linux/bluez_le_advertisement.h" -#include "internal/platform/implementation/linux/dbus.h" -#include "internal/platform/implementation/platform.h" -#include "internal/platform/mac_address.h" -#include "internal/platform/task_runner_impl.h" -#include "internal/platform/uuid.h" -#include "nlohmann/json.hpp" -#include "sharing/internal/api/app_info.h" -#include "sharing/internal/api/bluetooth_adapter.h" -#include "sharing/internal/api/fast_init_ble_beacon.h" -#include "sharing/internal/api/fast_initiation_manager.h" -#include "sharing/internal/api/network_monitor.h" -#include "sharing/internal/api/preference_manager.h" -#include "sharing/internal/api/private_certificate_data.h" -#include "sharing/internal/api/public_certificate_database.h" -#include "sharing/internal/api/system_info.h" #include "sharing/internal/public/pref_names.h" -#include "sharing/proto/rpc_resources.pb.h" - -namespace nearby::sharing { -namespace { - -using Json = nlohmann::json; -using ::nearby::sharing::api::PreferenceManager; -using ::nearby::sharing::api::PrivateCertificateData; -using ::nearby::sharing::api::PublicCertificateDatabase; -using ::nearby::sharing::PrefNames; -using ::nearby::sharing::proto::PublicCertificate; -using ::nearby::sharing::sync::SyncBindingPrefs; -using ::nearby::sharing::sync::SyncConfigPrefs; - -constexpr absl::string_view kPreferencesFile = "nearby_sharing.json"; -constexpr absl::string_view kFileSyncBindingName = "FileSync"; -constexpr absl::string_view kAppFirstRunPref = "nearby_sharing.app.first_run"; -constexpr absl::string_view kAppActivePref = "nearby_sharing.app.active"; -constexpr char kFastInitServiceUuid[] = "0000fe2c-0000-1000-8000-00805f9b34fb"; - -std::optional GetFastInitUuid() { - return Uuid::FromString(kFastInitServiceUuid); -} - -std::string GetFastInitSeed( - nearby::api::FastInitBleBeacon::FastInitType type, - const ::nearby::linux::BluetoothAdapter* adapter) { - std::string seed = absl::StrCat("linux-fast-init:", static_cast(type)); - if (adapter != nullptr) { - seed = absl::StrCat(seed, ":", adapter->GetMacAddress().ToString()); - } - return seed; -} - -std::array -BuildFastInitSecretIdHash(absl::string_view seed) { - std::array hash{}; - const std::uint64_t value = std::hash{}(std::string(seed)); - for (size_t i = 0; i < hash.size(); ++i) { - hash[i] = static_cast((value >> (i * 8)) & 0xff); - } - return hash; -} - -std::array BuildFastInitSalt( - absl::string_view seed) { - return {static_cast(std::hash{}(std::string(seed)) & - 0xff)}; -} - -std::array -BuildFastInitAdvertisingData(nearby::api::FastInitBleBeacon& beacon) { - std::array - data{}; - size_t offset = 0; - - data[offset++] = nearby::api::FastInitBleBeacon::kFastInitServiceUuid[0]; - data[offset++] = nearby::api::FastInitBleBeacon::kFastInitServiceUuid[1]; - - for (uint8_t byte : nearby::api::FastInitBleBeacon::kFastInitModelId) { - data[offset++] = byte; - } - - const uint8_t metadata = - (static_cast(beacon.GetVersion()) << 5) | - (static_cast(beacon.GetType()) << 2) | - (beacon.GetUwbSupported() ? 0x02 : 0x00) | - (beacon.GetSenderCertSupported() ? 0x01 : 0x00); - data[offset++] = metadata; - data[offset++] = static_cast(beacon.GetAdjustedTxPower()); - - for (uint8_t byte : beacon.GetUwbMetadata()) { - data[offset++] = byte; - } - for (uint8_t byte : beacon.GetUwbAddress()) { - data[offset++] = byte; - } - for (uint8_t byte : beacon.GetSalt()) { - data[offset++] = byte; - } - for (uint8_t byte : beacon.GetSecretIdHash()) { - data[offset++] = byte; - } - - data[offset++] = (beacon.GetRequireBtAdvertising() ? 0x80 : 0x00) | - (beacon.GetSelfOnlyAdvertising() ? 0x40 : 0x00); - return data; -} - -std::unique_ptr<::nearby::linux::BluetoothAdapter> CreateFastInitBluetoothAdapter() { - auto system_bus = ::nearby::linux::getSystemBusConnection(); - auto manager = ::nearby::linux::bluez::BluezObjectManager(*system_bus); - try { - auto interfaces = manager.GetManagedObjects(); - for (auto& [object, properties] : interfaces) { - if (properties.count(sdbus::InterfaceName(org::bluez::Adapter1_proxy::INTERFACE_NAME)) == 1) { - LOG(INFO) << __func__ << ": found bluetooth adapter " << object; - return std::make_unique<::nearby::linux::BluetoothAdapter>(system_bus, - object); - } - } - } catch (const sdbus::Error& e) { - DBUS_LOG_METHOD_CALL_ERROR(&manager, "GetManagedObjects", e); - } - - LOG(ERROR) << __func__ << ": couldn't find a bluetooth adapter on this system"; - return nullptr; -} - -std::optional GetLanguageCode() { - const char* lang = std::getenv("LANG"); - if (lang == nullptr || *lang == '\0') { - return std::string("en"); - } - - std::string value(lang); - size_t dot = value.find('.'); - if (dot != std::string::npos) value.resize(dot); - size_t underscore = value.find('_'); - if (underscore != std::string::npos) value.resize(underscore); - if (value.empty()) { - return std::string("en"); - } - return value; -} - -std::string GetEnvOrDefault(const char* key, std::string fallback) { - const char* value = std::getenv(key); - if (value == nullptr || *value == '\0') { - return fallback; - } - return value; -} - -FilePath BuildPathFromBase(const std::string& base, - std::initializer_list components) { - std::filesystem::path path(base); - for (const std::string& component : components) { - path /= component; - } - return FilePath(path.string()); -} - -std::string GetHomeDirectory() { - return GetEnvOrDefault("HOME", "/tmp"); -} - -bool HasNonLoopbackInterface() { - struct ifaddrs* interfaces = nullptr; - if (getifaddrs(&interfaces) != 0) { - return false; - } - - bool connected = false; - for (struct ifaddrs* current = interfaces; current != nullptr; - current = current->ifa_next) { - if (current->ifa_name == nullptr || current->ifa_flags == 0) { - continue; - } - if ((current->ifa_flags & IFF_UP) == 0 || - (current->ifa_flags & IFF_LOOPBACK) != 0) { - continue; - } - connected = true; - break; - } - - freeifaddrs(interfaces); - return connected; -} - -Json ToJson(const PrivateCertificateData& certificate) { - return Json{ - {PrivateCertificateData::kVisibility, certificate.visibility}, - {PrivateCertificateData::kNotBefore, certificate.not_before}, - {PrivateCertificateData::kNotAfter, certificate.not_after}, - {PrivateCertificateData::kKeyPair, certificate.key_pair}, - {PrivateCertificateData::kSecretKey, certificate.secret_key}, - {PrivateCertificateData::kMetadataEncryptionKey, - certificate.metadata_encryption_key}, - {PrivateCertificateData::kId, certificate.id}, - {PrivateCertificateData::kUnencryptedMetadata, - certificate.unencrypted_metadata_proto}, - {PrivateCertificateData::kConsumedSalts, certificate.consumed_salts}, - }; -} - -std::optional FromJson(const Json& value) { - if (!value.is_object()) { - return std::nullopt; - } - - PrivateCertificateData certificate; - try { - certificate.visibility = value.at(PrivateCertificateData::kVisibility); - certificate.not_before = value.at(PrivateCertificateData::kNotBefore); - certificate.not_after = value.at(PrivateCertificateData::kNotAfter); - certificate.key_pair = value.at(PrivateCertificateData::kKeyPair); - certificate.secret_key = value.at(PrivateCertificateData::kSecretKey); - certificate.metadata_encryption_key = - value.at(PrivateCertificateData::kMetadataEncryptionKey); - certificate.id = value.at(PrivateCertificateData::kId); - certificate.unencrypted_metadata_proto = - value.at(PrivateCertificateData::kUnencryptedMetadata); - certificate.consumed_salts = - value.at(PrivateCertificateData::kConsumedSalts); - return certificate; - } catch (const Json::exception&) { - return std::nullopt; - } -} - -class FailedSigninAttempt final : public SigninAttempt { - public: - std::string Start( - absl::AnyInvocable - callback) override { - if (callback) { - std::move(callback)(UNSUPPORTED, "", "", AccountInfo{}); - } - return {}; - } - - void Close() override {} -}; - -class LinuxAccountManager final : public AccountManager { - public: - std::optional GetCurrentAccount() override { return std::nullopt; } - - std::unique_ptr Login(absl::string_view client_id, - absl::string_view client_secret) - override { - last_client_id_ = std::string(client_id); - last_client_secret_ = std::string(client_secret); - return std::make_unique(); - } - - void Logout(absl::AnyInvocable logout_callback) override { - if (logout_callback) { - std::move(logout_callback)(absl::OkStatus()); - } - } - - bool GetAccessToken( - absl::AnyInvocable)> callback) - override { - if (!callback) { - return false; - } - std::move(callback)( - absl::UnavailableError("Linux account integration is not available")); - return true; - } - - std::pair GetOAuthClientCredential() override { - return {last_client_id_, last_client_secret_}; - } - - void AddObserver(Observer* observer) override { observers_.insert(observer); } - void RemoveObserver(Observer* observer) override { - observers_.erase(observer); - } - - void SaveAccountPrefs(absl::string_view user_id, absl::string_view client_id, - absl::string_view client_secret) override { - last_client_id_ = std::string(client_id); - last_client_secret_ = std::string(client_secret); - } - - private: - absl::flat_hash_set observers_; - std::string last_client_id_; - std::string last_client_secret_; -}; - -class SafeLinuxDeviceInfo final : public nearby::DeviceInfo { - public: - std::string GetOsDeviceName() const override { - char hostname[256] = {}; - if (gethostname(hostname, sizeof(hostname)) == 0 && hostname[0] != '\0') { - return hostname; - } - const char* env_hostname = std::getenv("HOSTNAME"); - if (env_hostname != nullptr && *env_hostname != '\0') { - return env_hostname; - } - return "Linux"; - } - - nearby::api::DeviceInfo::DeviceType GetDeviceType() const override { - return nearby::api::DeviceInfo::DeviceType::kLaptop; - } - - nearby::api::DeviceInfo::OsType GetOsType() const override { - return nearby::api::DeviceInfo::OsType::kWindows; - } - - FilePath GetDownloadPath() const override { - const char* xdg_download_dir = std::getenv("XDG_DOWNLOAD_DIR"); - if (xdg_download_dir != nullptr && *xdg_download_dir != '\0') { - return FilePath(std::string(xdg_download_dir)); - } - return BuildPathFromBase(GetHomeDirectory(), {"Downloads"}); - } - - FilePath GetAppDataPath() const override { - const std::string config_home = - GetEnvOrDefault("XDG_CONFIG_HOME", - BuildPathFromBase(GetHomeDirectory(), {".config"}) - .ToString()); - return BuildPathFromBase(config_home, {"Google Nearby"}); - } - - FilePath GetTemporaryPath() const override { - const char* runtime_dir = std::getenv("XDG_RUNTIME_DIR"); - if (runtime_dir != nullptr && *runtime_dir != '\0') { - return BuildPathFromBase(runtime_dir, {"Google Nearby"}); - } - const char* tmpdir = std::getenv("TMPDIR"); - if (tmpdir != nullptr && *tmpdir != '\0') { - return BuildPathFromBase(tmpdir, {"Google Nearby"}); - } - return BuildPathFromBase("/tmp", {"Google Nearby"}); - } - - FilePath GetLogPath() const override { - const std::string state_home = - GetEnvOrDefault("XDG_STATE_HOME", - BuildPathFromBase(GetHomeDirectory(), - {".local", "state"}) - .ToString()); - return BuildPathFromBase(state_home, {"Google Nearby", "logs"}); - } - - std::optional GetAvailableDiskSpaceInBytes( - const FilePath& path) const override { - return Files::GetAvailableDiskSpaceInBytes(path); - } - - bool IsScreenLocked() const override { return false; } - - void RegisterScreenLockedListener( - absl::string_view listener_name, - std::function callback) - override { - absl::MutexLock lock(&mutex_); - screen_lock_listeners_[std::string(listener_name)] = std::move(callback); - } - - void UnregisterScreenLockedListener(absl::string_view listener_name) override { - absl::MutexLock lock(&mutex_); - screen_lock_listeners_.erase(std::string(listener_name)); - } - - bool PreventSleep() override { return true; } - bool AllowSleep() override { return true; } - - private: - mutable absl::Mutex mutex_; - absl::flat_hash_map< - std::string, - std::function> - screen_lock_listeners_ ABSL_GUARDED_BY(mutex_); -}; - -class LinuxPreferenceManager final : public PreferenceManager { - public: - LinuxPreferenceManager() - : storage_(nearby::api::ImplementationPlatform::CreatePreferencesManager( - kPreferencesFile)) {} - - void SetBoolean(absl::string_view key, bool value) override { - if (storage_ != nullptr && storage_->SetBoolean(key, value)) { - NotifyPreferenceChanged(key); - } - } - - void SetInteger(absl::string_view key, int value) override { - if (storage_ != nullptr && storage_->SetInteger(key, value)) { - NotifyPreferenceChanged(key); - } - } - - void SetInt64(absl::string_view key, int64_t value) override { - if (storage_ != nullptr && storage_->SetInt64(key, value)) { - NotifyPreferenceChanged(key); - } - } - - void SetString(absl::string_view key, absl::string_view value) override { - if (storage_ != nullptr && storage_->SetString(key, value)) { - NotifyPreferenceChanged(key); - } - } - - void SetTime(absl::string_view key, absl::Time value) override { - if (storage_ != nullptr && storage_->SetTime(key, value)) { - NotifyPreferenceChanged(key); - } - } - - void SetBooleanArray(absl::string_view key, - absl::Span value) override { - if (storage_ != nullptr && storage_->SetBooleanArray(key, value)) { - NotifyPreferenceChanged(key); - } - } - - void SetIntegerArray(absl::string_view key, - absl::Span value) override { - if (storage_ != nullptr && storage_->SetIntegerArray(key, value)) { - NotifyPreferenceChanged(key); - } - } - - void SetInt64Array(absl::string_view key, - absl::Span value) override { - if (storage_ != nullptr && storage_->SetInt64Array(key, value)) { - NotifyPreferenceChanged(key); - } - } - - void SetStringArray(absl::string_view key, - absl::Span value) override { - if (storage_ != nullptr && storage_->SetStringArray(key, value)) { - NotifyPreferenceChanged(key); - } - } - - void SetPrivateCertificateArray( - absl::string_view key, absl::Span value) - override { - Json certificates = Json::array(); - for (const PrivateCertificateData& certificate : value) { - certificates.push_back(ToJson(certificate)); - } - if (storage_ != nullptr && storage_->Set(key, certificates)) { - NotifyPreferenceChanged(key); - } - } - - void SetCertificateExpirationArray( - absl::string_view key, - absl::Span> value) override { - Json expirations = Json::array(); - for (const auto& [id, expiration] : value) { - expirations.push_back(Json{{"id", id}, {"expiration", expiration}}); - } - if (storage_ != nullptr && storage_->Set(key, expirations)) { - NotifyPreferenceChanged(key); - } - } - - void SetDictionaryBooleanValue(absl::string_view key, - absl::string_view dictionary_item, - bool value) override { - SetDictionaryValue(key, dictionary_item, value); - } - - void SetDictionaryIntegerValue(absl::string_view key, - absl::string_view dictionary_item, - int value) override { - SetDictionaryValue(key, dictionary_item, value); - } - - void SetDictionaryInt64Value(absl::string_view key, - absl::string_view dictionary_item, - int64_t value) override { - SetDictionaryValue(key, dictionary_item, value); - } - - void SetDictionaryStringValue(absl::string_view key, - absl::string_view dictionary_item, - std::string value) override { - SetDictionaryValue(key, dictionary_item, std::move(value)); - } - - void RemoveDictionaryItem(absl::string_view key, - absl::string_view dictionary_item) override { - if (storage_ == nullptr) { - return; - } - Json dictionary = storage_->Get(key, Json::object()); - if (!dictionary.is_object() || !dictionary.contains(std::string(dictionary_item))) { - return; - } - dictionary.erase(std::string(dictionary_item)); - if (storage_->Set(key, dictionary)) { - NotifyPreferenceChanged(key); - } - } - - void SetSyncConfigValue(absl::string_view binding_id, - const SyncConfigPrefs& value) override { - SetString(absl::StrCat(PrefNames::kSyncConfigPrefix, binding_id), - value.SerializeAsString()); - } - - void SetSyncBindingValue(const SyncBindingPrefs& value) override { - SetString(absl::StrCat(PrefNames::kBindingConfigPrefix, kFileSyncBindingName), - value.SerializeAsString()); - } - - bool GetBoolean(absl::string_view key, bool default_value) const override { - return storage_ == nullptr ? default_value - : storage_->GetBoolean(key, default_value); - } - - int GetInteger(absl::string_view key, int default_value) const override { - return storage_ == nullptr ? default_value - : storage_->GetInteger(key, default_value); - } - - int64_t GetInt64(absl::string_view key, - int64_t default_value) const override { - return storage_ == nullptr ? default_value - : storage_->GetInt64(key, default_value); - } - - std::string GetString(absl::string_view key, - const std::string& default_value) const override { - return storage_ == nullptr ? default_value - : storage_->GetString(key, default_value); - } - - absl::Time GetTime(absl::string_view key, - absl::Time default_value) const override { - return storage_ == nullptr ? default_value - : storage_->GetTime(key, default_value); - } - - std::vector GetBooleanArray( - absl::string_view key, - absl::Span default_value) const override { - return storage_ == nullptr ? std::vector(default_value.begin(), - default_value.end()) - : storage_->GetBooleanArray(key, default_value); - } - - std::vector GetIntegerArray( - absl::string_view key, absl::Span default_value) const - override { - return storage_ == nullptr ? std::vector(default_value.begin(), - default_value.end()) - : storage_->GetIntegerArray(key, default_value); - } - - std::vector GetInt64Array( - absl::string_view key, absl::Span default_value) const - override { - return storage_ == nullptr - ? std::vector(default_value.begin(), default_value.end()) - : storage_->GetInt64Array(key, default_value); - } - - std::vector GetStringArray( - absl::string_view key, - absl::Span default_value) const override { - return storage_ == nullptr - ? std::vector(default_value.begin(), - default_value.end()) - : storage_->GetStringArray(key, default_value); - } - - std::vector GetPrivateCertificateArray( - absl::string_view key) const override { - std::vector certificates; - if (storage_ == nullptr) { - return certificates; - } - - Json values = storage_->Get(key, Json::array()); - if (!values.is_array()) { - return certificates; - } - - for (const Json& value : values) { - std::optional certificate = FromJson(value); - if (certificate.has_value()) { - certificates.push_back(*certificate); - } - } - return certificates; - } - - std::vector> GetCertificateExpirationArray( - absl::string_view key) const override { - std::vector> expirations; - if (storage_ == nullptr) { - return expirations; - } - - Json values = storage_->Get(key, Json::array()); - if (!values.is_array()) { - return expirations; - } - - for (const Json& value : values) { - if (!value.is_object()) { - continue; - } - try { - expirations.emplace_back(value.at("id"), value.at("expiration")); - } catch (const Json::exception&) { - } - } - return expirations; - } - - std::optional GetDictionaryBooleanValue( - absl::string_view key, absl::string_view dictionary_item) const - override { - return GetDictionaryValue(key, dictionary_item); - } - - std::optional GetDictionaryIntegerValue( - absl::string_view key, absl::string_view dictionary_item) const - override { - return GetDictionaryValue(key, dictionary_item); - } - - std::optional GetDictionaryInt64Value( - absl::string_view key, absl::string_view dictionary_item) const - override { - return GetDictionaryValue(key, dictionary_item); - } - - std::optional GetDictionaryStringValue( - absl::string_view key, absl::string_view dictionary_item) const - override { - return GetDictionaryValue(key, dictionary_item); - } - - std::optional GetSyncConfigValue( - absl::string_view binding_id) const override { - std::string serialized = - GetString(absl::StrCat(PrefNames::kSyncConfigPrefix, binding_id), ""); - if (serialized.empty()) { - return std::nullopt; - } - SyncConfigPrefs value; - if (!value.ParseFromString(serialized)) { - return std::nullopt; - } - return value; - } - - std::optional GetSyncBindingValue() const override { - std::string serialized = GetString( - absl::StrCat(PrefNames::kBindingConfigPrefix, kFileSyncBindingName), ""); - if (serialized.empty()) { - return std::nullopt; - } - SyncBindingPrefs value; - if (!value.ParseFromString(serialized)) { - return std::nullopt; - } - return value; - } - - void Remove(absl::string_view key) override { - if (storage_ == nullptr) { - return; - } - storage_->Remove(key); - NotifyPreferenceChanged(key); - } - - void RemoveAllSyncConfigs() override { - if (storage_ != nullptr) { - storage_->RemoveKeyPrefix(PrefNames::kSyncConfigPrefix); - } - } - - void RemoveAllBindingConfigs() override { - if (storage_ != nullptr) { - storage_->RemoveKeyPrefix(PrefNames::kBindingConfigPrefix); - } - } - - void AddObserver( - absl::string_view name, - std::function observer) override { - absl::MutexLock lock(&mutex_); - observers_[std::string(name)] = std::move(observer); - } - - void RemoveObserver(absl::string_view name) override { - absl::MutexLock lock(&mutex_); - observers_.erase(std::string(name)); - } - - private: - template - void SetDictionaryValue(absl::string_view key, - absl::string_view dictionary_item, T value) { - if (storage_ == nullptr) { - return; - } - Json dictionary = storage_->Get(key, Json::object()); - if (!dictionary.is_object()) { - return; - } - const std::string item(dictionary_item); - if (dictionary.contains(item) && dictionary[item] == value) { - return; - } - dictionary[item] = value; - if (storage_->Set(key, dictionary)) { - NotifyPreferenceChanged(key); - } - } - - template - std::optional GetDictionaryValue( - absl::string_view key, absl::string_view dictionary_item) const { - if (storage_ == nullptr) { - return std::nullopt; - } - Json dictionary = storage_->Get(key, Json::object()); - if (!dictionary.is_object()) { - return std::nullopt; - } - - const std::string item(dictionary_item); - if (!dictionary.contains(item)) { - return std::nullopt; - } - - try { - return dictionary.at(item).get(); - } catch (const Json::exception&) { - return std::nullopt; - } - } - - void NotifyPreferenceChanged(absl::string_view key) { - absl::flat_hash_map> - observers_copy; - { - absl::MutexLock lock(&mutex_); - observers_copy = observers_; - } - for (const auto& [name, observer] : observers_copy) { - if (observer) { - observer(key); - } - } - } - - std::unique_ptr storage_; - mutable absl::Mutex mutex_; - absl::flat_hash_map> - observers_ ABSL_GUARDED_BY(mutex_); -}; - -class LinuxNetworkMonitor final : public nearby::api::NetworkMonitor { - public: - LinuxNetworkMonitor(std::function lan_connected_callback, - std::function internet_connected_callback) - : nearby::api::NetworkMonitor(std::move(lan_connected_callback), - std::move(internet_connected_callback)) { - const bool lan_connected = IsLanConnected(); - const bool internet_connected = IsInternetConnected(); - if (lan_connected_callback_) { - lan_connected_callback_(lan_connected); - } - if (internet_connected_callback_) { - internet_connected_callback_(internet_connected); - } - } - - bool IsLanConnected() override { return HasNonLoopbackInterface(); } - bool IsInternetConnected() override { return HasNonLoopbackInterface(); } -}; - -class LinuxSystemInfo final : public nearby::api::SystemInfo { - public: - std::string GetComputerManufacturer() override { return "Unknown"; } - std::string GetComputerModel() override { return "Unknown"; } - - int64_t GetComputerPhysicalMemory() override { - struct sysinfo info; - if (sysinfo(&info) != 0) { - return 0; - } - return static_cast(info.totalram) * info.mem_unit; - } - - int GetComputerProcessorCount() override { - long processors = sysconf(_SC_NPROCESSORS_CONF); - return processors > 0 ? static_cast(processors) : 1; - } - - int GetComputerLogicProcessorCount() override { - long processors = sysconf(_SC_NPROCESSORS_ONLN); - return processors > 0 ? static_cast(processors) : 1; - } - - int GetProcessorMemoryInfo() override { return 0; } - - BatteryChargeStatus QueryBatteryInfo(int& seconds, int& percent, - bool& battery_saver) override { - seconds = 0; - percent = 0; - battery_saver = false; - return BatteryChargeStatus::UNKNOWN; - } - - std::string GetOsManufacturer() override { return "Linux"; } - std::string GetOsName() override { return "Linux"; } - - std::string GetOsVersion() override { - struct utsname info; - if (uname(&info) != 0) { - return {}; - } - return info.release; - } - - std::string GetOsArchitecture() override { - struct utsname info; - if (uname(&info) != 0) { - return {}; - } - return info.machine; - } - - std::string GetOsLanguage() override { - return GetLanguageCode().value_or("en"); - } - - std::string GetProcessorManufacturer() override { return "Unknown"; } - std::string GetProcessorName() override { return "Unknown"; } - std::list GetBluetoothDriverInfos() override { return {}; } - std::list GetNetworkDriverInfos() override { return {}; } - void GetBatteryUsageReport(const FilePath& save_path) override {} -}; - -class LinuxAppInfo final : public nearby::api::AppInfo { - public: - explicit LinuxAppInfo(PreferenceManager& preference_manager) - : preference_manager_(preference_manager) {} - - std::optional GetAppVersion() override { - return std::string("linux"); - } - - std::optional GetAppLanguage() override { - return GetLanguageCode(); - } - - std::optional GetUpdateTrack() override { return std::nullopt; } - - std::optional GetAppInstallSource() override { - return std::string("manual"); - } - - bool GetFirstRunDone() override { - return preference_manager_.GetBoolean(kAppFirstRunPref, false); - } - - bool SetFirstRunDone(bool value) override { - preference_manager_.SetBoolean(kAppFirstRunPref, value); - return true; - } - - bool SetActiveFlag() override { - preference_manager_.SetInt64(kAppActivePref, absl::ToUnixNanos(absl::Now())); - return true; - } - - private: - PreferenceManager& preference_manager_; -}; - -class LinuxFastInitBleBeacon final : public nearby::api::FastInitBleBeacon { - public: - void SerializeToByteArray() override { - SetAdDataByteArray(BuildFastInitAdvertisingData(*this)); - } - - void ParseFromByteArray() override { - const auto data = GetAdDataByteArray(); - if (data[0] != nearby::api::FastInitBleBeacon::kFastInitServiceUuid[0] || - data[1] != nearby::api::FastInitBleBeacon::kFastInitServiceUuid[1]) { - return; - } - - const uint8_t metadata = data[5]; - SetVersion(static_cast((metadata >> 5) & 0x07)); - SetType(static_cast((metadata >> 2) & 0x07)); - SetUwbSupported((metadata & 0x02) != 0); - SetSenderCertSupported((metadata & 0x01) != 0); - SetAdjustedTxPower(static_cast(data[6])); - - std::array uwb_metadata{}; - uwb_metadata[0] = data[7]; - SetUwbMetadata(uwb_metadata); - - std::array uwb_address{}; - for (size_t i = 0; i < uwb_address.size(); ++i) { - uwb_address[i] = data[8 + i]; - } - SetUwbAddress(uwb_address); - - std::array salt{}; - salt[0] = data[16]; - SetSalt(salt); - - std::array secret_id_hash{}; - for (size_t i = 0; i < secret_id_hash.size(); ++i) { - secret_id_hash[i] = data[17 + i]; - } - SetSecretIdHash(secret_id_hash); - - const uint8_t flags = data[25]; - SetRequireBtAdvertising((flags & 0x80) != 0); - SetSelfOnlyAdvertising((flags & 0x40) != 0); - } -}; - -class LinuxFastInitiationManager final - : public nearby::api::FastInitiationManager { - public: - explicit LinuxFastInitiationManager(nearby::api::FastInitBleBeacon& beacon) - : beacon_(beacon), adapter_(CreateFastInitBluetoothAdapter()) { - if (adapter_ != nullptr) { - adv_manager_ = - std::make_unique<::nearby::linux::bluez::LEAdvertisementManager>( - *adapter_->GetConnection(), *adapter_); - } - } - - void StartAdvertising( - nearby::api::FastInitBleBeacon::FastInitType type, - std::function callback, - std::function - error_callback) override { - absl::MutexLock lock(&mutex_); - if (advertisement_ != nullptr) { - if (error_callback) { - error_callback(nearby::api::FastInitiationManager::Error::kResourceInUse); - } - return; - } - - if (adapter_ == nullptr || !adapter_->IsEnabled()) { - if (error_callback) { - error_callback( - nearby::api::FastInitiationManager::Error::kBluetoothRadioUnavailable); - } - return; - } - - if (adv_manager_ == nullptr) { - if (error_callback) { - error_callback( - nearby::api::FastInitiationManager::Error::kHardwareNotSupported); - } - return; - } - - auto fast_init_uuid = GetFastInitUuid(); - if (!fast_init_uuid.has_value()) { - if (error_callback) { - error_callback(nearby::api::FastInitiationManager::Error::kUnknown); - } - return; - } - - beacon_.SetVersion(nearby::api::FastInitBleBeacon::FastInitVersion::kV1); - beacon_.SetType(type); - beacon_.SetUwbSupported(false); - beacon_.SetSenderCertSupported(false); - beacon_.SetAdjustedTxPower(0); - beacon_.SetUwbMetadata({}); - beacon_.SetUwbAddress({}); - - const std::string seed = GetFastInitSeed(type, adapter_.get()); - beacon_.SetSalt(BuildFastInitSalt(seed)); - beacon_.SetSecretIdHash(BuildFastInitSecretIdHash(seed)); - beacon_.SetRequireBtAdvertising(false); - beacon_.SetSelfOnlyAdvertising( - type == nearby::api::FastInitBleBeacon::FastInitType::kSilent); - beacon_.SerializeToByteArray(); - - const auto ad_data = beacon_.GetAdDataByteArray(); - nearby::api::ble::BleAdvertisementData advertising_data; - advertising_data.is_extended_advertisement = true; - advertising_data.service_data.insert( - {*fast_init_uuid, - nearby::ByteArray(reinterpret_cast(ad_data.data() + 2), - ad_data.size() - 2)}); - - nearby::api::ble::AdvertiseParameters advertising_parameters{ - .tx_power_level = nearby::api::ble::TxPowerLevel::kHigh, - .is_connectable = false, - }; - advertisement_ = - ::nearby::linux::bluez::LEAdvertisement::CreateLEAdvertisement( - *adapter_->GetConnection(), advertising_data, - advertising_parameters); - - try { - adv_manager_->RegisterAdvertisementSync(advertisement_->getObject().getObjectPath(), {}); - } catch (const sdbus::Error& e) { - advertisement_.reset(); - if (error_callback) { - if (e.getName() == "org.bluez.Error.AlreadyExists") { - error_callback( - nearby::api::FastInitiationManager::Error::kResourceInUse); - } else if (e.getName() == "org.bluez.Error.NotPermitted") { - error_callback(nearby::api::FastInitiationManager::Error::kDisabledByUser); - } else { - error_callback(nearby::api::FastInitiationManager::Error::kUnknown); - } - } - return; - } - - if (callback) { - callback(); - } - } - - void StopAdvertising(std::function callback) override { - absl::MutexLock lock(&mutex_); - if (advertisement_ != nullptr && adv_manager_ != nullptr) { - try { - adv_manager_->UnregisterAdvertisementSync(advertisement_->getObject().getObjectPath()); - } catch (const sdbus::Error& e) { - DBUS_LOG_METHOD_CALL_ERROR(adv_manager_.get(), "UnregisterAdvertisementSync", - e); - } - advertisement_.reset(); - } - if (callback) { - callback(); - } - } - - void StartScanning( - std::function devices_discovered_callback, - std::function devices_not_discovered_callback, - std::function - error_callback) override { - if (error_callback) { - error_callback( - nearby::api::FastInitiationManager::Error::kHardwareNotSupported); - } - } - - void StopScanning(std::function callback) override { - if (callback) { - callback(); - } - } - - bool IsAdvertising() override { - absl::MutexLock lock(&mutex_); - return advertisement_ != nullptr; - } - bool IsScanning() override { return false; } - - private: - nearby::api::FastInitBleBeacon& beacon_; - std::unique_ptr<::nearby::linux::BluetoothAdapter> adapter_; - std::unique_ptr<::nearby::linux::bluez::LEAdvertisementManager> adv_manager_; - absl::Mutex mutex_; - std::unique_ptr<::nearby::linux::bluez::LEAdvertisement> advertisement_ - ABSL_GUARDED_BY(mutex_); -}; - -class LinuxBluetoothAdapter final - : public nearby::sharing::api::BluetoothAdapter { - public: - bool IsPresent() const override { return GetAddress().IsSet(); } - - bool IsPowered() const override { - return adapter_.IsValid() && adapter_.IsEnabled(); - } - - bool IsLowEnergySupported() const override { return true; } - bool IsScanOffloadSupported() const override { return false; } - bool IsAdvertisementOffloadSupported() const override { return false; } - bool IsExtendedAdvertisingSupported() const override { return false; } - bool IsPeripheralRoleSupported() const override { return true; } - - PermissionStatus GetOsPermissionStatus() const override { - return PermissionStatus::kAllowed; - } - - void SetPowered(bool powered, std::function success_callback, - std::function error_callback) override { - if (!adapter_.IsValid()) { - if (error_callback) { - error_callback(); - } - return; - } - const bool success = adapter_.SetStatus( - powered ? nearby::api::BluetoothAdapter::Status::kEnabled - : nearby::api::BluetoothAdapter::Status::kDisabled); - if (success) { - if (success_callback) { - success_callback(); - } - return; - } - if (error_callback) { - error_callback(); - } - } - - std::optional GetAdapterId() const override { - if (!adapter_.IsValid()) { - return std::nullopt; - } - std::string name = adapter_.GetName(); - if (name.empty()) { - return std::nullopt; - } - return name; - } - - MacAddress GetAddress() const override { - if (!adapter_.IsValid()) { - return {}; - } - return adapter_.GetAddress(); - } - - void AddObserver(Observer* observer) override { observers_.insert(observer); } - void RemoveObserver(Observer* observer) override { - observers_.erase(observer); - } - bool HasObserver(Observer* observer) override { - return observers_.contains(observer); - } - - private: - nearby::BluetoothAdapter adapter_; - absl::flat_hash_set observers_; -}; - -class LinuxPublicCertificateDatabase final : public PublicCertificateDatabase { - public: - void Initialize(absl::AnyInvocable callback) override { - if (callback) { - std::move(callback)(InitStatus::kOk); - } - } - - void LoadEntries( - absl::AnyInvocable>) &&> - callback) override { - auto entries = std::make_unique>(); - { - absl::MutexLock lock(&mutex_); - for (const auto& [id, certificate] : entries_) { - entries->push_back(certificate); - } - } - if (callback) { - std::move(callback)(true, std::move(entries)); - } - } - - void LoadCertificate( - absl::string_view id, - absl::AnyInvocable) &&> - callback) override { - std::unique_ptr certificate; - { - absl::MutexLock lock(&mutex_); - auto it = entries_.find(std::string(id)); - if (it != entries_.end()) { - certificate = std::make_unique(it->second); - } - } - if (callback) { - std::move(callback)(certificate != nullptr, std::move(certificate)); - } - } - - void AddCertificates(absl::Span certificates, - absl::AnyInvocable callback) override { - { - absl::MutexLock lock(&mutex_); - for (const PublicCertificate& certificate : certificates) { - entries_[certificate.secret_id()] = certificate; - } - } - if (callback) { - std::move(callback)(true); - } - } - - void RemoveCertificatesById( - std::vector ids_to_remove, - absl::AnyInvocable callback) override { - { - absl::MutexLock lock(&mutex_); - for (const std::string& id : ids_to_remove) { - entries_.erase(id); - } - } - if (callback) { - std::move(callback)(true); - } - } - - void Destroy(absl::AnyInvocable callback) override { - { - absl::MutexLock lock(&mutex_); - entries_.clear(); - } - if (callback) { - std::move(callback)(true); - } - } - - private: - absl::Mutex mutex_; - std::map entries_ ABSL_GUARDED_BY(mutex_); -}; - -} // namespace +#include "sharing/linux/nearby_fast_init_ble_beacon.h" +#include "sharing/linux/nearby_fast_init_manager.h" +#include "sharing/linux/platform/linux_account_manager.h" +#include "sharing/linux/platform/linux_platform_components.h" +#include "sharing/linux/platform/linux_preference_manager.h" +#include "sharing/linux/platform/platform_util.h" + +namespace nearby::sharing::linux { LinuxSharingPlatform::LinuxSharingPlatform() { Initialize({}); } @@ -1300,17 +41,20 @@ LinuxSharingPlatform::LinuxSharingPlatform(std::string device_name_override) { LinuxSharingPlatform::~LinuxSharingPlatform() = default; void LinuxSharingPlatform::Initialize(std::string device_name_override) { - preference_manager_ = std::make_unique(); - account_manager_ = std::make_unique(); - bluetooth_adapter_ = std::make_unique(); + fast_init_adapter_ = internal::CreateFastInitBluetoothAdapter(); + preference_manager_ = internal::CreateLinuxPreferenceManager(); + account_manager_ = internal::CreateLinuxAccountManager(); + bluetooth_adapter_ = + internal::CreateLinuxBluetoothAdapter(fast_init_adapter_); fast_init_ble_beacon_ = std::make_unique(); fast_initiation_manager_ = - std::make_unique(*fast_init_ble_beacon_); - default_task_runner_ = std::make_unique(1); - device_info_ = std::make_unique(); + std::make_unique(*fast_init_ble_beacon_, + fast_init_adapter_); + device_info_ = internal::CreateLinuxDeviceInfo(); if (!device_name_override.empty()) { - preference_manager_->SetString(PrefNames::kDeviceName, device_name_override); + preference_manager_->SetString(PrefNames::kDeviceName, + device_name_override); } } @@ -1323,12 +67,11 @@ std::unique_ptr LinuxSharingPlatform::CreateNetworkMonitor( std::function lan_connected_callback, std::function internet_connected_callback) { - return std::make_unique( + return internal::CreateLinuxNetworkMonitor( std::move(lan_connected_callback), std::move(internet_connected_callback)); } -nearby::sharing::api::BluetoothAdapter& -LinuxSharingPlatform::GetBluetoothAdapter() { +api::BluetoothAdapter& LinuxSharingPlatform::GetBluetoothAdapter() { return *bluetooth_adapter_; } @@ -1343,15 +86,14 @@ LinuxSharingPlatform::GetFastInitiationManager() { std::unique_ptr LinuxSharingPlatform::CreateSystemInfo() { - return std::make_unique(); + return internal::CreateLinuxSystemInfo(); } std::unique_ptr LinuxSharingPlatform::CreateAppInfo() { - return std::make_unique(*preference_manager_); + return internal::CreateLinuxAppInfo(*preference_manager_); } -nearby::sharing::api::PreferenceManager& -LinuxSharingPlatform::GetPreferenceManager() { +api::PreferenceManager& LinuxSharingPlatform::GetPreferenceManager() { return *preference_manager_; } @@ -1359,22 +101,20 @@ AccountManager& LinuxSharingPlatform::GetAccountManager() { return *account_manager_; } -TaskRunner& LinuxSharingPlatform::GetDefaultTaskRunner() { - return *default_task_runner_; -} - -nearby::DeviceInfo& LinuxSharingPlatform::GetDeviceInfo() { +nearby::api::DeviceInfo& LinuxSharingPlatform::GetDeviceInfo() { return *device_info_; } -std::unique_ptr +std::unique_ptr LinuxSharingPlatform::CreatePublicCertificateDatabase( const FilePath& database_path) { - return std::make_unique(); + static_cast(database_path); + return internal::CreateLinuxPublicCertificateDatabase(); } bool LinuxSharingPlatform::UpdateFileOriginMetadata( std::vector& file_paths) { + static_cast(file_paths); return true; } diff --git a/sharing/linux/platform/linux_sharing_platform.h b/sharing/linux/platform/linux_sharing_platform.h index 58e3f57a..a7813f35 100644 --- a/sharing/linux/platform/linux_sharing_platform.h +++ b/sharing/linux/platform/linux_sharing_platform.h @@ -1,23 +1,30 @@ -// Copyright 2026 +// Copyright 2026 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 THIRD_PARTY_NEARBY_SHARING_LINUX_PLATFORM_LINUX_SHARING_PLATFORM_H_ -#define THIRD_PARTY_NEARBY_SHARING_LINUX_PLATFORM_LINUX_SHARING_PLATFORM_H_ +#ifndef SHARING_LINUX_PLATFORM_LINUX_SHARING_PLATFORM_H_ +#define SHARING_LINUX_PLATFORM_LINUX_SHARING_PLATFORM_H_ -#include #include #include -#include -#include "absl/strings/string_view.h" -#include "internal/base/file_path.h" -#include "internal/platform/device_info.h" -#include "internal/platform/implementation/account_manager.h" -#include "internal/platform/task_runner.h" +#include "internal/platform/implementation/linux/bluetooth_adapter.h" +#include "location/nearby/sharing/lib/account/account_manager.h" #include "sharing/internal/api/sharing_platform.h" -namespace nearby::sharing { +namespace nearby::sharing::linux { -class LinuxSharingPlatform final : public nearby::sharing::api::SharingPlatform { +class LinuxSharingPlatform final : public api::SharingPlatform { public: LinuxSharingPlatform(); explicit LinuxSharingPlatform(std::string device_name_override); @@ -33,32 +40,33 @@ class LinuxSharingPlatform final : public nearby::sharing::api::SharingPlatform std::function lan_connected_callback, std::function internet_connected_callback) override; - nearby::sharing::api::BluetoothAdapter& GetBluetoothAdapter() override; + api::BluetoothAdapter& GetBluetoothAdapter() override; nearby::api::FastInitBleBeacon& GetFastInitBleBeacon() override; nearby::api::FastInitiationManager& GetFastInitiationManager() override; std::unique_ptr CreateSystemInfo() override; std::unique_ptr CreateAppInfo() override; - nearby::sharing::api::PreferenceManager& GetPreferenceManager() override; + api::PreferenceManager& GetPreferenceManager() override; AccountManager& GetAccountManager() override; - TaskRunner& GetDefaultTaskRunner() override; - nearby::DeviceInfo& GetDeviceInfo() override; - std::unique_ptr + nearby::api::DeviceInfo& GetDeviceInfo() override; + std::unique_ptr CreatePublicCertificateDatabase(const FilePath& database_path) override; bool UpdateFileOriginMetadata(std::vector& file_paths) override; private: void Initialize(std::string device_name_override); - std::unique_ptr preference_manager_; + std::shared_ptr<::nearby::linux::BluetoothAdapter> fast_init_adapter_; + std::unique_ptr preference_manager_; std::unique_ptr account_manager_; - std::unique_ptr bluetooth_adapter_; + std::unique_ptr bluetooth_adapter_; std::unique_ptr fast_init_ble_beacon_; - std::unique_ptr fast_initiation_manager_; - std::unique_ptr default_task_runner_; - std::unique_ptr device_info_; + std::unique_ptr + fast_initiation_manager_; + std::unique_ptr device_info_; absl::string_view (*product_id_getter_)() = nullptr; }; } // namespace nearby::sharing::linux -#endif // THIRD_PARTY_NEARBY_SHARING_LINUX_PLATFORM_LINUX_SHARING_PLATFORM_H_ +#endif // SHARING_LINUX_PLATFORM_LINUX_SHARING_PLATFORM_H_ + diff --git a/sharing/linux/platform/platform_util.cc b/sharing/linux/platform/platform_util.cc new file mode 100644 index 00000000..ec059720 --- /dev/null +++ b/sharing/linux/platform/platform_util.cc @@ -0,0 +1,120 @@ +// Copyright 2026 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 "sharing/linux/platform/platform_util.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "internal/platform/implementation/linux/bluez.h" +#include "internal/platform/implementation/linux/dbus.h" +#include "internal/platform/logging.h" + +namespace nearby::sharing::linux::internal { + +std::string GetEnvOrDefault(const char* key, std::string fallback) { + const char* value = std::getenv(key); + if (value == nullptr || *value == '\0') { + return fallback; + } + return value; +} + +std::string GetHomeDirectory() { return GetEnvOrDefault("HOME", "/tmp"); } + +FilePath BuildPathFromBase(const std::string& base, + std::initializer_list components) { + std::filesystem::path path(base); + for (const std::string& component : components) { + path /= component; + } + return FilePath(path.string()); +} + +std::optional GetLanguageCode() { + const char* lang = std::getenv("LANG"); + if (lang == nullptr || *lang == '\0') { + return std::string("en"); + } + std::string value(lang); + size_t dot = value.find('.'); + if (dot != std::string::npos) { + value.resize(dot); + } + size_t underscore = value.find('_'); + if (underscore != std::string::npos) { + value.resize(underscore); + } + if (value.empty()) { + return std::string("en"); + } + return value; +} + +bool HasNonLoopbackInterface() { + struct ifaddrs* interfaces = nullptr; + if (getifaddrs(&interfaces) != 0) { + return false; + } + + bool connected = false; + for (struct ifaddrs* current = interfaces; current != nullptr; + current = current->ifa_next) { + if (current->ifa_name == nullptr || current->ifa_flags == 0) { + continue; + } + if ((current->ifa_flags & IFF_UP) == 0 || + (current->ifa_flags & IFF_LOOPBACK) != 0) { + continue; + } + connected = true; + break; + } + + freeifaddrs(interfaces); + return connected; +} + +std::shared_ptr<::nearby::linux::BluetoothAdapter> +CreateFastInitBluetoothAdapter() { + auto system_bus = ::nearby::linux::getSystemBusConnection(); + auto manager = ::nearby::linux::bluez::BluezObjectManager(*system_bus); + try { + auto interfaces = manager.GetManagedObjects(); + for (auto& [object, properties] : interfaces) { + if (properties.count(sdbus::InterfaceName( + org::bluez::Adapter1_proxy::INTERFACE_NAME)) == 1) { + LOG(INFO) << __func__ << ": found bluetooth adapter " << object; + return std::make_shared<::nearby::linux::BluetoothAdapter>(system_bus, + object); + } + } + } catch (const sdbus::Error& e) { + DBUS_LOG_METHOD_CALL_ERROR(&manager, "GetManagedObjects", e); + } + + LOG(WARNING) << __func__ + << ": couldn't find a bluetooth adapter on this system"; + return nullptr; +} + +} // namespace nearby::sharing::linux::internal diff --git a/sharing/linux/platform/platform_util.h b/sharing/linux/platform/platform_util.h new file mode 100644 index 00000000..be9a2a51 --- /dev/null +++ b/sharing/linux/platform/platform_util.h @@ -0,0 +1,40 @@ +// Copyright 2026 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 SHARING_LINUX_PLATFORM_PLATFORM_UTIL_H_ +#define SHARING_LINUX_PLATFORM_PLATFORM_UTIL_H_ + +#include +#include +#include +#include + +#include "internal/base/file_path.h" +#include "internal/platform/implementation/linux/bluetooth_adapter.h" + +namespace nearby::sharing::linux::internal { + +std::string GetEnvOrDefault(const char* key, std::string fallback); +std::string GetHomeDirectory(); +FilePath BuildPathFromBase(const std::string& base, + std::initializer_list components); +std::optional GetLanguageCode(); +bool HasNonLoopbackInterface(); + +std::shared_ptr<::nearby::linux::BluetoothAdapter> +CreateFastInitBluetoothAdapter(); + +} // namespace nearby::sharing::linux::internal + +#endif // SHARING_LINUX_PLATFORM_PLATFORM_UTIL_H_ diff --git a/sharing/linux/platform/system_clock_shim.cc b/sharing/linux/platform/system_clock_shim.cc deleted file mode 100644 index 26896af5..00000000 --- a/sharing/linux/platform/system_clock_shim.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2026 - -#include "internal/platform/system_clock.h" - -#include - -#include "absl/time/clock.h" -#include "absl/time/time.h" - -namespace nearby { - -void SystemClock::Init() {} - -absl::Time SystemClock::ElapsedRealtime() { - return absl::FromUnixNanos( - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count()); -} - -Exception SystemClock::Sleep(absl::Duration duration) { - absl::SleepFor(duration); - return {Exception::kSuccess}; -} - -} // namespace nearby diff --git a/sharing/linux/qml_tray_app/CMakeLists.txt b/sharing/linux/qml_tray_app/CMakeLists.txt deleted file mode 100644 index 2f87b894..00000000 --- a/sharing/linux/qml_tray_app/CMakeLists.txt +++ /dev/null @@ -1,60 +0,0 @@ -cmake_minimum_required(VERSION 3.21) -project(nearby_qml_tray_app LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) - -find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Qml Quick QuickControls2 DBus) - -# Where the Bazel-built Nearby .so and header were installed -set(NEARBY_PREFIX "/usr/local" CACHE PATH "Install prefix for the Nearby shared library") - -find_library(NEARBY_SHARING_LIB nearby_sharing_api_shared - HINTS "${NEARBY_PREFIX}/lib" REQUIRED) - -find_path(NEARBY_SHARING_INCLUDE sharing/linux/nearby_sharing_api.h - HINTS "${NEARBY_PREFIX}/include" REQUIRED) - -find_library(QRENCODE_LIB - NAMES qrencode libqrencode.so.4 - HINTS /usr/lib64 /usr/lib /usr/local/lib - REQUIRED) - -qt_add_executable(nearby_qml_file_tray_app - file_share_tray_main.cpp - file_share_tray_controller.cc - file_share_tray_controller.h - file_share_state.cc - file_share_state.h - share_target_model.h - transfer_model.h - string_utils.cc - string_utils.h - status_mapper.cc - status_mapper.h - qr_code_generator.cc - qr_code_generator.h - notification_manager.cpp - notification_manager.h - third_party/libqrencode/qrencode_compat.h - resources_file_share.qrc -) - -target_include_directories(nearby_qml_file_tray_app PRIVATE "${NEARBY_SHARING_INCLUDE}") - -target_link_libraries(nearby_qml_file_tray_app PRIVATE - Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Qml Qt6::Quick Qt6::QuickControls2 Qt6::DBus - "${QRENCODE_LIB}" - "${NEARBY_SHARING_LIB}" -) - -set_target_properties(nearby_qml_file_tray_app PROPERTIES - INSTALL_RPATH "$ORIGIN/../lib" -) - -include(GNUInstallDirs) - -install(TARGETS nearby_qml_file_tray_app RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") -install(FILES "${NEARBY_SHARING_LIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}") diff --git a/sharing/linux/qml_tray_app/FileShareTray.qml b/sharing/linux/qml_tray_app/FileShareTray.qml deleted file mode 100644 index 9ef509d9..00000000 --- a/sharing/linux/qml_tray_app/FileShareTray.qml +++ /dev/null @@ -1,110 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -import "components" - -ApplicationWindow { - id: root - width: 980 - height: 760 - minimumWidth: 820 - minimumHeight: 620 - visible: true - title: "Quick Share" - - background: Rectangle { color: "#f0fdf4" } - - onClosing: function(close) { - close.accepted = false - root.hide() - fileShareController.hideToTray() - } - - SettingsPanel { - id: settingsPanel - } - - ColumnLayout { - anchors.fill: parent - spacing: 0 - - AppHeader { - onSettingsRequested: settingsPanel.open() - } - - // ── Body ───────────────────────────────────────────────────────── - RowLayout { - Layout.fillWidth: true - Layout.fillHeight: true - spacing: 0 - - SideBar {} - - // ── Main content (white panel) ──────────────────────────────── - Rectangle { - id: mainContent - Layout.fillWidth: true - Layout.fillHeight: true - color: "#ffffff" - topLeftRadius: 48 - clip: true - - readonly property bool isSendMode: fileShareController.pendingSendFilePath.length > 0 - - // ── Idle: animated blob ─────────────────────────────────── - AnimatedBlob { visible: !mainContent.isSendMode } - - // ── Non-idle: scrollable device + transfer cards ────────── - Flickable { - id: mainFlickable - anchors.fill: parent - clip: true - visible: mainContent.isSendMode - contentWidth: width - contentHeight: mainCol.implicitHeight + 96 - ScrollBar.vertical: ScrollBar {} - - ColumnLayout { - id: mainCol - x: 48 - y: 48 - width: mainFlickable.width - 96 - spacing: 16 - - SendUrlPanel { - Layout.alignment: Qt.AlignHCenter - width: Math.max(240, Math.min(mainCol.width, 420)) - } - - - Label { - text: "Nearby devices" - font.pixelSize: 20 - font.weight: Font.Medium - color: "#111827" - } - - Item { - Layout.fillWidth: true - implicitHeight: deviceFlow.childrenRect.height - visible: fileShareController.discoveredTargets.length > 0 - - Flow { - id: deviceFlow - width: parent.width - spacing: 20 - - Repeater { - model: fileShareController.discoveredTargets - delegate: DeviceCard {} - } - } - } - - } - } - } - } - } -} diff --git a/sharing/linux/qml_tray_app/README.md b/sharing/linux/qml_tray_app/README.md deleted file mode 100644 index df4b5ce1..00000000 --- a/sharing/linux/qml_tray_app/README.md +++ /dev/null @@ -1,95 +0,0 @@ -# Nearby File Share Tray App - -This folder contains the Qt/QML **FileShareTray** application — a system tray -app for file sharing via Nearby Sharing, wired to: - -- `nearby::sharing::linux::NearbySharingApi` -- Send mode (discover nearby share targets + send file) -- Receive mode (incoming requests + accept/reject) -- Transfer status list (progress + transfer status) -- Persistent tray behavior (window close hides app to tray) -- Process log redirection to file (`stdout`/`stderr`) - -## Files - -- `file_share_tray_main.cpp`: Qt app bootstrap + system tray behavior. -- `file_share_tray_controller.h/.cc`: QML-facing backend wrapper around Nearby Sharing. -- `FileShareTray.qml`: Top-level UI for the file share tray app. -- `components/`: Shared QML UI components used by `FileShareTray.qml`. -- `resources_file_share.qrc`: Embeds `FileShareTray.qml` and components. - -## Runtime behavior - -- Close button does **not** terminate the process; it hides to tray. -- Use tray icon menu to show/hide/quit. -- Mode `Send`: - - Starts discovery. - - Shows discovered share targets. - - Sends the selected file to a chosen target. -- Mode `Receive`: - - Starts advertising. - - Shows pending incoming transfer requests. - - Lets you accept/reject incoming requests. -- Transfers are shown with target, direction, status, and progress. -- `stdout` and `stderr` are redirected at startup to the configured `logPath` - setting. -- Default log path is `/tmp/nearby_qml_file_tray.log` when `logPath` is unset. -- If `logPath` is changed from Settings, restart the app to apply redirection. - -## Building - -This CMake app links against the installed Nearby shared library and header: - -- `libnearby_sharing_api_shared.so` -- `sharing/linux/nearby_sharing_api.h` - -Install them first (repo root): - -```bash -./sharing/linux/install_nearby_sharing_service.sh -``` - -Then build the app (from `sharing/linux/qml_tray_app`): - -```bash -cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DNEARBY_PREFIX=/usr/local -cmake --build build -j -``` - -## Bundle `libnearby_sharing_api_shared.so` with the app - -From `sharing/linux/qml_tray_app`: - -```bash -cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$PWD/dist" -cmake --build build -j -cmake --install build -``` - -Bundle output: - -- `dist/bin/nearby_qml_file_tray_app` -- `dist/bin/libnearby_sharing_api_shared.so` - -The app is installed with `INSTALL_RPATH=$ORIGIN`, so it resolves the Nearby -shared library from the same folder in the bundle. - -## Build a distributable `.zip` (includes runtime dependencies) - -From `sharing/linux/qml_tray_app`: - -```bash -cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -cmake --build build -j -cpack --config build/CPackConfig.cmake -G ZIP -``` - -Output: - -- `build/nearby_qml_tray_app-Linux-x86_64.zip` - -This zip is created from the CMake install tree and includes: - -- `nearby_qml_file_tray_app` -- `libnearby_sharing_api_shared.so` -- Qt runtime libs/plugins/QML imports discovered by Qt deploy tooling diff --git a/sharing/linux/qml_tray_app/cmake/BuildNearbySoIfMissing.cmake b/sharing/linux/qml_tray_app/cmake/BuildNearbySoIfMissing.cmake deleted file mode 100644 index 693c780c..00000000 --- a/sharing/linux/qml_tray_app/cmake/BuildNearbySoIfMissing.cmake +++ /dev/null @@ -1,80 +0,0 @@ -set(_output_so "${OUTPUT_SO}") -set(_bazel_executable "${BAZEL_EXECUTABLE}") -set(_bazel_target "${BAZEL_TARGET}") -set(_bazel_build_options "${BAZEL_BUILD_OPTIONS}") -set(_repo_root "${REPO_ROOT}") -set(_rebuild_inputs "${REBUILD_INPUTS}") - -# Values passed via -D can arrive wrapped in literal quotes when emitted from -# a custom command. Strip one outer quote pair if present. -foreach(_var IN ITEMS _output_so _bazel_executable _bazel_target _bazel_build_options _repo_root) - string(REGEX REPLACE "^\"(.*)\"$" "\\1" ${_var} "${${_var}}") -endforeach() - -set(_needs_rebuild TRUE) -if(EXISTS "${_output_so}") - # Rebuild if any tracked input file is newer than the output. - set(_needs_rebuild FALSE) - foreach(_input IN LISTS _rebuild_inputs) - if(EXISTS "${_input}") - if("${_input}" IS_NEWER_THAN "${_output_so}") - set(_needs_rebuild TRUE) - message(STATUS "Input changed since last Bazel build: ${_input}") - break() - endif() - endif() - endforeach() - - if(NOT _needs_rebuild) - # Reuse an existing .so when it already exports the NearbySharingApi symbols. - # This avoids stale-cache link failures after facade changes. - find_program(_nm_program nm) - if(_nm_program) - execute_process( - COMMAND "${_nm_program}" -D -C "${_output_so}" - RESULT_VARIABLE _nm_result - OUTPUT_VARIABLE _nm_output - ERROR_QUIET - ) - if(_nm_result EQUAL 0) - string(FIND "${_nm_output}" "nearby::sharing::NearbySharingApi::NearbySharingApi()" _api_ctor_idx) - string(FIND "${_nm_output}" " U nearby::api::ImplementationPlatform::CreateScheduledExecutor()" _undef_platform_idx) - string(FIND "${_nm_output}" " U nearby::SystemClock::ElapsedRealtime()" _undef_clock_idx) - string(FIND "${_nm_output}" " U nearby::Crypto::Sha256(" _undef_crypto_idx) - if(NOT _api_ctor_idx EQUAL -1 - AND _undef_platform_idx EQUAL -1 - AND _undef_clock_idx EQUAL -1 - AND _undef_crypto_idx EQUAL -1) - set(_needs_rebuild FALSE) - else() - set(_needs_rebuild TRUE) - endif() - else() - set(_needs_rebuild TRUE) - endif() - endif() - endif() - - if(NOT _needs_rebuild) - message(STATUS "Using existing Bazel library: ${_output_so}") - return() - endif() - message(STATUS "Existing Bazel library is stale/incompatible, rebuilding: ${_output_so}") -endif() - -separate_arguments(_bazel_build_options_list NATIVE_COMMAND "${_bazel_build_options}") - -message(STATUS "Bazel library not found, building ${_bazel_target}") -execute_process( - COMMAND "${_bazel_executable}" build ${_bazel_build_options_list} "${_bazel_target}" - WORKING_DIRECTORY "${_repo_root}" - RESULT_VARIABLE BAZEL_BUILD_RESULT -) - -if(NOT BAZEL_BUILD_RESULT EQUAL 0) - message(FATAL_ERROR "Bazel build failed for ${_bazel_target} (exit ${BAZEL_BUILD_RESULT})") -endif() - -if(NOT EXISTS "${_output_so}") - message(FATAL_ERROR "Bazel build completed but expected output is missing: ${_output_so}") -endif() diff --git a/sharing/linux/qml_tray_app/components/AnimatedBlob.qml b/sharing/linux/qml_tray_app/components/AnimatedBlob.qml deleted file mode 100644 index 31f67efe..00000000 --- a/sharing/linux/qml_tray_app/components/AnimatedBlob.qml +++ /dev/null @@ -1,416 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import QtQuick.Effects - -Item { - anchors.fill: parent - - readonly property color textPrimary: "#111827" - readonly property color textMuted: "#6b7280" - readonly property color textSoft: "#4b5563" - readonly property color cardSurface: "#ffffff" - readonly property color cardBorder: "#d1fae5" - readonly property bool isSendMode: fileShareController.pendingSendFilePath.length > 0 - readonly property var incomingTransfer: findIncomingTransfer() - readonly property var incomingTarget: findTargetForTransfer(incomingTransfer) - readonly property bool hasIncomingTransfer: incomingTransfer !== null - && incomingTarget !== null - readonly property bool isReceivingActive: hasIncomingTransfer - && String(incomingTransfer.status || "") !== "Complete" - readonly property real receivingIntensity: { - if (!isReceivingActive) - return 0 - var numeric = Number(incomingTransfer.progress) - if (!isFinite(numeric) || numeric < 0) - numeric = 0 - return Math.max(0.35, Math.min(1.0, numeric)) - } - property real blobChaos: receivingIntensity - property string dismissedTransferKey: "" - - Behavior on blobChaos { - NumberAnimation { - duration: 220 - easing.type: Easing.OutCubic - } - } - - function isIncomingTransferActive(status) { - return status === "InProgress" - || status === "Queued" - || status === "Connecting" - || status === "AwaitingLocalConfirmation" - || status === "AwaitingRemoteAcceptance" - || status === "Complete" - } - - function transferKey(transfer) { - if (!transfer) - return "" - return String(transfer.targetId || "") - + "|" + String(transfer.status || "") - + "|" + String(transfer.filePath || "") - + "|" + String(transfer.fileName || "") - } - - function findIncomingTransfer() { - var transfers = fileShareController.transfers - var latestCompleted = null - for (var i = transfers.length - 1; i >= 0; --i) { - var entry = transfers[i] - if (!entry) - continue - if (String(entry.direction || "") !== "incoming") - continue - var status = String(entry.status || "") - if (!isIncomingTransferActive(status)) - continue - if (status === "Complete") { - if (latestCompleted === null) - latestCompleted = entry - continue - } - return entry - } - return latestCompleted - } - - function findTargetForTransfer(transfer) { - if (!transfer) - return null - var targets = fileShareController.discoveredTargets - for (var i = 0; i < targets.length; ++i) { - var entry = targets[i] - if (entry && entry.id === transfer.targetId) - return entry - } - return { - id: transfer.targetId, - name: String(transfer.targetName || "Incoming device"), - isIncoming: true - } - } - - function incomingHeadline(status) { - if (status === "Complete") - return "Received" - if (status === "AwaitingLocalConfirmation") - return "Incoming transfer" - if (status === "AwaitingRemoteAcceptance") - return "Preparing transfer" - if (status === "Connecting") - return "Connecting" - return "Receiving" - } - - // The card only becomes actionable once the file has landed on disk and we - // have a local path to open. - function incomingClickReady() { - return hasIncomingTransfer - && String(incomingTransfer.status || "") === "Complete" - && String(incomingTransfer.filePath || "").length > 0 - } - - Label { - x: 48; y: 48 - visible: fileShareController.running - text: isSendMode - ? "Ready to send" - : "Ready to receive" - font.pixelSize: 20 - font.weight: Font.Medium - color: textPrimary - } - Canvas { - id: blobCanvas3 - width: 380; height: 380 - anchors.centerIn: parent - visible: !isSendMode - - property real t: 0 - property double lastMs: Date.now() - - Timer { - interval: 16 - running: true - repeat: true - onTriggered: { - var now = Date.now() - var dt = Math.min(0.05, Math.max(0.0, (now - blobCanvas3.lastMs) * 0.001)) - blobCanvas3.lastMs = now - blobCanvas3.t += dt * (Math.PI * 2 / Math.max(4.6, 8.0 - blobChaos * 2.6)) - blobCanvas3.requestPaint() - } - } - - onPaint: { - var ctx = getContext("2d") - ctx.clearRect(0, 0, width, height) - - var chaos = blobChaos - var cx = width / 2 + Math.sin(t * 0.33) * chaos * 10 - var cy = height / 2 + Math.cos(t * 0.27) * chaos * 8 - var n = 10 - var pts = [] - - for (var i = 0; i < n; i++) { - var a = (i / n) * Math.PI * 2 - Math.PI / 2 - var r = 150 + chaos * 22 - + Math.sin(a * 2 + t) * (11 + chaos * 14) - + Math.cos(a * 3 - t * 0.2) * (8 + chaos * 10) - + Math.sin(a * 1.5 + t * 0.7) * (6 + chaos * 8) - + Math.sin(a * 1.5 + t) * (4 + chaos * 6) - + Math.cos(a * 5 - t * 1.2) * (chaos * 9) - + Math.sin(a * 7 + t * 0.85) * (chaos * 6) - pts.push({ x: cx + Math.cos(a) * r, y: cy + Math.sin(a) * r }) - } - - var len = pts.length - ctx.beginPath() - for (var j = 0; j < len; j++) { - var p0 = pts[(j - 1 + len) % len] - var p1 = pts[j] - var p2 = pts[(j + 1) % len] - var p3 = pts[(j + 2) % len] - var cp1x = p1.x + (p2.x - p0.x) / 6 - var cp1y = p1.y + (p2.y - p0.y) / 6 - var cp2x = p2.x - (p3.x - p1.x) / 6 - var cp2y = p2.y - (p3.y - p1.y) / 6 - if (j === 0) ctx.moveTo(p1.x, p1.y) - ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y) - } - ctx.closePath() - - var grad = ctx.createRadialGradient(cx - 40, cy - 40, 0, cx, cy, 150) - grad.addColorStop(0, "#e7faed") - ctx.fillStyle = grad - ctx.fill() - } - } - - Canvas { - id: blobCanvas2 - width: 380; height: 380 - anchors.centerIn: parent - visible: !isSendMode - - property real t: 0 - property double lastMs: Date.now() - - Timer { - interval: 16 - running: true - repeat: true - onTriggered: { - var now = Date.now() - var dt = Math.min(0.05, Math.max(0.0, (now - blobCanvas2.lastMs) * 0.001)) - blobCanvas2.lastMs = now - blobCanvas2.t += dt * (Math.PI * 2 / Math.max(4.9, 8.0 - blobChaos * 2.2)) - blobCanvas2.requestPaint() - } - } - - onPaint: { - var ctx = getContext("2d") - ctx.clearRect(0, 0, width, height) - - var chaos = blobChaos - var cx = width / 2 + Math.cos(t * 0.29) * chaos * 8 - var cy = height / 2 + Math.sin(t * 0.41) * chaos * 11 - var n = 10 - var pts = [] - - for (var i = 0; i < n; i++) { - var a = (i / n) * Math.PI * 2 - Math.PI / 2 - var r = 140 + chaos * 18 - + Math.sin(a * 2 + t) * (11 + chaos * 12) - + Math.cos(a * 3 - t * 0.8) * (8 + chaos * 9) - + Math.sin(a * 1.5 + t * 0.23) * (6 + chaos * 7) - + Math.sin(a * 1.5 + t * 0.85) * (4 + chaos * 6) - + Math.cos(a * 4 + t * 1.1) * (chaos * 7) - + Math.sin(a * 6 - t * 0.95) * (chaos * 5) - pts.push({ x: cx + Math.cos(a) * r, y: cy + Math.sin(a) * r }) - } - - var len = pts.length - ctx.beginPath() - for (var j = 0; j < len; j++) { - var p0 = pts[(j - 1 + len) % len] - var p1 = pts[j] - var p2 = pts[(j + 1) % len] - var p3 = pts[(j + 2) % len] - var cp1x = p1.x + (p2.x - p0.x) / 6 - var cp1y = p1.y + (p2.y - p0.y) / 6 - var cp2x = p2.x - (p3.x - p1.x) / 6 - var cp2y = p2.y - (p3.y - p1.y) / 6 - if (j === 0) ctx.moveTo(p1.x, p1.y) - ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y) - } - ctx.closePath() - - var grad = ctx.createRadialGradient(cx - 40, cy - 40, 0, cx, cy, 150) - grad.addColorStop(0, "#caeada") - ctx.fillStyle = grad - ctx.fill() - } - } - - - - Canvas { - id: blobCanvas - width: 380; height: 380 - anchors.centerIn: parent - visible: !isSendMode - - property real t: 0 - property double lastMs: Date.now() - - Timer { - interval: 16 - running: true - repeat: true - onTriggered: { - var now = Date.now() - var dt = Math.min(0.05, Math.max(0.0, (now - blobCanvas.lastMs) * 0.001)) - blobCanvas.lastMs = now - blobCanvas.t += dt * (Math.PI * 2 / Math.max(4.4, 8.0 - blobChaos * 3.0)) - blobCanvas.requestPaint() - } - } - - onPaint: { - var ctx = getContext("2d") - ctx.clearRect(0, 0, width, height) - - var chaos = blobChaos - var cx = width / 2 + Math.sin(t * 0.55) * chaos * 12 - var cy = height / 2 + Math.cos(t * 0.47) * chaos * 10 - var n = 10 - var pts = [] - - for (var i = 0; i < n; i++) { - var a = (i / n) * Math.PI * 2 - Math.PI / 2 - var r = 130 + chaos * 15 - + Math.sin(a * 2 + t) * (11 + chaos * 15) - + Math.cos(a * 3 - t * 0.6) * (8 + chaos * 12) - + Math.sin(a * 1.5 + t * 0.35) * (6 + chaos * 9) - + Math.sin(a + t * 0.75) * (3 + chaos * 6) - + Math.cos(a * 5 - t * 1.35) * (chaos * 10) - + Math.sin(a * 8 + t * 0.92) * (chaos * 6) - pts.push({ x: cx + Math.cos(a) * r, y: cy + Math.sin(a) * r }) - } - - var len = pts.length - ctx.beginPath() - for (var j = 0; j < len; j++) { - var p0 = pts[(j - 1 + len) % len] - var p1 = pts[j] - var p2 = pts[(j + 1) % len] - var p3 = pts[(j + 2) % len] - var cp1x = p1.x + (p2.x - p0.x) / 6 - var cp1y = p1.y + (p2.y - p0.y) / 6 - var cp2x = p2.x - (p3.x - p1.x) / 6 - var cp2y = p2.y - (p3.y - p1.y) / 6 - if (j === 0) ctx.moveTo(p1.x, p1.y) - ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y) - } - ctx.closePath() - - var grad = ctx.createRadialGradient(cx - 40, cy - 40, 0, cx, cy, 150) - grad.addColorStop(0, "#acdac4") - ctx.fillStyle = grad - ctx.fill() - } - } - - Rectangle { - id: incomingTransferCard - anchors.centerIn: parent - visible: !isSendMode && hasIncomingTransfer - && transferKey(incomingTransfer) !== dismissedTransferKey - width: 200 - height: 210 - radius: 34 - color: cardSurface - border.color: cardBorder - border.width: 1 - z: 10 - - Rectangle { - anchors.fill: parent - anchors.margins: 10 - radius: parent.radius - 10 - color: "#ffffff" - opacity: 0.84 - } - - Column { - anchors.fill: parent - anchors.margins: 24 - spacing: 10 - - Label { - anchors.horizontalCenter: parent.horizontalCenter - text: incomingHeadline(String(incomingTransfer.status || "")) - font.pixelSize: 12 - font.weight: Font.DemiBold - color: "#059669" - horizontalAlignment: Text.AlignHCenter - } - - DeviceCard { - anchors.horizontalCenter: parent.horizontalCenter - modelData: incomingTarget - } - - Label { - width: parent.width - visible: String(incomingTransfer.fileName || "").length > 0 - text: String(incomingTransfer.fileName || "") - font.pixelSize: 13 - font.weight: Font.StyleItalic - color: textPrimary - wrapMode: Text.Wrap - maximumLineCount: 2 - elide: Text.ElideRight - horizontalAlignment: Text.AlignHCenter - } - } - - MouseArea { - anchors.fill: parent - enabled: incomingClickReady() - cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor - onClicked: { - dismissedTransferKey = transferKey(incomingTransfer) - fileShareController.openFileLocation(String(incomingTransfer.filePath || "")) - } - } - } - - onIncomingTransferChanged: { - if (!incomingTransfer) { - dismissedTransferKey = "" - return - } - - if (transferKey(incomingTransfer) !== dismissedTransferKey) - return - - if (String(incomingTransfer.status || "") !== "Complete") - dismissedTransferKey = "" - } - - - Label { - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - anchors.bottomMargin: 48 - visible: !isSendMode - text: fileShareController.statusMessage - font.pixelSize: 13 - color: textMuted - } -} diff --git a/sharing/linux/qml_tray_app/components/AppHeader.qml b/sharing/linux/qml_tray_app/components/AppHeader.qml deleted file mode 100644 index cbf4f338..00000000 --- a/sharing/linux/qml_tray_app/components/AppHeader.qml +++ /dev/null @@ -1,62 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -Item { - Layout.fillWidth: true - height: 80 - - signal settingsRequested() - - readonly property color textPrimary: "#111827" - readonly property color textMuted: "#6b7280" - readonly property color accent: "#16a34a" - - RowLayout { - anchors.fill: parent - anchors.leftMargin: 24 - anchors.rightMargin: 24 - - ColumnLayout { - spacing: 2 - Label { - text: "Device name" - font.pixelSize: 12 - color: textMuted - } - Label { - text: fileShareController.deviceName - font.pixelSize: 22 - font.weight: Font.Medium - color: textPrimary - } - } - - Item { Layout.fillWidth: true } - - - - Rectangle { - width: 40 - height: 40 - radius: 12 - color: settingsBtn.containsMouse ? "#dcfce7" : "transparent" - border.color: settingsBtn.containsMouse ? "#86efac" : "transparent" - - Label { - anchors.centerIn: parent - text: "⚙" - font.pixelSize: 18 - color: settingsBtn.containsMouse ? accent : textMuted - } - - MouseArea { - id: settingsBtn - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: settingsRequested() - } - } - } -} diff --git a/sharing/linux/qml_tray_app/components/DeviceCard.qml b/sharing/linux/qml_tray_app/components/DeviceCard.qml deleted file mode 100644 index 90dd18e9..00000000 --- a/sharing/linux/qml_tray_app/components/DeviceCard.qml +++ /dev/null @@ -1,224 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -Item { - id: root - required property var modelData - - width: 116 - height: deviceColumn.implicitHeight - - readonly property color surface: "#ffffff" - readonly property color textPrimary: "#111827" - readonly property color textMuted: "#6b7280" - readonly property color avatarFill: "#dcfce7" - readonly property color avatarBorder: "#bbf7d0" - readonly property color ringBase: "#d1fae5" - readonly property color ringActive: "#10b981" - readonly property color ringComplete: "#16a34a" - readonly property color ringFailed: "#ef4444" - readonly property bool canSend: fileShareController.mode === "Send" - && fileShareController.pendingSendFilePath.length > 0 - - readonly property string targetName: modelData.name && modelData.name.length > 0 - ? modelData.name : "Unknown device" - readonly property var transferData: transferForTarget() - readonly property string transferStatus: transferData ? String(transferData.status || "") : "" - readonly property bool hasTransfer: transferData !== null - readonly property bool isTransferActive: transferStatus === "InProgress" - || transferStatus === "Queued" - || transferStatus === "Connecting" - || transferStatus === "AwaitingLocalConfirmation" - || transferStatus === "AwaitingRemoteAcceptance" - readonly property bool isTransferComplete: transferStatus === "Complete" - readonly property bool isTransferFailed: hasTransfer && !isTransferActive && !isTransferComplete - readonly property bool isConnecting: transferStatus === "Connecting" - property bool showCompletionTick: false - property string previousTransferStatus: "" - readonly property real transferProgress: { - if (!hasTransfer) - return 0 - if (isTransferComplete || isTransferFailed) - return 1 - var numeric = Number(transferData.progress) - if (!isFinite(numeric) || numeric < 0) - numeric = 0 - if (isTransferActive && numeric === 0) - return 0.08 - return Math.max(0, Math.min(1, numeric)) - } - readonly property color ringColor: isTransferComplete ? ringComplete - : isTransferFailed ? ringFailed - : ringActive - - function initialLetter(label) { - if (!label || label.length === 0) return "?" - return label.charAt(0).toUpperCase() - } - - function transferForTarget() { - var transfers = fileShareController.transfers - for (var i = 0; i < transfers.length; ++i) { - var entry = transfers[i] - if (entry && entry.targetId === modelData.id) - return entry - } - return null - } - - Column { - id: deviceColumn - anchors.horizontalCenter: parent.horizontalCenter - width: parent.width - spacing: 10 - - Rectangle { - anchors.horizontalCenter: parent.horizontalCenter - width: 84 - height: 84 - radius: 42 - color: surface - opacity: canSend ? 1.0 : 0.5 - - Canvas { - id: progressRing - anchors.fill: parent - antialiasing: true - transformOrigin: Item.Center - onWidthChanged: requestPaint() - onHeightChanged: requestPaint() - - onPaint: { - var ctx = getContext("2d") - ctx.clearRect(0, 0, width, height) - - var lineWidth = 5 - var radius = (Math.min(width, height) - lineWidth) / 2 - var center = width / 2 - - ctx.lineWidth = lineWidth - ctx.lineCap = "round" - - if (!root.hasTransfer) - return - - ctx.beginPath() - ctx.strokeStyle = root.ringColor - ctx.arc(center, center, radius, -Math.PI / 2, - -Math.PI / 2 + Math.PI * 2 * root.transferProgress, false) - ctx.stroke() - } - } - - NumberAnimation { - id: connectingSpin - target: progressRing - property: "rotation" - from: 0 - to: 360 - duration: 1100 - easing.type: Easing.Linear - loops: Animation.Infinite - running: root.isConnecting - } - - Rectangle { - anchors.fill: parent - anchors.margins: 9 - radius: width / 2 - color: avatarFill - - Label { - anchors.centerIn: parent - text: initialLetter(targetName) - font.pixelSize: 28 - font.weight: Font.DemiBold - color: textPrimary - } - } - - Rectangle { - anchors.fill: parent - anchors.margins: 9 - radius: width / 2 - color: "#16a34a" - opacity: showCompletionTick ? 0.94 : 0.0 - visible: opacity > 0 - - Behavior on opacity { - NumberAnimation { duration: 160; easing.type: Easing.OutCubic } - } - - Canvas { - anchors.centerIn: parent - width: 28 - height: 28 - - onPaint: { - var ctx = getContext("2d") - ctx.clearRect(0, 0, width, height) - ctx.strokeStyle = "#ffffff" - ctx.lineWidth = 4 - ctx.lineCap = "round" - ctx.lineJoin = "round" - ctx.beginPath() - ctx.moveTo(width * 0.18, height * 0.56) - ctx.lineTo(width * 0.42, height * 0.8) - ctx.lineTo(width * 0.84, height * 0.24) - ctx.stroke() - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor - enabled: canSend - onClicked: fileShareController.sendPendingFileToTarget(modelData.id) - } - } - - Label { - anchors.horizontalCenter: parent.horizontalCenter - width: parent.width - horizontalAlignment: Text.AlignHCenter - text: targetName - font.pixelSize: 13 - font.weight: Font.Bold - elide: Text.ElideRight - maximumLineCount: 2 - wrapMode: Text.Wrap - color: textPrimary - } - } - - Timer { - id: completionTickTimer - interval: 1200 - repeat: false - onTriggered: root.showCompletionTick = false - } - - onTransferDataChanged: progressRing.requestPaint() - onTransferProgressChanged: progressRing.requestPaint() - onRingColorChanged: progressRing.requestPaint() - onIsConnectingChanged: { - if (!isConnecting) - progressRing.rotation = 0 - } - onTransferStatusChanged: { - if (transferStatus === "Complete" && previousTransferStatus.length > 0 - && previousTransferStatus !== "Complete") { - showCompletionTick = true - completionTickTimer.restart() - } else if (transferStatus !== "Complete") { - showCompletionTick = false - completionTickTimer.stop() - } - - previousTransferStatus = transferStatus - } - - Component.onCompleted: previousTransferStatus = transferStatus -} diff --git a/sharing/linux/qml_tray_app/components/SendUrlPanel.qml b/sharing/linux/qml_tray_app/components/SendUrlPanel.qml deleted file mode 100644 index b730f737..00000000 --- a/sharing/linux/qml_tray_app/components/SendUrlPanel.qml +++ /dev/null @@ -1,131 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -ColumnLayout { - id: root - - property string urlText: fileShareController.qrCodeUrl - property var qrRows: fileShareController.qrCodeRows - property int qrSize: fileShareController.qrCodeSize - property string fileName: fileShareController.pendingSendFileName - readonly property color panelTint: "#ecfdf3" - readonly property color panelBorder: "#a7f3d0" - readonly property color qrPaper: "#fffdf7" - readonly property color qrInk: "#14532d" - readonly property color accentSoft: "#d1fae5" - readonly property color accentStrong: "#34d399" - readonly property bool compact: width < 360 - readonly property real qrFrameSize: 360 - readonly property real qrInnerSize: qrFrameSize - (compact ? 34 : 42) - - spacing: compact ? 14 : 18 - implicitWidth: 420 - - Label { - Layout.alignment: Qt.AlignHCenter - text: "Scan to connect" - font.pixelSize: compact ? 16 : 18 - font.weight: Font.DemiBold - color: "#111827" - } - - Rectangle { - Layout.alignment: Qt.AlignHCenter - width: root.qrFrameSize - height: width - radius: compact ? 24 : 32 - gradient: Gradient { - GradientStop { position: 0.0; color: "#f7fff9" } - GradientStop { position: 1.0; color: root.panelTint } - } - border.color: root.panelBorder - border.width: 1 - - Rectangle { - width: parent.width * 0.52 - height: width - radius: width / 2 - x: parent.width - width * 0.72 - y: -width * 0.22 - color: "#ffffff" - opacity: 0.35 - } - - - Rectangle { - anchors.centerIn: parent - width: root.qrInnerSize - height: width - radius: compact ? 18 : 24 - color: root.qrPaper - border.color: "#dcfce7" - border.width: 1 - - Canvas { - id: qrCanvas - anchors.fill: parent - anchors.margins: compact ? 16 : 22 - antialiasing: true - onWidthChanged: requestPaint() - onHeightChanged: requestPaint() - - onPaint: { - var ctx = getContext("2d") - ctx.clearRect(0, 0, width, height) - ctx.fillStyle = root.qrPaper - ctx.fillRect(0, 0, width, height) - - if (root.qrSize <= 0 || !root.qrRows || root.qrRows.length !== root.qrSize) - return - - var quietZone = 4 - var totalModules = root.qrSize + quietZone * 2 - - var moduleSize = Math.min(width, height) / totalModules - var drawSize = moduleSize * totalModules - var offsetX = (width - drawSize) / 2 - var offsetY = (height - drawSize) / 2 - var dotInset = moduleSize * 0.18 - var dotSize = Math.max(1, moduleSize - dotInset * 2) - var dotRadius = dotSize/1.2 - - ctx.fillStyle = root.qrInk - for (var row = 0; row < root.qrSize; ++row) { - var rowData = root.qrRows[row] - for (var col = 0; col < root.qrSize; ++col) { - if (rowData.charAt(col) !== "1") - continue - - var dotX = offsetX + (col + quietZone) * moduleSize + dotInset - var dotY = offsetY + (row + quietZone) * moduleSize + dotInset - - ctx.beginPath() - ctx.arc(dotX + dotRadius, dotY + dotRadius, dotRadius, 0, Math.PI * 2) - ctx.fill() - } - } - } - } - - Label { - anchors.centerIn: parent - visible: root.qrSize <= 0 - text: "Preparing QR code..." - font.pixelSize: compact ? 12 : 13 - color: "#6b7280" - } - } - } - - Label { - Layout.alignment: Qt.AlignHCenter - text: root.fileName.length > 0 ? "Sending: " + root.fileName : "" - font.pixelSize: compact ? 12 : 13 - color: "#6b7280" - visible: text.length > 0 - } - - onQrRowsChanged: qrCanvas.requestPaint() - onQrSizeChanged: qrCanvas.requestPaint() -} diff --git a/sharing/linux/qml_tray_app/components/SettingsPanel.qml b/sharing/linux/qml_tray_app/components/SettingsPanel.qml deleted file mode 100644 index 8677681a..00000000 --- a/sharing/linux/qml_tray_app/components/SettingsPanel.qml +++ /dev/null @@ -1,234 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -Drawer { - id: root - edge: Qt.RightEdge - width: 380 - height: parent ? parent.height : 0 - implicitWidth: 380 - implicitHeight: parent ? parent.height : 0 - - readonly property color bg: "#f0fdf4" - readonly property color surface: "#ffffff" - readonly property color accent: "#38aa62" - readonly property color accentLight: "#dcfce7" - readonly property color borderColor: "#bbf7d0" - readonly property color textPrimary: "#111827" - readonly property color textMuted: "#6b7280" - - background: Rectangle { color: root.bg } - - ColumnLayout { - width: root.width - height: root.height - spacing: 0 - - // Header - Rectangle { - Layout.fillWidth: true - height: 64 - color: "transparent" - - RowLayout { - anchors.fill: parent - anchors.leftMargin: 20 - anchors.rightMargin: 20 - - Label { - text: "Settings" - font.pixelSize: 20 - font.weight: Font.Bold - color: root.textPrimary - } - - Item { Layout.fillWidth: true } - - Rectangle { - width: 32 - height: 32 - radius: 8 - color: closeArea.containsMouse ? "#f3f4f6" : "transparent" - - Label { - anchors.centerIn: parent - text: "✕" - font.pixelSize: 14 - color: root.textMuted - } - - MouseArea { - id: closeArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: root.close() - } - } - } - } - - Flickable { - id: flick - Layout.fillWidth: true - Layout.fillHeight: true - clip: false - contentWidth: width - contentHeight: settingsCol.height + 32 - ScrollBar.vertical: ScrollBar {} - - Column { - id: settingsCol - x: 20 - y: 20 - width: flick.width - 40 - spacing: 20 - - SectionLabel { text: "DEVICE" } - SectionCard { - width: settingsCol.width - - ColumnLayout { - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.margins: 12 - spacing: 12 - - RowLayout { - Layout.fillWidth: true - spacing: 10 - Label { - text: "Device name" - font.pixelSize: 13 - color: root.textMuted - Layout.preferredWidth: 110 - } - ThemedField { - text: fileShareController.deviceName - onEditingFinished: fileShareController.deviceName = text - } - } - } - } - - SectionLabel { text: "SHARING" } - SectionCard { - width: settingsCol.width - - ColumnLayout { - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.margins: 12 - spacing: 12 - - Label { - Layout.fillWidth: true - wrapMode: Text.WordWrap - font.pixelSize: 12 - color: root.textMuted - text: "Nearby Sharing uses built-in transport and discovery settings." - } - - RowLayout { - Layout.fillWidth: true - spacing: 10 - Label { - Layout.fillWidth: true - color: root.textPrimary - font.pixelSize: 13 - text: "Auto-accept incoming" - } - ThemedToggle { - checked: fileShareController.autoAcceptIncoming - onToggled: fileShareController.autoAcceptIncoming = checked - } - } - - RowLayout { - Layout.fillWidth: true - spacing: 10 - Label { - Layout.fillWidth: true - color: root.textPrimary - font.pixelSize: 13 - text: "Enable 5 GHz hotspot" - } - ThemedToggle { - checked: fileShareController.enable5GhzHotspot - onToggled: fileShareController.enable5GhzHotspot = checked - } - } - } - } - - SectionLabel { text: "LOGGING" } - SectionCard { - width: settingsCol.width - - ColumnLayout { - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.margins: 12 - spacing: 12 - - RowLayout { - Layout.fillWidth: true - spacing: 10 - Label { - text: "Log path" - font.pixelSize: 13 - color: root.textMuted - Layout.preferredWidth: 110 - } - ThemedField { - font.pixelSize: 11 - text: fileShareController.logPath - onEditingFinished: fileShareController.logPath = text - } - } - } - } - } - } - } - - component SectionLabel: Label { - font.pixelSize: 11 - font.weight: Font.DemiBold - font.letterSpacing: 0.8 - color: root.accent - } - - component SectionCard: Rectangle { - radius: 12 - color: root.surface - border.color: root.borderColor - height: (children.length > 0 ? children[0].implicitHeight : 0) + 24 - } - - component ThemedField: TextField { - Layout.fillWidth: true - implicitHeight: 38 - font.pixelSize: 13 - leftPadding: 12 - rightPadding: 12 - topPadding: 0 - bottomPadding: 0 - verticalAlignment: TextInput.AlignVCenter - color: root.textPrimary - background: Rectangle { - radius: 8 - color: "#f9fafb" - border.color: parent.activeFocus ? root.accent : root.borderColor - border.width: parent.activeFocus ? 2 : 1 - } - } - - component ThemedToggle: Switch { - palette.highlight: root.accent - } -} diff --git a/sharing/linux/qml_tray_app/components/SideBar.qml b/sharing/linux/qml_tray_app/components/SideBar.qml deleted file mode 100644 index 653de941..00000000 --- a/sharing/linux/qml_tray_app/components/SideBar.qml +++ /dev/null @@ -1,153 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -Item { - Layout.preferredWidth: 280 - Layout.fillHeight: true - - readonly property color surface: "#ffffff" - readonly property color cardBorder: "#bbf7d0" - readonly property color textPrimary: "#111827" - readonly property color textMuted: "#6b7280" - - ColumnLayout { - anchors.fill: parent - anchors.margins: 12 - spacing: 0 - - // Receive mode: visibility info - ColumnLayout { - visible: fileShareController.pendingSendFilePath.length === 0 - Layout.fillWidth: true - spacing: 0 - - Label { - Layout.leftMargin: 12 - Layout.topMargin: 16 - Layout.bottomMargin: 8 - text: "Visibility state" - color: textMuted - font.pixelSize: 13 - } - - Rectangle { - Layout.fillWidth: true - height: 52 - radius: 12 - color: "#e8faf0" - border.color: cardBorder - - RowLayout { - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - - Label { - Layout.fillWidth: true - text: !fileShareController.running - ? "Inactive" - : fileShareController.mode === "Send" ? "Discovering" : "Always visible" - font.weight: Font.Medium - color: textPrimary - } - } - } - - Label { - Layout.fillWidth: true - Layout.leftMargin: 12 - Layout.topMargin: 8 - Layout.rightMargin: 12 - text: !fileShareController.running - ? "The service is not running. Start it to discover or receive files." - : fileShareController.mode === "Send" - ? "Discovering nearby devices. Select a device below to send your file." - : "Nearby devices can share files with you. You'll be notified and must approve each transfer." - wrapMode: Text.WordWrap - font.pixelSize: 12 - color: textMuted - } - } - - // Send mode: outbound file info - ColumnLayout { - visible: fileShareController.pendingSendFilePath.length > 0 - Layout.fillWidth: true - spacing: 0 - - Label { - Layout.leftMargin: 12 - Layout.topMargin: 16 - Layout.bottomMargin: 8 - text: "Sharing 1 file" - font.weight: Font.Medium - color: textPrimary - } - - Rectangle { - Layout.leftMargin: 12 - width: 72 - height: 72 - radius: 12 - color: surface - - Label { - anchors.centerIn: parent - text: "📄" - font.pixelSize: 28 - } - } - - Label { - Layout.fillWidth: true - Layout.leftMargin: 12 - Layout.topMargin: 8 - Layout.rightMargin: 12 - text: fileShareController.pendingSendFileName - elide: Text.ElideRight - font.pixelSize: 13 - color: textMuted - } - - Label { - Layout.fillWidth: true - Layout.leftMargin: 12 - Layout.topMargin: 12 - Layout.rightMargin: 12 - text: "Make sure both devices are unlocked, close together, and have Bluetooth turned on." - wrapMode: Text.WordWrap - font.pixelSize: 12 - color: textMuted - } - } - - Item { Layout.fillHeight: true } - - // Cancel (only visible in send mode) - Rectangle { - visible: fileShareController.pendingSendFilePath.length > 0 - Layout.leftMargin: 12 - Layout.bottomMargin: 12 - height: 40 - width: cancelLbl.implicitWidth + 24 - radius: 12 - color: "#f3f4f6" - border.color: "#d1d5db" - - Label { - id: cancelLbl - anchors.centerIn: parent - text: "Cancel" - font.weight: Font.Medium - color: textPrimary - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: fileShareController.switchToReceiveMode() - } - } - } -} diff --git a/sharing/linux/qml_tray_app/file_share_state.cc b/sharing/linux/qml_tray_app/file_share_state.cc deleted file mode 100644 index 4e416652..00000000 --- a/sharing/linux/qml_tray_app/file_share_state.cc +++ /dev/null @@ -1,157 +0,0 @@ -#include "file_share_state.h" -#include "status_mapper.h" - -FileShareState::FileShareState() = default; - -void FileShareState::AddOrUpdateTarget(qlonglong id, const QString& name, - bool is_incoming) { - target_names_[id] = name; - - if (discovered_row_by_target_.contains(id)) { - const int row_index = discovered_row_by_target_.value(id); - if (row_index >= 0 && row_index < discovered_targets_.size()) { - QVariantMap target; - target[QStringLiteral("id")] = id; - target[QStringLiteral("name")] = name; - target[QStringLiteral("isIncoming")] = is_incoming; - discovered_targets_[row_index] = target; - return; - } - } - - QVariantMap target; - target[QStringLiteral("id")] = id; - target[QStringLiteral("name")] = name; - target[QStringLiteral("isIncoming")] = is_incoming; - discovered_row_by_target_[id] = discovered_targets_.size(); - discovered_targets_.append(target); -} - -void FileShareState::RemoveTarget(qlonglong id) { - if (HasActiveTransferForTarget(id)) { - AddPendingTargetRemoval(id); - return; - } - - RemovePendingTargetRemoval(id); - target_names_.remove(id); - - if (!discovered_row_by_target_.contains(id)) { - return; - } - - const int removed_index = discovered_row_by_target_.take(id); - if (removed_index < 0 || removed_index >= discovered_targets_.size()) { - return; - } - - discovered_targets_.removeAt(removed_index); - for (auto it = discovered_row_by_target_.begin(); - it != discovered_row_by_target_.end(); ++it) { - if (it.value() > removed_index) { - it.value() = it.value() - 1; - } - } -} - -QString FileShareState::GetTargetName(qlonglong id) const { - const QString name = target_names_.value(id).trimmed(); - return name.isEmpty() ? QStringLiteral("Unknown device") : name; -} - -bool FileShareState::HasTarget(qlonglong id) const { - return discovered_row_by_target_.contains(id); -} - -void FileShareState::AddOrUpdateTransfer( - qlonglong target_id, const QString& target_name, const QString& status, - double progress, qulonglong transferred_bytes, const QString& direction, - const QString& file_name, const QString& file_path) { - QVariantMap transfer{ - {QStringLiteral("targetId"), target_id}, - {QStringLiteral("targetName"), target_name}, - {QStringLiteral("status"), status}, - {QStringLiteral("progress"), progress}, - {QStringLiteral("transferredBytes"), transferred_bytes}, - {QStringLiteral("direction"), direction}, - {QStringLiteral("fileName"), file_name}, - {QStringLiteral("filePath"), file_path}, - }; - - if (transfer_row_by_target_.contains(target_id)) { - const int row_index = transfer_row_by_target_.value(target_id); - if (row_index >= 0 && row_index < transfers_.size()) { - transfers_[row_index] = transfer; - return; - } - } - - transfer_row_by_target_.insert(target_id, transfers_.size()); - transfers_.append(transfer); -} - -void FileShareState::RemoveTransfer(qlonglong target_id) { - if (!transfer_row_by_target_.contains(target_id)) { - return; - } - - const int removed_index = transfer_row_by_target_.take(target_id); - if (removed_index < 0 || removed_index >= transfers_.size()) { - return; - } - - transfers_.removeAt(removed_index); - for (auto it = transfer_row_by_target_.begin(); - it != transfer_row_by_target_.end(); ++it) { - if (it.value() > removed_index) { - it.value() = it.value() - 1; - } - } -} - -bool FileShareState::HasActiveTransferForTarget(qlonglong target_id) const { - for (const QVariant& row_value : transfers_) { - const QVariantMap row = row_value.toMap(); - if (row.value(QStringLiteral("targetId")).toLongLong() != target_id) { - continue; - } - - const QString status = row.value(QStringLiteral("status")).toString(); - if (StatusMapper::IsActiveTransferStatus(status)) { - return true; - } - } - return false; -} - -bool FileShareState::HasActiveTransfers() const { - for (const QVariant& row_value : transfers_) { - const QVariantMap row = row_value.toMap(); - const QString status = row.value(QStringLiteral("status")).toString(); - if (StatusMapper::IsActiveTransferStatus(status)) { - return true; - } - } - return false; -} - -void FileShareState::AddPendingTargetRemoval(qlonglong id) { - pending_target_removals_.insert(id); -} - -void FileShareState::RemovePendingTargetRemoval(qlonglong id) { - pending_target_removals_.remove(id); -} - -bool FileShareState::IsPendingTargetRemoval(qlonglong id) const { - return pending_target_removals_.contains(id); -} - -void FileShareState::ClearAll() { - discovered_targets_.clear(); - discovered_row_by_target_.clear(); - target_names_.clear(); - transfers_.clear(); - transfer_row_by_target_.clear(); - pending_target_removals_.clear(); -} diff --git a/sharing/linux/qml_tray_app/file_share_state.h b/sharing/linux/qml_tray_app/file_share_state.h deleted file mode 100644 index d95f190e..00000000 --- a/sharing/linux/qml_tray_app/file_share_state.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_STATE_H_ -#define SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_STATE_H_ - -#include -#include -#include -#include -#include -#include -#include "share_target_model.h" -#include "transfer_model.h" - -class FileShareState { - public: - FileShareState(); - - // Getters - QString mode() const { return mode_; } - QString deviceName() const { return device_name_; } - QString statusMessage() const { return status_message_; } - bool running() const { return running_; } - bool autoAcceptIncoming() const { return auto_accept_incoming_; } - bool enable5GhzHotspot() const { return enable_5ghz_hotspot_; } - - QString pendingSendFileName() const { return pending_send_file_name_; } - QString pendingSendFilePath() const { return pending_send_file_path_; } - qlonglong pendingSendTargetId() const { return pending_send_target_id_; } - - QVariantList discoveredTargets() const { return discovered_targets_; } - QVariantList transfers() const { return transfers_; } - - QString qrCodeUrl() const { return qr_code_url_; } - QStringList qrCodeRows() const { return qr_code_rows_; } - int qrCodeSize() const { return qr_code_size_; } - - QString logPath() const { return log_path_; } - - // Setters - void SetMode(const QString& mode) { mode_ = mode; } - void SetDeviceName(const QString& name) { device_name_ = name; } - void SetStatusMessage(const QString& message) { status_message_ = message; } - void SetRunning(bool running) { running_ = running; } - void SetAutoAcceptIncoming(bool enabled) { auto_accept_incoming_ = enabled; } - void SetEnable5GhzHotspot(bool enabled) { enable_5ghz_hotspot_ = enabled; } - - void SetPendingSendFile(const QString& file_path, const QString& file_name, - qlonglong target_id) { - pending_send_file_path_ = file_path; - pending_send_file_name_ = file_name; - pending_send_target_id_ = target_id; - } - - void ClearPendingSendFile() { - pending_send_file_path_.clear(); - pending_send_file_name_.clear(); - pending_send_target_id_ = 0; - } - - void SetQrCodeData(const QString& url, const QStringList& rows, int size) { - qr_code_url_ = url; - qr_code_rows_ = rows; - qr_code_size_ = size; - } - - void SetLogPath(const QString& path) { log_path_ = path; } - - // Target management - void AddOrUpdateTarget(qlonglong id, const QString& name, bool is_incoming); - void RemoveTarget(qlonglong id); - QString GetTargetName(qlonglong id) const; - bool HasTarget(qlonglong id) const; - - // Transfer management - void AddOrUpdateTransfer(qlonglong target_id, const QString& target_name, - const QString& status, double progress, - qulonglong transferred_bytes, - const QString& direction, const QString& file_name, - const QString& file_path); - void RemoveTransfer(qlonglong target_id); - bool HasActiveTransferForTarget(qlonglong target_id) const; - bool HasActiveTransfers() const; - - // Pending target removal management - void AddPendingTargetRemoval(qlonglong id); - void RemovePendingTargetRemoval(qlonglong id); - bool IsPendingTargetRemoval(qlonglong id) const; - - void ClearAll(); - - private: - QString mode_ = QStringLiteral("Receive"); - QString device_name_ = QStringLiteral("NearbyLinux"); - QString status_message_ = QStringLiteral("Idle"); - bool running_ = false; - bool auto_accept_incoming_ = true; - bool enable_5ghz_hotspot_ = true; - - // QR Code - QString qr_code_url_; - QStringList qr_code_rows_; - int qr_code_size_ = 0; - QString log_path_ = QStringLiteral("/tmp/nearby_qml_file_tray.log"); - - // Pending send state - QString pending_send_file_path_; - QString pending_send_file_name_; - qlonglong pending_send_target_id_ = 0; - - // Discovered targets - QVariantList discovered_targets_; - QHash discovered_row_by_target_; - QHash target_names_; - - // Transfers - QVariantList transfers_; - QHash transfer_row_by_target_; - - // Pending removals - QSet pending_target_removals_; -}; - -#endif // SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_STATE_H_ diff --git a/sharing/linux/qml_tray_app/file_share_tray_controller.cc b/sharing/linux/qml_tray_app/file_share_tray_controller.cc deleted file mode 100644 index 7f692bb1..00000000 --- a/sharing/linux/qml_tray_app/file_share_tray_controller.cc +++ /dev/null @@ -1,587 +0,0 @@ -#include "file_share_tray_controller.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "string_utils.h" -#include "status_mapper.h" -#include "qr_code_generator.h" - -FileShareTrayController::FileShareTrayController(QObject* parent) - : QObject(parent) { - const QString host = QSysInfo::machineHostName().trimmed(); - if (!host.isEmpty()) { - state_.SetDeviceName(host); - } - - loadSettings(); - initializeService(); -} - -FileShareTrayController::~FileShareTrayController() { - stop(); - if (service_) { - service_->Shutdown([](NearbySharingApi::StatusCode) {}); - } -} - -void FileShareTrayController::initializeService() { - service_ = std::make_unique(state_.deviceName().toStdString()); - service_->Set5GhzHotspotEnabled(state_.enable5GhzHotspot()); - state_.SetQrCodeData(QString::fromStdString(service_->GetQrCodeUrl()), {}, 0); - updateQrCodeData(); - emit qrCodeUrlChanged(); - emit qrCodeChanged(); - attachServiceListeners(); - // service_ ->StartFastInitiationScanning([](auto a) - // { - // std::cout << "Probably fine"; - // }); -} - -void FileShareTrayController::updateQrCodeData() { - const auto qr_data = QrCodeGenerator::GenerateQrCode(state_.qrCodeUrl()); - state_.SetQrCodeData(state_.qrCodeUrl(), qr_data.rows, qr_data.size); - emit qrCodeChanged(); -} - -void FileShareTrayController::attachServiceListeners() { - NearbySharingApi::Listener listener; - - listener.target_discovered_cb = [this](const NearbySharingApi::ShareTargetInfo& info) { - QMetaObject::invokeMethod(this, [this, info]() { updateTargetFromInfo(info); }, - Qt::QueuedConnection); - }; - - listener.target_updated_cb = [this](const NearbySharingApi::ShareTargetInfo& info) { - QMetaObject::invokeMethod(this, [this, info]() { updateTargetFromInfo(info); }, - Qt::QueuedConnection); - }; - - listener.target_lost_cb = [this](int64_t share_target_id) { - QMetaObject::invokeMethod( - this, [this, share_target_id]() { - state_.RemoveTarget(share_target_id); - emit discoveredTargetsChanged(); - }, - Qt::QueuedConnection); - }; - - listener.transfer_update_cb = [this](const NearbySharingApi::TransferUpdateInfo& update) { - QMetaObject::invokeMethod(this, [this, update]() { handleTransferUpdate(update); }, - Qt::QueuedConnection); - }; - - service_->SetListener(std::move(listener)); -} - -void FileShareTrayController::updateTargetFromInfo( - const NearbySharingApi::ShareTargetInfo& info) { - const QString name = StringUtils::TrimmedOrFallback( - StringUtils::FromStdString(info.device_name), - QStringLiteral("Unknown device")); - state_.AddOrUpdateTarget(info.id, name, info.is_incoming); - emit discoveredTargetsChanged(); -} - -void FileShareTrayController::handleTransferUpdate( - const NearbySharingApi::TransferUpdateInfo& update) { - const QString target_name = StringUtils::TrimmedFromStdString(update.device_name); - if (!target_name.isEmpty()) { - state_.AddOrUpdateTarget(update.share_target_id, target_name, update.is_incoming); - } - - const QString name = state_.GetTargetName(update.share_target_id); - const QString status = StatusMapper::TransferStatusToString(update.status); - const QString direction = - update.is_incoming ? QStringLiteral("incoming") : QStringLiteral("outgoing"); - - QString file_name = StringUtils::FromStdString(update.first_file_name); - if (file_name.isEmpty() && !update.is_incoming && - state_.pendingSendTargetId() == update.share_target_id && - !state_.pendingSendFileName().isEmpty()) { - file_name = state_.pendingSendFileName(); - } - - state_.AddOrUpdateTransfer(update.share_target_id, name, status, update.progress, - update.transferred_bytes, direction, file_name, - StringUtils::FromStdString(update.first_file_path)); - emit transfersChanged(); - - setStatus(QStringLiteral("%1 (%2)").arg(status, name)); - - // Auto-accept incoming transfers if enabled - if (update.status == NearbySharingApi::TransferStatus::kAwaitingLocalConfirmation && - state_.autoAcceptIncoming()) { - service_->Accept(update.share_target_id, [](NearbySharingApi::StatusCode) {}); - } - - // Handle final transfer status - if (StatusMapper::IsFinalTransferStatus(update.status)) { - handleTransferComplete(update); - } -} - -void FileShareTrayController::handleTransferComplete( - const NearbySharingApi::TransferUpdateInfo& update) { - const bool success = update.status == NearbySharingApi::TransferStatus::kComplete; - const QString name = state_.GetTargetName(update.share_target_id); - - if (update.is_incoming) { - handleIncomingTransferComplete(update, name, success); - } else { - handleOutgoingTransferComplete(update, name, success); - } - - // Cleanup pending send state - if (state_.pendingSendTargetId() == update.share_target_id) { - state_.ClearPendingSendFile(); - emit pendingSendFilePathChanged(); - emit pendingSendFileNameChanged(); - - // Auto-switch to receive mode after successful send - if (!update.is_incoming && success) { - switchToReceiveMode(); - } - } - - // Deferred target removal - if (state_.IsPendingTargetRemoval(update.share_target_id)) { - QTimer::singleShot(1400, this, [this, target_id = update.share_target_id]() { - if (state_.IsPendingTargetRemoval(target_id)) { - state_.RemoveTarget(target_id); - emit discoveredTargetsChanged(); - } - }); - } -} - -void FileShareTrayController::handleIncomingTransferComplete( - const NearbySharingApi::TransferUpdateInfo& update, const QString& name, - bool success) { - if (!success) { - emit requestTrayMessage( - QStringLiteral("Receive failed"), - QStringLiteral("Transfer from %1 failed").arg(name)); - return; - } - - const QString file_name = - StringUtils::FromStdString(update.first_file_name).isEmpty() - ? QStringLiteral("file") - : StringUtils::FromStdString(update.first_file_name); - - // Check for received URL - for (const auto& text : update.text_attachments) { - if (text.type == NearbySharingApi::TextAttachmentType::kUrl) { - const QString link = StringUtils::TrimmedFromStdString(text.text_body); - if (!link.isEmpty()) { - emit requestCopyLinkTrayMessage(QStringLiteral("Link received"), - QStringLiteral("%1 from %2").arg(link, name), - link); - return; - } - } - } - - // Check for received text - if (!update.text_attachments.empty()) { - const QString text_summary = [&]() { - for (const auto& text : update.text_attachments) { - const QString title = StringUtils::TrimmedFromStdString(text.text_title); - if (!title.isEmpty()) return title; - const QString body = StringUtils::TrimmedFromStdString(text.text_body); - if (!body.isEmpty()) return body; - } - return QStringLiteral("Text"); - }(); - emit requestTrayMessage(QStringLiteral("Text received"), - QStringLiteral("%1 from %2").arg(text_summary, name)); - return; - } - - emit requestTrayMessage(QStringLiteral("File received"), - QStringLiteral("%1 from %2").arg(file_name, name)); -} - -void FileShareTrayController::handleOutgoingTransferComplete( - const NearbySharingApi::TransferUpdateInfo& update, const QString& name, - bool success) { - const QString file_name = - StringUtils::FromStdString(update.first_file_name).isEmpty() - ? QStringLiteral("file") - : StringUtils::FromStdString(update.first_file_name); - - if (success) { - emit requestTrayMessage(QStringLiteral("Send complete"), - QStringLiteral("%1 sent to %2").arg(file_name, name)); - } else { - emit requestTrayMessage(QStringLiteral("Send failed"), - QStringLiteral("%1 failed to send to %2").arg(file_name, name)); - } -} - -void FileShareTrayController::loadSettings() { - QSettings settings(QStringLiteral("Nearby"), QStringLiteral("QmlFileTrayApp")); - - const QString stored_device_name = - settings.value(QStringLiteral("deviceName"), state_.deviceName()) - .toString() - .trimmed(); - if (!stored_device_name.isEmpty()) { - state_.SetDeviceName(stored_device_name); - } - - const bool stored_auto_accept = - settings.value(QStringLiteral("autoAcceptIncoming"), true).toBool(); - state_.SetAutoAcceptIncoming(stored_auto_accept); - - const bool stored_enable_5ghz_hotspot = - settings.value(QStringLiteral("enable5GhzHotspot"), true).toBool(); - state_.SetEnable5GhzHotspot(stored_enable_5ghz_hotspot); - - const QString stored_log_path = - settings.value(QStringLiteral("logPath"), QStringLiteral("/tmp/nearby_qml_file_tray.log")) - .toString() - .trimmed(); - if (!stored_log_path.isEmpty()) { - state_.SetLogPath(stored_log_path); - } -} - -void FileShareTrayController::saveSettings() const { - QSettings settings(QStringLiteral("Nearby"), QStringLiteral("QmlFileTrayApp")); - settings.setValue(QStringLiteral("deviceName"), state_.deviceName()); - settings.setValue(QStringLiteral("autoAcceptIncoming"), state_.autoAcceptIncoming()); - settings.setValue(QStringLiteral("enable5GhzHotspot"), - state_.enable5GhzHotspot()); - settings.setValue(QStringLiteral("logPath"), state_.logPath()); -} - -void FileShareTrayController::setDeviceName(const QString& device_name) { - const QString trimmed = device_name.trimmed(); - if (trimmed.isEmpty() || trimmed == state_.deviceName()) { - return; - } - - state_.SetDeviceName(trimmed); - saveSettings(); - emit deviceNameChanged(); - - if (state_.running()) { - stop(); - initializeService(); - start(); - } -} - -void FileShareTrayController::setAutoAcceptIncoming(bool enabled) { - if (enabled == state_.autoAcceptIncoming()) { - return; - } - state_.SetAutoAcceptIncoming(enabled); - saveSettings(); - emit autoAcceptIncomingChanged(); -} - -void FileShareTrayController::setEnable5GhzHotspot(bool enabled) { - if (enabled == state_.enable5GhzHotspot()) { - return; - } - state_.SetEnable5GhzHotspot(enabled); - if (service_) { - service_->Set5GhzHotspotEnabled(enabled); - } - saveSettings(); - emit enable5GhzHotspotChanged(); -} - -void FileShareTrayController::setLogPath(const QString& path) { - const QString trimmed = path.trimmed(); - if (trimmed.isEmpty() || trimmed == state_.logPath()) { - return; - } - state_.SetLogPath(trimmed); - saveSettings(); - emit logPathChanged(); -} - -void FileShareTrayController::start() { - if (state_.running()) { - return; - } - - state_.SetRunning(true); - emit runningChanged(); - -} - -void FileShareTrayController::stop() { - if (!state_.running()) { - return; - } - - state_.SetRunning(false); - emit runningChanged(); - - service_->StopSendMode([](NearbySharingApi::StatusCode) {}); - service_->StopReceiveMode([](NearbySharingApi::StatusCode) {}); - - state_.ClearAll(); - emit discoveredTargetsChanged(); - emit transfersChanged(); - - setStatus(QStringLiteral("Stopped")); -} - -void FileShareTrayController::startSendMode() { - service_->StopReceiveMode([this](NearbySharingApi::StatusCode status) { - if (status == NearbySharingApi::StatusCode::kOk || - status == NearbySharingApi::StatusCode::kStatusAlreadyStopped) { - service_->StartSendMode([this](NearbySharingApi::StatusCode status) { - QMetaObject::invokeMethod( - this, - [this, status]() { - setStatus(QStringLiteral("StartSendMode: %1") - .arg(StatusMapper::ApiStatusToString(status))); - if (status != NearbySharingApi::StatusCode::kOk) { - state_.SetRunning(false); - emit runningChanged(); - } - }, - Qt::QueuedConnection); - }); - } - }); -} - -void FileShareTrayController::startReceiveMode() { - service_->StopSendMode([this](NearbySharingApi::StatusCode status) { - if (status == NearbySharingApi::StatusCode::kOk || - status == NearbySharingApi::StatusCode::kStatusAlreadyStopped) { - service_->StartReceiveMode([this](NearbySharingApi::StatusCode status) { - QMetaObject::invokeMethod( - this, - [this, status]() { - setStatus(QStringLiteral("StartReceiveMode: %1") - .arg(StatusMapper::ApiStatusToString(status))); - if (status != NearbySharingApi::StatusCode::kOk) { - state_.SetRunning(false); - emit runningChanged(); - } - }, - Qt::QueuedConnection); - }); - } - }); -} - -void FileShareTrayController::switchToReceiveMode() { - if (state_.running() && state_.HasActiveTransfers()) { - setStatus(QStringLiteral("Cannot switch mode while transfer is active")); - emit requestTrayMessage( - QStringLiteral("Transfer in progress"), - QStringLiteral("Wait for the current transfer to complete.")); - return; - } - if (state_.running()) - { - startReceiveMode(); - state_.SetMode(QStringLiteral("Receive")); - emit modeChanged(); - } -} - -void FileShareTrayController::switchToSendModeWithFile(const QString& file_path) { - const QString trimmed_path = file_path.trimmed(); - QFileInfo info(trimmed_path); - - if (trimmed_path.isEmpty() || !info.exists() || !info.isFile()) { - setStatus(QStringLiteral("Selected file is not valid")); - emit requestTrayMessage(QStringLiteral("Send canceled"), - QStringLiteral("Please choose a valid file.")); - return; - } - - state_.SetPendingSendFile(info.absoluteFilePath(), info.fileName(), 0); - emit pendingSendFilePathChanged(); - emit pendingSendFileNameChanged(); - - if (state_.running() && state_.HasActiveTransfers()) { - setStatus(QStringLiteral("Cannot switch mode while transfer is active")); - emit requestTrayMessage( - QStringLiteral("Transfer in progress"), - QStringLiteral("Wait for the current transfer to complete.")); - return; - } - if (state_.running()) - { - startSendMode(); - state_.SetMode(QStringLiteral("Send")); - emit modeChanged(); - } - - setStatus(QStringLiteral("Discovery started. Choose a nearby device.")); - emit requestTrayMessage( - QStringLiteral("Send mode"), - QStringLiteral("Selected %1. Choose a nearby device to send.") - .arg(info.fileName())); -} - -void FileShareTrayController::sendPendingFileToTarget(qlonglong share_target_id) { - if (share_target_id <= 0) { - return; - } - - const QString file_path = state_.pendingSendFilePath(); - QFileInfo file_info(file_path); - - if (file_path.isEmpty() || !file_info.exists() || !file_info.isFile()) { - setStatus(QStringLiteral("Selected file is not available")); - emit requestTrayMessage(QStringLiteral("Send failed"), - QStringLiteral("Selected file is not available.")); - return; - } - - const QString target_name = state_.GetTargetName(share_target_id); - state_.SetPendingSendFile(file_path, file_info.fileName(), share_target_id); - - state_.AddOrUpdateTransfer(share_target_id, target_name, QStringLiteral("Queued"), 0.0, 0, - QStringLiteral("outgoing"), file_info.fileName(), - file_info.absoluteFilePath()); - emit transfersChanged(); - - service_->SendFile( - share_target_id, file_info.absoluteFilePath().toStdString(), - [this, share_target_id](NearbySharingApi::StatusCode status) { - QMetaObject::invokeMethod( - this, - [this, share_target_id, status]() { - const QString target_name = state_.GetTargetName(share_target_id); - if (status == NearbySharingApi::StatusCode::kOk) { - setStatus(QStringLiteral("Sending %1 to %2") - .arg(state_.pendingSendFileName(), target_name)); - return; - } - - emit requestTrayMessage( - QStringLiteral("Send failed"), - QStringLiteral("Could not send to %1").arg(target_name)); - - state_.AddOrUpdateTransfer(share_target_id, target_name, - QStringLiteral("Failed"), 0.0, 0, - QStringLiteral("outgoing"), - state_.pendingSendFileName(), - state_.pendingSendFilePath()); - emit transfersChanged(); - state_.SetPendingSendFile("", "", 0); - }, - Qt::QueuedConnection); - }); -} - -void FileShareTrayController::copyTextToClipboard(const QString& text) { - const QString trimmed = text.trimmed(); - if (trimmed.isEmpty()) { - return; - } - - QClipboard* clipboard = QGuiApplication::clipboard(); - if (clipboard == nullptr) { - emit requestTrayMessage(QStringLiteral("Copy failed"), - QStringLiteral("Clipboard is not available.")); - return; - } - - clipboard->setText(trimmed, QClipboard::Clipboard); - setStatus(QStringLiteral("Connection URL copied to clipboard")); - emit requestTrayMessage(QStringLiteral("URL copied"), - QStringLiteral("Link copied to clipboard.")); -} - -void FileShareTrayController::openFileLocation(const QString& file_path) { - const QString trimmed = file_path.trimmed(); - if (trimmed.isEmpty()) { - emit requestTrayMessage(QStringLiteral("Open location failed"), - QStringLiteral("No received file location is available.")); - return; - } - - QFileInfo info(trimmed); - QString target_path; - if (info.exists() && info.isFile()) { - // Open the containing folder so the file is visible in the user's file - // manager regardless of the desktop environment. - target_path = info.absolutePath(); - } else if (info.exists() && info.isDir()) { - target_path = info.absoluteFilePath(); - } else { - // Some transfer updates can outlive the exact file entry we saw earlier; - // fall back to the parent directory when it still exists. - const QFileInfo parent_info(info.absolutePath()); - if (parent_info.exists() && parent_info.isDir()) { - target_path = parent_info.absoluteFilePath(); - } - } - - if (target_path.isEmpty()) { - emit requestTrayMessage(QStringLiteral("Open location failed"), - QStringLiteral("The file location is no longer available.")); - return; - } - - const bool opened = - QDesktopServices::openUrl(QUrl::fromLocalFile(target_path)); - if (!opened) { - emit requestTrayMessage(QStringLiteral("Open location failed"), - QStringLiteral("Could not open the file location.")); - } -} - -void FileShareTrayController::clearTransfers() { - state_.ClearAll(); - emit discoveredTargetsChanged(); - emit transfersChanged(); -} - -void FileShareTrayController::hideToTray() { - // This is handled by the main window, but can be extended here if needed -} - -void FileShareTrayController::setStatus(const QString& status) { - if (status == state_.statusMessage()) { - return; - } - state_.SetStatusMessage(status); - emit statusMessageChanged(); -} - -void FileShareTrayController::notifyStateChange(const QString& property) { - if (property == QStringLiteral("mode")) { - emit modeChanged(); - } else if (property == QStringLiteral("deviceName")) { - emit deviceNameChanged(); - } else if (property == QStringLiteral("statusMessage")) { - emit statusMessageChanged(); - } else if (property == QStringLiteral("running")) { - emit runningChanged(); - } else if (property == QStringLiteral("autoAcceptIncoming")) { - emit autoAcceptIncomingChanged(); - } else if (property == QStringLiteral("enable5GhzHotspot")) { - emit enable5GhzHotspotChanged(); - } else if (property == QStringLiteral("discoveredTargets")) { - emit discoveredTargetsChanged(); - } else if (property == QStringLiteral("transfers")) { - emit transfersChanged(); - } -} diff --git a/sharing/linux/qml_tray_app/file_share_tray_controller.h b/sharing/linux/qml_tray_app/file_share_tray_controller.h deleted file mode 100644 index 05f88dbe..00000000 --- a/sharing/linux/qml_tray_app/file_share_tray_controller.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_TRAY_CONTROLLER_H_ -#define SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_TRAY_CONTROLLER_H_ - -#include -#include - -#include "file_share_state.h" -#include - -using NearbySharingApi = nearby::sharing::NearbySharingApi; - -class FileShareTrayController : public QObject { - Q_OBJECT - Q_PROPERTY(QString mode READ mode NOTIFY modeChanged) - Q_PROPERTY(QString deviceName READ deviceName WRITE setDeviceName NOTIFY deviceNameChanged) - Q_PROPERTY(QString statusMessage READ statusMessage NOTIFY statusMessageChanged) - Q_PROPERTY(bool running READ running NOTIFY runningChanged) - Q_PROPERTY(QString pendingSendFileName READ pendingSendFileName NOTIFY pendingSendFileNameChanged) - Q_PROPERTY(QString pendingSendFilePath READ pendingSendFilePath NOTIFY pendingSendFilePathChanged) - Q_PROPERTY(QVariantList discoveredTargets READ discoveredTargets NOTIFY discoveredTargetsChanged) - Q_PROPERTY(QVariantList transfers READ transfers NOTIFY transfersChanged) - Q_PROPERTY(bool autoAcceptIncoming READ autoAcceptIncoming WRITE setAutoAcceptIncoming NOTIFY autoAcceptIncomingChanged) - Q_PROPERTY(bool enable5GhzHotspot READ enable5GhzHotspot WRITE setEnable5GhzHotspot NOTIFY enable5GhzHotspotChanged) - Q_PROPERTY(QString qrCodeUrl READ qrCodeUrl NOTIFY qrCodeUrlChanged) - Q_PROPERTY(QStringList qrCodeRows READ qrCodeRows NOTIFY qrCodeChanged) - Q_PROPERTY(int qrCodeSize READ qrCodeSize NOTIFY qrCodeChanged) - Q_PROPERTY(QString logPath READ logPath WRITE setLogPath NOTIFY logPathChanged) - - public: - explicit FileShareTrayController(QObject* parent = nullptr); - ~FileShareTrayController() override; - - // Property accessors - QString mode() const { return state_.mode(); } - QString deviceName() const { return state_.deviceName(); } - QString statusMessage() const { return state_.statusMessage(); } - bool running() const { return state_.running(); } - QString pendingSendFileName() const { return state_.pendingSendFileName(); } - QString pendingSendFilePath() const { return state_.pendingSendFilePath(); } - QVariantList discoveredTargets() const { return state_.discoveredTargets(); } - QVariantList transfers() const { return state_.transfers(); } - bool autoAcceptIncoming() const { return state_.autoAcceptIncoming(); } - bool enable5GhzHotspot() const { return state_.enable5GhzHotspot(); } - QString qrCodeUrl() const { return state_.qrCodeUrl(); } - QStringList qrCodeRows() const { return state_.qrCodeRows(); } - int qrCodeSize() const { return state_.qrCodeSize(); } - QString logPath() const { return state_.logPath(); } - - // Public methods - void setDeviceName(const QString& device_name); - void setAutoAcceptIncoming(bool enabled); - void setEnable5GhzHotspot(bool enabled); - void setLogPath(const QString& path); - - Q_INVOKABLE void start(); - Q_INVOKABLE void stop(); - Q_INVOKABLE void switchToReceiveMode(); - Q_INVOKABLE void switchToSendModeWithFile(const QString& file_path); - Q_INVOKABLE void sendPendingFileToTarget(qlonglong share_target_id); - Q_INVOKABLE void copyTextToClipboard(const QString& text); - Q_INVOKABLE void openFileLocation(const QString& file_path); - Q_INVOKABLE void clearTransfers(); - Q_INVOKABLE void hideToTray(); - - signals: - void modeChanged(); - void deviceNameChanged(); - void statusMessageChanged(); - void runningChanged(); - void pendingSendFileNameChanged(); - void pendingSendFilePathChanged(); - void discoveredTargetsChanged(); - void transfersChanged(); - void autoAcceptIncomingChanged(); - void enable5GhzHotspotChanged(); - void qrCodeUrlChanged(); - void qrCodeChanged(); - void logPathChanged(); - - void requestTrayMessage(const QString& title, const QString& body); - void requestCopyLinkTrayMessage(const QString& title, const QString& body, - const QString& link); - - private: - void initializeService(); - void attachServiceListeners(); - void loadSettings(); - void saveSettings() const; - void updateQrCodeData(); - - void startSendMode(); - void startReceiveMode(); - - void updateTargetFromInfo(const NearbySharingApi::ShareTargetInfo& info); - void handleTransferUpdate(const NearbySharingApi::TransferUpdateInfo& update); - void handleTransferComplete(const NearbySharingApi::TransferUpdateInfo& update); - void handleIncomingTransferComplete(const NearbySharingApi::TransferUpdateInfo& update, - const QString& name, bool success); - void handleOutgoingTransferComplete(const NearbySharingApi::TransferUpdateInfo& update, - const QString& name, bool success); - - void setStatus(const QString& status); - void notifyStateChange(const QString& property); - - std::unique_ptr service_; - FileShareState state_; -}; - -#endif // SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_TRAY_CONTROLLER_H_ diff --git a/sharing/linux/qml_tray_app/file_share_tray_main.cpp b/sharing/linux/qml_tray_app/file_share_tray_main.cpp deleted file mode 100644 index 355ef007..00000000 --- a/sharing/linux/qml_tray_app/file_share_tray_main.cpp +++ /dev/null @@ -1,240 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "file_share_tray_controller.h" -#include "notification_manager.h" - -namespace { - -constexpr char kDefaultLogPath[] = "/tmp/nearby_qml_file_tray.log"; - -bool EnsureLogDirectory(const QString& file_path) { - const QFileInfo file_info(file_path); - QDir directory = file_info.absoluteDir(); - if (directory.exists()) { - return true; - } - return directory.mkpath(QStringLiteral(".")); -} - -bool RedirectStdStreamsToFile(const QString& file_path) { - const QByteArray encoded_path = QFile::encodeName(file_path); - const int fd = ::open(encoded_path.constData(), O_CREAT | O_APPEND | O_WRONLY, 0644); - if (fd < 0) { - return false; - } - - const bool redirected_stdout = ::dup2(fd, STDOUT_FILENO) >= 0; - const bool redirected_stderr = ::dup2(fd, STDERR_FILENO) >= 0; - ::close(fd); - return redirected_stdout && redirected_stderr; -} - -QString ResolveConfiguredLogPath() { - QSettings settings(QStringLiteral("Nearby"), QStringLiteral("QmlFileTrayApp")); - const QString configured_path = - settings.value(QStringLiteral("logPath"), - QString::fromLatin1(kDefaultLogPath)) - .toString() - .trimmed(); - if (configured_path.isEmpty()) { - return QString::fromLatin1(kDefaultLogPath); - } - return configured_path; -} - -void RedirectProcessLogsToConfiguredPath() { - QString log_path = ResolveConfiguredLogPath(); - if (EnsureLogDirectory(log_path) && RedirectStdStreamsToFile(log_path)) { - return; - } - - const QString fallback_path = QString::fromLatin1(kDefaultLogPath); - if (log_path == fallback_path) { - return; - } - if (!EnsureLogDirectory(fallback_path)) { - return; - } - RedirectStdStreamsToFile(fallback_path); -} - -QIcon BuildTintedSymbolicIcon(const QString& source, const QColor& color) { - QIcon source_icon(source); - if (source_icon.isNull()) { - return QIcon(); - } - - QIcon tinted_icon; - for (int size : {16, 18, 20, 22, 24, 32}) { - QPixmap pixmap = source_icon.pixmap(size, size); - if (pixmap.isNull()) { - continue; - } - - QPixmap tinted(pixmap.size()); - tinted.fill(Qt::transparent); - - QPainter painter(&tinted); - painter.drawPixmap(0, 0, pixmap); - painter.setCompositionMode(QPainter::CompositionMode_SourceIn); - painter.fillRect(tinted.rect(), color); - painter.end(); - - tinted_icon.addPixmap(tinted); - } - - return tinted_icon; -} - -} // namespace - -int main(int argc, char* argv[]) { - RedirectProcessLogsToConfiguredPath(); - - QApplication app(argc, argv); - app.setQuitOnLastWindowClosed(false); - - FileShareTrayController controller; - - QQmlApplicationEngine engine; - engine.rootContext()->setContextProperty("fileShareController", &controller); - engine.load(QUrl(QStringLiteral("qrc:/qml/FileShareTray.qml"))); - if (engine.rootObjects().isEmpty()) { - return 1; - } - - - auto* window = qobject_cast(engine.rootObjects().first()); - if (window == nullptr) { - return 1; - } - - const auto resolve_tray_icon = [&app]() { - const QColor white = "white"; - QIcon tray_icon = BuildTintedSymbolicIcon( - QStringLiteral(":/icons/tray_icon-symbolic.svg"), white); - if (tray_icon.isNull()) { - tray_icon = QIcon::fromTheme(QStringLiteral("network-wireless-symbolic")); - } - if (tray_icon.isNull()) { - tray_icon = QIcon(QStringLiteral(":/icons/tray_icon.png")); - } - if (tray_icon.isNull()) { - tray_icon = app.windowIcon(); - } - return tray_icon; - }; - - QSystemTrayIcon tray(resolve_tray_icon()); - tray.setToolTip(QStringLiteral("Nearby File Tray")); - NotificationManager notification_manager(&tray, &app); - - QMenu tray_menu; - QAction* send_action = tray_menu.addAction(QStringLiteral("Send")); - QAction* receive_action = tray_menu.addAction(QStringLiteral("Receive")); - tray_menu.addSeparator(); - QAction* show_action = tray_menu.addAction(QStringLiteral("Show")); - QAction* hide_action = tray_menu.addAction(QStringLiteral("Hide")); - tray_menu.addSeparator(); - QAction* quit_action = tray_menu.addAction(QStringLiteral("Quit")); - - QObject::connect(send_action, &QAction::triggered, window, - [&controller, window]() { - const QString file = QFileDialog::getOpenFileName( - nullptr, QStringLiteral("Select file to send")); - if (file.isEmpty()) { - return; - } - controller.switchToSendModeWithFile(file); - window->show(); - window->raise(); - window->requestActivate(); - }); - - QObject::connect(receive_action, &QAction::triggered, - [&controller, window]() { - controller.switchToReceiveMode(); - window->show(); - window->raise(); - window->requestActivate(); - }); - - QObject::connect(show_action, &QAction::triggered, window, [window]() { - window->show(); - window->raise(); - window->requestActivate(); - }); - - QObject::connect(hide_action, &QAction::triggered, window, [window]() { - window->hide(); - }); - - QObject::connect(quit_action, &QAction::triggered, &app, - [&controller, &app]() { - controller.stop(); - app.quit(); - }); - - QObject::connect(&tray, &QSystemTrayIcon::activated, window, - [window](QSystemTrayIcon::ActivationReason reason) { - if (reason != QSystemTrayIcon::Trigger && - reason != QSystemTrayIcon::DoubleClick) { - return; - } - if (window->isVisible()) { - window->hide(); - } else { - window->show(); - window->raise(); - window->requestActivate(); - } - }); - - QObject::connect(&controller, &FileShareTrayController::requestTrayMessage, - ¬ification_manager, &NotificationManager::ShowNotification); - QObject::connect(&controller, - &FileShareTrayController::requestCopyLinkTrayMessage, - ¬ification_manager, - [¬ification_manager](const QString& title, - const QString& body, - const QString& link) { - notification_manager.ShowCopyableNotification( - title, body, link, QStringLiteral("Copy link")); - }); - - QObject::connect(&app, &QCoreApplication::aboutToQuit, &controller, - [&controller]() { controller.stop(); }); -#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) - QObject::connect(app.styleHints(), &QStyleHints::colorSchemeChanged, &tray, - [&tray, &resolve_tray_icon](Qt::ColorScheme) { - tray.setIcon(resolve_tray_icon()); - }); -#endif - - tray.setContextMenu(&tray_menu); - tray.show(); - - controller.start(); - //controller. - controller.switchToReceiveMode(); - - return app.exec(); -} diff --git a/sharing/linux/qml_tray_app/install_nearby_file_share.sh b/sharing/linux/qml_tray_app/install_nearby_file_share.sh deleted file mode 100755 index 91fa408c..00000000 --- a/sharing/linux/qml_tray_app/install_nearby_file_share.sh +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -MODE="user" -PREFIX="" - -usage() { - cat <<'USAGE' -Usage: ./install_nearby_file_share.sh [options] - -Install Nearby File Share app artifacts from an extracted release bundle. - -Options: - --user Install under $HOME/.local (default) - --system Install under /usr/local (uses sudo when needed) - --prefix DIR Custom install prefix (overrides --user/--system default) - -h, --help Show this help - -Examples: - ./install_nearby_file_share.sh - ./install_nearby_file_share.sh --system - ./install_nearby_file_share.sh --prefix "$HOME/.local" -USAGE -} - -nearest_existing_parent() { - local path="$1" - while [[ ! -e "$path" ]]; do - path="$(dirname "$path")" - done - printf '%s\n' "$path" -} - -while [[ $# -gt 0 ]]; do - case "$1" in - --user) - MODE="user" - shift - ;; - --system) - MODE="system" - shift - ;; - --prefix) - PREFIX="$2" - shift 2 - ;; - -h|--help) - usage - exit 0 - ;; - *) - echo "Unknown argument: $1" >&2 - usage - exit 1 - ;; - esac -done - -if [[ -z "$PREFIX" ]]; then - if [[ "$MODE" == "system" ]]; then - PREFIX="/usr/local" - else - PREFIX="$HOME/.local" - fi -fi - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -BIN_SRC="$SCRIPT_DIR/bin/nearby_qml_file_tray_app" -LIB_SRC="$SCRIPT_DIR/lib/libnearby_sharing_api_shared.so" -DESKTOP_SRC="$SCRIPT_DIR/share/applications/nearby-file-share.desktop" -ICON_SRC_STAGED="$SCRIPT_DIR/share/icons/hicolor/256x256/apps/nearby-file-share.png" -ICON_SRC_FALLBACK="$SCRIPT_DIR/nearby-linux-desktop.png" -ICON_SRC="$ICON_SRC_STAGED" -if [[ ! -f "$ICON_SRC" && -f "$ICON_SRC_FALLBACK" ]]; then - ICON_SRC="$ICON_SRC_FALLBACK" -fi -HEADER_SRC="$SCRIPT_DIR/include/sharing/linux/nearby_sharing_api.h" - -for required in "$BIN_SRC" "$LIB_SRC" "$DESKTOP_SRC" "$ICON_SRC"; do - if [[ ! -f "$required" ]]; then - echo "Missing required bundle artifact: $required" >&2 - echo "Run this installer from the extracted release bundle root." >&2 - exit 1 - fi -done - -BINDIR="$PREFIX/bin" -LIBDIR="$PREFIX/lib" -INCLUDEDIR="$PREFIX/include/sharing/linux" -DESKTOP_DIR="$PREFIX/share/applications" -ICON_DIR="$PREFIX/share/icons/hicolor/256x256/apps" - -NEEDS_ELEVATION=0 -for path in "$BINDIR" "$LIBDIR" "$DESKTOP_DIR" "$ICON_DIR"; do - parent="$(nearest_existing_parent "$path")" - if [[ ! -w "$parent" ]]; then - NEEDS_ELEVATION=1 - break - fi -done - -INSTALL_PREFIX=() -if [[ "$NEEDS_ELEVATION" -eq 1 && "$(id -u)" -ne 0 ]]; then - if ! command -v sudo >/dev/null 2>&1; then - echo "Install requires elevated privileges, but sudo is not available." >&2 - exit 1 - fi - INSTALL_PREFIX=(sudo) -fi - -TMP_DESKTOP="$(mktemp)" -trap 'rm -f "$TMP_DESKTOP"' EXIT - -sed \ - -e "s|^Exec=.*|Exec=${BINDIR}/nearby_qml_file_tray_app|" \ - -e "s|^Icon=.*|Icon=${ICON_DIR}/nearby-file-share.png|" \ - "$DESKTOP_SRC" > "$TMP_DESKTOP" - -echo "[1/5] Installing application binary" -"${INSTALL_PREFIX[@]}" install -d "$BINDIR" -"${INSTALL_PREFIX[@]}" install -m 0755 "$BIN_SRC" "$BINDIR/" - -echo "[2/5] Installing shared library" -"${INSTALL_PREFIX[@]}" install -d "$LIBDIR" -"${INSTALL_PREFIX[@]}" install -m 0755 "$LIB_SRC" "$LIBDIR/" - -if [[ -f "$HEADER_SRC" ]]; then - echo "[3/5] Installing public header" - "${INSTALL_PREFIX[@]}" install -d "$INCLUDEDIR" - "${INSTALL_PREFIX[@]}" install -m 0644 "$HEADER_SRC" "$INCLUDEDIR/" -else - echo "[3/5] Header not present in bundle; skipping" -fi - -echo "[4/5] Installing desktop entry and icon" -"${INSTALL_PREFIX[@]}" install -d "$DESKTOP_DIR" -"${INSTALL_PREFIX[@]}" install -d "$ICON_DIR" -"${INSTALL_PREFIX[@]}" install -m 0644 "$TMP_DESKTOP" "$DESKTOP_DIR/nearby-file-share.desktop" -"${INSTALL_PREFIX[@]}" install -m 0644 "$ICON_SRC" "$ICON_DIR/nearby-file-share.png" - -echo "[5/5] Refreshing desktop/system caches" -if command -v update-desktop-database >/dev/null 2>&1; then - "${INSTALL_PREFIX[@]}" update-desktop-database "$DESKTOP_DIR" || true -fi -if command -v ldconfig >/dev/null 2>&1 && [[ "$PREFIX" == "/usr" || "$PREFIX" == "/usr/local" ]]; then - "${INSTALL_PREFIX[@]}" ldconfig || true -fi - -echo "Installed Nearby File Share:" -echo " binary : $BINDIR/nearby_qml_file_tray_app" -echo " library: $LIBDIR/libnearby_sharing_api_shared.so" -echo " desktop: $DESKTOP_DIR/nearby-file-share.desktop" -echo " icon : $ICON_DIR/nearby-file-share.png" -if [[ -f "$HEADER_SRC" ]]; then - echo " header : $INCLUDEDIR/nearby_sharing_api.h" -fi diff --git a/sharing/linux/qml_tray_app/nearby-file-share.desktop b/sharing/linux/qml_tray_app/nearby-file-share.desktop deleted file mode 100644 index 948b022d..00000000 --- a/sharing/linux/qml_tray_app/nearby-file-share.desktop +++ /dev/null @@ -1,10 +0,0 @@ -[Desktop Entry] -Type=Application -Name=Nearby File Share -Comment=Share files with nearby devices using Nearby Connections -Exec=nearby_qml_file_tray_app -Icon=nearby-file-share -Categories=Utility;Network;FileTransfer; -Keywords=share;file;nearby;transfer; -StartupNotify=false -Terminal=false diff --git a/sharing/linux/qml_tray_app/nearby-linux-desktop.png b/sharing/linux/qml_tray_app/nearby-linux-desktop.png deleted file mode 100644 index 2273e633..00000000 Binary files a/sharing/linux/qml_tray_app/nearby-linux-desktop.png and /dev/null differ diff --git a/sharing/linux/qml_tray_app/notification_manager.cpp b/sharing/linux/qml_tray_app/notification_manager.cpp deleted file mode 100644 index cdf19cda..00000000 --- a/sharing/linux/qml_tray_app/notification_manager.cpp +++ /dev/null @@ -1,210 +0,0 @@ -#include "notification_manager.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -constexpr char kNotificationsService[] = "org.freedesktop.Notifications"; -constexpr char kNotificationsPath[] = "/org/freedesktop/Notifications"; -constexpr char kNotificationsInterface[] = "org.freedesktop.Notifications"; -constexpr char kCopyActionId[] = "copy_value"; -constexpr char kDesktopEntryId[] = "nearby-file-share"; - -} // namespace - -NotificationManager::NotificationManager(QSystemTrayIcon* tray_icon, - QObject* parent) - : QObject(parent), tray_icon_(tray_icon) { - QDBusConnection session_bus = QDBusConnection::sessionBus(); - if (!session_bus.isConnected()) { - return; - } - - session_bus.connect(QString::fromLatin1(kNotificationsService), - QString::fromLatin1(kNotificationsPath), - QString::fromLatin1(kNotificationsInterface), - QStringLiteral("ActionInvoked"), this, - SLOT(OnActionInvoked(uint,QString))); - session_bus.connect(QString::fromLatin1(kNotificationsService), - QString::fromLatin1(kNotificationsPath), - QString::fromLatin1(kNotificationsInterface), - QStringLiteral("NotificationClosed"), this, - SLOT(OnNotificationClosed(uint,uint))); - - QDBusInterface notification_interface( - QString::fromLatin1(kNotificationsService), - QString::fromLatin1(kNotificationsPath), - QString::fromLatin1(kNotificationsInterface), session_bus); - QDBusReply capabilities_reply = - notification_interface.call(QStringLiteral("GetCapabilities")); - if (capabilities_reply.isValid()) { - supports_actions_ = - capabilities_reply.value().contains(QStringLiteral("actions")); - } -} - -void NotificationManager::ShowNotification(const QString& title, - const QString& body) { - if (tray_icon_ != nullptr) { - tray_icon_->showMessage(title, body, QSystemTrayIcon::Information, 4000); - } -} - -void NotificationManager::ShowCopyableNotification( - const QString& title, const QString& body, const QString& text_to_copy, - const QString& action_label) { - const QString trimmed_text = text_to_copy.trimmed(); - const QString trimmed_action_label = action_label.trimmed().isEmpty() - ? QStringLiteral("Copy") - : action_label.trimmed(); - if (trimmed_text.isEmpty()) { - ShowNotification(title, body); - return; - } - - if (supports_actions_) { - QDBusInterface notification_interface( - QString::fromLatin1(kNotificationsService), - QString::fromLatin1(kNotificationsPath), - QString::fromLatin1(kNotificationsInterface), - QDBusConnection::sessionBus()); - const QString application_name = QCoreApplication::applicationName(); - const QString notification_icon = EnsureNotificationIconPath(); - QVariantMap hints{{QStringLiteral("desktop-entry"), - QString::fromLatin1(kDesktopEntryId)}}; - if (!notification_icon.isEmpty()) { - hints.insert(QStringLiteral("image-path"), notification_icon); - } - QDBusReply reply = notification_interface.call( - QStringLiteral("Notify"), application_name, - static_cast(0), - notification_icon.isEmpty() ? QString::fromLatin1(kDesktopEntryId) - : notification_icon, - title, body, - QStringList{QString::fromLatin1(kCopyActionId), trimmed_action_label}, - hints, 8000); - if (reply.isValid()) { - copy_actions_.insert(reply.value(), CopyActionState{trimmed_text}); - return; - } - } - - ShowFallbackDialog(title, body, trimmed_text, trimmed_text, - trimmed_action_label); -} - -void NotificationManager::OnActionInvoked(uint notification_id, - const QString& action_key) { - if (action_key != QString::fromLatin1(kCopyActionId)) { - return; - } - - auto it = copy_actions_.find(notification_id); - if (it == copy_actions_.end()) { - return; - } - - CopyTextToClipboard(it->text_to_copy, QStringLiteral("Copied"), - QStringLiteral("Copied to clipboard.")); - copy_actions_.erase(it); -} - -void NotificationManager::OnNotificationClosed(uint notification_id, - uint reason) { - Q_UNUSED(reason); - copy_actions_.remove(notification_id); -} - -void NotificationManager::CopyTextToClipboard( - const QString& text_to_copy, const QString& confirmation_title, - const QString& confirmation_body) const { - QClipboard* clipboard = QGuiApplication::clipboard(); - if (clipboard != nullptr) { - clipboard->setText(text_to_copy); - } - - if (tray_icon_ != nullptr) { - tray_icon_->showMessage(confirmation_title, confirmation_body, - QSystemTrayIcon::Information, 2500); - } -} - -QString NotificationManager::EnsureNotificationIconPath() { - if (!notification_icon_path_.isEmpty() && - QFileInfo::exists(notification_icon_path_)) { - return notification_icon_path_; - } - - if (tray_icon_ == nullptr || tray_icon_->icon().isNull()) { - return {}; - } - - QString cache_dir = - QStandardPaths::writableLocation(QStandardPaths::CacheLocation); - if (cache_dir.isEmpty()) { - cache_dir = QDir::tempPath() + QStringLiteral("/nearby-file-share"); - } - - QDir dir(cache_dir); - if (!dir.exists() && !dir.mkpath(QStringLiteral("."))) { - return {}; - } - - const QString icon_path = dir.filePath(QStringLiteral("notification-icon.png")); - const QPixmap icon_pixmap = tray_icon_->icon().pixmap(128, 128); - if (icon_pixmap.isNull() || !icon_pixmap.save(icon_path, "PNG")) { - return {}; - } - - notification_icon_path_ = icon_path; - return notification_icon_path_; -} - -void NotificationManager::ShowFallbackDialog(const QString& title, - const QString& body, - const QString& informative_text, - const QString& text_to_copy, - const QString& action_label) { - auto* message_box = - new QMessageBox(QMessageBox::Information, title, body, QMessageBox::NoButton); - message_box->setAttribute(Qt::WA_DeleteOnClose); - message_box->setTextFormat(Qt::PlainText); - message_box->setInformativeText(informative_text); - message_box->setWindowFlag(Qt::WindowStaysOnTopHint); - if (tray_icon_ != nullptr && !tray_icon_->icon().isNull()) { - message_box->setWindowIcon(tray_icon_->icon()); - } - - QAbstractButton* copy_button = - message_box->addButton(action_label, QMessageBox::ActionRole); - message_box->addButton(QMessageBox::Close); - - QObject::connect(message_box, &QMessageBox::buttonClicked, message_box, - [this, message_box, copy_button, text_to_copy]( - QAbstractButton* button) { - if (button == copy_button) { - CopyTextToClipboard(text_to_copy, QStringLiteral("Copied"), - QStringLiteral("Copied to clipboard.")); - } - message_box->close(); - }); - - message_box->show(); - message_box->raise(); - message_box->activateWindow(); -} diff --git a/sharing/linux/qml_tray_app/notification_manager.h b/sharing/linux/qml_tray_app/notification_manager.h deleted file mode 100644 index b90dcdf0..00000000 --- a/sharing/linux/qml_tray_app/notification_manager.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef SHARING_LINUX_QML_TRAY_APP_NOTIFICATION_MANAGER_H_ -#define SHARING_LINUX_QML_TRAY_APP_NOTIFICATION_MANAGER_H_ - -#include - -#include -#include - -class QSystemTrayIcon; - -class NotificationManager : public QObject { - Q_OBJECT - - public: - explicit NotificationManager(QSystemTrayIcon* tray_icon, - QObject* parent = nullptr); - - void ShowNotification(const QString& title, const QString& body); - void ShowCopyableNotification(const QString& title, const QString& body, - const QString& text_to_copy, - const QString& action_label); - - private slots: - void OnActionInvoked(uint notification_id, const QString& action_key); - void OnNotificationClosed(uint notification_id, uint reason); - - private: - struct CopyActionState { - QString text_to_copy; - }; - - void CopyTextToClipboard(const QString& text_to_copy, - const QString& confirmation_title, - const QString& confirmation_body) const; - QString EnsureNotificationIconPath(); - void ShowFallbackDialog(const QString& title, const QString& body, - const QString& informative_text, - const QString& text_to_copy, - const QString& action_label); - - bool supports_actions_ = false; - QHash copy_actions_; - QString notification_icon_path_; - QSystemTrayIcon* tray_icon_ = nullptr; -}; - -#endif // SHARING_LINUX_QML_TRAY_APP_NOTIFICATION_MANAGER_H_ diff --git a/sharing/linux/qml_tray_app/qr_code_generator.cc b/sharing/linux/qml_tray_app/qr_code_generator.cc deleted file mode 100644 index f105b7ec..00000000 --- a/sharing/linux/qml_tray_app/qr_code_generator.cc +++ /dev/null @@ -1,48 +0,0 @@ -#include "qr_code_generator.h" -#include -#include "third_party/libqrencode/qrencode_compat.h" - -namespace QrCodeGenerator { - -QrCodeData GenerateQrCode(const QString& url) { - QrCodeData result; - result.size = 0; - - const QByteArray encoded_url = url.trimmed().toUtf8(); - if (encoded_url.isEmpty()) { - return result; - } - - QRcode* qr_code = QRcode_encodeData( - encoded_url.size(), - reinterpret_cast(encoded_url.constData()), 0, - QR_ECLEVEL_M); - - if (qr_code == nullptr || qr_code->data == nullptr || qr_code->width <= 0) { - if (qr_code != nullptr) { - QRcode_free(qr_code); - } - return result; - } - - result.size = qr_code->width; - result.rows.reserve(result.size); - - for (int row = 0; row < result.size; ++row) { - QString row_data; - row_data.reserve(result.size); - - for (int col = 0; col < result.size; ++col) { - const unsigned char module = - qr_code->data[row * result.size + col] & 0x1; - row_data.append(module ? QLatin1Char('1') : QLatin1Char('0')); - } - - result.rows.append(row_data); - } - - QRcode_free(qr_code); - return result; -} - -} // namespace QrCodeGenerator diff --git a/sharing/linux/qml_tray_app/qr_code_generator.h b/sharing/linux/qml_tray_app/qr_code_generator.h deleted file mode 100644 index 8506cfad..00000000 --- a/sharing/linux/qml_tray_app/qr_code_generator.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef SHARING_LINUX_QML_TRAY_APP_QR_CODE_GENERATOR_H_ -#define SHARING_LINUX_QML_TRAY_APP_QR_CODE_GENERATOR_H_ - -#include -#include - -namespace QrCodeGenerator { - -struct QrCodeData { - QStringList rows; - int size; -}; - -QrCodeData GenerateQrCode(const QString& url); - -} // namespace QrCodeGenerator - -#endif // SHARING_LINUX_QML_TRAY_APP_QR_CODE_GENERATOR_H_ diff --git a/sharing/linux/qml_tray_app/resources_file_share.qrc b/sharing/linux/qml_tray_app/resources_file_share.qrc deleted file mode 100644 index 14d75129..00000000 --- a/sharing/linux/qml_tray_app/resources_file_share.qrc +++ /dev/null @@ -1,15 +0,0 @@ - - - FileShareTray.qml - components/AppHeader.qml - components/SideBar.qml - components/AnimatedBlob.qml - components/SendUrlPanel.qml - components/DeviceCard.qml - components/SettingsPanel.qml - - - tray_icon.png - tray_icon-symbolic.svg - - diff --git a/sharing/linux/qml_tray_app/share_target_model.h b/sharing/linux/qml_tray_app/share_target_model.h deleted file mode 100644 index 33e3a3df..00000000 --- a/sharing/linux/qml_tray_app/share_target_model.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef SHARING_LINUX_QML_TRAY_APP_SHARE_TARGET_MODEL_H_ -#define SHARING_LINUX_QML_TRAY_APP_SHARE_TARGET_MODEL_H_ - -#include -#include - -class ShareTarget { - public: - explicit ShareTarget(qlonglong id, const QString& name, bool is_incoming) - : id_(id), name_(name), is_incoming_(is_incoming) {} - - qlonglong id() const { return id_; } - QString name() const { return name_; } - bool isIncoming() const { return is_incoming_; } - - QVariantMap toVariantMap() const { - QVariantMap map; - map[QStringLiteral("id")] = id_; - map[QStringLiteral("name")] = name_; - map[QStringLiteral("isIncoming")] = is_incoming_; - return map; - } - - private: - qlonglong id_; - QString name_; - bool is_incoming_; -}; - -#endif // SHARING_LINUX_QML_TRAY_APP_SHARE_TARGET_MODEL_H_ diff --git a/sharing/linux/qml_tray_app/status_mapper.cc b/sharing/linux/qml_tray_app/status_mapper.cc deleted file mode 100644 index 13dc677d..00000000 --- a/sharing/linux/qml_tray_app/status_mapper.cc +++ /dev/null @@ -1,44 +0,0 @@ -#include "status_mapper.h" - -namespace StatusMapper { - -QString TransferStatusToString(NearbySharingApi::TransferStatus status) { - return QString::fromStdString(NearbySharingApi::TransferStatusToString(status)); -} - -QString ApiStatusToString(NearbySharingApi::StatusCode status) { - return QString::fromStdString(NearbySharingApi::StatusCodeToString(status)); -} - -bool IsActiveTransferStatus(const QString& status) { - return status == QStringLiteral("Queued") || - status == QStringLiteral("Connecting") || - status == QStringLiteral("AwaitingLocalConfirmation") || - status == QStringLiteral("AwaitingRemoteAcceptance") || - status == QStringLiteral("InProgress"); -} - -bool IsFinalTransferStatus(NearbySharingApi::TransferStatus status) { - switch (status) { - case NearbySharingApi::TransferStatus::kComplete: - case NearbySharingApi::TransferStatus::kFailed: - case NearbySharingApi::TransferStatus::kRejected: - case NearbySharingApi::TransferStatus::kCancelled: - case NearbySharingApi::TransferStatus::kTimedOut: - case NearbySharingApi::TransferStatus::kMediaUnavailable: - case NearbySharingApi::TransferStatus::kNotEnoughSpace: - case NearbySharingApi::TransferStatus::kUnsupportedAttachmentType: - case NearbySharingApi::TransferStatus::kDeviceAuthenticationFailed: - case NearbySharingApi::TransferStatus::kIncompletePayloads: - return true; - case NearbySharingApi::TransferStatus::kUnknown: - case NearbySharingApi::TransferStatus::kConnecting: - case NearbySharingApi::TransferStatus::kAwaitingLocalConfirmation: - case NearbySharingApi::TransferStatus::kAwaitingRemoteAcceptance: - case NearbySharingApi::TransferStatus::kInProgress: - return false; - } - return false; -} - -} // namespace StatusMapper diff --git a/sharing/linux/qml_tray_app/status_mapper.h b/sharing/linux/qml_tray_app/status_mapper.h deleted file mode 100644 index fc3efd8f..00000000 --- a/sharing/linux/qml_tray_app/status_mapper.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef SHARING_LINUX_QML_TRAY_APP_STATUS_MAPPER_H_ -#define SHARING_LINUX_QML_TRAY_APP_STATUS_MAPPER_H_ - -#include -#include "sharing/linux/nearby_sharing_api.h" - -using NearbySharingApi = nearby::sharing::NearbySharingApi; - -namespace StatusMapper { - -QString TransferStatusToString(NearbySharingApi::TransferStatus status); - -QString ApiStatusToString(NearbySharingApi::StatusCode status); - -bool IsActiveTransferStatus(const QString& status); - -bool IsFinalTransferStatus(NearbySharingApi::TransferStatus status); - -} // namespace StatusMapper - -#endif // SHARING_LINUX_QML_TRAY_APP_STATUS_MAPPER_H_ diff --git a/sharing/linux/qml_tray_app/string_utils.cc b/sharing/linux/qml_tray_app/string_utils.cc deleted file mode 100644 index 9ef97e7a..00000000 --- a/sharing/linux/qml_tray_app/string_utils.cc +++ /dev/null @@ -1,18 +0,0 @@ -#include "string_utils.h" - -namespace StringUtils { - -QString TrimmedOrFallback(const QString& value, const QString& fallback) { - const QString trimmed = value.trimmed(); - return trimmed.isEmpty() ? fallback : trimmed; -} - -QString FromStdString(const std::string& value) { - return QString::fromStdString(value); -} - -QString TrimmedFromStdString(const std::string& value) { - return QString::fromStdString(value).trimmed(); -} - -} // namespace StringUtils diff --git a/sharing/linux/qml_tray_app/string_utils.h b/sharing/linux/qml_tray_app/string_utils.h deleted file mode 100644 index ebba9354..00000000 --- a/sharing/linux/qml_tray_app/string_utils.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef SHARING_LINUX_QML_TRAY_APP_STRING_UTILS_H_ -#define SHARING_LINUX_QML_TRAY_APP_STRING_UTILS_H_ - -#include -#include - -namespace StringUtils { - -QString TrimmedOrFallback(const QString& value, const QString& fallback); - -QString FromStdString(const std::string& value); - -QString TrimmedFromStdString(const std::string& value); - -} // namespace StringUtils - -#endif // SHARING_LINUX_QML_TRAY_APP_STRING_UTILS_H_ diff --git a/sharing/linux/qml_tray_app/third_party/libqrencode/qrencode_compat.h b/sharing/linux/qml_tray_app/third_party/libqrencode/qrencode_compat.h deleted file mode 100644 index d52cec3c..00000000 --- a/sharing/linux/qml_tray_app/third_party/libqrencode/qrencode_compat.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SHARING_LINUX_QML_TRAY_APP_THIRD_PARTY_LIBQRENCODE_QRENCODE_COMPAT_H_ -#define SHARING_LINUX_QML_TRAY_APP_THIRD_PARTY_LIBQRENCODE_QRENCODE_COMPAT_H_ - -// Minimal libqrencode ABI used by this app when the runtime shared library is -// available but the development headers are not installed. - -#if defined(__cplusplus) -extern "C" { -#endif - -typedef enum { - QR_ECLEVEL_L = 0, - QR_ECLEVEL_M, - QR_ECLEVEL_Q, - QR_ECLEVEL_H -} QRecLevel; - -typedef struct { - int version; - int width; - unsigned char* data; -} QRcode; - -QRcode* QRcode_encodeData(int size, const unsigned char* data, int version, - QRecLevel level); -void QRcode_free(QRcode* qrcode); - -#if defined(__cplusplus) -} // extern "C" -#endif - -#endif // SHARING_LINUX_QML_TRAY_APP_THIRD_PARTY_LIBQRENCODE_QRENCODE_COMPAT_H_ diff --git a/sharing/linux/qml_tray_app/transfer_model.h b/sharing/linux/qml_tray_app/transfer_model.h deleted file mode 100644 index 9241c3a5..00000000 --- a/sharing/linux/qml_tray_app/transfer_model.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef SHARING_LINUX_QML_TRAY_APP_TRANSFER_MODEL_H_ -#define SHARING_LINUX_QML_TRAY_APP_TRANSFER_MODEL_H_ - -#include -#include - -class Transfer { - public: - Transfer(qlonglong target_id, const QString& target_name, - const QString& status, double progress, qulonglong transferred_bytes, - const QString& direction, const QString& file_name) - : target_id_(target_id), - target_name_(target_name), - status_(status), - progress_(progress), - transferred_bytes_(transferred_bytes), - direction_(direction), - file_name_(file_name) {} - - qlonglong targetId() const { return target_id_; } - QString targetName() const { return target_name_; } - QString status() const { return status_; } - double progress() const { return progress_; } - qulonglong transferredBytes() const { return transferred_bytes_; } - QString direction() const { return direction_; } - QString fileName() const { return file_name_; } - - QVariantMap toVariantMap() const { - QVariantMap map; - map[QStringLiteral("targetId")] = target_id_; - map[QStringLiteral("targetName")] = target_name_; - map[QStringLiteral("status")] = status_; - map[QStringLiteral("progress")] = progress_; - map[QStringLiteral("transferredBytes")] = static_cast(transferred_bytes_); - map[QStringLiteral("direction")] = direction_; - map[QStringLiteral("fileName")] = file_name_; - return map; - } - - private: - qlonglong target_id_; - QString target_name_; - QString status_; - double progress_; - qulonglong transferred_bytes_; - QString direction_; - QString file_name_; -}; - -#endif // SHARING_LINUX_QML_TRAY_APP_TRANSFER_MODEL_H_ diff --git a/sharing/linux/qml_tray_app/tray_icon-symbolic.svg b/sharing/linux/qml_tray_app/tray_icon-symbolic.svg deleted file mode 100644 index 645e746a..00000000 --- a/sharing/linux/qml_tray_app/tray_icon-symbolic.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/sharing/linux/qml_tray_app/tray_icon.png b/sharing/linux/qml_tray_app/tray_icon.png deleted file mode 100644 index e9e17f54..00000000 Binary files a/sharing/linux/qml_tray_app/tray_icon.png and /dev/null differ diff --git a/sharing/linux/stubs/sync_manager.h b/sharing/linux/stubs/sync_manager.h deleted file mode 100644 index c964cbaa..00000000 --- a/sharing/linux/stubs/sync_manager.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2026 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 THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_SYNC_MANAGER_H_ -#define THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_SYNC_MANAGER_H_ - -#include - -#include "absl/strings/string_view.h" -#include "sharing/internal/api/preference_manager.h" - -namespace nearby::sharing { - -class SyncManager { - public: - explicit SyncManager(api::PreferenceManager* preference_manager) - : preference_manager_(preference_manager) {} - - bool IsFileSyncBinding(absl::string_view binding_id) const { - static_cast(binding_id); - return false; - } - - std::optional GetSyncConfig( - absl::string_view binding_id) const { - static_cast(binding_id); - return std::nullopt; - } - - private: - api::PreferenceManager* preference_manager_; -}; - -} // namespace nearby::sharing - -#endif // THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_SYNC_MANAGER_H_ diff --git a/sharing/linux/tui/BUILD b/sharing/linux/tui/BUILD new file mode 100644 index 00000000..4ef42336 --- /dev/null +++ b/sharing/linux/tui/BUILD @@ -0,0 +1,113 @@ +load("@rules_cc//cc:cc_binary.bzl", "cc_binary") +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:cc_test.bzl", "cc_test") + +load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") + +refresh_compile_commands( + name = "refresh_compile_commands", + + # Specify the targets of interest. + # For example, specify a dict of targets and any flags required to build. + targets = { + ":tui" : "", + }, + # No need to add flags already in .bazelrc. They're automatically picked up. + # If you don't need flags, a list of targets is also okay, as is a single target string. + # Wildcard patterns, like //... for everything, *are* allowed here, just like a build. + # As are additional targets (+) and subtractions (-), like in bazel query https://docs.bazel.build/versions/main/query.html#expressions + # And if you're working on a header-only library, specify a test or binary target that compiles it. +) + +cc_binary( + name = "tui", + srcs = [ + "main.cc", + ], + deps = [ + #":app", + "nearby_sharing_dbus_client" + ] +) + +cc_library( + name ="nearby_sharing_dbus_client", + hdrs = [ + 'nearby_sharing_dbus_client.h' + ], + srcs = [ + 'nearby_sharing_dbus_client.cc' + ], + deps = [ + "//sharing/linux/daemon:nearby_sharing_daemon", + "@sdbus_cpp", + ] +) + +cc_library( + name = "app", + srcs = [ + "app.cc", + ], + hdrs = [ + "app.h", + ], + deps = [ + ":file_picker", + ":page", + "//sharing/linux/tui/ui:home_screen", + "@ftxui//:ftxui", + ], +) + +cc_library( + name = "page", + hdrs = [ + "page.h", + ], +) + +cc_library( + name = "ipc_client", + srcs = [ + "ipc_client.cc", + ], + hdrs = [ + "ipc_client.h", + ], + deps = [ + "@nlohmann_json//:json", + ], +) + +cc_test( + name = "ipc_client_test", + srcs = [ + "ipc_client_test.cc", + ], + deps = [ + ":ipc_client", + "@com_google_googletest//:gtest_main", + "@nlohmann_json//:json", + ], +) + +cc_library( + name = "file_picker", + srcs = [ + "file_picker.cc", + ], + hdrs = [ + "file_picker.h", + ], +) + +cc_library( + name = "palette", + hdrs = [ + "palette.h", + ], + deps = [ + "@ftxui//:ftxui", + ] +) diff --git a/sharing/linux/tui/app.cc b/sharing/linux/tui/app.cc new file mode 100644 index 00000000..160e8ceb --- /dev/null +++ b/sharing/linux/tui/app.cc @@ -0,0 +1,74 @@ +#include "sharing/linux/tui/app.h" + +#include + +#include + +#include "ftxui/component/component.hpp" +#include "sharing/linux/tui/ui/home_screen.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; +namespace { + +std::string GetHostname() { + char hostname[256] = {}; + gethostname(hostname, sizeof(hostname)); + return hostname; +} + +} // namespace + +TuiApp::TuiApp() : screen_(ScreenInteractive::Fullscreen()) { + hostname_ = GetHostname(); + screen_.TrackMouse(true); +} + +int TuiApp::Run() { + auto component = HomeScreen({ + .hostname = hostname_, + .current_page = ¤t_page_, + .selected_file = &selected_file_, + .file_picker = &file_picker_, + .on_file_selected = + [this](std::string path) { + selected_file_ = path; + current_page_ = Page::Sharing; + }, + .incoming_share_device_name = incoming_share_device_name_, + .incoming_share_device_type = incoming_share_device_type_, + .on_incoming_share_accept = + [this]() { current_page_ = Page::FilePicker; }, + .on_incoming_share_decline = + [this]() { current_page_ = Page::FilePicker; }, + }); + + auto app = + CatchEvent(component, [this](Event event) { return HandleEvent(event); }); + + screen_.Loop(app); + return 0; +} + +bool TuiApp::HandleEvent(Event event) { + if (event == Event::Character('q') || event == Event::Escape) { + screen_.ExitLoopClosure()(); + return true; + } + + if (event == Event::Backspace) { + if (current_page_ == Page::Sharing) { + current_page_ = Page::FilePicker; + selected_file_.clear(); + return true; + } + if (current_page_ == Page::IncomingShare) { + current_page_ = Page::FilePicker; + return true; + } + } + + return false; +} + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/app.h b/sharing/linux/tui/app.h new file mode 100644 index 00000000..5f4d047a --- /dev/null +++ b/sharing/linux/tui/app.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "ftxui/component/screen_interactive.hpp" +#include "sharing/linux/tui/components/share_target.h" +#include "sharing/linux/tui/file_picker.h" +#include "sharing/linux/tui/page.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +class TuiApp { + public: + TuiApp(); + + int Run(); + + private: + + ScreenInteractive screen_; + ZenityFilePicker file_picker_; + std::string hostname_; + std::string selected_file_; +}; + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/BUILD b/sharing/linux/tui/components/BUILD new file mode 100644 index 00000000..e396d7b1 --- /dev/null +++ b/sharing/linux/tui/components/BUILD @@ -0,0 +1,68 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +cc_library( + name = "icons", + srcs = [ + "file_icon.cc", + "brand.cc", + "laptop_icon.cc", + "phone_icon.cc", + "tablet_icon.cc", + ], + hdrs = [ + "file_icon.h", + "brand.h", + "laptop_icon.h", + "phone_icon.h", + "tablet_icon.h", + ], + deps = [ + "@ftxui//:ftxui", + ], +) + +cc_library( + name = "share_target", + srcs = [ + "share_target.cc", + ], + hdrs = [ + "share_target.h", + ], + deps = [ + "@ftxui//:ftxui", + "//sharing/linux/tui:palette", + ":icons", + ], +) + +cc_library( + name = "incoming_share_card", + srcs = [ + "incoming_share_card.cc", + ], + hdrs = [ + "incoming_share_card.h", + ], + deps = [ + ":share_target", + "//sharing/linux/tui:palette", + "@ftxui//:ftxui", + ], +) + +cc_library( + name = "file_picker_card", + srcs = [ + "file_picker_card.cc", + ], + hdrs = [ + "file_picker_card.h", + ], + deps = [ + ":icons", + "//sharing/linux/tui:file_picker", + "//sharing/linux/tui:palette", + "@ftxui//:ftxui", + ], +) diff --git a/sharing/linux/tui/components/brand.cc b/sharing/linux/tui/components/brand.cc new file mode 100644 index 00000000..40a66c93 --- /dev/null +++ b/sharing/linux/tui/components/brand.cc @@ -0,0 +1,15 @@ +#include "sharing/linux/tui/components/brand.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element Brand() { + return vbox({ + text(" ___ _ _ ___ _ "), + text(" / _ \\ _ _(_)__| |__ / __| |_ __ _ _ _ ___ "), + text(" | (_) | || | / _| / / \\__ \\ ' \\/ _` | '_/ -_)"), + text(" \\__\\_\\\\_,_|_\\__|_\\_\\ |___/_||_\\__,_|_| \\___|"), + text(" "), + }); +} +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/brand.h b/sharing/linux/tui/components/brand.h new file mode 100644 index 00000000..faf69999 --- /dev/null +++ b/sharing/linux/tui/components/brand.h @@ -0,0 +1,10 @@ +#pragma once + +#include "ftxui/dom/elements.hpp" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element Brand(); + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/file_icon.cc b/sharing/linux/tui/components/file_icon.cc new file mode 100644 index 00000000..a1928829 --- /dev/null +++ b/sharing/linux/tui/components/file_icon.cc @@ -0,0 +1,30 @@ +#include "sharing/linux/tui/components/file_icon.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element FileIcon(FileIconSize size) { + if (size == FileIconSize::Small) { + return vbox({ + text(" ______ ") | center, + text(" / | | ") | center, + text(" /__| | ") | center, + text(" | ----- | ") | center, + text(" | ----- | ") | center, + text(" |_______| ") | center, + }); + } + + return vbox({ + text(" __________ ") | center, + text(" / | | ") | center, + text(" /__| | ") | center, + text(" | --------- | ") | center, + text(" | --------- | ") | center, + text(" | --------- | ") | center, + text(" | --------- | ") | center, + text(" |___________| ") | center, + }); +} + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/file_icon.h b/sharing/linux/tui/components/file_icon.h new file mode 100644 index 00000000..9ecb1652 --- /dev/null +++ b/sharing/linux/tui/components/file_icon.h @@ -0,0 +1,15 @@ +#pragma once + +#include "ftxui/dom/elements.hpp" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +enum class FileIconSize { + Large, + Small, +}; + +Element FileIcon(FileIconSize size); + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/file_picker_card.cc b/sharing/linux/tui/components/file_picker_card.cc new file mode 100644 index 00000000..0de27611 --- /dev/null +++ b/sharing/linux/tui/components/file_picker_card.cc @@ -0,0 +1,67 @@ +#include "sharing/linux/tui/components/file_picker_card.h" + +#include + +#include "ftxui/dom/elements.hpp" +#include "ftxui/component/event.hpp" +#include "ftxui/screen/box.hpp" +#include "sharing/linux/tui/components/file_icon.h" +#include "sharing/linux/tui/palette.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Component FilePickerCard(FilePickerCardOptions options) { + auto picker_box = std::make_shared(); + auto hovered = std::make_shared(false); + + auto card = Renderer([picker_box, hovered] { + auto picker_card = + vbox({ + FileIcon(FileIconSize::Large) | color(Palette::secondary), + separatorEmpty(), + text("Select file to share") | bold | center, + }) | + borderStyled(*hovered ? Palette::primary : Palette::secondary) | + size(WIDTH, GREATER_THAN, 33) | size(HEIGHT, EQUAL, 13) | + reflect(*picker_box); + + return vbox({ + filler(), + hbox({ + filler(), + picker_card, + filler(), + }), + filler(), + }) | + flex; + }); + + return CatchEvent(card, [picker_box, hovered, options](Event event) { + if (!event.is_mouse()) { + return false; + } + + auto mouse = event.mouse(); + bool inside_picker = + mouse.x >= picker_box->x_min && mouse.x <= picker_box->x_max && + mouse.y >= picker_box->y_min && mouse.y <= picker_box->y_max; + + *hovered = inside_picker; + + if (!inside_picker || mouse.button != Mouse::Left || + mouse.motion != Mouse::Pressed || options.file_picker == nullptr) { + return false; + } + + std::string path = options.file_picker->PickFile(); + if (!path.empty() && options.on_file_selected) { + options.on_file_selected(path); + } + + return true; + }); +} + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/file_picker_card.h b/sharing/linux/tui/components/file_picker_card.h new file mode 100644 index 00000000..ec28dd63 --- /dev/null +++ b/sharing/linux/tui/components/file_picker_card.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#include "ftxui/component/component.hpp" +#include "sharing/linux/tui/file_picker.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +struct FilePickerCardOptions { + FilePicker* file_picker = nullptr; + std::function on_file_selected; +}; + +Component FilePickerCard(FilePickerCardOptions options); + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/incoming_share_card.cc b/sharing/linux/tui/components/incoming_share_card.cc new file mode 100644 index 00000000..af97934d --- /dev/null +++ b/sharing/linux/tui/components/incoming_share_card.cc @@ -0,0 +1,44 @@ +#include "sharing/linux/tui/components/incoming_share_card.h" +#include "ftxui/component/component.hpp" +#include "ftxui/dom/elements.hpp" +#include "sharing/linux/tui/components/share_target.h" +#include "sharing/linux/tui/palette.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Component IncomingShareCard(IncomingShareCardOptions options) { + auto accept_button = Button("Accept", [options] { + if (options.on_accept) { + options.on_accept(); + } + }); + auto decline_button = Button("Decline", [options] { + if (options.on_decline) { + options.on_decline(); + } + }); + auto controls = Container::Horizontal({accept_button, decline_button}); + + return Renderer(controls, [accept_button, decline_button, options] { + return vbox({ + window(text(" Incoming share ") | center, + vbox({ + paragraph("Do you want to accept this share?") | + bold | center, + separator(), + ShareTarget(options.device_name, options.device_type), + separator(), + hbox({ + accept_button->Render() | flex, + separator(), + decline_button->Render() | flex, + }), + })), + }) | + borderStyled(Palette::border) | bgcolor(Palette::surface) | + size(WIDTH, GREATER_THAN, 40); + }); +} + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/incoming_share_card.h b/sharing/linux/tui/components/incoming_share_card.h new file mode 100644 index 00000000..912768ba --- /dev/null +++ b/sharing/linux/tui/components/incoming_share_card.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "ftxui/component/component.hpp" +#include "sharing/linux/tui/components/share_target.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +struct IncomingShareCardOptions { + std::string device_name; + ShareTargetType device_type = ShareTargetType::kUnknown; + std::function on_accept; + std::function on_decline; +}; + +Component IncomingShareCard(IncomingShareCardOptions options); + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/laptop_icon.cc b/sharing/linux/tui/components/laptop_icon.cc new file mode 100644 index 00000000..4c9d828d --- /dev/null +++ b/sharing/linux/tui/components/laptop_icon.cc @@ -0,0 +1,15 @@ +#include "sharing/linux/tui/components/laptop_icon.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element Laptop() { + return vbox({ + text(" +========+ ") | center, + text(" | | ") | center, + text(" |________| ") | center, + text(" /--------\\ ") | center, + text("/__________\\") | center, + }); +} +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/laptop_icon.h b/sharing/linux/tui/components/laptop_icon.h new file mode 100644 index 00000000..93ec1b8d --- /dev/null +++ b/sharing/linux/tui/components/laptop_icon.h @@ -0,0 +1,10 @@ +#pragma once + +#include "ftxui/dom/elements.hpp" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element Laptop(); + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/phone_icon.cc b/sharing/linux/tui/components/phone_icon.cc new file mode 100644 index 00000000..0bcae284 --- /dev/null +++ b/sharing/linux/tui/components/phone_icon.cc @@ -0,0 +1,15 @@ +#include "sharing/linux/tui/components/phone_icon.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element Phone() { + return vbox({ + text("______") | center, + text("| |") | center, + text("| |") | center, + text("| |") | center, + text("|____|") | center, + }); +} +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/phone_icon.h b/sharing/linux/tui/components/phone_icon.h new file mode 100644 index 00000000..40ef60b7 --- /dev/null +++ b/sharing/linux/tui/components/phone_icon.h @@ -0,0 +1,10 @@ +#pragma once + +#include "ftxui/dom/elements.hpp" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element Phone(); + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/share_target.cc b/sharing/linux/tui/components/share_target.cc new file mode 100644 index 00000000..065dce45 --- /dev/null +++ b/sharing/linux/tui/components/share_target.cc @@ -0,0 +1,32 @@ +#include "ftxui/dom/elements.hpp" +#include "sharing/linux/tui/components/share_target.h" +#include "sharing/linux/tui/components/laptop_icon.h" +#include "sharing/linux/tui/components/phone_icon.h" +#include "sharing/linux/tui/components/tablet_icon.h" +#include "sharing/linux/tui/palette.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element ShareTarget(std::string device_name, ShareTargetType device_type) { + Element icon; + switch (device_type) { + case ShareTargetType::kPhone: + icon = Phone(); + break; + case ShareTargetType::kTablet: + icon = Tablet(); + break; + default: + icon = Laptop(); + break; + } + return vbox( + {icon, separatorLight() | dim, + paragraph(device_name) | color(Palette::secondary) | bold | center + + }) |bgcolor(Palette::surface)| + size(WIDTH, EQUAL, 14) | borderStyled(Palette::border); +}; + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/share_target.h b/sharing/linux/tui/components/share_target.h new file mode 100644 index 00000000..b5f72f42 --- /dev/null +++ b/sharing/linux/tui/components/share_target.h @@ -0,0 +1,30 @@ +#pragma once + +#include "ftxui/dom/elements.hpp" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +enum class ShareTargetType { + // Unknown device type. + kUnknown = 0, + // A phone. + kPhone = 1, + // A tablet. + kTablet = 2, + // A laptop. + kLaptop = 3, + // A car. + kCar = 4, + // A foldable. + kFoldable = 5, + // An XR device. + kXR = 6, +}; + +Element ShareTarget( + std::string device_name, + ShareTargetType device_type + ); + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/tablet_icon.cc b/sharing/linux/tui/components/tablet_icon.cc new file mode 100644 index 00000000..8f227c58 --- /dev/null +++ b/sharing/linux/tui/components/tablet_icon.cc @@ -0,0 +1,15 @@ +#include "sharing/linux/tui/components/tablet_icon.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element Tablet() { + return vbox({ + text("____________") | center, + text("| |") | center, + text("| |") | center, + text("|__________|") | center, + text(" ") | center, + }); +} +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/components/tablet_icon.h b/sharing/linux/tui/components/tablet_icon.h new file mode 100644 index 00000000..752b2efe --- /dev/null +++ b/sharing/linux/tui/components/tablet_icon.h @@ -0,0 +1,10 @@ +#pragma once + +#include "ftxui/dom/elements.hpp" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element Tablet(); + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/file_picker.cc b/sharing/linux/tui/file_picker.cc new file mode 100644 index 00000000..0bf60574 --- /dev/null +++ b/sharing/linux/tui/file_picker.cc @@ -0,0 +1,30 @@ +#include "sharing/linux/tui/file_picker.h" + +#include +#include + +namespace nearby::sharing::linux_tui { + +std::string ZenityFilePicker::PickFile() { + FILE* pipe = popen("zenity --file-selection 2>/dev/null", "r"); + if (pipe == nullptr) { + return {}; + } + + char buffer[4096]; + std::string result; + + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + result = buffer; + } + + pclose(pipe); + + if (!result.empty() && result.back() == '\n') { + result.pop_back(); + } + + return result; +} + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/file_picker.h b/sharing/linux/tui/file_picker.h new file mode 100644 index 00000000..597f4577 --- /dev/null +++ b/sharing/linux/tui/file_picker.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace nearby::sharing::linux_tui { + +class FilePicker { + public: + virtual ~FilePicker() = default; + + virtual std::string PickFile() = 0; +}; + +class ZenityFilePicker final : public FilePicker { + public: + std::string PickFile() override; +}; + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/main.cc b/sharing/linux/tui/main.cc new file mode 100644 index 00000000..db72303c --- /dev/null +++ b/sharing/linux/tui/main.cc @@ -0,0 +1,15 @@ +#include "nearby_sharing_dbus_client.h" +#include + +int main() { + auto sharing = + NearbySharingService(sdbus::ServiceName("com.google.nearby.sharing"), + sdbus::ObjectPath("/com/google/nearby/sharing")); + + sharing.StartReceive(); + sleep(10); + sharing.StopReceive(); + // nearby::sharing::linux_tui::TuiApp app; + // return app.Run(); + return 0; +} diff --git a/sharing/linux/tui/nearby_sharing_dbus_client.cc b/sharing/linux/tui/nearby_sharing_dbus_client.cc new file mode 100644 index 00000000..9ae3d107 --- /dev/null +++ b/sharing/linux/tui/nearby_sharing_dbus_client.cc @@ -0,0 +1,143 @@ +#include "sharing/linux/tui/nearby_sharing_dbus_client.h" + +// Helper to extract a value from the map with a fallback if missing or type +// mismatches +template +T get_field(const std::map& map, + const std::string& key, T default_value = T{}) { + auto it = map.find(key); + if (it != map.end()) { + try { + return it->second.get(); + } catch (const sdbus::Error& e) { + // Log type mismatch error if necessary + } + } + return default_value; +} + +// Helper for optional fields +template +std::optional get_optional_field( + const std::map& map, const std::string& key) { + auto it = map.find(key); + if (it != map.end()) { + try { + return it->second.get(); + } catch (const sdbus::Error& e) { + // Log type mismatch error if necessary + } + } + return std::nullopt; +} + +// Converter: Map -> ShareTarget +ShareTarget convertToShareTarget( + const std::map& map) { + ShareTarget target; + target.id = get_field(map, "id"); + target.device_name = get_field(map, "device_name"); + target.type = get_field(map, "type"); + target.is_incoming = get_field(map, "is_incoming"); + target.is_known = get_field(map, "is_known"); + target.device_id = get_field(map, "device_id"); + target.for_self_share = get_field(map, "for_self_share"); + target.vendor_id = get_field(map, "vendor_id"); + target.receive_disabled = get_field(map, "receive_disabled"); + return target; +} + +// Converter: Map -> Transfer +Transfer convertToTransfer(const std::map& map) { + Transfer transfer; + transfer.status = get_field(map, "status"); + transfer.progress = get_field(map, "progress"); + transfer.transferred_bytes = get_field(map, "transferred_bytes"); + transfer.total_bytes = get_field(map, "total_bytes"); + transfer.transfer_speed = get_field(map, "transfer_speed"); + transfer.estimated_time_remaining = + get_field(map, "estimated_time_remaining"); + transfer.total_attachments_count = + get_field(map, "total_attachments_count"); + transfer.transferred_attachments_count = + get_field(map, "transferred_attachments_count"); + transfer.is_final_status = get_field(map, "is_final_status"); + transfer.is_self_share = get_field(map, "is_self_share"); + transfer.binding_id = get_field(map, "binding_id"); + + // Optional fields + transfer.token = get_optional_field(map, "token"); + transfer.in_progress_attachment_id = + get_optional_field(map, "in_progress_attachment_id"); + transfer.in_progress_attachment_transferred_bytes = + get_optional_field(map, + "in_progress_attachment_transferred_bytes"); + transfer.in_progress_attachment_total_bytes = + get_optional_field(map, "in_progress_attachment_total_bytes"); + return transfer; +} + +// Converter: Map -> Status +Status convertToStatus(const std::map& map) { + Status status; + status.receive_registered = get_field(map, "receive_registered"); + status.discovery_registered = get_field(map, "discovery_registered"); + status.is_transferring = get_field(map, "is_transferring"); + status.is_scanning = get_field(map, "is_scanning"); + status.bluetooth_present = get_field(map, "bluetooth_present"); + status.bluetooth_powered = get_field(map, "bluetooth_powered"); + status.lan_connected = get_field(map, "lan_connected"); + + // Unpack aa{sv} (vector of maps) into vector of ShareTarget structs + auto it = map.find("targets"); + if (it != map.end()) { + try { + auto raw_targets = + it->second.get>>(); + for (const auto& target_map : raw_targets) { + status.targets.push_back(convertToShareTarget(target_map)); + } + } catch (const sdbus::Error& e) { + // Handle type mismatch for targets array + } + } + return status; +} + +void NearbySharingService::onTargetDiscovered( + const std::map& share_target) { + auto target = convertToShareTarget(share_target); + targets_[target.id] = target; +}; + +void NearbySharingService::onTargetUpdated( + const std::map& share_target) { + auto target = convertToShareTarget(share_target); + targets_[target.id] = target; +}; + +void NearbySharingService::onTargetLost( + const std::map& share_target) { + auto target = convertToShareTarget(share_target); + if (targets_.find(target.id) != targets_.end()) { + targets_.erase(target.id); + } +}; + +void NearbySharingService::onIncomingTransfer( + const std::string& direction, + const std::map& share_target, + const std::map& transfer) { + auto target = convertToShareTarget(share_target); + auto incoming_transfer = convertToTransfer(transfer); + + incoming_transfer_ = incoming_transfer; +}; + +void NearbySharingService::onTransferUpdate( + const std::string& direction, + const std::map& share_target, + const std::map& transfer) {}; + +void NearbySharingService::onStatusChanged( + const std::map& share_target) {}; diff --git a/sharing/linux/tui/nearby_sharing_dbus_client.h b/sharing/linux/tui/nearby_sharing_dbus_client.h new file mode 100644 index 00000000..346a7c4b --- /dev/null +++ b/sharing/linux/tui/nearby_sharing_dbus_client.h @@ -0,0 +1,78 @@ +#include "sharing/linux/daemon/nearby_sharing_client.h" + +struct ShareTarget { + int64_t id; + std::string device_name; + int32_t type; + bool is_incoming; + bool is_known; + std::string device_id; + bool for_self_share; + int32_t vendor_id; + bool receive_disabled; +}; + +struct Transfer { + std::string status; + int32_t progress; + int64_t transferred_bytes; + int64_t total_bytes; + int64_t transfer_speed; + int64_t estimated_time_remaining; + int32_t total_attachments_count; + int32_t transferred_attachments_count; + bool is_final_status; + bool is_self_share; + std::string binding_id; + std::optional token; + std::optional in_progress_attachment_id; + std::optional in_progress_attachment_transferred_bytes; + std::optional in_progress_attachment_total_bytes; +}; + +struct Status { + bool receive_registered; + bool discovery_registered; + bool is_transferring; + bool is_scanning; + bool bluetooth_present; + bool bluetooth_powered; + bool lan_connected; + + // aa{sv} maps to a vector of ShareTarget structs + std::vector targets; +}; + + +class NearbySharingService + : public sdbus::ProxyInterfaces { + public: + NearbySharingService(sdbus::ServiceName dest, sdbus::ObjectPath objectPath) + : ProxyInterfaces(std::move(dest), std::move(objectPath)) { + registerProxy(); + } + ~NearbySharingService() { unregisterProxy(); } + + void onTargetDiscovered( + const std::map& share_target) override; + void onTargetUpdated( + const std::map& share_target) override; + void onTargetLost( + const std::map& share_target) override; + void onIncomingTransfer( + const std::string& direction, + const std::map& share_target, + const std::map& transfer) override; + void onTransferUpdate( + const std::string& direction, + const std::map& share_target, + const std::map& transfer) override; + void onStatusChanged( + const std::map& status) override; + + private: + Status status_; + std::map targets_; + Transfer outgoing_transfer_; + Transfer incoming_transfer_; +}; diff --git a/sharing/linux/tui/page.h b/sharing/linux/tui/page.h new file mode 100644 index 00000000..9a04bb4c --- /dev/null +++ b/sharing/linux/tui/page.h @@ -0,0 +1,11 @@ +#pragma once + +namespace nearby::sharing::linux_tui { + +enum class Page { + FilePicker, + Sharing, + IncomingShare, +}; + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/palette.h b/sharing/linux/tui/palette.h new file mode 100644 index 00000000..8ed13daf --- /dev/null +++ b/sharing/linux/tui/palette.h @@ -0,0 +1,46 @@ +#pragma once + +#include "ftxui/screen/color.hpp" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +struct Palette { + // === Core Typography & Brand === + // Primary light text for high-contrast readability against dark backgrounds + inline static const Color primary = Color::RGB(0xF1, 0xF9, 0xF6); // Off-white mint tint + + // Secondary text, inactive states, or subtle accents + inline static const Color secondary = Color::RGB(0x8A, 0xAF, 0xA4); // Muted sage gray + + // Main brand accent, focused element backgrounds, or prominent icons (Unchanged) + inline static const Color accent = Color::RGB(0xA3, 0xD9, 0xC9); // Original Mint Leaf + + // === Surface & Structure === + // Active menu item highlights, selection cards, or focused component backgrounds + inline static const Color active = Color::RGB(0x23, 0x3D, 0x34); // Deep forest green tint + + // Base application window background + inline static const Color base = Color::RGB(0x12, 0x1D, 0x1A); // Near-black deep mint/charcoal + + // Explicit component surface background (e.g., sidebars, modals, or inactive cards) + inline static const Color surface = Color::RGB(0x19, 0x2A, 0x25); // Mid-tone dark green + + // Standard UI borders, dividers, and subtle grid lines + inline static const Color border = Color::RGB(0x32, 0x52, 0x47); // Defined slate green + + // Extremely muted text, placeholders, or disabled options + inline static const Color disabled = Color::RGB(0x56, 0x73, 0x6B); // Ghostly sage green + + // === Functional / Status Elements === + // Critical errors, destructive actions, or alerts (toned down for dark mode) + inline static const Color error = Color::RGB(0xE0, 0x7A, 0x7A); // Soft desaturated coral/red + + // Warnings, pending indicators, or high-priority notifications + inline static const Color warning = Color::RGB(0xE6, 0xC2, 0x80); // Soft amber gold + + // Success messages, online badges, or completed operations + inline static const Color success = Color::RGB(0x81, 0xC7, 0x9D); // Vibrant spring mint +}; + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/ui/BUILD b/sharing/linux/tui/ui/BUILD new file mode 100644 index 00000000..018810c1 --- /dev/null +++ b/sharing/linux/tui/ui/BUILD @@ -0,0 +1,66 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +cc_library( + name = "home_header", + srcs = [ + "home_header.cc", + ], + hdrs = [ + "home_header.h", + ], + deps = [ + "@ftxui//:ftxui", + "//sharing/linux/tui/components:icons" + ], +) + +cc_library( + name = "sidebar", + srcs = [ + "sidebar.cc", + ], + hdrs = [ + "sidebar.h", + ], + deps = [ + "//sharing/linux/tui:palette", + "//sharing/linux/tui/components:icons", + "@ftxui//:ftxui", + ], +) + +cc_library( + name = "file_selected_screen", + srcs = [ + "file_selected_screen.cc", + ], + hdrs = [ + "file_selected_screen.h", + ], + deps = [ + "//sharing/linux/tui:palette", + "//sharing/linux/tui/components:share_target", + "@ftxui//:ftxui", + ], +) + +cc_library( + name = "home_screen", + srcs = [ + "home_screen.cc", + ], + hdrs = [ + "home_screen.h", + ], + deps = [ + ":file_selected_screen", + ":home_header", + ":sidebar", + "//sharing/linux/tui/components:incoming_share_card", + "//sharing/linux/tui:file_picker", + "//sharing/linux/tui:page", + "//sharing/linux/tui:palette", + "//sharing/linux/tui/components:file_picker_card", + "@ftxui//:ftxui", + ], +) diff --git a/sharing/linux/tui/ui/file_selected_screen.cc b/sharing/linux/tui/ui/file_selected_screen.cc new file mode 100644 index 00000000..df77a620 --- /dev/null +++ b/sharing/linux/tui/ui/file_selected_screen.cc @@ -0,0 +1,37 @@ +#include "sharing/linux/tui/ui/file_selected_screen.h" + +#include "sharing/linux/tui/components/share_target.h" +#include "sharing/linux/tui/palette.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element FileSelectedScreen(const std::string& selected_file) { + FlexboxConfig config; + config.direction = FlexboxConfig::Direction::Row; + config.wrap = FlexboxConfig::Wrap::Wrap; + config.gap_x = 1; + config.gap_y = 1; + return vbox({ + hbox( + {filler(), + flexbox( + { + ShareTarget("Lasan's A55", ShareTargetType::kPhone), + ShareTarget("lasan-laptop", ShareTargetType::kLaptop), + ShareTarget("lasan-laptop", ShareTargetType::kLaptop), + ShareTarget("lasan-laptop", ShareTargetType::kLaptop), + ShareTarget("Lasan's S9+", ShareTargetType::kTablet), + ShareTarget("Lasan's S9+", ShareTargetType::kTablet), + + }, + config) | + flex + + }), + filler(), + }) | + flex; +} + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/ui/file_selected_screen.h b/sharing/linux/tui/ui/file_selected_screen.h new file mode 100644 index 00000000..71a300f1 --- /dev/null +++ b/sharing/linux/tui/ui/file_selected_screen.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include "ftxui/dom/elements.hpp" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element FileSelectedScreen(const std::string& selected_file); + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/ui/home_header.cc b/sharing/linux/tui/ui/home_header.cc new file mode 100644 index 00000000..5fef2503 --- /dev/null +++ b/sharing/linux/tui/ui/home_header.cc @@ -0,0 +1,11 @@ +#include "sharing/linux/tui/ui/home_header.h" +#include "sharing/linux/tui/components/brand.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element HomeHeader() { + return Brand(); +} + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/ui/home_header.h b/sharing/linux/tui/ui/home_header.h new file mode 100644 index 00000000..b023df1b --- /dev/null +++ b/sharing/linux/tui/ui/home_header.h @@ -0,0 +1,10 @@ +#pragma once + +#include "ftxui/dom/elements.hpp" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element HomeHeader(); + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/ui/home_screen.cc b/sharing/linux/tui/ui/home_screen.cc new file mode 100644 index 00000000..96a3061d --- /dev/null +++ b/sharing/linux/tui/ui/home_screen.cc @@ -0,0 +1,60 @@ +#include "sharing/linux/tui/ui/home_screen.h" + +#include "ftxui/dom/elements.hpp" +#include "sharing/linux/tui/components/file_picker_card.h" +#include "sharing/linux/tui/components/incoming_share_card.h" +#include "sharing/linux/tui/palette.h" +#include "sharing/linux/tui/ui/file_selected_screen.h" +#include "sharing/linux/tui/ui/home_header.h" +#include "sharing/linux/tui/ui/sidebar.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Component HomeScreen(HomeScreenOptions options) { + auto file_picker_card = + FilePickerCard({.file_picker = options.file_picker, + .on_file_selected = options.on_file_selected}); + auto incoming_share_card = + IncomingShareCard({.device_name = options.incoming_share_device_name, + .device_type = options.incoming_share_device_type, + .on_accept = options.on_incoming_share_accept, + .on_decline = options.on_incoming_share_decline}); + auto content = Container::Vertical({file_picker_card, incoming_share_card}); + + return Renderer(content, [file_picker_card, incoming_share_card, options] { + const std::string selected_file = + options.selected_file == nullptr ? "" : *options.selected_file; + const Page current_page = options.current_page == nullptr + ? Page::FilePicker + : *options.current_page; + + Element main_panel; + switch (current_page) { + case Page::FilePicker: + main_panel = file_picker_card->Render(); + break; + case Page::IncomingShare: + main_panel = incoming_share_card->Render(); + break; + case Page::Sharing: + main_panel = FileSelectedScreen(selected_file); + break; + } + + return vbox({ + HomeHeader() | color(Palette::accent), + separator(), + hbox({ + Sidebar({.hostname = options.hostname, + .selected_file = selected_file}), + separator() | color(Palette::border), + main_panel | flex, + }) | flex, + }) | + bgcolor(Palette::base) | size(WIDTH, GREATER_THAN, 80) | + size(HEIGHT, GREATER_THAN, 24); + }); +} + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/ui/home_screen.h b/sharing/linux/tui/ui/home_screen.h new file mode 100644 index 00000000..867aaa65 --- /dev/null +++ b/sharing/linux/tui/ui/home_screen.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include "ftxui/component/component.hpp" +#include "sharing/linux/tui/components/share_target.h" +#include "sharing/linux/tui/file_picker.h" +#include "sharing/linux/tui/page.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +struct HomeScreenOptions { + std::string hostname; + const Page* current_page = nullptr; + const std::string* selected_file = nullptr; + FilePicker* file_picker = nullptr; + std::function on_file_selected; + std::string incoming_share_device_name; + ShareTargetType incoming_share_device_type = ShareTargetType::kUnknown; + std::function on_incoming_share_accept; + std::function on_incoming_share_decline; +}; + +Component HomeScreen(HomeScreenOptions options); + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/ui/sidebar.cc b/sharing/linux/tui/ui/sidebar.cc new file mode 100644 index 00000000..655209ae --- /dev/null +++ b/sharing/linux/tui/ui/sidebar.cc @@ -0,0 +1,38 @@ +#include "sharing/linux/tui/ui/sidebar.h" + +#include "sharing/linux/tui/components/file_icon.h" +#include "sharing/linux/tui/palette.h" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +Element Sidebar(const SidebarState& state) { + auto visible_as_box = + window(text(" Visible as "), vbox({ + text(""), + text(state.hostname) | bold | center, + text(""), + })) | + size(WIDTH, GREATER_THAN, 24) | size(HEIGHT, EQUAL, 5); + + if (state.selected_file.empty()) { + return vbox({ + visible_as_box, + filler(), + }); + } + + return vbox({ + visible_as_box, + filler(), + window(text(" Selected to share ") | center, + vbox({ + FileIcon(FileIconSize::Small) | color(Palette::accent), + text(""), + paragraph(state.selected_file) | dim, + })) | + size(WIDTH, EQUAL, 24), + }); +} + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/linux/tui/ui/sidebar.h b/sharing/linux/tui/ui/sidebar.h new file mode 100644 index 00000000..cf240ac0 --- /dev/null +++ b/sharing/linux/tui/ui/sidebar.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include "ftxui/dom/elements.hpp" + +namespace nearby::sharing::linux_tui { +using namespace ftxui; + +struct SidebarState { + std::string hostname; + std::string selected_file; +}; + +Element Sidebar(const SidebarState& state); + +} // namespace nearby::sharing::linux_tui diff --git a/sharing/local_device_data/BUILD b/sharing/local_device_data/BUILD index 030085b3..22930d93 100644 --- a/sharing/local_device_data/BUILD +++ b/sharing/local_device_data/BUILD @@ -30,9 +30,8 @@ cc_library( visibility = ["//visibility:public"], deps = [ "//internal/base", - "//internal/platform:types", - "//internal/platform/implementation:account_manager", "//internal/platform/implementation:types", + "//location/nearby/sharing/lib/account:account_manager", "//sharing/common:enum", "//sharing/internal/api:platform", "//sharing/internal/base:utf_utils", @@ -68,9 +67,10 @@ cc_test( ], deps = [ ":local_device_data", - "//internal/platform/implementation:account_manager", "//internal/platform/implementation:platform_impl", "//internal/test", + "//location/nearby/sharing/lib/account:account_manager", + "//location/nearby/sharing/lib/account:fake_account_manager", "//sharing/common", "//sharing/common:enum", "//sharing/internal/test:nearby_test", diff --git a/sharing/local_device_data/nearby_share_local_device_data_manager_impl.cc b/sharing/local_device_data/nearby_share_local_device_data_manager_impl.cc index 80f2ffbb..e3ab98b2 100644 --- a/sharing/local_device_data/nearby_share_local_device_data_manager_impl.cc +++ b/sharing/local_device_data/nearby_share_local_device_data_manager_impl.cc @@ -21,11 +21,10 @@ #include #include +#include "location/nearby/sharing/lib/account/account_manager.h" #include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" -#include "absl/strings/substitute.h" -#include "internal/platform/device_info.h" -#include "internal/platform/implementation/account_manager.h" #include "internal/platform/implementation/device_info.h" #include "sharing/common/nearby_share_enums.h" #include "sharing/internal/api/preference_manager.h" @@ -37,14 +36,11 @@ #include "sharing/proto/rpc_resources.pb.h" #include "sharing/proto/timestamp.pb.h" -namespace nearby { -namespace sharing { +namespace nearby::sharing { namespace { using ::nearby::api::DeviceInfo; using ::nearby::sharing::api::PreferenceManager; -constexpr absl::string_view kDefaultDeviceName = "$0\'s $1"; - // Returns a truncated version of |name| that is |max_length| characters long. // For example, name="Reallylongname" with max_length=9 will return "Really...". // name="Reallylongname" with max_length=20 will return "Reallylongname". @@ -72,7 +68,7 @@ NearbyShareLocalDeviceDataManagerImpl::Factory* std::unique_ptr NearbyShareLocalDeviceDataManagerImpl::Factory::Create( PreferenceManager& preference_manager, - AccountManager& account_manager, nearby::DeviceInfo& device_info) { + AccountManager& account_manager, nearby::api::DeviceInfo& device_info) { if (test_factory_) { return test_factory_->CreateInstance(); } @@ -91,7 +87,7 @@ NearbyShareLocalDeviceDataManagerImpl::Factory::~Factory() = default; NearbyShareLocalDeviceDataManagerImpl::NearbyShareLocalDeviceDataManagerImpl( PreferenceManager& preference_manager, AccountManager& account_manager, - nearby::DeviceInfo& device_info) + nearby::api::DeviceInfo& device_info) : preference_manager_(preference_manager), account_manager_(account_manager), device_info_(device_info) {} @@ -148,21 +144,24 @@ std::string NearbyShareLocalDeviceDataManagerImpl::GetDefaultDeviceName() if (os_type == DeviceInfo::OsType::kMacOS || os_type == DeviceInfo::OsType::kIos || !account.has_value() || account->given_name.empty()) { - std::string device_name = device_info_.GetOsDeviceName(); + std::string device_name = + device_info_.GetOsDeviceName().value_or("unknown"); return GetTruncatedName(device_name, kNearbyShareDeviceNameMaxLength); } std::string given_name = account->given_name; - std::string device_type = device_info_.GetDeviceTypeName(); - uint64_t untruncated_length = - absl::Substitute(kDefaultDeviceName, given_name, device_type).length(); + DeviceInfo::DeviceType device_type = device_info_.GetDeviceType(); + std::string device_name = absl::StrCat(given_name, "'s ", device_type); + uint64_t untruncated_length = device_name.length(); + if (untruncated_length <= kNearbyShareDeviceNameMaxLength) { + return device_name; + } uint64_t overflow_length = untruncated_length - kNearbyShareDeviceNameMaxLength; std::string truncated_name = GetTruncatedName(given_name, given_name.length() - overflow_length); - return absl::Substitute(kDefaultDeviceName, truncated_name, device_type); + return absl::StrCat(truncated_name, "'s ", device_type); } -} // namespace sharing -} // namespace nearby +} // namespace nearby::sharing diff --git a/sharing/local_device_data/nearby_share_local_device_data_manager_impl.h b/sharing/local_device_data/nearby_share_local_device_data_manager_impl.h index 176b3b94..7df292b0 100644 --- a/sharing/local_device_data/nearby_share_local_device_data_manager_impl.h +++ b/sharing/local_device_data/nearby_share_local_device_data_manager_impl.h @@ -18,9 +18,9 @@ #include #include +#include "location/nearby/sharing/lib/account/account_manager.h" #include "absl/strings/string_view.h" -#include "internal/platform/device_info.h" -#include "internal/platform/implementation/account_manager.h" +#include "internal/platform/implementation/device_info.h" #include "sharing/common/nearby_share_enums.h" #include "sharing/internal/api/preference_manager.h" #include "sharing/local_device_data/nearby_share_local_device_data_manager.h" @@ -40,7 +40,7 @@ class NearbyShareLocalDeviceDataManagerImpl public: static std::unique_ptr Create( nearby::sharing::api::PreferenceManager& preference_manager, - AccountManager& account_manager, nearby::DeviceInfo& device_info); + AccountManager& account_manager, nearby::api::DeviceInfo& device_info); static void SetFactoryForTesting(Factory* test_factory); protected: @@ -61,7 +61,7 @@ class NearbyShareLocalDeviceDataManagerImpl private: NearbyShareLocalDeviceDataManagerImpl( nearby::sharing::api::PreferenceManager& preference_manager, - AccountManager& account_manager, nearby::DeviceInfo& device_info); + AccountManager& account_manager, nearby::api::DeviceInfo& device_info); DeviceNameValidationResult ValidateDeviceName(absl::string_view name); @@ -73,7 +73,7 @@ class NearbyShareLocalDeviceDataManagerImpl nearby::sharing::api::PreferenceManager& preference_manager_; AccountManager& account_manager_; - nearby::DeviceInfo& device_info_; + nearby::api::DeviceInfo& device_info_; }; } // namespace nearby::sharing diff --git a/sharing/local_device_data/nearby_share_local_device_data_manager_impl_test.cc b/sharing/local_device_data/nearby_share_local_device_data_manager_impl_test.cc index ca3fcdad..d33eb8d6 100644 --- a/sharing/local_device_data/nearby_share_local_device_data_manager_impl_test.cc +++ b/sharing/local_device_data/nearby_share_local_device_data_manager_impl_test.cc @@ -21,11 +21,11 @@ #include #include +#include "location/nearby/sharing/lib/account/account_manager.h" +#include "location/nearby/sharing/lib/account/fake_account_manager.h" #include "gtest/gtest.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" -#include "internal/platform/implementation/account_manager.h" -#include "internal/test/fake_account_manager.h" #include "internal/test/fake_device_info.h" #include "sharing/common/nearby_share_enums.h" #include "sharing/common/nearby_share_prefs.h" @@ -109,16 +109,16 @@ class NearbyShareLocalDeviceDataManagerImplTest } std::string GetDeviceName() const { - return fake_device_info_.GetOsDeviceName(); + return fake_device_info_.GetOsDeviceName().value_or("unknown"); } - std::string GetDeviceTypeName() const { - return fake_device_info_.GetDeviceTypeName(); + nearby::FakeDeviceInfo::DeviceType GetDeviceType() const { + return fake_device_info_.GetDeviceType(); } protected: nearby::FakePreferenceManager preference_manager_; - nearby::FakeAccountManager fake_account_manager_; + FakeAccountManager fake_account_manager_; nearby::FakeDeviceInfo fake_device_info_; std::vector notifications_; std::unique_ptr manager_; @@ -138,8 +138,9 @@ TEST_F(NearbyShareLocalDeviceDataManagerImplTest, DefaultDeviceName) { fake_account_manager().SetAccount(account); EXPECT_EQ(absl::Substitute(kDefaultDeviceName, kFakeGivenName, - GetDeviceTypeName()), + GetDeviceType()), manager()->GetDeviceName()); + EXPECT_EQ(manager()->GetDeviceName(), "Barack奥巴马's PC"); // Make sure that when we use a given name that is very long we truncate // correctly. @@ -152,7 +153,7 @@ TEST_F(NearbyShareLocalDeviceDataManagerImplTest, SetDeviceName) { CreateManager(); std::string expected_default_device_name = - absl::Substitute(kDefaultDeviceName, kFakeGivenName, GetDeviceTypeName()); + absl::Substitute(kDefaultDeviceName, kFakeGivenName, GetDeviceType()); EXPECT_EQ(manager()->GetDeviceName(), expected_default_device_name); EXPECT_TRUE(notifications().empty()); diff --git a/sharing/nearby_connection_impl.cc b/sharing/nearby_connection_impl.cc index 42ab97eb..eecca33d 100644 --- a/sharing/nearby_connection_impl.cc +++ b/sharing/nearby_connection_impl.cc @@ -22,12 +22,12 @@ #include #include "absl/synchronization/mutex.h" -#include "internal/platform/device_info.h" +#include "internal/platform/implementation/device_info.h" #include "sharing/internal/public/logging.h" namespace nearby::sharing { -NearbyConnectionImpl::NearbyConnectionImpl(nearby::DeviceInfo& device_info) +NearbyConnectionImpl::NearbyConnectionImpl(nearby::api::DeviceInfo& device_info) : device_info_(device_info) { if (!device_info_.PreventSleep()) { LOG(WARNING) << __func__ << ":Failed to prevent device sleep."; diff --git a/sharing/nearby_connection_impl.h b/sharing/nearby_connection_impl.h index 35725791..090bf9eb 100644 --- a/sharing/nearby_connection_impl.h +++ b/sharing/nearby_connection_impl.h @@ -23,7 +23,7 @@ #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" -#include "internal/platform/device_info.h" +#include "internal/platform/implementation/device_info.h" #include "sharing/nearby_connection.h" namespace nearby::sharing { @@ -32,7 +32,7 @@ class NearbyConnectionsManager; class NearbyConnectionImpl : public NearbyConnection { public: - explicit NearbyConnectionImpl(nearby::DeviceInfo& device_info); + explicit NearbyConnectionImpl(nearby::api::DeviceInfo& device_info); ~NearbyConnectionImpl() override; // NearbyConnection: @@ -46,7 +46,7 @@ class NearbyConnectionImpl : public NearbyConnection { void WriteMessage(std::vector bytes) ABSL_LOCKS_EXCLUDED(mutex_); private: - nearby::DeviceInfo& device_info_; + nearby::api::DeviceInfo& device_info_; absl::Mutex mutex_; std::function> bytes)> read_callback_ diff --git a/sharing/nearby_connections_manager_factory.cc b/sharing/nearby_connections_manager_factory.cc index 31816390..cba3ccbd 100644 --- a/sharing/nearby_connections_manager_factory.cc +++ b/sharing/nearby_connections_manager_factory.cc @@ -16,8 +16,8 @@ #include -#include "internal/analytics/event_logger.h" -#include "internal/platform/device_info.h" +#include "location/nearby/analytics/cpp/logging/event_logger.h" +#include "internal/platform/implementation/device_info.h" #include "internal/platform/task_runner.h" #include "sharing/internal/public/context.h" #include "sharing/nearby_connections_manager.h" @@ -29,7 +29,7 @@ namespace nearby::sharing { std::unique_ptr NearbyConnectionsManagerFactory::CreateConnectionsManager( nearby::TaskRunner* connections_callback_task_runner, Context* context, - nearby::DeviceInfo& device_info, + nearby::api::DeviceInfo& device_info, nearby::analytics::EventLogger* event_logger) { return std::make_unique( connections_callback_task_runner, context, diff --git a/sharing/nearby_connections_manager_factory.h b/sharing/nearby_connections_manager_factory.h index f22da1ef..f856a140 100644 --- a/sharing/nearby_connections_manager_factory.h +++ b/sharing/nearby_connections_manager_factory.h @@ -17,8 +17,8 @@ #include -#include "internal/analytics/event_logger.h" -#include "internal/platform/device_info.h" +#include "location/nearby/analytics/cpp/logging/event_logger.h" +#include "internal/platform/implementation/device_info.h" #include "internal/platform/task_runner.h" #include "sharing/internal/public/context.h" #include "sharing/nearby_connections_manager.h" @@ -33,7 +33,7 @@ class NearbyConnectionsManagerFactory { // that NearbySharingService is running on. static std::unique_ptr CreateConnectionsManager( nearby::TaskRunner* connections_callback_task_runner, Context* context, - nearby::DeviceInfo& device_info, + nearby::api::DeviceInfo& device_info, nearby::analytics::EventLogger* event_logger = nullptr); private: diff --git a/sharing/nearby_connections_manager_impl.cc b/sharing/nearby_connections_manager_impl.cc index 20b6e1ef..0b57e8c0 100644 --- a/sharing/nearby_connections_manager_impl.cc +++ b/sharing/nearby_connections_manager_impl.cc @@ -32,7 +32,7 @@ #include "absl/types/span.h" #include "internal/base/file_path.h" #include "internal/flags/nearby_flags.h" -#include "internal/platform/device_info.h" +#include "internal/platform/implementation/device_info.h" #include "internal/platform/mutex_lock.h" #include "internal/platform/task_runner.h" #include "sharing/advertisement.h" @@ -150,7 +150,8 @@ std::string PayloadStatusToString(PayloadStatus status) { NearbyConnectionsManagerImpl::NearbyConnectionsManagerImpl( TaskRunner* connections_callback_task_runner, Context* context, - ConnectivityManager& connectivity_manager, nearby::DeviceInfo& device_info, + ConnectivityManager& connectivity_manager, + nearby::api::DeviceInfo& device_info, std::unique_ptr nearby_connections_service) : connections_callback_task_runner_(connections_callback_task_runner), context_(context), @@ -333,6 +334,12 @@ void NearbyConnectionsManagerImpl::StopDiscovery() { }); } +void NearbyConnectionsManagerImpl::RemoveTransferManagerOnCallbackThread( + std::unique_ptr transfer_manager) const { + connections_callback_task_runner_->PostTask( + [transfer_manager = std::move(transfer_manager)]() {}); +} + void NearbyConnectionsManagerImpl::Connect( std::vector endpoint_info, absl::string_view endpoint_id, std::optional> bluetooth_mac_address, @@ -415,15 +422,20 @@ void NearbyConnectionsManagerImpl::Connect( [this, endpoint_id = std::string(endpoint_id)](ConnectionsStatus status) { MutexLock lock(&mutex_); if (status != ConnectionsStatus::kSuccess) { - transfer_managers_.erase(endpoint_id); + auto node = transfer_managers_.extract(endpoint_id); + if (!node.empty()) { + RemoveTransferManagerOnCallbackThread(std::move(node.mapped())); + } } OnConnectionRequested(endpoint_id, status); }); // Setup transfer manager. if (IsTransportTypeFlagsSet(transport_type, TransportType::kHighQuality)) { - transfer_managers_[endpoint_id] = - std::make_unique(context_, endpoint_id); + transfer_managers_[endpoint_id] = std::make_unique( + connections_callback_task_runner_, endpoint_id, + absl::bind_front(&NearbyConnectionsManagerImpl::SendWithoutDelay, + this)); } } @@ -498,23 +510,18 @@ void NearbyConnectionsManagerImpl::Send( RegisterPayloadStatusListener(payload->id, listener); } - if (transfer_managers_.contains(endpoint_id) && payload->content.is_file()) { - VLOG(1) << __func__ << ": Send payload " << payload->id << " to " - << endpoint_id << " to transfer manager. payload is file: " - << payload->content.is_file() << ", is bytes " - << payload->content.is_bytes(); - transfer_managers_.at(endpoint_id) - ->Send([&, endpoint_id = std::string(endpoint_id), - payload_copy = *payload]() { - VLOG(1) << __func__ << ": Send payload " << payload_copy.id << " to " - << endpoint_id; - auto sent_payload = std::make_unique(payload_copy); - SendWithoutDelay(endpoint_id, std::move(sent_payload)); - }); - transfer_managers_.at(endpoint_id)->StartTransfer(); - return; + if (payload->content.is_file()) { + const auto& it = transfer_managers_.find(endpoint_id); + if (it != transfer_managers_.end()) { + VLOG(1) << __func__ << ": Send payload " << payload->id << " to " + << endpoint_id << " to transfer manager. payload is file: " + << payload->content.is_file() << ", is bytes " + << payload->content.is_bytes(); + it->second->Send(std::move(payload)); + it->second->StartTransfer(); + return; + } } - SendWithoutDelay(endpoint_id, std::move(payload)); } @@ -739,9 +746,13 @@ void NearbyConnectionsManagerImpl::OnDisconnected( absl::string_view endpoint_id) { MutexLock lock(&mutex_); // Remove transfer manager. - if (transfer_managers_.contains(endpoint_id)) { - transfer_managers_[endpoint_id]->CancelTransfer(); - transfer_managers_.erase(endpoint_id); + const auto& transfer_manager_it = transfer_managers_.find(endpoint_id); + if (transfer_manager_it != transfer_managers_.end()) { + transfer_manager_it->second->CancelTransfer(); + auto node = transfer_managers_.extract(transfer_manager_it); + if (!node.empty()) { + RemoveTransferManagerOnCallbackThread(std::move(node.mapped())); + } } Status connection_layer_status = Status::kUnknown; @@ -771,8 +782,9 @@ void NearbyConnectionsManagerImpl::OnBandwidthChanged( << ": Bandwidth changed to medium=" << static_cast(medium) << "; endpoint_id=" << endpoint_id; - if (transfer_managers_.contains(endpoint_id)) { - transfer_managers_[endpoint_id]->OnMediumQualityChanged(medium); + const auto& transfer_manager_it = transfer_managers_.find(endpoint_id); + if (transfer_manager_it != transfer_managers_.end()) { + transfer_manager_it->second->OnMediumQualityChanged(medium); } current_upgraded_mediums_.insert_or_assign(endpoint_id, medium); @@ -907,7 +919,11 @@ void NearbyConnectionsManagerImpl::Reset() { for (auto& transfer_manager : transfer_managers_) { transfer_manager.second->CancelTransfer(); } - transfer_managers_.clear(); + absl::flat_hash_map> + transfer_managers; + transfer_managers.swap(transfer_managers_); + connections_callback_task_runner_->PostTask( + [transfer_managers = std::move(transfer_managers)]() {}); for (auto& entry : pending_outgoing_connections_) std::move(entry.second)(entry.first, /*connection=*/nullptr, diff --git a/sharing/nearby_connections_manager_impl.h b/sharing/nearby_connections_manager_impl.h index 0de7d791..174f83f8 100644 --- a/sharing/nearby_connections_manager_impl.h +++ b/sharing/nearby_connections_manager_impl.h @@ -27,7 +27,7 @@ #include "absl/container/flat_hash_set.h" #include "absl/strings/string_view.h" #include "internal/base/file_path.h" -#include "internal/platform/device_info.h" +#include "internal/platform/implementation/device_info.h" #include "internal/platform/mutex.h" #include "internal/platform/task_runner.h" #include "internal/platform/timer.h" @@ -49,7 +49,7 @@ class NearbyConnectionsManagerImpl : public NearbyConnectionsManager { explicit NearbyConnectionsManagerImpl( nearby::TaskRunner* connections_callback_task_runner, Context* context, nearby::ConnectivityManager& connectivity_manager, - nearby::DeviceInfo& device_info, + nearby::api::DeviceInfo& device_info, std::unique_ptr nearby_connections_service); ~NearbyConnectionsManagerImpl() override; NearbyConnectionsManagerImpl(const NearbyConnectionsManagerImpl&) = delete; @@ -143,10 +143,13 @@ class NearbyConnectionsManagerImpl : public NearbyConnectionsManager { void SendWithoutDelay(absl::string_view endpoint_id, std::unique_ptr payload); + void RemoveTransferManagerOnCallbackThread( + std::unique_ptr transfer_manager) const; + nearby::TaskRunner* const connections_callback_task_runner_; Context* const context_; nearby::ConnectivityManager& connectivity_manager_; - nearby::DeviceInfo& device_info_; + nearby::api::DeviceInfo& device_info_; // Nearby Connections Manager is called from different threads and may have // multiple calls to the class from one thread. To avoid deadlock and access diff --git a/sharing/nearby_connections_manager_impl_test.cc b/sharing/nearby_connections_manager_impl_test.cc index 2e46ec1f..f3c32490 100644 --- a/sharing/nearby_connections_manager_impl_test.cc +++ b/sharing/nearby_connections_manager_impl_test.cc @@ -33,7 +33,6 @@ #include "absl/strings/string_view.h" #include "absl/synchronization/notification.h" #include "absl/time/time.h" -#include "absl/types/optional.h" #include "absl/types/span.h" #include "internal/base/file_path.h" #include "internal/base/files.h" @@ -1037,7 +1036,7 @@ TEST_F(NearbyConnectionsManagerImplTest, ConnectClosedByRemote) { [&]() { close_notification.Notify(); }); Sync(); absl::Notification read_notification; - nearby_connection->Read([&](absl::optional> bytes) { + nearby_connection->Read([&](std::optional> bytes) { EXPECT_FALSE(bytes); read_notification.Notify(); }); @@ -1071,7 +1070,7 @@ TEST_F(NearbyConnectionsManagerImplTest, ConnectClosedByClient) { [&]() { close_notification.Notify(); }); Sync(); absl::Notification read_notification; - nearby_connection->Read([&](absl::optional> bytes) { + nearby_connection->Read([&](std::optional> bytes) { EXPECT_FALSE(bytes); read_notification.Notify(); }); diff --git a/sharing/nearby_connections_service_impl.cc b/sharing/nearby_connections_service_impl.cc index b0d3a89d..42a8ba75 100644 --- a/sharing/nearby_connections_service_impl.cc +++ b/sharing/nearby_connections_service_impl.cc @@ -23,6 +23,7 @@ #include #include +#include "location/nearby/analytics/cpp/logging/event_logger.h" #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" @@ -31,6 +32,7 @@ #include "connections/connection_options.h" #include "connections/core.h" #include "connections/discovery_options.h" +#include "connections/implementation/analytics/analytics_recorder_impl.h" #include "connections/implementation/service_controller_router.h" #include "connections/listeners.h" #include "connections/medium_selector.h" @@ -39,7 +41,6 @@ #include "connections/payload_type.h" #include "connections/status.h" #include "connections/strategy.h" -#include "internal/analytics/event_logger.h" #include "internal/platform/byte_array.h" #include "internal/platform/logging.h" #include "internal/platform/mac_address.h" @@ -51,6 +52,7 @@ namespace nearby { namespace sharing { namespace { +using ::nearby::analytics::AnalyticsRecorderImpl; using ::nearby::connections::ConnectionRequestInfo; using ::nearby::connections::ConnectionResponseInfo; using ::nearby::connections::Core; @@ -85,7 +87,8 @@ NearbyConnectionsServiceImpl::NearbyConnectionsServiceImpl( // at an invalid instance. return connectivity_manager_.IsHPRealtekDevice(); }); - static Core* core = new Core(event_logger, router); + static Core* core = + new Core(std::make_unique(event_logger), router); service_handle_ = core; } diff --git a/sharing/nearby_connections_service_impl.h b/sharing/nearby_connections_service_impl.h index cdcfcf30..76d34891 100644 --- a/sharing/nearby_connections_service_impl.h +++ b/sharing/nearby_connections_service_impl.h @@ -22,10 +22,10 @@ #include #include +#include "location/nearby/analytics/cpp/logging/event_logger.h" #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" -#include "internal/analytics/event_logger.h" #include "sharing/internal/public/connectivity_manager.h" #include "sharing/nearby_connections_service.h" #include "sharing/nearby_connections_types.h" diff --git a/sharing/nearby_sharing_service.h b/sharing/nearby_sharing_service.h index 8d1f8a1a..317b5a02 100644 --- a/sharing/nearby_sharing_service.h +++ b/sharing/nearby_sharing_service.h @@ -20,25 +20,23 @@ #include #include +#include "location/nearby/sharing/lib/sync/sync_manager.h" #include "absl/functional/any_invocable.h" +#include "absl/strings/string_view.h" #include "absl/time/time.h" #include "internal/platform/clock.h" #include "sharing/advertisement.h" #include "sharing/attachment_container.h" #include "sharing/certificates/nearby_share_certificate_manager.h" -#include "sharing/local_device_data/nearby_share_local_device_data_manager.h" #include "sharing/nearby_sharing_settings.h" +#include "sharing/outgoing_targets_manager.h" #include "sharing/share_target_discovered_callback.h" #include "sharing/transfer_update_callback.h" -namespace nearby { +namespace nearby::sharing { class AccountManager; - -namespace sharing { - class NearbyNotificationDelegate; -class NearbyShareContactManager; // This service implements Nearby Sharing on top of the Nearby Connections mojo. // Currently, only single profile will be allowed to be bound at a time and only @@ -143,28 +141,28 @@ class NearbySharingService { ShareTargetDiscoveredCallback* discovery_callback, SendSurfaceState state, Advertisement::BlockedVendorId blocked_vendor_id, bool disable_wifi_hotspot, - std::function status_codes_callback) = 0; + absl::AnyInvocable status_codes_callback) = 0; // Unregisters the current send surface. virtual void UnregisterSendSurface( TransferUpdateCallback* transfer_callback, - std::function status_codes_callback) = 0; + absl::AnyInvocable status_codes_callback) = 0; // Registers a receiver surface for handling payload transfer status, and // advertises the vendor ID specified by |vendor_id|. virtual void RegisterReceiveSurface( TransferUpdateCallback* transfer_callback, ReceiveSurfaceState state, Advertisement::BlockedVendorId vendor_id, - std::function status_codes_callback) = 0; + absl::AnyInvocable status_codes_callback) = 0; // Unregisters the current receive surface. virtual void UnregisterReceiveSurface( TransferUpdateCallback* transfer_callback, - std::function status_codes_callback) = 0; + absl::AnyInvocable status_codes_callback) = 0; // Unregisters all foreground receive surfaces. virtual void ClearForegroundReceiveSurfaces( - std::function status_codes_callback) = 0; + absl::AnyInvocable status_codes_callback) = 0; // Returns true if there is an ongoing file transfer. virtual bool IsTransferring() const = 0; @@ -208,6 +206,12 @@ class NearbySharingService { int64_t share_target_id, std::function status_codes_callback) = 0; + virtual void InitiatePairing( + int64_t share_target_id, + service::proto::BindingRequest::Type binding_type, + absl::AnyInvocable + status_codes_callback) = 0; + // Checks to make sure visibility setting is valid and updates the service's // visibility if so. virtual void SetVisibility( @@ -218,16 +222,19 @@ class NearbySharingService { virtual void UpdateFilePathsInProgress(bool update_file_paths) = 0; virtual NearbyShareSettings* GetSettings() = 0; - virtual NearbyShareLocalDeviceDataManager* GetLocalDeviceDataManager() = 0; - virtual NearbyShareContactManager* GetContactManager() = 0; virtual NearbyShareCertificateManager* GetCertificateManager() = 0; virtual AccountManager* GetAccountManager() = 0; virtual Clock& GetClock() = 0; virtual void SetAlternateServiceUuidForDiscovery( uint16_t alternate_service_uuid) = 0; + virtual SyncManager& sync_manager() = 0; + virtual OutgoingTargetsManager& outgoing_targets_manager() = 0; + virtual void UpdateBackupSavePath( + absl::string_view binding_id, absl::string_view save_path, + absl::AnyInvocable + status_codes_callback) = 0; }; -} // namespace sharing -} // namespace nearby +} // namespace nearby::sharing #endif // THIRD_PARTY_NEARBY_SHARING_NEARBY_SHARING_SERVICE_H_ diff --git a/sharing/nearby_sharing_service_factory.cc b/sharing/nearby_sharing_service_factory.cc index f2c4ea74..3bbb801c 100644 --- a/sharing/nearby_sharing_service_factory.cc +++ b/sharing/nearby_sharing_service_factory.cc @@ -17,10 +17,10 @@ #include #include -#include "internal/analytics/event_logger.h" +#include "location/nearby/analytics/cpp/logging/event_logger.h" +#include "location/nearby/sharing/lib/rpc/grpc_async_client_factory.h" #include "internal/platform/task_runner.h" #include "sharing/analytics/analytics_recorder.h" -#include "sharing/contacts/nearby_share_contact_manager_impl.h" #include "sharing/internal/api/sharing_platform.h" #include "sharing/internal/public/context_impl.h" #include "sharing/nearby_connections_manager_factory.h" @@ -42,7 +42,7 @@ NearbySharingService* NearbySharingServiceFactory::CreateSharingService( analytics::AnalyticsRecorder* analytics_recorder, ::nearby::analytics::EventLogger* event_logger, bool supports_file_sync) { if (nearby_sharing_service_ != nullptr) { - return nearby_sharing_service_.get(); + return nullptr; } context_ = @@ -58,21 +58,13 @@ NearbySharingService* NearbySharingServiceFactory::CreateSharingService( std::make_unique( &sharing_platform.GetAccountManager(), context_->GetClock(), analytics_recorder); - nearby_share_client_ = nearby_share_client_factory_->CreateInstance(); nearby_identity_client_ = nearby_share_client_factory_->CreateIdentityInstance(); - auto nearby_share_contact_manager = - std::make_unique( - context_.get(), sharing_platform.GetAccountManager(), - nearby_share_client_.get()); nearby_sharing_service_ = std::make_unique( std::move(service_thread), context_.get(), sharing_platform, - nearby_identity_client_.get(), - nearby_share_client_.get(), - std::move(nearby_connections_manager), - std::move(nearby_share_contact_manager), analytics_recorder, - supports_file_sync); + nearby_identity_client_.get(), std::move(nearby_connections_manager), + analytics_recorder, supports_file_sync); return nearby_sharing_service_.get(); } diff --git a/sharing/nearby_sharing_service_factory.h b/sharing/nearby_sharing_service_factory.h index c0862d5c..100221cf 100644 --- a/sharing/nearby_sharing_service_factory.h +++ b/sharing/nearby_sharing_service_factory.h @@ -17,18 +17,13 @@ #include -#include "internal/analytics/event_logger.h" +#include "location/nearby/analytics/cpp/logging/event_logger.h" +#include "location/nearby/sharing/lib/rpc/grpc_async_client_factory.h" +#include "location/nearby/sharing/lib/rpc/sharing_rpc_client.h" #include "sharing/analytics/analytics_recorder.h" #include "sharing/internal/api/sharing_platform.h" #include "sharing/internal/public/context.h" #include "sharing/nearby_sharing_service.h" -#if defined(__linux__) -#include "sharing/linux/stubs/grpc_async_client_factory.h" -#include "sharing/linux/stubs/sharing_rpc_client.h" -#else -#include "location/nearby/sharing/lib/rpc/grpc_async_client_factory.h" -#include "location/nearby/sharing/lib/rpc/sharing_rpc_client.h" -#endif namespace nearby::sharing { @@ -50,7 +45,6 @@ class NearbySharingServiceFactory { std::unique_ptr nearby_sharing_service_; std::unique_ptr nearby_share_client_factory_; - std::unique_ptr nearby_share_client_; std::unique_ptr nearby_identity_client_; }; diff --git a/sharing/nearby_sharing_service_impl.cc b/sharing/nearby_sharing_service_impl.cc index 9c3b5890..03fb483b 100644 --- a/sharing/nearby_sharing_service_impl.cc +++ b/sharing/nearby_sharing_service_impl.cc @@ -31,6 +31,9 @@ #include #include +#include "location/nearby/sharing/lib/account/account_manager.h" +#include "location/nearby/sharing/lib/rpc/sharing_rpc_client.h" +#include "location/nearby/sharing/lib/sync/sync_binding_prefs.pb.h" #include "absl/base/nullability.h" #include "absl/container/flat_hash_map.h" #include "absl/functional/any_invocable.h" @@ -46,8 +49,6 @@ #include "internal/flags/nearby_flags.h" #include "internal/network/url.h" #include "internal/platform/clock.h" -#include "internal/platform/device_info.h" -#include "internal/platform/implementation/account_manager.h" #include "internal/platform/implementation/device_info.h" #include "internal/platform/task_runner.h" #include "proto/sharing_enums.pb.h" @@ -64,7 +65,6 @@ #include "sharing/common/nearby_share_enums.h" #include "sharing/common/nearby_share_prefs.h" #include "sharing/constants.h" -#include "sharing/contacts/nearby_share_contact_manager.h" #include "sharing/fast_initiation/nearby_fast_initiation.h" #include "sharing/fast_initiation/nearby_fast_initiation_impl.h" #include "sharing/file_attachment.h" @@ -96,6 +96,7 @@ #include "sharing/proto/wire_format.pb.h" #include "sharing/scheduling/nearby_share_scheduler_utils.h" #include "sharing/share_session.h" +#include "sharing/share_session_usage.h" #include "sharing/share_target.h" #include "sharing/share_target_discovered_callback.h" #include "sharing/thread_timer.h" @@ -112,11 +113,12 @@ using ::absl::Milliseconds; using ::location::nearby::proto::sharing::OSType; using ::location::nearby::proto::sharing::ResponseToIntroduction; using ::location::nearby::proto::sharing::SessionStatus; -using ::nearby::sharing::api::SharingPlatform; -using ::nearby::sharing::api::SharingRpcClient; using ::nearby::sharing::api::IdentityRpcClient; +using ::nearby::sharing::api::SharingPlatform; using ::nearby::sharing::proto::DataUsage; using ::nearby::sharing::proto::DeviceVisibility; +using ::nearby::sharing::service::proto::BindingRequest; +using ::nearby::sharing::service::proto::BindingResponse; using ::nearby::sharing::service::proto::ConnectionResponseFrame; using ::nearby::sharing::service::proto::IntroductionFrame; @@ -230,6 +232,25 @@ std::string SendSurfaceStateToString( } } +sync::SyncBinding::SourceDeviceType ShareTargetTypeToSourceDeviceType( + ShareTargetType share_target_type) { + switch (share_target_type) { + case ShareTargetType::kPhone: + return sync::SyncBinding::SOURCE_DEVICE_TYPE_PHONE; + case ShareTargetType::kTablet: + return sync::SyncBinding::SOURCE_DEVICE_TYPE_TABLET; + case ShareTargetType::kLaptop: + return sync::SyncBinding::SOURCE_DEVICE_TYPE_LAPTOP; + case ShareTargetType::kCar: + return sync::SyncBinding::SOURCE_DEVICE_TYPE_CAR; + case ShareTargetType::kFoldable: + return sync::SyncBinding::SOURCE_DEVICE_TYPE_FOLDABLE; + case ShareTargetType::kXR: + return sync::SyncBinding::SOURCE_DEVICE_TYPE_XR; + case ShareTargetType::kUnknown: + return sync::SyncBinding::SOURCE_DEVICE_TYPE_UNKNOWN; + } +} } // namespace NearbySharingServiceImpl::NearbySharingServiceImpl( @@ -237,9 +258,7 @@ NearbySharingServiceImpl::NearbySharingServiceImpl( SharingPlatform& sharing_platform, nearby::sharing::api::IdentityRpcClient* absl_nonnull nearby_identity_client, - nearby::sharing::api::SharingRpcClient* absl_nonnull nearby_share_client, std::unique_ptr nearby_connections_manager, - std::unique_ptr contact_manager, analytics::AnalyticsRecorder* analytics_recorder, bool supports_file_sync) : service_thread_(std::move(service_thread)), context_(context), @@ -249,16 +268,16 @@ NearbySharingServiceImpl::NearbySharingServiceImpl( analytics_recorder_(*analytics_recorder), supports_file_sync_(supports_file_sync), nearby_connections_manager_(std::move(nearby_connections_manager)), - nearby_share_client_(nearby_share_client), + nearby_identity_client_(nearby_identity_client), local_device_data_manager_( NearbyShareLocalDeviceDataManagerImpl::Factory::Create( preference_manager_, account_manager_, device_info_)), - contact_manager_(std::move(contact_manager)), nearby_fast_initiation_( NearbyFastInitiationImpl::Factory::Create(context_)), settings_(std::make_unique( - context_, context_->GetClock(), device_info_, preference_manager_, - local_device_data_manager_.get(), &analytics_recorder_)), + service_thread_.get(), context_->GetClock(), device_info_, + preference_manager_, local_device_data_manager_.get(), + &analytics_recorder_)), service_extension_(std::make_unique()), file_handler_(sharing_platform), app_info_(sharing_platform.CreateAppInfo()), @@ -273,13 +292,13 @@ NearbySharingServiceImpl::NearbySharingServiceImpl( this), absl::bind_front(&NearbySharingServiceImpl::OnOutgoingTransferUpdate, this)), - sync_manager_(&preference_manager_) { + sync_manager_(nearby_identity_client_, &preference_manager_) { CHECK(nearby_connections_manager_); CHECK(analytics_recorder); is_shutting_down_ = std::make_unique(false); FilePath profile_path = - device_info_.GetAppDataPath().append(FilePath(kProfileRelativePath)); + device_info_.GetLocalAppDataPath(FilePath(kProfileRelativePath)); certificate_manager_ = NearbyShareCertificateManagerImpl::Factory::Create( context_, sharing_platform, local_device_data_manager_.get(), @@ -431,16 +450,16 @@ void NearbySharingServiceImpl::RegisterSendSurface( TransferUpdateCallback* transfer_callback, ShareTargetDiscoveredCallback* discovery_callback, SendSurfaceState state, BlockedVendorId blocked_vendor_id, bool disable_wifi_hotspot, - std::function status_codes_callback) { + absl::AnyInvocable status_codes_callback) { RunOnNearbySharingServiceThread( "api_register_send_surface", [this, transfer_callback, discovery_callback, state, blocked_vendor_id, disable_wifi_hotspot, - status_codes_callback = std::move(status_codes_callback)]() { + status_codes_callback = std::move(status_codes_callback)]() mutable { if (state != SendSurfaceState::kForeground && state != SendSurfaceState::kBackground) { LOG(ERROR) << "Invalid SendSurfaceState: " << static_cast(state); - std::move(status_codes_callback)(StatusCodes::kInvalidArgument); + status_codes_callback(StatusCodes::kInvalidArgument); return; } DCHECK(transfer_callback); @@ -457,7 +476,7 @@ void NearbySharingServiceImpl::RegisterSendSurface( background_send_surface_map_.contains(transfer_callback)) { VLOG(1) << "RegisterSendSurface failed. Already registered for a " "different state."; - std::move(status_codes_callback)(StatusCodes::kInvalidArgument); + status_codes_callback(StatusCodes::kInvalidArgument); return; } BlockedVendorId sending_id = GetSendingVendorId(); @@ -465,7 +484,7 @@ void NearbySharingServiceImpl::RegisterSendSurface( LOG(INFO) << "RegisterSendSurface failed. Already registered to " "block a different vendor ID " << static_cast(sending_id); - std::move(status_codes_callback)(StatusCodes::kInvalidArgument); + status_codes_callback(StatusCodes::kInvalidArgument); return; } WrappedShareTargetDiscoveredCallback wrapped_callback( @@ -484,8 +503,7 @@ void NearbySharingServiceImpl::RegisterSendSurface( VLOG(1) << "Ignore registering (and unregistering if registered) send " "surface because we're currently receiving files."; - std::move(status_codes_callback)( - StatusCodes::kTransferAlreadyInProgress); + status_codes_callback(StatusCodes::kTransferAlreadyInProgress); return; } @@ -539,17 +557,17 @@ void NearbySharingServiceImpl::RegisterSendSurface( << background_send_surface_map_.size(); InvalidateSendSurfaceState(); - std::move(status_codes_callback)(StatusCodes::kOk); + status_codes_callback(StatusCodes::kOk); }); } void NearbySharingServiceImpl::UnregisterSendSurface( TransferUpdateCallback* transfer_callback, - std::function status_codes_callback) { + absl::AnyInvocable status_codes_callback) { RunOnNearbySharingServiceThread( "api_unregister_send_surface", [this, transfer_callback, - status_codes_callback = std::move(status_codes_callback)]() { + status_codes_callback = std::move(status_codes_callback)]() mutable { StatusCodes status_codes = InternalUnregisterSendSurface(transfer_callback); @@ -558,23 +576,23 @@ void NearbySharingServiceImpl::UnregisterSendSurface( << ", background_send_surface_map_:" << background_send_surface_map_.size(); - std::move(status_codes_callback)(status_codes); + status_codes_callback(status_codes); }); } void NearbySharingServiceImpl::RegisterReceiveSurface( TransferUpdateCallback* transfer_callback, ReceiveSurfaceState state, BlockedVendorId vendor_id, - std::function status_codes_callback) { + absl::AnyInvocable status_codes_callback) { RunOnNearbySharingServiceThread( "api_register_receive_surface", [this, transfer_callback, state, vendor_id, - status_codes_callback = std::move(status_codes_callback)]() { + status_codes_callback = std::move(status_codes_callback)]() mutable { if (state != ReceiveSurfaceState::kForeground && state != ReceiveSurfaceState::kBackground) { LOG(ERROR) << "Invalid ReceiveSurfaceState: " << static_cast(state); - std::move(status_codes_callback)(StatusCodes::kInvalidArgument); + status_codes_callback(StatusCodes::kInvalidArgument); return; } DCHECK(transfer_callback); @@ -592,14 +610,14 @@ void NearbySharingServiceImpl::RegisterReceiveSurface( if (GetReceiveCallbacksMapFromState(state).contains( transfer_callback)) { VLOG(1) << "transfer callback already registered, ignoring"; - std::move(status_codes_callback)(StatusCodes::kOk); + status_codes_callback(StatusCodes::kOk); return; } if (foreground_receive_callbacks_map_.contains(transfer_callback) || background_receive_callbacks_map_.contains(transfer_callback)) { LOG(ERROR) << ": transfer callback already registered but for a " "different state."; - std::move(status_codes_callback)(StatusCodes::kInvalidArgument); + status_codes_callback(StatusCodes::kInvalidArgument); return; } if (ShouldBlockSurfaceRegistration(vendor_id, @@ -610,7 +628,7 @@ void NearbySharingServiceImpl::RegisterReceiveSurface( << static_cast(vendor_id) << " because the current vendor_id is " << static_cast(GetReceivingVendorId()); - std::move(status_codes_callback)(StatusCodes::kInvalidArgument); + status_codes_callback(StatusCodes::kInvalidArgument); return; } @@ -647,39 +665,38 @@ void NearbySharingServiceImpl::RegisterReceiveSurface( << background_receive_callbacks_map_.size(); if (IsVisibleInBackground(settings_->GetVisibility())) { - // The Identity API does not support contact manager which triggers - // Certificate refresh in DownloadContacts. Force upload explicitly. VLOG(1) << "[Call Identity API] ForceUploadPrivateCertificates."; certificate_manager_->ForceUploadPrivateCertificates(); } InvalidateReceiveSurfaceState(); - std::move(status_codes_callback)(StatusCodes::kOk); + status_codes_callback(StatusCodes::kOk); }); } void NearbySharingServiceImpl::UnregisterReceiveSurface( TransferUpdateCallback* transfer_callback, - std::function status_codes_callback) { + absl::AnyInvocable status_codes_callback) { RunOnNearbySharingServiceThread( "api_unregister_receive_surface", [this, transfer_callback, - status_codes_callback = std::move(status_codes_callback)]() { + status_codes_callback = std::move(status_codes_callback)]() mutable { StatusCodes status_codes = InternalUnregisterReceiveSurface(transfer_callback); VLOG(1) << "UnregisterReceiveSurface: foreground_receive_callbacks_:" << foreground_receive_callbacks_map_.size() << ", background_receive_callbacks_:" << background_receive_callbacks_map_.size(); - std::move(status_codes_callback)(status_codes); + status_codes_callback(status_codes); return; }); } void NearbySharingServiceImpl::ClearForegroundReceiveSurfaces( - std::function status_codes_callback) { + absl::AnyInvocable status_codes_callback) { RunOnNearbySharingServiceThread( "api_clear_foreground_receive_surfaces", - [this, status_codes_callback = std::move(status_codes_callback)]() { + [this, + status_codes_callback = std::move(status_codes_callback)]() mutable { std::vector fg_receivers; for (const auto& callback : foreground_receive_callbacks_map_) { fg_receivers.push_back(callback.first); @@ -690,7 +707,7 @@ void NearbySharingServiceImpl::ClearForegroundReceiveSurfaces( if (InternalUnregisterReceiveSurface(callback) != StatusCodes::kOk) status = StatusCodes::kError; } - std::move(status_codes_callback)(status); + status_codes_callback(status); }); } @@ -738,16 +755,6 @@ void NearbySharingServiceImpl::SendAttachments( return; } } - // Outgoing connections always announces with contacts visibility. - std::optional> endpoint_info = - CreateEndpointInfo(DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, - local_device_data_manager_->GetDeviceName()); - if (!endpoint_info) { - LOG(WARNING) << "Could not create local endpoint info."; - std::move(status_codes_callback)(StatusCodes::kError); - return; - } - OutgoingShareSession* session = outgoing_targets_manager_.GetOutgoingShareSession(share_target_id); if (!session) { @@ -755,29 +762,39 @@ void NearbySharingServiceImpl::SendAttachments( std::move(status_codes_callback)(StatusCodes::kInvalidArgument); return; } - - app_info_->SetActiveFlag(); - + StatusCodes status_code = StatusCodes::kOk; if (session->InitiateSendAttachments( - std::move(attachment_container))) { - OutgoingSessionConnect(*session, std::move(*endpoint_info)); + std::move(attachment_container ))) { + status_code = ConnectOutgoingSessionOnServiceThread(*session); } - std::move(status_codes_callback)(StatusCodes::kOk); + std::move(status_codes_callback)(status_code); }); } -void NearbySharingServiceImpl::OutgoingSessionConnect( - OutgoingShareSession& session, std::vector endpoint_info) { +NearbySharingService::StatusCodes +NearbySharingServiceImpl::ConnectOutgoingSessionOnServiceThread( + OutgoingShareSession& session) { + // Outgoing connections always announces with contacts visibility. + std::optional> endpoint_info = + CreateEndpointInfo(DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, + local_device_data_manager_->GetDeviceName()); + if (!endpoint_info) { + LOG(WARNING) << "Could not create local endpoint info."; + return StatusCodes::kError; + } + app_info_->SetActiveFlag(); + OnTransferStarted(/*is_incoming=*/false); is_connecting_ = true; InvalidateSendSurfaceState(); int64_t share_target_id = session.share_target().id; session.Connect( - std::move(endpoint_info), settings_->GetDataUsage(), + std::move(*endpoint_info), settings_->GetDataUsage(), GetDisableWifiHotspotState(), absl::bind_front(&NearbySharingServiceImpl::OnOutgoingConnection, this, share_target_id)); + return StatusCodes::kOk; } bool NearbySharingServiceImpl::OutgoingSessionAccept( @@ -857,6 +874,7 @@ void NearbySharingServiceImpl::Reject( session->UpdateTransferMetadata( TransferMetadataBuilder() + .set_usage(session->session_usage()) .set_status(TransferMetadata::Status::kRejected) .build()); @@ -913,6 +931,7 @@ void NearbySharingServiceImpl::DoCancel( // UpdateTransferMetadata. session->UpdateTransferMetadata( TransferMetadataBuilder() + .set_usage(session->session_usage()) .set_status(TransferMetadata::Status::kCancelled) .build()); @@ -949,6 +968,33 @@ void NearbySharingServiceImpl::DoCancel( std::move(status_codes_callback)(StatusCodes::kOk); } +void NearbySharingServiceImpl::InitiatePairing( + int64_t share_target_id, BindingRequest::Type binding_type, + absl::AnyInvocable + status_codes_callback) { + RunOnNearbySharingServiceThread( + "api_initiate_pairing", + [this, share_target_id, binding_type, + status_codes_callback = std::move(status_codes_callback)]() mutable { + LOG(INFO) << "InitiatePairing is called"; + OutgoingShareSession* session = + outgoing_targets_manager_.GetOutgoingShareSession(share_target_id); + if (!session) { + LOG(WARNING) << "InitiatePairing invoked for unknown share target"; + std::move(status_codes_callback)(StatusCodes::kInvalidArgument); + return; + } + if (binding_type != BindingRequest::FILESYNC) { + LOG(WARNING) << __func__ << "Only FileSync bindings are supported."; + std::move(status_codes_callback)(StatusCodes::kInvalidArgument); + return; + } + // Start connection without attachments will initiate pairing. + std::move(status_codes_callback)( + ConnectOutgoingSessionOnServiceThread(*session)); + }); +} + void NearbySharingServiceImpl::SetVisibility( proto::DeviceVisibility visibility, absl::Duration expiration, absl::AnyInvocable callback) { @@ -977,15 +1023,6 @@ NearbyShareSettings* NearbySharingServiceImpl::GetSettings() { return settings_.get(); } -NearbyShareLocalDeviceDataManager* -NearbySharingServiceImpl::GetLocalDeviceDataManager() { - return local_device_data_manager_.get(); -} - -NearbyShareContactManager* NearbySharingServiceImpl::GetContactManager() { - return contact_manager_.get(); -} - NearbyShareCertificateManager* NearbySharingServiceImpl::GetCertificateManager() { return certificate_manager_.get(); @@ -1372,7 +1409,7 @@ void NearbySharingServiceImpl::AdapterPresentChanged( void NearbySharingServiceImpl::AdapterPoweredChanged( sharing::api::BluetoothAdapter* adapter, bool powered) { - // When adpater is powered on, it takes some time for the RFCOMM service to + // When adapter is powered on, it takes some time for the RFCOMM service to // be ready. If we don't wait the RfCommServiceProvider::CreateAsync() call // fails with a "device is not ready for use" error. // Waiting 500ms seems to be enough to allow it to reliably work. @@ -1509,17 +1546,12 @@ NearbySharingServiceImpl::CreateEndpointInfo( ShareTargetType device_type = static_cast(device_info_.GetDeviceType()); - AdvertisementCapabilities capabilities{}; - if (supports_file_sync_ && NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_sharing_feature::kEnableFileSync)) { - capabilities.Add(AdvertisementCapabilities::Capability::kFileSync); - } std::unique_ptr advertisement = Advertisement::NewInstance( std::move(salt), std::move(encrypted_key), device_type, device_name, visibility == DeviceVisibility::DEVICE_VISIBILITY_EVERYONE ? static_cast(GetReceivingVendorId()) : static_cast(BlockedVendorId::kNone), - std::move(capabilities)); + /*capabilities=*/{}); if (advertisement) { return advertisement->ToEndpointInfo(); } else { @@ -1903,9 +1935,7 @@ void NearbySharingServiceImpl::InvalidateReceiveSurfaceState() { void NearbySharingServiceImpl::InvalidateAdvertisingState() { // Do not advertise on lock screen unless Self Share is enabled. - if (is_screen_locked_ && - !NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_sharing_feature::kEnableSelfShareUi)) { + if (is_screen_locked_) { StopAdvertising(); VLOG(1) << __func__ << ": Stopping advertising because the screen is locked."; @@ -2196,9 +2226,10 @@ void NearbySharingServiceImpl::OnOutgoingConnection( session->RunPairedKeyVerification( ToProtoOsType(device_info_.GetOsType()), { - .visibility = settings_->GetVisibility(), - .last_visibility = settings_->GetLastVisibility(), - .last_visibility_time = settings_->GetLastVisibilityTimestamp(), + // Sender always uses ALL_CONTACTS cert to sign and verify signature. + .visibility = DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, + .last_visibility = DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, + .last_visibility_time = absl::UnixEpoch(), }, GetCertificateManager(), absl::bind_front( @@ -2237,16 +2268,15 @@ void NearbySharingServiceImpl::OnIncomingAdvertisementDecoded( // data to lambda. GetCertificateManager()->GetDecryptedPublicCertificate( std::move(encrypted_metadata_key), - [this, endpoint_id, advertisement_copy = *advertisement, - placeholder_share_target_id]( + [this, endpoint_id = std::string(endpoint_id), + advertisement_copy = *advertisement, placeholder_share_target_id]( std::optional decrypted_public_certificate) { RunOnNearbySharingServiceThread( "incoming_decrypted_certificate", // capture endpoint_id string_view as a std::string to ensure the // data does not go out of scope. - [this, endpoint_id = std::string(endpoint_id), advertisement_copy, - placeholder_share_target_id, + [this, endpoint_id, advertisement_copy, placeholder_share_target_id, decrypted_public_certificate = std::move(decrypted_public_certificate)]() { OnIncomingDecryptedCertificate(endpoint_id, advertisement_copy, @@ -2454,7 +2484,7 @@ void NearbySharingServiceImpl::OnIncomingSessionFrameRead( if (is_timeout) { LOG(WARNING) << __func__ << ": Timed out reading frame from target: " << share_target_id; - session->Abort(TransferMetadata::Status::kFailed); + session->Abort(TransferMetadata::Status::kTimedOut); return; } if (!frame.has_value()) { @@ -2479,12 +2509,6 @@ void NearbySharingServiceImpl::OnIncomingSessionFrameRead( OnReceivedIntroduction(*session, frame->introduction()); // OnReceivedIntroduction will schedule the next ReadFrame. return; - case service::proto::V1Frame::FILE_SYNC: - if (NearbyFlags::GetInstance().GetBoolFlag( - config_package_nearby::nearby_sharing_feature::kEnableFileSync)) { - session->ProcessSyncFrame(sync_manager_, frame->file_sync()); - } - break; default: LOG(ERROR) << __func__ << ": Discarding unknown frame of type: " << static_cast(frame->type()); @@ -2532,10 +2556,19 @@ void NearbySharingServiceImpl::OnOutgoingConnectionKeyVerificationDone( session->Abort(TransferMetadata::Status::kDeviceAuthenticationFailed); return; } + if (session->is_transfer_session()) { + BeginOutgoingTransfer(*session); + } else { + BeginOutgoingPairing(*session); + } +} +void NearbySharingServiceImpl::BeginOutgoingTransfer( + OutgoingShareSession& session) { VLOG(1) << __func__ << ": Preparing to send introduction to " - << share_target_id; - if (!session->SendIntroduction([this, share_target_id]() { + << session.share_target().id; + if (!session.SendIntroduction([this, share_target_id = + session.share_target().id]() { VLOG(1) << "Outgoing mutual acceptance timed out, closing connection for " << share_target_id; @@ -2548,27 +2581,120 @@ void NearbySharingServiceImpl::OnOutgoingConnectionKeyVerificationDone( })) { LOG(WARNING) << __func__ << ": No payloads tied to transfer, disconnecting."; - session->Abort(TransferMetadata::Status::kMediaUnavailable); + session.Abort(TransferMetadata::Status::kMediaUnavailable); return; } // Auto Accept if key verification is successful or skip sender confirmation. bool protection_enabled = preference_manager_.GetBoolean(PrefNames::kAdvancedProtectionEnabled, /*default_value=*/false); - session->SetAdvancedProtectionStatus(protection_enabled, - /*advanced_protection_mismatch=*/false); - if (session->token().empty() || !protection_enabled) { + session.SetAdvancedProtectionStatus(protection_enabled, + /*advanced_protection_mismatch=*/false); + if (session.token().empty() || !protection_enabled) { // Auto accept if no token or if advanced protection is disabled. - OutgoingSessionAccept(*session); + OutgoingSessionAccept(session); } else { - session->UpdateTransferMetadata( + session.UpdateTransferMetadata( TransferMetadataBuilder() + .set_usage(session.session_usage()) .set_status(TransferMetadata::Status::kAwaitingLocalConfirmation) - .set_token(session->token()) + .set_token(session.token()) .build()); } } +void NearbySharingServiceImpl::BeginOutgoingPairing( + OutgoingShareSession& session) { + VLOG(1) << __func__ << ": Preparing to initiate pairing with " + << session.share_target().id; + session.set_session_usage(ShareSessionUsage::kPairing); + // Verify that remote really authenticated with self share certificate. + if (!session.self_share()) { + LOG(WARNING) << __func__ << ": Not self share, skipping pairing."; + session.Abort(TransferMetadata::Status::kDeviceAuthenticationFailed); + return; + } + // Call InitiateBinding rpc. + sync_manager_.AsyncInitiateSyncBinding( + [this, share_target_id = session.share_target().id]( + absl::StatusOr binding_status) { + LOG(INFO) << __func__ << ": Sync binding rpc completed."; + OnInitiateSyncBindingResponse(share_target_id, + std::move(binding_status)); + }); +} + +void NearbySharingServiceImpl::OnInitiateSyncBindingResponse( + int64_t share_target_id, absl::StatusOr binding_status) { + RunOnNearbySharingServiceThread( + "start_peer_binding", + [this, share_target_id, binding_status = std::move(binding_status)]() { + OutgoingShareSession* session = + outgoing_targets_manager_.GetOutgoingShareSession(share_target_id); + if (!session || !session->IsConnected()) { + LOG(WARNING) << __func__ + << ": Session not connected, stop binding to: " + << share_target_id; + return; + } + if (binding_status.ok()) { + std::string binding_id = binding_status.value(); + LOG(INFO) << __func__ + << ": Sync binding rpc succeeded: id=" << binding_id; + session->StartPeerBinding( + binding_id, BindingRequest::FILESYNC, + [this, share_target_id, + binding_id](BindingResponse::Status status) { + OnPeerSyncBindingComplete(share_target_id, binding_id, status); + }); + } else { + LOG(INFO) << __func__ << ": Sync binding rpc failed."; + session->Abort(TransferMetadata::Status::kFailed); + } + }); +} + +void NearbySharingServiceImpl::OnPeerSyncBindingComplete( + int64_t share_target_id, absl::string_view binding_id, + BindingResponse::Status status) { + OutgoingShareSession* session = + outgoing_targets_manager_.GetOutgoingShareSession(share_target_id); + if (!session || !session->IsConnected()) { + LOG(WARNING) << __func__ << ": Session not connected, stop binding to: " + << share_target_id; + return; + } + if (status != BindingResponse::SUCCESS) { + LOG(INFO) << __func__ << ": Sync binding response failed."; + session->Abort(TransferMetadata::Status::kFailed); + return; + } + LOG(INFO) << __func__ << ": Sync binding response succeeded, disconnecting."; + // Binding receiver side will wait for connection disconnect after sending the + // BindingResponse message. + session->Disconnect(); + + sync::SyncBinding binding; + binding.set_binding_id(binding_id); + binding.set_source_name(session->share_target().device_name); + // Set default destination directory to Downloads/`device_name`. + FilePath destination_path{settings_->GetCustomSavePath()}; + destination_path.append(FilePath(session->share_target().device_name)); + binding.set_destination_directory(destination_path.ToString()); + binding.set_source_device_type( + ShareTargetTypeToSourceDeviceType(session->share_target().type)); + sync_manager_.AddSyncBinding(binding); + session->UpdateTransferMetadata( + TransferMetadataBuilder() + .set_usage(session->session_usage()) + .set_binding_id(binding_id) + .set_status(TransferMetadata::Status::kComplete) + .build()); + + // Download public certificates again to update the newly added sync binding. + certificate_manager_->DownloadPublicCertificates(); +} + void NearbySharingServiceImpl::OnReceivedIntroduction( IncomingShareSession& session, const IntroductionFrame& frame) { LOG(INFO) << __func__ << ": Successfully read the introduction frame."; @@ -2580,6 +2706,28 @@ void NearbySharingServiceImpl::OnReceivedIntroduction( return; } FilePath save_path{settings_->GetCustomSavePath()}; + // If transfer is for file sync, override the save path to the custom save + // path. + if (frame.use_case() == IntroductionFrame::FILE_SYNC) { + if (!session.certificate().has_value() || + session.certificate()->binding_id().empty()) { + LOG(ERROR) << __func__ + << ": Binding id is empty for file sync session."; + Fail(session, TransferMetadata::Status::kRejected); + return; + } + std::optional binding = + sync_manager_.GetSyncBinding(session.certificate()->binding_id()); + if (!binding.has_value()) { + LOG(ERROR) << __func__ + << ": Sync binding not found for binding id: " + << session.certificate()->binding_id(); + Fail(session, TransferMetadata::Status::kRejected); + return; + } + save_path = FilePath(binding->destination_directory()); + session.set_session_usage(ShareSessionUsage::kFileSync); + } // Override save path for this connection. // This must be called before the transfer is accepted and payloads are being // received. @@ -2591,20 +2739,22 @@ void NearbySharingServiceImpl::OnReceivedIntroduction( session.session_id(), session.share_target(), /*referrer_package=*/std::nullopt, session.os_type()); - if (IsOutOfStorage(device_info_, save_path, - session.attachment_container().GetStorageSize())) { + std::optional available_storage = + device_info_.GetAvailableDiskSpaceInBytes(save_path); + if (available_storage.has_value() && + *available_storage <= session.attachment_container().GetStorageSize()) { Fail(session, TransferMetadata::Status::kNotEnoughSpace); LOG(WARNING) << __func__ << ": Not enough space on the receiver. We have informed " << session.share_target().id; return; } - OnStorageCheckCompleted(session); } void NearbySharingServiceImpl::OnReceiveConnectionResponse( - int64_t share_target_id, std::optional frame) { + int64_t share_target_id, bool is_timeout, + std::optional frame) { OutgoingShareSession* session = outgoing_targets_manager_.GetOutgoingShareSession(share_target_id); if (!session || !session->IsConnected()) { @@ -2615,7 +2765,7 @@ void NearbySharingServiceImpl::OnReceiveConnectionResponse( } std::optional status = - session->HandleConnectionResponse(std::move(frame)); + session->HandleConnectionResponse(is_timeout, std::move(frame)); if (status.has_value()) { session->Abort(*status); return; @@ -2799,6 +2949,7 @@ void NearbySharingServiceImpl::OnIncomingFilesMetadataUpdated( int64_t share_target_id, TransferMetadata metadata, bool success) { if (!success) { metadata = TransferMetadataBuilder() + .set_usage(metadata.usage()) .set_status(TransferMetadata::Status::kIncompletePayloads) .build(); } @@ -3178,4 +3329,17 @@ void NearbySharingServiceImpl::UpdateFilePathsInProgress( << ": Update file paths in progress: " << update_file_paths; } +void NearbySharingServiceImpl::UpdateBackupSavePath( + absl::string_view binding_id, absl::string_view save_path, + absl::AnyInvocable + status_codes_callback) { + absl::StatusOr original_path = + sync_manager_.UpdateSyncBindingDestinationDirectory(binding_id, + FilePath(save_path)); + // TODO: b/485307320 - If original destination directory exists, move + // contents to the new destination directory. + status_codes_callback( + original_path.ok() ? StatusCodes::kOk : StatusCodes::kError); +} + } // namespace nearby::sharing diff --git a/sharing/nearby_sharing_service_impl.h b/sharing/nearby_sharing_service_impl.h index b737a8b3..b4d763be 100644 --- a/sharing/nearby_sharing_service_impl.h +++ b/sharing/nearby_sharing_service_impl.h @@ -27,16 +27,19 @@ #include #include +#include "location/nearby/sharing/lib/account/account_manager.h" +#include "location/nearby/sharing/lib/rpc/sharing_rpc_client.h" +#include "location/nearby/sharing/lib/sync/sync_manager.h" #include "absl/base/nullability.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/functional/any_invocable.h" +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "absl/types/span.h" #include "internal/platform/clock.h" -#include "internal/platform/device_info.h" -#include "internal/platform/implementation/account_manager.h" +#include "internal/platform/implementation/device_info.h" #include "internal/platform/task_runner.h" #include "proto/sharing_enums.pb.h" #include "sharing/advertisement.h" @@ -74,16 +77,8 @@ #include "sharing/transfer_metadata.h" #include "sharing/transfer_update_callback.h" #include "sharing/wrapped_share_target_discovered_callback.h" -#if defined(__linux__) -#include "sharing/linux/stubs/sharing_rpc_client.h" -#include "sharing/linux/stubs/sync_manager.h" -#else -#include "location/nearby/sharing/lib/rpc/sharing_rpc_client.h" -#include "location/nearby/sharing/lib/sync/sync_manager.h" -#endif namespace nearby::sharing { -class NearbyShareContactManager; namespace NearbySharingServiceUnitTests { class NearbySharingServiceImplTest_CreateShareTarget_Test; @@ -95,7 +90,7 @@ class NearbySharingServiceImpl : public NearbySharingService, public NearbyShareSettings::Observer, public NearbyShareCertificateManager::Observer, - public ::nearby::AccountManager::Observer, + public AccountManager::Observer, public NearbyFastInitiation::Observer, public sharing::api::BluetoothAdapter::Observer, public NearbyConnectionsManager::IncomingConnectionListener, @@ -111,9 +106,7 @@ class NearbySharingServiceImpl nearby::sharing::api::SharingPlatform& sharing_platform, nearby::sharing::api::IdentityRpcClient* absl_nonnull nearby_identity_client, - nearby::sharing::api::SharingRpcClient* absl_nonnull nearby_share_client, std::unique_ptr nearby_connections_manager, - std::unique_ptr contact_manager, analytics::AnalyticsRecorder* analytics_recorder, bool supports_file_sync); ~NearbySharingServiceImpl() override; @@ -128,19 +121,19 @@ class NearbySharingServiceImpl ShareTargetDiscoveredCallback* discovery_callback, SendSurfaceState state, Advertisement::BlockedVendorId blocked_vendor_id, bool disable_wifi_hotspot, - std::function status_codes_callback) override; + absl::AnyInvocable status_codes_callback) override; void UnregisterSendSurface( TransferUpdateCallback* transfer_callback, - std::function status_codes_callback) override; + absl::AnyInvocable status_codes_callback) override; void RegisterReceiveSurface( TransferUpdateCallback* transfer_callback, ReceiveSurfaceState state, Advertisement::BlockedVendorId vendor_id, - std::function status_codes_callback) override; + absl::AnyInvocable status_codes_callback) override; void UnregisterReceiveSurface( TransferUpdateCallback* transfer_callback, - std::function status_codes_callback) override; + absl::AnyInvocable status_codes_callback) override; void ClearForegroundReceiveSurfaces( - std::function status_codes_callback) override; + absl::AnyInvocable status_codes_callback) override; bool IsTransferring() const override; bool IsScanning() const override; bool IsBluetoothPresent() const override; @@ -161,12 +154,14 @@ class NearbySharingServiceImpl void Cancel(int64_t share_target_id, std::function status_codes_callback) override; + void InitiatePairing(int64_t share_target_id, + service::proto::BindingRequest::Type binding_type, + absl::AnyInvocable + status_codes_callback) override; void SetVisibility( proto::DeviceVisibility visibility, absl::Duration expiration, absl::AnyInvocable callback) override; NearbyShareSettings* GetSettings() override; - NearbyShareLocalDeviceDataManager* GetLocalDeviceDataManager() override; - NearbyShareContactManager* GetContactManager() override; NearbyShareCertificateManager* GetCertificateManager() override; AccountManager* GetAccountManager() override; Clock& GetClock() override { return *context_->GetClock(); } @@ -174,6 +169,13 @@ class NearbySharingServiceImpl uint16_t alternate_service_uuid) override { alternate_service_uuid_ = alternate_service_uuid; } + SyncManager& sync_manager() override { return sync_manager_; } + OutgoingTargetsManager& outgoing_targets_manager() override { + return outgoing_targets_manager_; + } + void UpdateBackupSavePath( + absl::string_view binding_id, absl::string_view save_path, + absl::AnyInvocable status_codes_callback) override; // NearbyConnectionsManager::IncomingConnectionListener: void OnIncomingConnection(absl::string_view endpoint_id, @@ -296,8 +298,8 @@ class NearbySharingServiceImpl absl::string_view endpoint_id, NearbyConnection* connection, Status status); - void OutgoingSessionConnect(OutgoingShareSession& session, - std::vector endpoint_info); + StatusCodes ConnectOutgoingSessionOnServiceThread( + OutgoingShareSession& session); void Fail(IncomingShareSession& session, TransferMetadata::Status status); void OnIncomingAdvertisementDecoded( @@ -320,6 +322,8 @@ class NearbySharingServiceImpl int64_t share_target_id, PairedKeyVerificationRunner::PairedKeyVerificationResult result, ::location::nearby::proto::sharing::OSType share_target_os_type); + void BeginOutgoingTransfer(OutgoingShareSession& session); + void BeginOutgoingPairing(OutgoingShareSession& session); void OnIncomingSessionFrameRead( int64_t share_target_id, bool is_timeout, @@ -328,7 +332,7 @@ class NearbySharingServiceImpl IncomingShareSession& session, const nearby::sharing::service::proto::IntroductionFrame& frame); void OnReceiveConnectionResponse( - int64_t share_target_id, + int64_t share_target_id, bool is_timeout, std::optional frame); void OnStorageCheckCompleted(IncomingShareSession& session); @@ -402,6 +406,13 @@ class NearbySharingServiceImpl bool OutgoingSessionAccept(OutgoingShareSession& session); void OnIncomingFilesMetadataUpdated(int64_t share_target_id, TransferMetadata metadata, bool success); + // Called when InitiateBinding rpc returns. + void OnInitiateSyncBindingResponse( + int64_t share_target_id, absl::StatusOr binding_status); + // Called when Bindings response frame is received from the peer. + void OnPeerSyncBindingComplete( + int64_t share_target_id, absl::string_view binding_id, + service::proto::BindingResponse::Status status); // Notify all registered send surfaces of share target state changes. void NotifyShareTargetDiscovered(const ShareTarget& share_target); @@ -414,7 +425,7 @@ class NearbySharingServiceImpl // Used to run nearby sharing service APIs. std::unique_ptr service_thread_; Context* const context_; - nearby::DeviceInfo& device_info_; + nearby::api::DeviceInfo& device_info_; nearby::sharing::api::PreferenceManager& preference_manager_; AccountManager& account_manager_; // Used to create analytics events. @@ -423,10 +434,9 @@ class NearbySharingServiceImpl const bool supports_file_sync_; std::unique_ptr nearby_connections_manager_; - nearby::sharing::api::SharingRpcClient* absl_nonnull const - nearby_share_client_; + nearby::sharing::api::IdentityRpcClient* absl_nonnull const + nearby_identity_client_; std::unique_ptr local_device_data_manager_; - std::unique_ptr contact_manager_; std::unique_ptr certificate_manager_; std::unique_ptr nearby_fast_initiation_; diff --git a/sharing/nearby_sharing_service_impl_test.cc b/sharing/nearby_sharing_service_impl_test.cc index a009f000..6814ec81 100644 --- a/sharing/nearby_sharing_service_impl_test.cc +++ b/sharing/nearby_sharing_service_impl_test.cc @@ -30,6 +30,11 @@ #include #include +#include "location/nearby/analytics/cpp/logging/mock_event_logger.h" +#include "location/nearby/sharing/lib/account/fake_account_manager.h" +#include "location/nearby/sharing/lib/account/mock_account_observer.h" +#include "location/nearby/sharing/lib/account/signin_attempt.h" +#include "location/nearby/sharing/lib/analytics/analytics_recorder_impl.h" #include "location/nearby/sharing/lib/rpc/fake_nearby_share_client.h" #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" @@ -44,18 +49,13 @@ #include "absl/time/clock.h" #include "absl/time/time.h" #include "absl/types/span.h" -#include "internal/analytics/mock_event_logger.h" #include "internal/base/file_path.h" #include "internal/base/files.h" #include "internal/flags/nearby_flags.h" -#include "internal/platform/implementation/signin_attempt.h" -#include "internal/test/fake_account_manager.h" #include "internal/test/fake_device_info.h" #include "internal/test/fake_task_runner.h" -#include "internal/test/mock_account_observer.h" #include "sharing/advertisement.h" #include "sharing/advertisement_capabilities.h" -#include "sharing/analytics/analytics_recorder.h" #include "sharing/attachment_container.h" #include "sharing/certificates/fake_nearby_share_certificate_manager.h" #include "sharing/certificates/nearby_share_certificate_manager_impl.h" @@ -64,7 +64,6 @@ #include "sharing/common/nearby_share_enums.h" #include "sharing/common/nearby_share_prefs.h" #include "sharing/constants.h" -#include "sharing/contacts/fake_nearby_share_contact_manager.h" #include "sharing/fake_nearby_connections_manager.h" #include "sharing/fast_initiation/fake_nearby_fast_initiation.h" #include "sharing/fast_initiation/nearby_fast_initiation_impl.h" @@ -89,6 +88,7 @@ #include "sharing/proto/enums.pb.h" #include "sharing/proto/rpc_resources.pb.h" #include "sharing/proto/wire_format.pb.h" +#include "sharing/share_session_usage.h" #include "sharing/share_target.h" #include "sharing/share_target_discovered_callback.h" #include "sharing/text_attachment.h" @@ -115,6 +115,7 @@ using ::nearby::sharing::service::proto::PairedKeyResultFrame; using ::nearby::sharing::service::proto::TextMetadata; using ::nearby::sharing::service::proto::V1Frame; using ::testing::_; +using ::protobuf_matchers::EqualsProto; using ::testing::InSequence; using ::testing::NiceMock; using ::testing::Return; @@ -273,11 +274,13 @@ std::unique_ptr GetTextPayload(int64_t payload_id, std::vector(text.begin(), text.end())); } -std::unique_ptr GetValidIntroductionFrame() { +std::unique_ptr GetValidIntroductionFrame( + IntroductionFrame::SharingUseCase use_case) { IntroductionFrame* introduction_frame = IntroductionFrame::default_instance().New(); auto text_metadatas = introduction_frame->mutable_text_metadata(); introduction_frame->set_start_transfer(true); + introduction_frame->set_use_case(use_case); for (int i = 1; i <= 3; ++i) { nearby::sharing::service::proto::TextMetadata* text_metadata = @@ -429,7 +432,6 @@ class NearbySharingServiceImplTest : public testing::Test { auto fake_task_runner = std::make_unique(fake_context_.fake_clock(), 1); sharing_service_task_runner_ = fake_task_runner.get(); - contact_manager_ = new FakeNearbyShareContactManager(); fake_nearby_connections_manager_ = new FakeNearbyConnectionsManager(); connection_ = std::make_unique(fake_device_info_); fake_nearby_connections_manager_->set_send_payload_callback( @@ -453,7 +455,7 @@ class NearbySharingServiceImplTest : public testing::Test { SetBluetoothIsPowered(true); SetScreenLocked(false); SetLanConnected(true); - analytics_recorder_ = std::make_unique( + analytics_recorder_ = std::make_unique( /*vendor_id=*/0, /*event_logger=*/nullptr); service_ = CreateService(std::move(fake_task_runner)); @@ -484,9 +486,9 @@ class NearbySharingServiceImplTest : public testing::Test { std::unique_ptr task_runner) { return std::make_unique( std::move(task_runner), &fake_context_, mock_sharing_platform_, - &nearby_identity_client_, &nearby_share_client_, + &nearby_identity_client_, absl::WrapUnique(fake_nearby_connections_manager_), - absl::WrapUnique(contact_manager_), analytics_recorder_.get(), + analytics_recorder_.get(), /*supports_file_sync=*/false); } @@ -666,10 +668,9 @@ class NearbySharingServiceImplTest : public testing::Test { EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising()); } - void ProcessLatestPublicCertificateDecryption(size_t expected_num_calls, - bool success, - bool for_self_share = false, - uint8_t vendor_id = 0) { + void ProcessLatestPublicCertificateDecryption( + size_t expected_num_calls, bool success, bool for_self_share = false, + uint8_t vendor_id = 0, absl::string_view binding_id = "") { // Ensure that all pending mojo messages are processed and the certificate // manager state is as expected up to this point. std::vector< @@ -689,6 +690,9 @@ class NearbySharingServiceImplTest : public testing::Test { DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, GetNearbyShareTestNotBefore(), vendor_id); cert.set_for_self_share(for_self_share); + if (!binding_id.empty()) { + cert.set_binding_id(binding_id); + } std::move(calls.back().callback)( NearbyShareDecryptedPublicCertificate::DecryptPublicCertificate( cert, GetNearbyShareTestEncryptedMetadataKey())); @@ -745,13 +749,17 @@ class NearbySharingServiceImplTest : public testing::Test { return advertisement->ToEndpointInfo(); } - void SetUpIntroductionFrameDecoder(bool return_empty_introduction_frame) { - std::unique_ptr frame; - if (return_empty_introduction_frame) { - frame = GetEmptyIntroductionFrame(); - } else { - frame = GetValidIntroductionFrame(); - } + void SetUpEmptyIntroductionFrameDecoder() { + std::unique_ptr frame = GetEmptyIntroductionFrame(); + std::vector bytes(frame->ByteSizeLong()); + frame->SerializeToArray(bytes.data(), bytes.size()); + ReceiveMessageFromConnection(std::move(bytes)); + } + + void SetUpIntroductionFrameDecoder( + IntroductionFrame::SharingUseCase use_case = + IntroductionFrame::NEARBY_SHARE) { + std::unique_ptr frame = GetValidIntroductionFrame(use_case); std::vector bytes(frame->ByteSizeLong()); frame->SerializeToArray(bytes.data(), bytes.size()); ReceiveMessageFromConnection(std::move(bytes)); @@ -776,7 +784,7 @@ class NearbySharingServiceImplTest : public testing::Test { bool for_self_share = false) { fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, GetToken()); - SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false); + SetUpIntroductionFrameDecoder(); int64_t share_target_id; SetLanConnected(true); @@ -842,19 +850,22 @@ class NearbySharingServiceImplTest : public testing::Test { int64_t SetUpOutgoingShareTarget( MockTransferUpdateCallback& transfer_callback, - MockShareTargetDiscoveredCallback& discovery_callback) { + MockShareTargetDiscoveredCallback& discovery_callback, + bool for_self_share = false) { SetUpKeyVerification( /*is_incoming=*/false, PairedKeyResultFrame::SUCCESS); fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, GetToken()); fake_nearby_connections_manager_->set_nearby_connection(connection_.get()); - return DiscoverShareTarget(transfer_callback, discovery_callback); + return DiscoverShareTarget(transfer_callback, discovery_callback, + for_self_share); } int64_t DiscoverShareTarget( MockTransferUpdateCallback& transfer_callback, - MockShareTargetDiscoveredCallback& discovery_callback) { + MockShareTargetDiscoveredCallback& discovery_callback, + bool for_self_share = false) { SetLanConnected(true); // Start discovering, to ensure a discovery listener is registered. @@ -876,7 +887,7 @@ class NearbySharingServiceImplTest : public testing::Test { std::move(endpoint_info)); FlushTesting(); ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1, - /*success=*/true); + /*success=*/true, for_self_share); return discovered_target_id; } @@ -1262,13 +1273,12 @@ class NearbySharingServiceImplTest : public testing::Test { FakeNearbyConnectionsManager* fake_nearby_connections_manager_ = nullptr; FakeNearbyShareLocalDeviceDataManager::Factory local_device_data_manager_factory_; - FakeNearbyShareContactManager* contact_manager_ = nullptr; FakeNearbyShareCertificateManager::Factory certificate_manager_factory_; std::unique_ptr nearby_fast_initiation_factory_; std::unique_ptr connection_; StrictMock* mock_app_info_ = nullptr; - std::unique_ptr analytics_recorder_; + std::unique_ptr analytics_recorder_; std::unique_ptr service_; int expect_transfer_updates_count_ = 0; std::function expect_transfer_updates_callback_; @@ -1280,7 +1290,6 @@ class NearbySharingServiceImplTest : public testing::Test { std::queue written_payloads_ ABSL_GUARDED_BY(connection_output_mutex_); FakeNearbyIdentityClient nearby_identity_client_; - FakeNearbyShareClient nearby_share_client_; }; struct ValidSendSurfaceTestData { @@ -1705,7 +1714,7 @@ TEST_F(NearbySharingServiceImplTest, ForegroundRegisterReceiveSurfaceIsAdvertisingAllContacts) { SetLanConnected(true); SetVisibility(DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS); - ::nearby::AccountManager::Account account; + AccountManager::Account account; account.id = kTestAccountId; account_manager().SetAccount(account); local_device_data_manager()->SetDeviceName(kDeviceName); @@ -1759,7 +1768,7 @@ TEST_F(NearbySharingServiceImplTest, BackgroundRegisterReceiveSurfaceIsAdvertisingSelectedContacts) { SetLanConnected(true); SetVisibility(DeviceVisibility::DEVICE_VISIBILITY_SELECTED_CONTACTS); - ::nearby::AccountManager::Account account; + AccountManager::Account account; account.id = kTestAccountId; account_manager().SetAccount(account); SetVisibility(DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS); @@ -2375,7 +2384,7 @@ TEST_F(NearbySharingServiceImplTest, TEST_F(NearbySharingServiceImplTest, IncomingConnectionEmptyIntroductionFrame) { fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, GetToken()); - SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/true); + SetUpEmptyIntroductionFrameDecoder(); SetLanConnected(true); NiceMock callback; @@ -2414,7 +2423,7 @@ TEST_F(NearbySharingServiceImplTest, IncomingConnectionValidIntroductionFrameInvalidCertificate) { fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, GetToken()); - SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false); + SetUpIntroductionFrameDecoder(); SetLanConnected(true); NiceMock callback; @@ -2464,6 +2473,7 @@ TEST_F(NearbySharingServiceImplTest, IncomingConnectionTimedOut) { const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_TRUE(metadata.is_final_status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kTimedOut); }); @@ -2487,6 +2497,7 @@ TEST_F(NearbySharingServiceImplTest, const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_TRUE(metadata.is_final_status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kFailed); }); @@ -2614,7 +2625,7 @@ TEST_F(NearbySharingServiceImplTest, IncomingConnectionValidIntroductionFrameValidCertificate) { fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, GetToken()); - SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false); + SetUpIntroductionFrameDecoder(); SetLanConnected(true); NiceMock callback; @@ -2655,6 +2666,128 @@ TEST_F(NearbySharingServiceImplTest, .has_value()); } +TEST_F(NearbySharingServiceImplTest, + IncomingConnectionValidIntroductionFrameValidCertificateFileSync) { + fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, + GetToken()); + SetUpIntroductionFrameDecoder(IntroductionFrame::FILE_SYNC); + + constexpr absl::string_view kBindingId = "binding_id"; + sync::SyncBinding binding; + binding.set_binding_id(kBindingId); + binding.set_source_name(kDeviceName); + binding.set_destination_directory( + FilePath("Downloads").append(FilePath(kDeviceName)).ToString()); + binding.set_source_device_type(sync::SyncBinding::SOURCE_DEVICE_TYPE_PHONE); + service_->sync_manager().AddSyncBinding(binding); + + SetLanConnected(true); + NiceMock callback; + absl::Notification notification; + EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_, testing::_)) + .WillOnce([¬ification](const ShareTarget& share_target, + const AttachmentContainer& container, + TransferMetadata metadata) { + EXPECT_FALSE(metadata.is_final_status()); + EXPECT_EQ(TransferMetadata::Status::kAwaitingLocalConfirmation, + metadata.status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kFileSync); + EXPECT_TRUE(share_target.is_incoming); + EXPECT_TRUE(share_target.is_known); + EXPECT_TRUE(container.HasAttachments()); + EXPECT_EQ(container.GetTextAttachments().size(), 3u); + EXPECT_EQ(container.GetFileAttachments().size(), 1u); + EXPECT_EQ(share_target.device_name, kDeviceName); + EXPECT_EQ(share_target.type, kDeviceType); + EXPECT_NE(share_target.device_id, kEndpointId); + EXPECT_EQ(share_target.full_name, kTestMetadataFullName); + EXPECT_FALSE(share_target.for_self_share); + EXPECT_FALSE(metadata.is_self_share()); + EXPECT_TRUE(metadata.token().has_value()); + notification.Notify(); + }); + + SetUpKeyVerification(/*is_incoming=*/true, PairedKeyResultFrame::SUCCESS); + SetUpForegroundReceiveSurface(callback); + ScopedReceiveSurface r(service_.get(), &callback); + EXPECT_CALL(*mock_app_info_, SetActiveFlag()); + StartIncomingConnection(); + ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1, + /*success=*/true, + /*for_self_share=*/false, + /*vendor_id=*/0, kBindingId); + EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); + + EXPECT_TRUE( + fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId) + .has_value()); + ASSERT_TRUE(fake_nearby_connections_manager_->custom_save_path(kEndpointId) + .has_value()); + EXPECT_EQ(fake_nearby_connections_manager_->custom_save_path(kEndpointId) + ->ToString(), + binding.destination_directory()); +} + +TEST_F(NearbySharingServiceImplTest, + IncomingIntroductionFrameCertificateEmptyBindingId) { + fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, + GetToken()); + SetUpIntroductionFrameDecoder(IntroductionFrame::FILE_SYNC); + + SetLanConnected(true); + NiceMock callback; + absl::Notification notification; + EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_, testing::_)) + .WillOnce([¬ification](const ShareTarget& share_target, + const AttachmentContainer& container, + TransferMetadata metadata) { + EXPECT_EQ(TransferMetadata::Status::kRejected, metadata.status()); + notification.Notify(); + }); + + SetUpKeyVerification(/*is_incoming=*/true, PairedKeyResultFrame::SUCCESS); + SetUpForegroundReceiveSurface(callback); + ScopedReceiveSurface r(service_.get(), &callback); + EXPECT_CALL(*mock_app_info_, SetActiveFlag()); + StartIncomingConnection(); + ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1, + /*success=*/true, + /*for_self_share=*/false, + /*vendor_id=*/0, /*binding_id=*/""); + EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); +} + +TEST_F(NearbySharingServiceImplTest, + IncomingIntroductionFrameFileSyncBindingNotFound) { + fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, + GetToken()); + SetUpIntroductionFrameDecoder(IntroductionFrame::FILE_SYNC); + + constexpr absl::string_view kBindingId = "binding_id"; + + SetLanConnected(true); + NiceMock callback; + absl::Notification notification; + EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_, testing::_)) + .WillOnce([¬ification](const ShareTarget& share_target, + const AttachmentContainer& container, + TransferMetadata metadata) { + EXPECT_EQ(TransferMetadata::Status::kRejected, metadata.status()); + notification.Notify(); + }); + + SetUpKeyVerification(/*is_incoming=*/true, PairedKeyResultFrame::SUCCESS); + SetUpForegroundReceiveSurface(callback); + ScopedReceiveSurface r(service_.get(), &callback); + EXPECT_CALL(*mock_app_info_, SetActiveFlag()); + StartIncomingConnection(); + ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1, + /*success=*/true, + /*for_self_share=*/false, + /*vendor_id=*/0, kBindingId); + EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); +} + TEST_F(NearbySharingServiceImplTest, AcceptInvalidShareTarget) { absl::Notification notification; service_->Accept( @@ -2711,6 +2844,7 @@ TEST_F(NearbySharingServiceImplTest, const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_FALSE(metadata.is_final_status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kInProgress); progress_notification.Notify(); }); @@ -2785,6 +2919,7 @@ TEST_F(NearbySharingServiceImplTest, AcceptValidShareTargetPayloadFailed) { const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_TRUE(metadata.is_final_status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kFailed); ASSERT_TRUE(container.HasAttachments()); EXPECT_EQ(container.GetFileAttachments().size(), 1u); @@ -2831,6 +2966,7 @@ TEST_F(NearbySharingServiceImplTest, AcceptValidShareTargetPayloadCancelled) { const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_TRUE(metadata.is_final_status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kCancelled); ASSERT_TRUE(container.HasAttachments()); EXPECT_EQ(container.GetFileAttachments().size(), 1u); @@ -2883,6 +3019,7 @@ TEST_F(NearbySharingServiceImplTest, RejectValidShareTarget) { const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_TRUE(metadata.is_final_status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kRejected); }); @@ -2909,7 +3046,7 @@ TEST_F(NearbySharingServiceImplTest, IncomingConnectionKeyVerificationRunnerStatusUnable) { fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, GetToken()); - SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false); + SetUpIntroductionFrameDecoder(); SetLanConnected(true); NiceMock callback; @@ -2952,7 +3089,7 @@ TEST_F(NearbySharingServiceImplTest, IncomingConnectionKeyVerificationRunnerStatusUnableLowPower) { fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, GetToken()); - SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false); + SetUpIntroductionFrameDecoder(); SetLanConnected(true); NiceMock callback; @@ -3654,6 +3791,7 @@ TEST_F(NearbySharingServiceImplTest, CancelReceiverInitiator) { const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_EQ(share_target.id, target_id); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kCancelled); }); EXPECT_FALSE( @@ -3702,6 +3840,7 @@ TEST_F(NearbySharingServiceImplTest, CancelReceiverNoninitiator) { const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_EQ(target_id, share_target.id); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(TransferMetadata::Status::kCancelled, metadata.status()); notification.Notify(); }); @@ -4661,8 +4800,7 @@ TEST_F(NearbySharingServiceImplTest, LoginAndLogoutShouldResetSettings) { ASSERT_TRUE(service_->GetAccountManager()->GetCurrentAccount().has_value()); EXPECT_EQ(service_->GetAccountManager()->GetCurrentAccount()->id, kTestAccountId); - device_id = - preference_manager_.GetString(PrefNames::kDeviceId, ""); + device_id = preference_manager_.GetString(PrefNames::kDeviceId, ""); EXPECT_FALSE(device_id.empty()); EXPECT_EQ(device_id.size(), 10u); for (const char c : device_id) EXPECT_TRUE(std::isalnum(c)); @@ -4679,8 +4817,7 @@ TEST_F(NearbySharingServiceImplTest, LoginAndLogoutShouldResetSettings) { EXPECT_TRUE(service_->GetSettings()->GetIsAnalyticsEnabled()); EXPECT_FALSE(service_->GetAccountManager()->GetCurrentAccount().has_value()); EXPECT_TRUE(sharing_service_task_runner_->SyncWithTimeout(kTaskWaitTimeout)); - device_id = - preference_manager_.GetString(PrefNames::kDeviceId, ""); + device_id = preference_manager_.GetString(PrefNames::kDeviceId, ""); EXPECT_TRUE(device_id.empty()); } @@ -4852,8 +4989,8 @@ TEST_F(NearbySharingServiceImplTest, RemoveIncomingPayloads) { unknown_file_paths_to_delete, UnorderedElementsAre(FilePath("test1.txt"), FilePath("test2.txt"))); nearby::analytics::MockEventLogger mock_event_logger; - analytics::AnalyticsRecorder analytics_recorder{/*vendor_id=*/0, - &mock_event_logger}; + analytics::AnalyticsRecorderImpl analytics_recorder{/*vendor_id=*/0, + &mock_event_logger}; ShareTarget share_target; share_target.is_incoming = true; IncomingShareSession session( @@ -4883,7 +5020,7 @@ TEST_F(NearbySharingServiceImplTest, RemoveIncomingPayloads) { TEST_F(NearbySharingServiceImplTest, NotifyLogoutSucceededWithCredentialError) { TestObserver observer(service_.get()); - ::nearby::AccountManager::Account account; + AccountManager::Account account; account.id = kTestAccountId; account_manager().SetAccount(account); @@ -4897,5 +5034,309 @@ TEST_F(NearbySharingServiceImplTest, NotifyLogoutSucceededWithCredentialError) { FlushTesting(); } +TEST_F(NearbySharingServiceImplTest, InitiatePairingNotSelfShare) { + MockTransferUpdateCallback transfer_callback; + MockShareTargetDiscoveredCallback discovery_callback; + int64_t target_id = SetUpOutgoingShareTarget( + transfer_callback, discovery_callback, /*for_self_share=*/false); + ScopedSendSurface s(service_.get(), &transfer_callback); + + absl::Notification notification; + ExpectTransferUpdates(transfer_callback, target_id, + {TransferMetadata::Status::kConnecting, + TransferMetadata::Status::kDeviceAuthenticationFailed}, + [&] { notification.Notify(); }); + + absl::Notification pairing_notification; + NearbySharingServiceImpl::StatusCodes pairing_result; + EXPECT_CALL(*mock_app_info_, SetActiveFlag()); + service_->InitiatePairing( + target_id, service::proto::BindingRequest::FILESYNC, + [&](NearbySharingServiceImpl::StatusCodes status_code) { + pairing_result = status_code; + pairing_notification.Notify(); + }); + EXPECT_TRUE( + pairing_notification.WaitForNotificationWithTimeout(kTaskWaitTimeout)); + EXPECT_EQ(pairing_result, NearbySharingServiceImpl::StatusCodes::kOk); + + FlushTesting(); + // Verify data sent to the remote device so far. + EXPECT_TRUE(ExpectPairedKeyEncryptionFrame()); + EXPECT_TRUE(ExpectPairedKeyResultFrame()); + // Wait for the transfer updates. + EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); +} + +TEST_F(NearbySharingServiceImplTest, InitiatePairingBindingRpcFailed) { + MockTransferUpdateCallback transfer_callback; + MockShareTargetDiscoveredCallback discovery_callback; + int64_t target_id = SetUpOutgoingShareTarget( + transfer_callback, discovery_callback, /*for_self_share=*/true); + ScopedSendSurface s(service_.get(), &transfer_callback); + + absl::Notification notification; + ExpectTransferUpdates(transfer_callback, target_id, + {TransferMetadata::Status::kConnecting, + TransferMetadata::Status::kFailed}, + [&] { notification.Notify(); }); + + absl::Notification pairing_notification; + NearbySharingServiceImpl::StatusCodes pairing_result; + EXPECT_CALL(*mock_app_info_, SetActiveFlag()); + nearby_identity_client_.SetInitiateBindingResponses( + {absl::InternalError("Binding RPC failed")}); + service_->InitiatePairing( + target_id, service::proto::BindingRequest::FILESYNC, + [&](NearbySharingServiceImpl::StatusCodes status_code) { + pairing_result = status_code; + pairing_notification.Notify(); + }); + EXPECT_TRUE( + pairing_notification.WaitForNotificationWithTimeout(kTaskWaitTimeout)); + EXPECT_EQ(pairing_result, NearbySharingServiceImpl::StatusCodes::kOk); + + FlushTesting(); + // Verify data sent to the remote device so far. + EXPECT_TRUE(ExpectPairedKeyEncryptionFrame()); + EXPECT_TRUE(ExpectPairedKeyResultFrame()); + // Wait for the transfer updates. + EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); +} + +TEST_F(NearbySharingServiceImplTest, + InitiatePairingPeerBindingResponseTimeout) { + MockTransferUpdateCallback transfer_callback; + MockShareTargetDiscoveredCallback discovery_callback; + int64_t target_id = SetUpOutgoingShareTarget( + transfer_callback, discovery_callback, /*for_self_share=*/true); + ScopedSendSurface s(service_.get(), &transfer_callback); + absl::Notification notification; + ExpectTransferUpdates(transfer_callback, target_id, + {TransferMetadata::Status::kConnecting, + TransferMetadata::Status::kAwaitingRemoteAcceptance, + TransferMetadata::Status::kFailed}, + [&] { notification.Notify(); }); + + absl::Notification pairing_notification; + NearbySharingServiceImpl::StatusCodes pairing_result; + EXPECT_CALL(*mock_app_info_, SetActiveFlag()); + constexpr absl::string_view kBindingId = "binding_id"; + google::nearby::identity::v1::InitiateBindingResponse response; + response.set_binding_id(kBindingId); + nearby_identity_client_.SetInitiateBindingResponses({response}); + service_->InitiatePairing( + target_id, service::proto::BindingRequest::FILESYNC, + [&](NearbySharingServiceImpl::StatusCodes status_code) { + pairing_result = status_code; + pairing_notification.Notify(); + }); + EXPECT_TRUE( + pairing_notification.WaitForNotificationWithTimeout(kTaskWaitTimeout)); + EXPECT_EQ(pairing_result, NearbySharingServiceImpl::StatusCodes::kOk); + + FlushTesting(); + // Verify data sent to the remote device so far. + if (!ExpectPairedKeyEncryptionFrame()) { + return; + } + + if (!ExpectPairedKeyResultFrame()) { + return; + } + // Check BindingRequest frame sent to the remote device. + std::unique_ptr frame = GetWrittenFrame(); + ASSERT_TRUE(frame->has_v1()); + EXPECT_EQ(frame->v1().type(), service::proto::V1Frame::BINDINGS); + EXPECT_EQ(frame->v1().bindings().binding_request().binding_id(), kBindingId); + EXPECT_EQ(frame->v1().bindings().binding_request().type(), + service::proto::BindingRequest::FILESYNC); + + // BindingResponse frame timeout. + FastForward(absl::Seconds(60)); + // Wait for the transfer updates. + EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); +} + +TEST_F(NearbySharingServiceImplTest, InitiatePairingSuccess) { + MockTransferUpdateCallback transfer_callback; + MockShareTargetDiscoveredCallback discovery_callback; + int64_t target_id = SetUpOutgoingShareTarget( + transfer_callback, discovery_callback, /*for_self_share=*/true); + ScopedSendSurface s(service_.get(), &transfer_callback); + absl::Notification notification; + ExpectTransferUpdates(transfer_callback, target_id, + {TransferMetadata::Status::kConnecting, + TransferMetadata::Status::kAwaitingRemoteAcceptance, + TransferMetadata::Status::kComplete}, + [&] { notification.Notify(); }); + + absl::Notification pairing_notification; + NearbySharingServiceImpl::StatusCodes pairing_result; + EXPECT_CALL(*mock_app_info_, SetActiveFlag()); + constexpr absl::string_view kBindingId = "binding_id"; + google::nearby::identity::v1::InitiateBindingResponse response; + response.set_binding_id(kBindingId); + nearby_identity_client_.SetInitiateBindingResponses({response}); + service_->InitiatePairing( + target_id, service::proto::BindingRequest::FILESYNC, + [&](NearbySharingServiceImpl::StatusCodes status_code) { + pairing_result = status_code; + pairing_notification.Notify(); + }); + EXPECT_TRUE( + pairing_notification.WaitForNotificationWithTimeout(kTaskWaitTimeout)); + EXPECT_EQ(pairing_result, NearbySharingServiceImpl::StatusCodes::kOk); + + FlushTesting(); + // Verify data sent to the remote device so far. + EXPECT_TRUE(ExpectPairedKeyEncryptionFrame()); + EXPECT_TRUE(ExpectPairedKeyResultFrame()); + + // Check BindingRequest frame sent to the remote device. + std::unique_ptr frame = GetWrittenFrame(); + ASSERT_TRUE(frame->has_v1()); + EXPECT_EQ(frame->v1().type(), service::proto::V1Frame::BINDINGS); + EXPECT_EQ(frame->v1().bindings().binding_request().binding_id(), kBindingId); + EXPECT_EQ(frame->v1().bindings().binding_request().type(), + service::proto::BindingRequest::FILESYNC); + + preference_manager_.SetString(PrefNames::kCustomSavePath, "Downloads"); + Frame binding_response_frame; + binding_response_frame.set_version(Frame::V1); + binding_response_frame.mutable_v1()->set_type( + service::proto::V1Frame::BINDINGS); + binding_response_frame.mutable_v1() + ->mutable_bindings() + ->mutable_binding_response() + ->set_status(service::proto::BindingResponse::SUCCESS); + std::vector result_bytes(binding_response_frame.ByteSizeLong()); + binding_response_frame.SerializeToArray(result_bytes.data(), + result_bytes.size()); + ReceiveMessageFromConnection(std::move(result_bytes)); + + // Verify that connection is closed. + EXPECT_FALSE( + fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId) + .has_value()); + // Once from RegisterSendSurface and once from OnPeerSyncBindingComplete. + EXPECT_EQ(certificate_manager()->num_download_public_certificates_calls(), 2); + + std::optional binding = + preference_manager_.GetSyncBindingValue(); + ASSERT_TRUE(binding.has_value()); + EXPECT_EQ(binding->sync_bindings().size(), 1); + sync::SyncBinding expected_binding; + expected_binding.set_binding_id(kBindingId); + expected_binding.set_source_name(kDeviceName); + expected_binding.set_destination_directory( + FilePath("Downloads").append(FilePath(kDeviceName)).ToString()); + expected_binding.set_source_device_type( + sync::SyncBinding::SOURCE_DEVICE_TYPE_PHONE); + EXPECT_THAT(binding->sync_bindings(0), EqualsProto(expected_binding)); +} + +TEST_F(NearbySharingServiceImplTest, + InitiatePairingSuccessCheckUsageAndBindingId) { + MockTransferUpdateCallback transfer_callback; + MockShareTargetDiscoveredCallback discovery_callback; + int64_t target_id = SetUpOutgoingShareTarget( + transfer_callback, discovery_callback, /*for_self_share=*/true); + ScopedSendSurface s(service_.get(), &transfer_callback); + absl::Notification notification; + + constexpr absl::string_view kBindingId = "binding_id"; + + EXPECT_CALL(transfer_callback, + OnTransferUpdate(testing::_, testing::_, testing::_)) + .WillOnce([&](const ShareTarget& share_target, + const AttachmentContainer& container, + const TransferMetadata& metadata) { + EXPECT_EQ(share_target.id, target_id); + EXPECT_EQ(metadata.status(), TransferMetadata::Status::kConnecting); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kUnknown); + EXPECT_TRUE(metadata.binding_id().empty()); + }) + .WillOnce([&](const ShareTarget& share_target, + const AttachmentContainer& container, + const TransferMetadata& metadata) { + EXPECT_EQ(share_target.id, target_id); + EXPECT_EQ(metadata.status(), + TransferMetadata::Status::kAwaitingRemoteAcceptance); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kPairing); + EXPECT_TRUE(metadata.binding_id().empty()); + }) + .WillOnce([&](const ShareTarget& share_target, + const AttachmentContainer& container, + const TransferMetadata& metadata) { + EXPECT_EQ(share_target.id, target_id); + EXPECT_EQ(metadata.status(), TransferMetadata::Status::kComplete); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kPairing); + EXPECT_EQ(metadata.binding_id(), kBindingId); + notification.Notify(); + }); + + absl::Notification pairing_notification; + NearbySharingServiceImpl::StatusCodes pairing_result; + EXPECT_CALL(*mock_app_info_, SetActiveFlag()); + google::nearby::identity::v1::InitiateBindingResponse response; + response.set_binding_id(kBindingId); + nearby_identity_client_.SetInitiateBindingResponses({response}); + service_->InitiatePairing( + target_id, service::proto::BindingRequest::FILESYNC, + [&](NearbySharingServiceImpl::StatusCodes status_code) { + pairing_result = status_code; + pairing_notification.Notify(); + }); + EXPECT_TRUE( + pairing_notification.WaitForNotificationWithTimeout(kTaskWaitTimeout)); + EXPECT_EQ(pairing_result, NearbySharingServiceImpl::StatusCodes::kOk); + + FlushTesting(); + // Verify data sent to the remote device so far. + EXPECT_TRUE(ExpectPairedKeyEncryptionFrame()); + EXPECT_TRUE(ExpectPairedKeyResultFrame()); + + // Check BindingRequest frame sent to the remote device. + std::unique_ptr frame = GetWrittenFrame(); + ASSERT_TRUE(frame->has_v1()); + EXPECT_EQ(frame->v1().type(), service::proto::V1Frame::BINDINGS); + EXPECT_EQ(frame->v1().bindings().binding_request().binding_id(), kBindingId); + EXPECT_EQ(frame->v1().bindings().binding_request().type(), + service::proto::BindingRequest::FILESYNC); + + preference_manager_.SetString(PrefNames::kCustomSavePath, "Downloads"); + Frame binding_response_frame; + binding_response_frame.set_version(Frame::V1); + binding_response_frame.mutable_v1()->set_type( + service::proto::V1Frame::BINDINGS); + binding_response_frame.mutable_v1() + ->mutable_bindings() + ->mutable_binding_response() + ->set_status(service::proto::BindingResponse::SUCCESS); + std::vector result_bytes(binding_response_frame.ByteSizeLong()); + binding_response_frame.SerializeToArray(result_bytes.data(), + result_bytes.size()); + ReceiveMessageFromConnection(std::move(result_bytes)); + + // Verify that connection is closed. + EXPECT_FALSE( + fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId) + .has_value()); + + std::optional binding = + preference_manager_.GetSyncBindingValue(); + ASSERT_TRUE(binding.has_value()); + EXPECT_EQ(binding->sync_bindings().size(), 1); + sync::SyncBinding expected_binding; + expected_binding.set_binding_id(kBindingId); + expected_binding.set_source_name(kDeviceName); + expected_binding.set_destination_directory( + FilePath("Downloads").append(FilePath(kDeviceName)).ToString()); + expected_binding.set_source_device_type( + sync::SyncBinding::SOURCE_DEVICE_TYPE_PHONE); + EXPECT_THAT(binding->sync_bindings(0), EqualsProto(expected_binding)); +} + } // namespace NearbySharingServiceUnitTests } // namespace nearby::sharing diff --git a/sharing/nearby_sharing_settings.cc b/sharing/nearby_sharing_settings.cc index 9a189aed..8d915aa9 100644 --- a/sharing/nearby_sharing_settings.cc +++ b/sharing/nearby_sharing_settings.cc @@ -22,17 +22,18 @@ #include #include +#include "absl/base/nullability.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" #include "internal/platform/clock.h" -#include "internal/platform/device_info.h" +#include "internal/platform/implementation/device_info.h" +#include "internal/platform/task_runner.h" #include "proto/sharing_enums.pb.h" #include "sharing/analytics/analytics_recorder.h" #include "sharing/common/nearby_share_enums.h" #include "sharing/common/nearby_share_prefs.h" #include "sharing/internal/api/preference_manager.h" -#include "sharing/internal/public/context.h" #include "sharing/internal/public/logging.h" #include "sharing/internal/public/pref_names.h" #include "sharing/local_device_data/nearby_share_local_device_data_manager.h" @@ -48,6 +49,7 @@ using ::nearby::sharing::api::PreferenceManager; using ::nearby::sharing::proto::DataUsage; using ::nearby::sharing::proto::DeviceVisibility; using ::nearby::sharing::proto::FastInitiationNotificationState; +using ::nearby::sharing::sync::SyncBindingPrefs; constexpr absl::string_view kPreferencesObserverName = "nearby-sharing-settings"; @@ -69,12 +71,12 @@ ShowNotificationStatus GetNotificationStatus( } // namespace NearbyShareSettings::NearbyShareSettings( - Context* context, nearby::Clock* clock, nearby::DeviceInfo& device_info, - PreferenceManager& preference_manager, + TaskRunner* absl_nonnull task_runner, nearby::Clock* absl_nonnull clock, + nearby::api::DeviceInfo& device_info, PreferenceManager& preference_manager, NearbyShareLocalDeviceDataManager* local_device_data_manager, analytics::AnalyticsRecorder* analytics_recorder) - : context_(context), - clock_(clock), + : task_runner_(*task_runner), + clock_(*clock), device_info_(device_info), preference_manager_(preference_manager), local_device_data_manager_(local_device_data_manager), @@ -142,7 +144,7 @@ void NearbyShareSettings::StartVisibilityTimer( LOG(INFO) << __func__ << ": start visibility timer. expiration=" << expiration; visibility_expiration_timer_ = std::make_unique( - *context_->GetTaskRunner(), "nearby_share_settings_visibility_timer", + task_runner_, "nearby_share_settings_visibility_timer", expiration, [this]() { LOG(INFO) << __func__ << ": visibility timer expired."; proto::DeviceVisibility visibility; @@ -163,7 +165,7 @@ void NearbyShareSettings::RestoreFallbackVisibility() { static_cast(prefs::kDefaultFallbackVisibility)); fallback_visibility_ = static_cast(fallback_visibility); - int64_t now_seconds = absl::ToUnixSeconds(clock_->Now()); + int64_t now_seconds = absl::ToUnixSeconds(clock_.Now()); int64_t remaining_seconds = expiration_seconds - now_seconds; int64_t diff = kMaxVisibilityExpirationSeconds - remaining_seconds; LOG(INFO) << __func__ << ": diff=" << diff << ", now=" << now_seconds @@ -186,6 +188,16 @@ std::string NearbyShareSettings::GetCustomSavePath() const { PrefNames::kCustomSavePath, device_info_.GetDownloadPath().ToString()); } +SyncBindingPrefs NearbyShareSettings::GetSyncBindingPrefs() const { + return preference_manager_.GetSyncBindingValue().value_or( + SyncBindingPrefs()); +} + +void NearbyShareSettings::SetSyncBindingPrefs( + const SyncBindingPrefs& prefs) { + preference_manager_.SetSyncBindingValue(prefs); +} + bool NearbyShareSettings::IsDisabledByPolicy() const { return false; } void NearbyShareSettings::AddSettingsObserver(Observer* observer) { @@ -265,7 +277,7 @@ void NearbyShareSettings::SetVisibility(DeviceVisibility visibility, visibility_expiration_timer_.reset(); SetFallbackVisibility(last_visibility); - absl::Time now = clock_->Now(); + absl::Time now = clock_.Now(); if (expiration != absl::ZeroDuration()) { VLOG(1) << __func__ << ": temporary visibility timer starts."; absl::Time fallback_visibility_timestamp = now + expiration; diff --git a/sharing/nearby_sharing_settings.h b/sharing/nearby_sharing_settings.h index 09fa7016..8e40847e 100644 --- a/sharing/nearby_sharing_settings.h +++ b/sharing/nearby_sharing_settings.h @@ -22,18 +22,20 @@ #include #include +#include "location/nearby/sharing/lib/sync/sync_binding_prefs.pb.h" +#include "absl/base/nullability.h" #include "absl/base/thread_annotations.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" #include "internal/base/observer_list.h" #include "internal/platform/clock.h" -#include "internal/platform/device_info.h" +#include "internal/platform/implementation/device_info.h" +#include "internal/platform/task_runner.h" #include "proto/sharing_enums.pb.h" #include "sharing/analytics/analytics_recorder.h" #include "sharing/common/nearby_share_enums.h" #include "sharing/internal/api/preference_manager.h" -#include "sharing/internal/public/context.h" #include "sharing/internal/public/logging.h" #include "sharing/local_device_data/nearby_share_local_device_data_manager.h" #include "sharing/proto/settings_observer_data.pb.h" @@ -138,7 +140,8 @@ class NearbyShareSettings }; NearbyShareSettings( - Context* context, nearby::Clock* clock, nearby::DeviceInfo& device_info, + TaskRunner* absl_nonnull task_runner, nearby::Clock* absl_nonnull clock, + nearby::api::DeviceInfo& device_info, nearby::sharing::api::PreferenceManager& preference_manager, NearbyShareLocalDeviceDataManager* local_device_data_manager, analytics::AnalyticsRecorder* analytics_recorder = nullptr); @@ -160,6 +163,10 @@ class NearbyShareSettings std::string GetCustomSavePath() const; + nearby::sharing::sync::SyncBindingPrefs GetSyncBindingPrefs() const; + void SetSyncBindingPrefs( + const nearby::sharing::sync::SyncBindingPrefs& prefs); + // Returns true if the feature is disabled by policy. bool IsDisabledByPolicy() const; @@ -218,9 +225,9 @@ class NearbyShareSettings // Make sure thread safe to access Nearby settings mutable absl::Mutex mutex_; - Context* context_; - nearby::Clock* const clock_; - nearby::DeviceInfo& device_info_; + TaskRunner& task_runner_; + nearby::Clock& clock_; + nearby::api::DeviceInfo& device_info_; nearby::sharing::api::PreferenceManager& preference_manager_; NearbyShareLocalDeviceDataManager* const local_device_data_manager_; // Used to create analytics events. diff --git a/sharing/nearby_sharing_settings_test.cc b/sharing/nearby_sharing_settings_test.cc index 531e4ed7..4e6f029f 100644 --- a/sharing/nearby_sharing_settings_test.cc +++ b/sharing/nearby_sharing_settings_test.cc @@ -19,6 +19,8 @@ #include #include +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" #include "absl/base/thread_annotations.h" #include "absl/strings/string_view.h" @@ -27,12 +29,12 @@ #include "absl/time/clock.h" #include "absl/time/time.h" #include "internal/base/files.h" +#include "internal/test/fake_clock.h" #include "internal/test/fake_device_info.h" #include "internal/test/fake_task_runner.h" #include "sharing/common/nearby_share_enums.h" #include "sharing/common/nearby_share_prefs.h" #include "sharing/internal/public/pref_names.h" -#include "sharing/internal/test/fake_context.h" #include "sharing/internal/test/fake_preference_manager.h" #include "sharing/local_device_data/fake_nearby_share_local_device_data_manager.h" #include "sharing/proto/enums.pb.h" @@ -43,6 +45,7 @@ namespace { using ::nearby::sharing::proto::DataUsage; using ::nearby::sharing::proto::DeviceVisibility; using ::nearby::sharing::proto::FastInitiationNotificationState; +using ::protobuf_matchers::EqualsProto; constexpr char kDefaultDeviceName[] = "Josh's Chromebook"; @@ -117,11 +120,12 @@ class FakeNearbyShareSettingsObserver : public NearbyShareSettings::Observer { class NearbyShareSettingsTest : public ::testing::Test { public: NearbyShareSettingsTest() - : local_device_data_manager_(kDefaultDeviceName) { + : local_device_data_manager_(kDefaultDeviceName), + fake_task_runner_(&fake_clock_, /*count=*/1) { prefs::RegisterNearbySharingPrefs(preference_manager_); nearby_share_settings_ = std::make_unique( - &context_, context_.GetClock(), fake_device_info_, preference_manager_, - &local_device_data_manager_); + &fake_task_runner_, &fake_clock_, fake_device_info_, + preference_manager_, &local_device_data_manager_); nearby_share_settings_->AddSettingsObserver(&observer_); } @@ -144,11 +148,11 @@ class NearbyShareSettingsTest : public ::testing::Test { // Waits for running tasks to complete. void Flush() { absl::SleepFor(absl::Seconds(1)); - context_.fake_task_runner()->SyncWithTimeout(absl::Milliseconds(200)); + fake_task_runner_.SyncWithTimeout(absl::Milliseconds(200)); } void FastForward(absl::Duration duration) { - context_.fake_clock()->FastForward(duration); + fake_clock_.FastForward(duration); } bool Contains(std::vector v, std::string val) { @@ -161,8 +165,9 @@ class NearbyShareSettingsTest : public ::testing::Test { protected: nearby::FakeDeviceInfo fake_device_info_; nearby::FakePreferenceManager preference_manager_; - FakeContext context_; FakeNearbyShareLocalDeviceDataManager local_device_data_manager_; + FakeClock fake_clock_; + FakeTaskRunner fake_task_runner_; FakeNearbyShareSettingsObserver observer_; std::unique_ptr nearby_share_settings_; }; @@ -298,7 +303,7 @@ TEST_F(NearbyShareSettingsTest, // Set our initial visibility to self share. settings()->SetVisibility(DeviceVisibility::DEVICE_VISIBILITY_SELF_SHARE); // Set everyone mode temporarily. - absl::Time now = context_.GetClock()->Now(); + absl::Time now = fake_clock_.Now(); settings()->SetVisibility( DeviceVisibility::DEVICE_VISIBILITY_EVERYONE, absl::Seconds(prefs::kDefaultMaxVisibilityExpirationSeconds)); @@ -354,7 +359,7 @@ TEST_F(NearbyShareSettingsTest, TemporaryVisibilityIsCorrect) { DeviceVisibility::DEVICE_VISIBILITY_UNSPECIFIED); EXPECT_EQ(fallback_visibility.fallback_time, absl::UnixEpoch()); // Transition to temporary everyone mode. - absl::Time now = context_.GetClock()->Now(); + absl::Time now = fake_clock_.Now(); settings()->SetVisibility( DeviceVisibility::DEVICE_VISIBILITY_EVERYONE, absl::Seconds(prefs::kDefaultMaxVisibilityExpirationSeconds)); @@ -375,7 +380,7 @@ TEST_F(NearbyShareSettingsTest, SetVisibilityWithExpirationTooLong) { absl::Hours(1)); // Expiration capped at 10minutes. absl::Time expected_fallback_time = - context_.GetClock()->Now() + absl::Minutes(10); + fake_clock_.Now() + absl::Minutes(10); NearbyShareSettings::FallbackVisibilityInfo fallback_visibility = settings()->GetFallbackVisibility(); // default visibility was hidden. @@ -386,9 +391,39 @@ TEST_F(NearbyShareSettingsTest, SetVisibilityWithExpirationTooLong) { EXPECT_LT(absl::AbsDuration(time_diff), absl::Seconds(1)); } +TEST_F(NearbyShareSettingsTest, GetSyncBindingPrefs_NoBindings) { + EXPECT_THAT(settings()->GetSyncBindingPrefs(), + EqualsProto(sync::SyncBindingPrefs::default_instance())); +} + +TEST_F(NearbyShareSettingsTest, GetSyncBindingPerfs_Success) { + sync::SyncBindingPrefs sync_binding_prefs; + sync_binding_prefs.add_sync_bindings()->set_binding_id("binding_id"); + sync_binding_prefs.add_sync_bindings()->set_source_name("source_name"); + sync_binding_prefs.add_sync_bindings()->set_destination_directory( + "destination_name"); + preference_manager_.SetSyncBindingValue(sync_binding_prefs); + EXPECT_THAT(settings()->GetSyncBindingPrefs(), + EqualsProto(sync_binding_prefs)); +} + +TEST_F(NearbyShareSettingsTest, SetSyncBindingPerfs_Success) { + sync::SyncBindingPrefs sync_binding_prefs; + sync_binding_prefs.add_sync_bindings()->set_binding_id("binding_id"); + sync_binding_prefs.add_sync_bindings()->set_source_name("source_name"); + sync_binding_prefs.add_sync_bindings()->set_destination_directory( + "destination_name"); + settings()->SetSyncBindingPrefs(sync_binding_prefs); + auto sync_binding_value = preference_manager_.GetSyncBindingValue(); + ASSERT_TRUE(sync_binding_value.has_value()); + EXPECT_THAT(sync_binding_value.value(), + EqualsProto(sync_binding_prefs)); +} + TEST(NearbyShareVisibilityTest, RestoresFallbackVisibility_ExpiredTimer) { // Create Nearby Share settings dependencies. - FakeContext context; + FakeClock fake_clock; + FakeTaskRunner fake_task_runner(&fake_clock, /*count=*/1); FakeDeviceInfo fake_device_info; FakePreferenceManager preference_manager; FakeNearbyShareLocalDeviceDataManager local_device_data_manager( @@ -400,13 +435,13 @@ TEST(NearbyShareVisibilityTest, RestoresFallbackVisibility_ExpiredTimer) { // Set expiration to 10 seconds ago. preference_manager.SetInteger( PrefNames::kVisibilityExpirationSeconds, - absl::ToUnixSeconds(context.GetClock()->Now() - absl::Seconds(10))); + absl::ToUnixSeconds(fake_clock.Now() - absl::Seconds(10))); // Set fallback visibility to self share. preference_manager.SetInteger( PrefNames::kFallbackVisibility, static_cast(DeviceVisibility::DEVICE_VISIBILITY_SELF_SHARE)); // Create a Nearby Share settings instance. - NearbyShareSettings settings(&context, context.GetClock(), fake_device_info, + NearbyShareSettings settings(&fake_task_runner, &fake_clock, fake_device_info, preference_manager, &local_device_data_manager); // Make sure we restore the correct visibility. @@ -416,7 +451,8 @@ TEST(NearbyShareVisibilityTest, RestoresFallbackVisibility_ExpiredTimer) { TEST(NearbyShareVisibilityTest, RestoresFallbackVisibility_FutureTimer) { // Create Nearby Share settings dependencies. - FakeContext context; + FakeClock fake_clock; + FakeTaskRunner fake_task_runner(&fake_clock, /*count=*/1); FakeDeviceInfo fake_device_info; FakePreferenceManager preference_manager; FakeNearbyShareLocalDeviceDataManager local_device_data_manager( @@ -428,13 +464,13 @@ TEST(NearbyShareVisibilityTest, RestoresFallbackVisibility_FutureTimer) { // Set expiration to 10 seconds in the future. preference_manager.SetInteger( PrefNames::kVisibilityExpirationSeconds, - absl::ToUnixSeconds(context.GetClock()->Now() + absl::Seconds(10))); + absl::ToUnixSeconds(fake_clock.Now() + absl::Seconds(10))); // Set fallback visibility to self share. preference_manager.SetInteger( PrefNames::kFallbackVisibility, static_cast(DeviceVisibility::DEVICE_VISIBILITY_SELF_SHARE)); // Create a Nearby Share settings instance. - NearbyShareSettings settings(&context, context.GetClock(), fake_device_info, + NearbyShareSettings settings(&fake_task_runner, &fake_clock, fake_device_info, preference_manager, &local_device_data_manager); // Make sure we restore the correct visibility. diff --git a/sharing/nearby_sharing_util.cc b/sharing/nearby_sharing_util.cc index 1fed0e12..06c38de2 100644 --- a/sharing/nearby_sharing_util.cc +++ b/sharing/nearby_sharing_util.cc @@ -14,10 +14,7 @@ #include "sharing/nearby_sharing_util.h" -#include #include -#include -#include #include #include #include @@ -26,8 +23,6 @@ #include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" -#include "internal/base/file_path.h" -#include "internal/platform/device_info.h" #include "proto/sharing_enums.pb.h" #include "sharing/advertisement.h" #include "sharing/certificates/nearby_share_decrypted_public_certificate.h" @@ -115,16 +110,4 @@ std::string GetDeviceId( return std::string(endpoint_id); } -bool IsOutOfStorage(DeviceInfo& device_info, FilePath file_path, - int64_t storage_required) { - std::optional available_storage = - device_info.GetAvailableDiskSpaceInBytes(file_path); - - if (!available_storage.has_value()) { - return false; - } - - return *available_storage <= storage_required; -} - } // namespace nearby::sharing diff --git a/sharing/nearby_sharing_util.h b/sharing/nearby_sharing_util.h index bcc4c90d..d2b2df59 100644 --- a/sharing/nearby_sharing_util.h +++ b/sharing/nearby_sharing_util.h @@ -21,23 +21,13 @@ #include #include "absl/strings/string_view.h" -#include "internal/platform/device_info.h" #include "proto/sharing_enums.pb.h" -#include "internal/base/file_path.h" #include "sharing/advertisement.h" #include "sharing/certificates/nearby_share_decrypted_public_certificate.h" #include "sharing/common/nearby_share_enums.h" namespace nearby::sharing { -// Checks whether having enough disk space for required storage. -// -// device_info - Nearby Share DeviceInfo -// file_path - The path is to store sharing contents. -// storage_required - required storage space. -bool IsOutOfStorage(nearby::DeviceInfo& device_info, FilePath file_path, - int64_t storage_required); - // Decodes certificate to find MAC address encoded in it. std::optional> GetBluetoothMacAddressFromCertificate( const NearbyShareDecryptedPublicCertificate& certificate); diff --git a/sharing/outgoing_share_session.cc b/sharing/outgoing_share_session.cc index 2522d02a..7b9b52a9 100644 --- a/sharing/outgoing_share_session.cc +++ b/sharing/outgoing_share_session.cc @@ -40,9 +40,10 @@ #include "sharing/nearby_connections_manager.h" #include "sharing/nearby_connections_types.h" #include "sharing/nearby_sharing_util.h" -#include "sharing/paired_key_verification_runner.h" #include "sharing/payload_tracker.h" +#include "sharing/proto/wire_format.pb.h" #include "sharing/share_session.h" +#include "sharing/share_session_usage.h" #include "sharing/share_target.h" #include "sharing/text_attachment.h" #include "sharing/thread_timer.h" @@ -56,6 +57,8 @@ namespace { using ::location::nearby::proto::sharing::ConnectionLayerStatus; using ::location::nearby::proto::sharing::EstablishConnectionStatus; using ::nearby::sharing::proto::DataUsage; +using ::nearby::sharing::service::proto::BindingRequest; +using ::nearby::sharing::service::proto::BindingResponse; using ::nearby::sharing::service::proto::ConnectionResponseFrame; using ::nearby::sharing::service::proto::Frame; using ::nearby::sharing::service::proto::IntroductionFrame; @@ -151,6 +154,7 @@ void OutgoingShareSession::InvokeTransferUpdateCallback( bool OutgoingShareSession::InitiateSendAttachments( std::unique_ptr attachment_container) { SetAttachmentContainer(std::move(*attachment_container)); + is_transfer_session_ = true; is_connecting_ = true; // Set session ID. @@ -182,6 +186,7 @@ bool OutgoingShareSession::InitiateSendAttachments( "create payloads."; UpdateTransferMetadata( TransferMetadataBuilder() + .set_usage(session_usage()) .set_status(TransferMetadata::Status::kMediaUnavailable) .build()); } @@ -316,7 +321,8 @@ bool OutgoingShareSession::FillIntroductionFrame( } bool OutgoingShareSession::AcceptTransfer( - std::function)> + std::function)> response_callback) { if (!IsConnected()) { LOG(WARNING) << "Accept invoked for unconnected share target"; @@ -330,6 +336,7 @@ bool OutgoingShareSession::AcceptTransfer( // Wait for remote accept in response frame. UpdateTransferMetadata( TransferMetadataBuilder() + .set_usage(session_usage()) .set_token(token()) .set_status(TransferMetadata::Status::kAwaitingRemoteAcceptance) .build()); @@ -339,10 +346,10 @@ bool OutgoingShareSession::AcceptTransfer( [callback = std::move(response_callback)](bool is_timeout, std::optional frame) { if (!frame.has_value()) { - callback(std::nullopt); + callback(is_timeout, std::nullopt); return; } - callback(frame->connection_response()); + callback(is_timeout, frame->connection_response()); }, kReadResponseFrameTimeout); return true; @@ -404,6 +411,7 @@ void OutgoingShareSession::SendAttachmentsCompleted( bool OutgoingShareSession::SendIntroduction( std::function timeout_callback) { + set_session_usage(ShareSessionUsage::kSharing); Frame frame; frame.set_version(Frame::V1); V1Frame* v1_frame = frame.mutable_v1(); @@ -429,14 +437,15 @@ bool OutgoingShareSession::SendIntroduction( std::optional OutgoingShareSession::HandleConnectionResponse( - std::optional response) { + bool is_timeout, std::optional response) { // Stop accept timer. mutual_acceptance_timeout_.reset(); if (!response.has_value()) { LOG(WARNING) << "Failed to read a response from the remote device. Disconnecting."; - return TransferMetadata::Status::kFailed; + return is_timeout ? TransferMetadata::Status::kTimedOut + : TransferMetadata::Status::kFailed; } VLOG(1) << "Successfully read the connection response frame."; @@ -445,6 +454,7 @@ OutgoingShareSession::HandleConnectionResponse( case ConnectionResponseFrame::ACCEPT: { UpdateTransferMetadata( TransferMetadataBuilder() + .set_usage(session_usage()) .set_status(TransferMetadata::Status::kInProgress) .build()); return std::nullopt; @@ -546,6 +556,7 @@ void OutgoingShareSession::Connect( // Send process initialized successfully, from now on status updated // will be sent out via TransferUpdates. UpdateTransferMetadata(TransferMetadataBuilder() + .set_usage(session_usage()) .set_status(TransferMetadata::Status::kConnecting) .build()); connection_start_time_ = clock().Now(); @@ -622,12 +633,55 @@ OutgoingShareSession::ProcessPayloadTransferUpdates() { return std::nullopt; } - std::optional metadata; + std::optional metadata_builder; for (; !updates.empty(); updates.pop()) { - metadata = + metadata_builder = get_payload_tracker()->ProcessPayloadUpdate(std::move(updates.front())); } - return metadata; + return metadata_builder.has_value() + ? std::make_optional( + metadata_builder->set_usage(session_usage()).build()) + : std::nullopt; +} + +void OutgoingShareSession::StartPeerBinding( + std::string binding_id, BindingRequest::Type binding_type, + absl::AnyInvocable callback) { + Frame frame; + frame.set_version(Frame::V1); + V1Frame* v1_frame = frame.mutable_v1(); + v1_frame->set_type(V1Frame::BINDINGS); + BindingRequest* binding_request = + v1_frame->mutable_bindings()->mutable_binding_request(); + binding_request->set_binding_id(binding_id); + binding_request->set_type(binding_type); + WriteFrame(frame); + LOG(INFO) << "Waiting for bindings response frame from " << share_target().id; + UpdateTransferMetadata( + TransferMetadataBuilder() + .set_usage(session_usage()) + .set_token(token()) + .set_status(TransferMetadata::Status::kAwaitingRemoteAcceptance) + .build()); + frames_reader()->ReadFrame( + nearby::sharing::service::proto::V1Frame::BINDINGS, + [callback = std::move(callback)]( + bool is_timeout, std::optional frame) mutable { + if (!frame.has_value()) { + std::move(callback)(BindingResponse::FAILURE); + return; + } + if (!frame->has_bindings() || + !frame->bindings().has_binding_response() || + frame->bindings().binding_response().status() != + BindingResponse::SUCCESS) { + std::move(callback)(BindingResponse::FAILURE); + return; + } + // Peer binding flow completed successfully. + std::move(callback)(BindingResponse::SUCCESS); + }, + kReadResponseFrameTimeout); } } // namespace nearby::sharing diff --git a/sharing/outgoing_share_session.h b/sharing/outgoing_share_session.h index 21b8b8e6..fb28496a 100644 --- a/sharing/outgoing_share_session.h +++ b/sharing/outgoing_share_session.h @@ -34,7 +34,6 @@ #include "sharing/nearby_connection.h" #include "sharing/nearby_connections_manager.h" #include "sharing/nearby_connections_types.h" -#include "sharing/paired_key_verification_runner.h" #include "sharing/proto/enums.pb.h" #include "sharing/share_session.h" #include "sharing/share_target.h" @@ -81,14 +80,16 @@ class OutgoingShareSession : public ShareSession { // ConnectionResponseFrame. bool AcceptTransfer( std::function< - void(std::optional< - nearby::sharing::service::proto::ConnectionResponseFrame>)> + void(bool is_timeout, + std::optional< + nearby::sharing::service::proto::ConnectionResponseFrame>)> response_callback); // Process the ConnectionResponseFrame. // On success, returns std::nullopt. // On failure, returns the status if the connection should be aborted. std::optional HandleConnectionResponse( + bool is_timeout, std::optional response); @@ -159,6 +160,21 @@ class OutgoingShareSession : public ShareSession { const std::vector& file_payloads() const { return file_payloads_; } + // Returns true if the session is a transfer session. + // Otherwise, it is a pairing session. + bool is_transfer_session() const { return is_transfer_session_; } + + // Initiates the peer binding message exchange with the remote device. + // `binding_id` is the result of a successful call to InitiateBinding rpc. + // `callback` is called when either a BindingResponse frame is received or a + // timeout occurs. + void StartPeerBinding( + std::string binding_id, + nearby::sharing::service::proto::BindingRequest::Type binding_type, + absl::AnyInvocable< + void(nearby::sharing::service::proto::BindingResponse::Status)> + callback); + protected: void InvokeTransferUpdateCallback(const TransferMetadata& metadata) override; void OnConnectionDisconnected() override; @@ -196,6 +212,8 @@ class OutgoingShareSession : public ShareSession { bool advanced_protection_enabled_ = false; bool advanced_protection_mismatch_ = false; bool is_connecting_ = false; + // Session can be for transfer or pairing. + bool is_transfer_session_ = false; }; } // namespace nearby::sharing diff --git a/sharing/outgoing_share_session_test.cc b/sharing/outgoing_share_session_test.cc index 983fc8fb..21b01d6c 100644 --- a/sharing/outgoing_share_session_test.cc +++ b/sharing/outgoing_share_session_test.cc @@ -22,20 +22,23 @@ #include #include +#include "location/nearby/analytics/cpp/logging/mock_event_logger.h" +#include "location/nearby/analytics/cpp/logging/sharing_log_matchers.h" +#include "location/nearby/analytics/cpp/proto/nearby_sharing_log.pb.h" +#include "location/nearby/analytics/cpp/proto/nearby_sharing_log.proto.static_reflection.h" +#include "location/nearby/sharing/lib/analytics/analytics_recorder_impl.h" +#include "net/proto2/contrib/parse_proto/parse_text_proto.h" #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" -#include "internal/analytics/mock_event_logger.h" -#include "internal/analytics/sharing_log_matchers.h" #include "internal/base/file_path.h" #include "internal/base/files.h" #include "internal/network/url.h" #include "internal/test/fake_clock.h" #include "internal/test/fake_device_info.h" #include "internal/test/fake_task_runner.h" -#include "sharing/analytics/analytics_recorder.h" #include "sharing/attachment_container.h" #include "sharing/certificates/test_util.h" #include "sharing/common/nearby_share_enums.h" @@ -45,10 +48,8 @@ #include "sharing/nearby_connection_impl.h" #include "sharing/nearby_connections_manager.h" #include "sharing/nearby_connections_types.h" -#include "sharing/paired_key_verification_runner.h" -#include "sharing/proto/analytics/nearby_sharing_log.pb.h" -#include "sharing/proto/analytics/nearby_sharing_log.proto.static_reflection.h" #include "sharing/proto/wire_format.pb.h" +#include "sharing/share_session_usage.h" #include "sharing/share_target.h" #include "sharing/text_attachment.h" #include "sharing/transfer_metadata.h" @@ -61,10 +62,11 @@ namespace { using ::location::nearby::proto::sharing::EstablishConnectionStatus; using ::location::nearby::proto::sharing::EventCategory; using ::location::nearby::proto::sharing::EventType; -using ::location::nearby::proto::sharing::OSType; using ::nearby::analytics::HasCategory; using ::nearby::analytics::HasEventType; using ::nearby::sharing::analytics::proto::SharingLog; +using ::nearby::sharing::service::proto::BindingRequest; +using ::nearby::sharing::service::proto::BindingResponse; using ::nearby::sharing::service::proto::ConnectionResponseFrame; using ::nearby::sharing::service::proto::Frame; using ::nearby::sharing::service::proto::IntroductionFrame; @@ -73,6 +75,7 @@ using ::nearby::sharing::service::proto::WifiCredentials; using ::testing::_; using ::testing::AllOf; using ::testing::Eq; +using ::protobuf_matchers::EqualsProto; using ::testing::InSequence; using ::testing::IsEmpty; using ::testing::IsFalse; @@ -159,8 +162,8 @@ class OutgoingShareSessionTest : public ::testing::Test { FakeClock fake_clock_; FakeTaskRunner fake_task_runner_{&fake_clock_, 1}; nearby::analytics::MockEventLogger mock_event_logger_; - analytics::AnalyticsRecorder analytics_recorder_{/*vendor_id=*/0, - &mock_event_logger_}; + analytics::AnalyticsRecorderImpl analytics_recorder_{/*vendor_id=*/0, + &mock_event_logger_}; ShareTarget share_target_; MockFunction transfer_metadata_callback_; @@ -507,7 +510,7 @@ TEST_F(OutgoingShareSessionTest, SendIntroductionTimeoutCancelled) { Call(_, HasStatus(TransferMetadata::Status::kInProgress))); std::optional status = - session_.HandleConnectionResponse(response); + session_.HandleConnectionResponse(/*is_timeout=*/false, response); EXPECT_THAT(status.has_value(), IsFalse()); fake_clock_.FastForward(absl::Seconds(60)); @@ -517,9 +520,9 @@ TEST_F(OutgoingShareSessionTest, SendIntroductionTimeoutCancelled) { } TEST_F(OutgoingShareSessionTest, AcceptTransferNotConnected) { - EXPECT_THAT( - session_.AcceptTransfer([](std::optional) {}), - IsFalse()); + EXPECT_THAT(session_.AcceptTransfer( + [](bool, std::optional) {}), + IsFalse()); } TEST_F(OutgoingShareSessionTest, AcceptTransferNotReady) { @@ -527,9 +530,9 @@ TEST_F(OutgoingShareSessionTest, AcceptTransferNotReady) { session_.set_session_id(1234); ConnectionSuccess(&connection); - EXPECT_THAT( - session_.AcceptTransfer([](std::optional) {}), - IsFalse()); + EXPECT_THAT(session_.AcceptTransfer( + [](bool, std::optional) {}), + IsFalse()); } TEST_F(OutgoingShareSessionTest, AcceptTransferSuccess) { @@ -553,12 +556,12 @@ TEST_F(OutgoingShareSessionTest, AcceptTransferSuccess) { Call(_, HasStatus(TransferMetadata::Status::kAwaitingRemoteAcceptance))); bool connection_response_received = false; - EXPECT_THAT( - session_.AcceptTransfer([&connection_response_received]( - std::optional) { - connection_response_received = true; - }), - IsTrue()); + EXPECT_THAT(session_.AcceptTransfer( + [&connection_response_received]( + bool, std::optional) { + connection_response_received = true; + }), + IsTrue()); // Send response frame nearby::sharing::service::proto::Frame frame = @@ -575,19 +578,28 @@ TEST_F(OutgoingShareSessionTest, AcceptTransferSuccess) { EXPECT_THAT(connection_response_received, IsTrue()); } -TEST_F(OutgoingShareSessionTest, HandleConnectionResponseEmptyResponse) { +TEST_F(OutgoingShareSessionTest, HandleConnectionResponseEmptyResponseFailed) { std::optional status = - session_.HandleConnectionResponse(std::nullopt); + session_.HandleConnectionResponse(/*is_timeout=*/false, std::nullopt); ASSERT_THAT(status.has_value(), IsTrue()); EXPECT_THAT(status.value(), Eq(TransferMetadata::Status::kFailed)); } +TEST_F(OutgoingShareSessionTest, + HandleConnectionResponseEmptyResponseTimedOut) { + std::optional status = + session_.HandleConnectionResponse(/*is_timeout=*/true, std::nullopt); + + ASSERT_THAT(status.has_value(), IsTrue()); + EXPECT_THAT(status.value(), Eq(TransferMetadata::Status::kTimedOut)); +} + TEST_F(OutgoingShareSessionTest, HandleConnectionResponseRejectResponse) { ConnectionResponseFrame response; response.set_status(ConnectionResponseFrame::REJECT); std::optional status = - session_.HandleConnectionResponse(response); + session_.HandleConnectionResponse(/*is_timeout=*/false, response); ASSERT_THAT(status.has_value(), IsTrue()); EXPECT_THAT(status.value(), Eq(TransferMetadata::Status::kRejected)); @@ -598,7 +610,7 @@ TEST_F(OutgoingShareSessionTest, ConnectionResponseFrame response; response.set_status(ConnectionResponseFrame::NOT_ENOUGH_SPACE); std::optional status = - session_.HandleConnectionResponse(response); + session_.HandleConnectionResponse(/*is_timeout=*/false, response); ASSERT_THAT(status.has_value(), IsTrue()); EXPECT_THAT(status.value(), Eq(TransferMetadata::Status::kNotEnoughSpace)); @@ -609,7 +621,7 @@ TEST_F(OutgoingShareSessionTest, ConnectionResponseFrame response; response.set_status(ConnectionResponseFrame::UNSUPPORTED_ATTACHMENT_TYPE); std::optional status = - session_.HandleConnectionResponse(response); + session_.HandleConnectionResponse(/*is_timeout=*/false, response); ASSERT_THAT(status.has_value(), IsTrue()); EXPECT_THAT(status.value(), @@ -620,7 +632,7 @@ TEST_F(OutgoingShareSessionTest, HandleConnectionResponseTimeoutResponse) { ConnectionResponseFrame response; response.set_status(ConnectionResponseFrame::TIMED_OUT); std::optional status = - session_.HandleConnectionResponse(response); + session_.HandleConnectionResponse(/*is_timeout=*/true, response); ASSERT_THAT(status.has_value(), IsTrue()); EXPECT_THAT(status.value(), Eq(TransferMetadata::Status::kTimedOut)); @@ -636,7 +648,7 @@ TEST_F(OutgoingShareSessionTest, HandleConnectionResponseAcceptResponse) { Call(_, HasStatus(TransferMetadata::Status::kInProgress))); std::optional status = - session_.HandleConnectionResponse(response); + session_.HandleConnectionResponse(/*is_timeout=*/false, response); ASSERT_THAT(status.has_value(), IsFalse()); } @@ -909,5 +921,193 @@ TEST_F(OutgoingShareSessionTest, EXPECT_TRUE(session_.certificate().has_value()); EXPECT_THAT(session_.endpoint_id(), Eq(endpoint_id_org)); } + +TEST_F(OutgoingShareSessionTest, StartPeerBindingSuccess) { + session_.set_session_id(1234); + session_.set_session_usage(ShareSessionUsage::kPairing); + NearbyConnectionImpl connection(device_info_); + ConnectionSuccess(&connection); + Frame expected_binding_request_frame = + proto2::contrib::parse_proto::ParseTextProtoOrDie( + R"pb( + version: V1 + v1 { + type: BINDINGS + bindings { + binding_request { + binding_id: "test_binding_id" + type: FILESYNC + } + } + } + )pb"); + std::vector frame_data; + connections_manager_.set_send_payload_callback( + [&](std::unique_ptr payload, + std::weak_ptr + listener) { + frame_data = std::move(payload->content.bytes_payload.bytes); + }); + EXPECT_CALL( + transfer_metadata_callback_, + Call(_, + AllOf(HasStatus(TransferMetadata::Status::kAwaitingRemoteAcceptance), + HasUsage(ShareSessionUsage::kPairing)))); + + BindingResponse::Status binding_response_status = BindingResponse::FAILURE; + session_.StartPeerBinding("test_binding_id", BindingRequest::FILESYNC, + [&binding_response_status]( + BindingResponse::Status status) { + binding_response_status = status; + }); + + Frame frame; + ASSERT_THAT(frame.ParseFromArray(frame_data.data(), frame_data.size()), + IsTrue()); + EXPECT_THAT(frame, EqualsProto(expected_binding_request_frame)); + + // Send response frame + nearby::sharing::service::proto::Frame response_frame = + proto2::contrib::parse_proto::ParseTextProtoOrDie( + R"pb( + version: V1 + v1 { + type: BINDINGS + bindings { + binding_response { + status: SUCCESS + } + } + } + )pb" + ); + std::vector data; + data.resize(response_frame.ByteSizeLong()); + EXPECT_THAT(response_frame.SerializeToArray(data.data(), data.size()), + IsTrue()); + connection.WriteMessage(std::move(data)); + + EXPECT_THAT(binding_response_status, Eq(BindingResponse::SUCCESS)); +} + +TEST_F(OutgoingShareSessionTest, StartPeerBindingTimeout) { + session_.set_session_id(1234); + session_.set_session_usage(ShareSessionUsage::kPairing); + NearbyConnectionImpl connection(device_info_); + ConnectionSuccess(&connection); + Frame expected_binding_request_frame = + proto2::contrib::parse_proto::ParseTextProtoOrDie( + R"pb( + version: V1 + v1 { + type: BINDINGS + bindings { + binding_request { + binding_id: "test_binding_id" + type: FILESYNC + } + } + } + )pb"); + std::vector frame_data; + connections_manager_.set_send_payload_callback( + [&](std::unique_ptr payload, + std::weak_ptr + listener) { + frame_data = std::move(payload->content.bytes_payload.bytes); + }); + EXPECT_CALL( + transfer_metadata_callback_, + Call(_, + AllOf(HasStatus(TransferMetadata::Status::kAwaitingRemoteAcceptance), + HasUsage(ShareSessionUsage::kPairing)))); + + BindingResponse::Status binding_response_status = BindingResponse::FAILURE; + session_.StartPeerBinding("test_binding_id", BindingRequest::FILESYNC, + [&binding_response_status]( + BindingResponse::Status status) { + binding_response_status = status; + }); + + Frame frame; + ASSERT_THAT(frame.ParseFromArray(frame_data.data(), frame_data.size()), + IsTrue()); + EXPECT_THAT(frame, EqualsProto(expected_binding_request_frame)); + + // Fast forward to the disconnection timeout. + fake_clock_.FastForward(absl::Seconds(60)); + fake_task_runner_.SyncWithTimeout(absl::Milliseconds(100)); + + EXPECT_THAT(binding_response_status, Eq(BindingResponse::FAILURE)); +} + +TEST_F(OutgoingShareSessionTest, StartPeerBindingFailure) { + session_.set_session_id(1234); + session_.set_session_usage(ShareSessionUsage::kPairing); + NearbyConnectionImpl connection(device_info_); + ConnectionSuccess(&connection); + Frame expected_binding_request_frame = + proto2::contrib::parse_proto::ParseTextProtoOrDie( + R"pb( + version: V1 + v1 { + type: BINDINGS + bindings { + binding_request { + binding_id: "test_binding_id" + type: FILESYNC + } + } + } + )pb"); + std::vector frame_data; + connections_manager_.set_send_payload_callback( + [&](std::unique_ptr payload, + std::weak_ptr + listener) { + frame_data = std::move(payload->content.bytes_payload.bytes); + }); + EXPECT_CALL( + transfer_metadata_callback_, + Call(_, + AllOf(HasStatus(TransferMetadata::Status::kAwaitingRemoteAcceptance), + HasUsage(ShareSessionUsage::kPairing)))); + + BindingResponse::Status binding_response_status = BindingResponse::FAILURE; + session_.StartPeerBinding("test_binding_id", BindingRequest::FILESYNC, + [&binding_response_status]( + BindingResponse::Status status) { + binding_response_status = status; + }); + + Frame frame; + ASSERT_THAT(frame.ParseFromArray(frame_data.data(), frame_data.size()), + IsTrue()); + EXPECT_THAT(frame, EqualsProto(expected_binding_request_frame)); + + // Send response frame + nearby::sharing::service::proto::Frame response_frame = + proto2::contrib::parse_proto::ParseTextProtoOrDie( + R"pb( + version: V1 + v1 { + type: BINDINGS + bindings { + binding_response { + status: FAILURE + } + } + } + )pb" + ); + std::vector data; + data.resize(response_frame.ByteSizeLong()); + EXPECT_THAT(response_frame.SerializeToArray(data.data(), data.size()), + IsTrue()); + connection.WriteMessage(std::move(data)); + + EXPECT_THAT(binding_response_status, Eq(BindingResponse::FAILURE)); +} + } // namespace } // namespace nearby::sharing diff --git a/sharing/outgoing_targets_manager.cc b/sharing/outgoing_targets_manager.cc index 86087baa..eeb490d4 100644 --- a/sharing/outgoing_targets_manager.cc +++ b/sharing/outgoing_targets_manager.cc @@ -342,4 +342,19 @@ void OutgoingTargetsManager::ForEachShareTarget( } } +std::vector OutgoingTargetsManager::GetBindingIds( + int64_t share_target_id) { + std::vector binding_ids; + auto session_it = outgoing_share_session_map_.find(share_target_id); + if (session_it == outgoing_share_session_map_.end()) { + return {}; + } + std::optional certificate = + session_it->second.certificate(); + if (certificate.has_value()) { + return {certificate->binding_id()}; + } + return {}; +} + } // namespace nearby::sharing diff --git a/sharing/outgoing_targets_manager.h b/sharing/outgoing_targets_manager.h index 6917f0e4..7da8d2b4 100644 --- a/sharing/outgoing_targets_manager.h +++ b/sharing/outgoing_targets_manager.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "absl/base/nullability.h" #include "absl/container/flat_hash_map.h" @@ -94,6 +95,8 @@ class OutgoingTargetsManager { void ForEachShareTarget( absl::AnyInvocable callback); + std::vector GetBindingIds(int64_t share_target_id); + private: // If an existing target matching either endpoint_id or share_target.device_id // is found, the existing share target id is returned. diff --git a/sharing/outgoing_targets_manager_test.cc b/sharing/outgoing_targets_manager_test.cc index 60965259..863d707b 100644 --- a/sharing/outgoing_targets_manager_test.cc +++ b/sharing/outgoing_targets_manager_test.cc @@ -18,6 +18,7 @@ #include #include +#include "location/nearby/sharing/lib/analytics/analytics_recorder_impl.h" #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" @@ -25,8 +26,9 @@ #include "internal/test/fake_clock.h" #include "internal/test/fake_device_info.h" #include "internal/test/fake_task_runner.h" -#include "sharing/analytics/analytics_recorder.h" #include "sharing/attachment_container.h" +#include "sharing/certificates/nearby_share_decrypted_public_certificate.h" +#include "sharing/certificates/test_util.h" #include "sharing/fake_nearby_connections_manager.h" #include "sharing/nearby_connection_impl.h" #include "sharing/nearby_connections_types.h" @@ -59,7 +61,7 @@ class OutgoingTargetsManagerTest : public ::testing::Test { FakeClock clock_; FakeTaskRunner service_thread_; FakeNearbyConnectionsManager connections_manager_; - analytics::AnalyticsRecorder analytics_recorder_; + analytics::AnalyticsRecorderImpl analytics_recorder_; testing::MockFunction share_target_discovered_callback_; testing::MockFunction @@ -781,5 +783,36 @@ TEST_F(OutgoingTargetsManagerTest, AllTargetsLostConnectedSessionsNotClosed) { nullptr); } +TEST_F(OutgoingTargetsManagerTest, GetBindingIds_NonExistentTarget) { + EXPECT_TRUE(outgoing_targets_manager_.GetBindingIds(1234).empty()); +} + +TEST_F(OutgoingTargetsManagerTest, GetBindingIds_NoCertificate) { + constexpr int kShareTargetId = 1234; + constexpr absl::string_view kEndpointId = "endpoint_id"; + ShareTarget target; + target.id = kShareTargetId; + + outgoing_targets_manager_.OnShareTargetDiscovered( + target, kEndpointId, /*certificate=*/std::nullopt); + + EXPECT_TRUE(outgoing_targets_manager_.GetBindingIds(kShareTargetId).empty()); +} + +TEST_F(OutgoingTargetsManagerTest, GetBindingIds_WithCertificate) { + constexpr int kShareTargetId = 1234; + constexpr absl::string_view kEndpointId = "endpoint_id"; + ShareTarget target; + target.id = kShareTargetId; + NearbyShareDecryptedPublicCertificate cert = + GetNearbyShareTestDecryptedPublicCertificate(); + + outgoing_targets_manager_.OnShareTargetDiscovered(target, kEndpointId, cert); + + std::vector binding_ids = + outgoing_targets_manager_.GetBindingIds(kShareTargetId); + EXPECT_THAT(binding_ids, ElementsAre(cert.binding_id())); +} + } // namespace } // namespace nearby::sharing diff --git a/sharing/paired_key_verification_runner.cc b/sharing/paired_key_verification_runner.cc index e8cd74f3..213d6799 100644 --- a/sharing/paired_key_verification_runner.cc +++ b/sharing/paired_key_verification_runner.cc @@ -26,6 +26,7 @@ #include #include +#include "absl/base/nullability.h" #include "absl/functional/any_invocable.h" #include "absl/time/time.h" #include "internal/platform/clock.h" @@ -56,20 +57,18 @@ namespace { // if a valid signature cannot be generated. This size is consistent with the // GmsCore implementation. const size_t kNearbyShareNumBytesRandomSignature = 72; -constexpr absl::Duration kRelaxAfterSetVisibilityTimeout = absl::Minutes(15); +constexpr absl::Duration kRelaxAfterSetVisibilityTimeout = absl::Minutes(1); PairedKeyVerificationRunner::PairedKeyVerificationResult Convert( nearby::sharing::service::proto::PairedKeyResultFrame::Status status) { switch (status) { - case PairedKeyResultFrame::UNKNOWN: - return PairedKeyVerificationRunner::PairedKeyVerificationResult::kUnknown; - case PairedKeyResultFrame::SUCCESS: return PairedKeyVerificationRunner::PairedKeyVerificationResult::kSuccess; case PairedKeyResultFrame::FAIL: return PairedKeyVerificationRunner::PairedKeyVerificationResult::kFail; + case PairedKeyResultFrame::UNKNOWN: case PairedKeyResultFrame::UNABLE: return PairedKeyVerificationRunner::PairedKeyVerificationResult::kUnable; } @@ -91,40 +90,24 @@ std::ostream& operator<<( } PairedKeyVerificationRunner::PairedKeyVerificationRunner( - Clock* clock, OSType os_type, bool share_target_is_incoming, + Clock* absl_nonnull clock, OSType os_type, bool share_target_is_incoming, const VisibilityHistory& visibility_history, const std::vector& token, absl::AnyInvocable frame_writer, const std::optional& certificate, - NearbyShareCertificateManager* certificate_manager, - IncomingFramesReader* frames_reader, absl::Duration read_frame_timeout) - : clock_(clock), + NearbyShareCertificateManager* absl_nonnull certificate_manager, + IncomingFramesReader* absl_nonnull frames_reader, + absl::Duration read_frame_timeout) + : clock_(*clock), + certificate_manager_(*certificate_manager), + frames_reader_(*frames_reader), + share_target_is_incoming_(share_target_is_incoming), os_type_(os_type), - raw_token_(token), - frame_writer_(std::move(frame_writer)), + visibility_history_(visibility_history), certificate_(certificate), - certificate_manager_(certificate_manager), - frames_reader_(frames_reader), - read_frame_timeout_(read_frame_timeout) { - DCHECK(clock_); - DCHECK(certificate_manager); - DCHECK(frames_reader); - - if (share_target_is_incoming) { - local_prefix_ = kNearbyShareReceiverVerificationPrefix; - remote_prefix_ = kNearbyShareSenderVerificationPrefix; - visibility_history_ = visibility_history; - } else { - remote_prefix_ = kNearbyShareReceiverVerificationPrefix; - local_prefix_ = kNearbyShareSenderVerificationPrefix; - // Sender always uses ALL_CONTACTS cert to sign and verify signature. - visibility_history_ = { - .visibility = DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, - .last_visibility = DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, - .last_visibility_time = absl::UnixEpoch(), - }; - } -} + read_frame_timeout_(read_frame_timeout), + raw_token_(token), + frame_writer_(std::move(frame_writer)) {} PairedKeyVerificationRunner::~PairedKeyVerificationRunner() = default; @@ -135,7 +118,7 @@ void PairedKeyVerificationRunner::Run( verification_result_ = PairedKeyVerificationResult::kSuccess; SendPairedKeyEncryptionFrame(); - frames_reader_->ReadFrame( + frames_reader_.ReadFrame( V1Frame::PAIRED_KEY_ENCRYPTION, [&, runner = GetWeakPtr()](bool is_timeout, std::optional frame) { @@ -169,6 +152,17 @@ void PairedKeyVerificationRunner::OnReadPairedKeyEncryptionFrame( } } + if (auth_token_hash_result == PairedKeyVerificationResult::kUnable) { + if (share_target_is_incoming_ && + visibility_history_.visibility != + DeviceVisibility::DEVICE_VISIBILITY_EVERYONE) { + VLOG(1) << __func__ << ": Incoming connection with non-everyone " + "visibility cannot verify public certificate. " + "Treating as kFail."; + auth_token_hash_result = PairedKeyVerificationResult::kFail; + } + } + ApplyResult(auth_token_hash_result); VLOG(1) << __func__ << ": Remote public certificate verification result " << auth_token_hash_result; @@ -178,10 +172,18 @@ void PairedKeyVerificationRunner::OnReadPairedKeyEncryptionFrame( ApplyResult(local_result); VLOG(1) << __func__ << ": Paired key encryption verification result " << local_result; + if (share_target_is_incoming_ && visibility_history_.visibility == + DeviceVisibility::DEVICE_VISIBILITY_HIDDEN) { + VLOG(1) << __func__ + << ": device is hidden, reject all incoming connections."; + local_result = PairedKeyVerificationResult::kFail; + ApplyResult(PairedKeyVerificationResult::kFail); + } + SendPairedKeyResultFrame(local_result); - frames_reader_->ReadFrame( + frames_reader_.ReadFrame( V1Frame::PAIRED_KEY_RESULT, [this, runner = GetWeakPtr()](bool is_timeout, std::optional frame) { @@ -240,10 +242,6 @@ void PairedKeyVerificationRunner::SendPairedKeyResultFrame( case PairedKeyVerificationResult::kFail: result_frame->set_status(PairedKeyResultFrame::FAIL); break; - - case PairedKeyVerificationResult::kUnknown: - result_frame->set_status(PairedKeyResultFrame::UNKNOWN); - break; } // Set OS type to allow remote device knowns the paring device OS type. @@ -253,9 +251,13 @@ void PairedKeyVerificationRunner::SendPairedKeyResultFrame( } void PairedKeyVerificationRunner::SendPairedKeyEncryptionFrame() { + std::vector padded_token = PadPrefix( + share_target_is_incoming_ ? kNearbyShareReceiverVerificationPrefix + : kNearbyShareSenderVerificationPrefix, + raw_token_); std::optional> signature = - certificate_manager_->SignWithPrivateCertificate( - visibility_history_.visibility, PadPrefix(local_prefix_, raw_token_)); + certificate_manager_.SignWithPrivateCertificate( + visibility_history_.visibility, padded_token); if (!signature.has_value() || signature->empty()) { signature = GenerateRandomBytes(kNearbyShareNumBytesRandomSignature); } @@ -280,9 +282,8 @@ void PairedKeyVerificationRunner::SendPairedKeyEncryptionFrame() { LOG(INFO) << "Attempts to sign authentication token with a previous private key."; std::optional> optional_signature = - certificate_manager_->SignWithPrivateCertificate( - visibility_history_.last_visibility, - PadPrefix(local_prefix_, raw_token_)); + certificate_manager_.SignWithPrivateCertificate( + visibility_history_.last_visibility, padded_token); if (optional_signature.has_value()) { encryption_frame->set_optional_signed_data(optional_signature->data(), @@ -300,7 +301,7 @@ PairedKeyVerificationRunner::VerifyAuthTokenHashWithPrivateCertificate( DeviceVisibility visibility, const nearby::sharing::service::proto::V1Frame& frame) { std::optional> hash = - certificate_manager_->HashAuthenticationTokenWithPrivateCertificate( + certificate_manager_.HashAuthenticationTokenWithPrivateCertificate( visibility, raw_token_); const std::string& frame_hash = @@ -328,8 +329,11 @@ PairedKeyVerificationRunner::VerifyPairedKeyEncryptionFrame( auto signed_data = frame.paired_key_encryption().signed_data(); std::vector data(signed_data.begin(), signed_data.end()); - if (!certificate_->VerifySignature(PadPrefix(remote_prefix_, raw_token_), - data)) { + std::vector padded_token = PadPrefix( + share_target_is_incoming_ ? kNearbyShareSenderVerificationPrefix + : kNearbyShareReceiverVerificationPrefix, + raw_token_); + if (!certificate_->VerifySignature(padded_token, data)) { if (!frame.paired_key_encryption().has_optional_signed_data()) { LOG(WARNING) << __func__ << ": Unable to verify remote paired key encryption frame. " @@ -341,8 +345,7 @@ PairedKeyVerificationRunner::VerifyPairedKeyEncryptionFrame( frame.paired_key_encryption().optional_signed_data(); std::vector optional_data(optional_signed_data.begin(), optional_signed_data.end()); - if (certificate_->VerifySignature(PadPrefix(remote_prefix_, raw_token_), - optional_data)) { + if (certificate_->VerifySignature(padded_token, optional_data)) { LOG(INFO) << "Successfully verified remote paired key encryption " "frame with the optional signed data."; } else { @@ -374,17 +377,13 @@ void PairedKeyVerificationRunner::ApplyResult( case PairedKeyVerificationResult::kUnable: verification_result_ = PairedKeyVerificationResult::kUnable; break; - case PairedKeyVerificationResult::kUnknown: - default: - verification_result_ = PairedKeyVerificationResult::kUnable; - break; } } bool PairedKeyVerificationRunner::IsVisibilityRecentlyUpdated() const { return visibility_history_.visibility != visibility_history_.last_visibility && - (clock_->Now() - visibility_history_.last_visibility_time < + (clock_.Now() - visibility_history_.last_visibility_time < kRelaxAfterSetVisibilityTimeout); } diff --git a/sharing/paired_key_verification_runner.h b/sharing/paired_key_verification_runner.h index 6bfc8026..372e656c 100644 --- a/sharing/paired_key_verification_runner.h +++ b/sharing/paired_key_verification_runner.h @@ -22,6 +22,7 @@ #include #include +#include "absl/base/nullability.h" #include "absl/functional/any_invocable.h" #include "absl/time/time.h" #include "internal/platform/clock.h" @@ -38,8 +39,6 @@ class PairedKeyVerificationRunner : public std::enable_shared_from_this { public: enum class PairedKeyVerificationResult { - // Default value for verification result. - kUnknown, // Succeeded with verification. kSuccess, // Failed to verify. @@ -55,7 +54,8 @@ class PairedKeyVerificationRunner }; PairedKeyVerificationRunner( - Clock* clock, location::nearby::proto::sharing::OSType os_type, + Clock* absl_nonnull clock, + location::nearby::proto::sharing::OSType os_type, bool share_target_is_incoming, const VisibilityHistory& visibility_history, const std::vector& token, @@ -63,8 +63,9 @@ class PairedKeyVerificationRunner void(const nearby::sharing::service::proto::Frame& frame)> frame_writer, const std::optional& certificate, - NearbyShareCertificateManager* certificate_manager, - IncomingFramesReader* frames_reader, absl::Duration read_frame_timeout); + NearbyShareCertificateManager* absl_nonnull certificate_manager, + IncomingFramesReader* absl_nonnull frames_reader, + absl::Duration read_frame_timeout); ~PairedKeyVerificationRunner(); @@ -95,22 +96,21 @@ class PairedKeyVerificationRunner // True if visibility has changed recently. bool IsVisibilityRecentlyUpdated() const; - nearby::Clock* const clock_; + nearby::Clock& clock_; + NearbyShareCertificateManager& certificate_manager_; + IncomingFramesReader& frames_reader_; + const bool share_target_is_incoming_; const location::nearby::proto::sharing::OSType os_type_; - VisibilityHistory visibility_history_; + const VisibilityHistory visibility_history_; + const std::optional certificate_; + const absl::Duration read_frame_timeout_; std::vector raw_token_; absl::AnyInvocable frame_writer_; - std::optional certificate_; - NearbyShareCertificateManager* certificate_manager_; - IncomingFramesReader* frames_reader_; - const absl::Duration read_frame_timeout_; std::function callback_; PairedKeyVerificationResult verification_result_; - char local_prefix_; - char remote_prefix_; }; } // namespace nearby::sharing diff --git a/sharing/paired_key_verification_runner_test.cc b/sharing/paired_key_verification_runner_test.cc index c022e972..6b02462d 100644 --- a/sharing/paired_key_verification_runner_test.cc +++ b/sharing/paired_key_verification_runner_test.cc @@ -28,6 +28,7 @@ #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/functional/any_invocable.h" #include "absl/time/time.h" #include "internal/platform/task_runner.h" #include "internal/test/fake_clock.h" @@ -123,7 +124,6 @@ GenerateVisibilityHistory() { DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, DeviceVisibility::DEVICE_VISIBILITY_SELF_SHARE, DeviceVisibility::DEVICE_VISIBILITY_EVERYONE, - DeviceVisibility::DEVICE_VISIBILITY_HIDDEN, }; std::list result; for (DeviceVisibility visibility : kValidVisibilities) { @@ -142,18 +142,18 @@ class MockIncomingFramesReader : public IncomingFramesReader { NearbyConnection* connection) : IncomingFramesReader(service_thread, connection) {} - MOCK_METHOD( - void, ReadFrame, - (std::function)> callback, - absl::Duration timeout), - (override)); + MOCK_METHOD(void, ReadFrame, + (absl::AnyInvocable)> + callback, + absl::Duration timeout), + (override)); - MOCK_METHOD( - void, ReadFrame, - (service::proto::V1Frame_FrameType frame_type, - std::function)> callback, - absl::Duration timeout), - (override)); + MOCK_METHOD(void, ReadFrame, + (service::proto::V1Frame_FrameType frame_type, + absl::AnyInvocable)> + callback, + absl::Duration timeout), + (override)); }; PairedKeyVerificationRunner::PairedKeyVerificationResult Merge( @@ -240,9 +240,9 @@ class PairedKeyVerificationRunnerTest : public testing::Test { ReadFrame(testing::Eq(V1Frame::PAIRED_KEY_ENCRYPTION), testing::_, testing::Eq(kTimeout))) .WillOnce(testing::WithArg<1>( - [frame_type]( - std::function)> - callback) { + [frame_type](absl::AnyInvocable)> + callback) { if (frame_type == ReturnFrameType::kNull) { std::move(callback)(/*is_timeout=*/false, std::nullopt); return; @@ -302,7 +302,8 @@ class PairedKeyVerificationRunnerTest : public testing::Test { ReadFrame(testing::Eq(V1Frame::PAIRED_KEY_RESULT), testing::_, testing::Eq(kTimeout))) .WillOnce(testing::WithArg<1>( - [=](std::function)> + [=](absl::AnyInvocable)> callback) { if (frame_type == ReturnFrameType::kNull) { std::move(callback)(/*is_timeout=*/false, std::nullopt); @@ -353,13 +354,32 @@ class PairedKeyVerificationRunnerTest : public testing::Test { }; TEST_F(PairedKeyVerificationRunnerTest, - NullCertificate_InvalidPairedKeyEncryptionFrame) { + Incoming_NullCertificate_InvalidPairedKeyEncryptionFrame) { // Empty key encryption frame fails the certificate verification. SetUpPairedKeyEncryptionFrame(ReturnFrameType::kEmpty); SetUpPairedKeyResultFrame(ReturnFrameType::kValid); RunVerification( - true, + /*is_incoming=*/true, + /*use_valid_public_certificate=*/false, + {.visibility = DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, + .last_visibility = DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, + .last_visibility_time = GetFakeClock()->Now()}, + /*expected_result=*/ + PairedKeyVerificationResult::kFail); + + ExpectPairedKeyEncryptionFrameSent(); + ExpectPairedKeyResultFrameSent(PairedKeyResultFrame::UNABLE); +} + +TEST_F(PairedKeyVerificationRunnerTest, + Outgoing_NullCertificate_InvalidPairedKeyEncryptionFrame) { + // Empty key encryption frame fails the certificate verification. + SetUpPairedKeyEncryptionFrame(ReturnFrameType::kEmpty); + SetUpPairedKeyResultFrame(ReturnFrameType::kValid); + + RunVerification( + /*is_incoming=*/false, /*use_valid_public_certificate=*/false, {.visibility = DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, .last_visibility = DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, @@ -371,6 +391,25 @@ TEST_F(PairedKeyVerificationRunnerTest, ExpectPairedKeyResultFrameSent(PairedKeyResultFrame::UNABLE); } +TEST_F(PairedKeyVerificationRunnerTest, + Incoming_HiddenDevice_FailsConnection) { + // Empty key encryption frame fails the certificate verification. + SetUpPairedKeyEncryptionFrame(ReturnFrameType::kEmpty); + SetUpPairedKeyResultFrame(ReturnFrameType::kValid); + + RunVerification( + /*is_incoming=*/true, + /*use_valid_public_certificate=*/false, + {.visibility = DeviceVisibility::DEVICE_VISIBILITY_HIDDEN, + .last_visibility = DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, + .last_visibility_time = GetFakeClock()->Now()}, + /*expected_result=*/ + PairedKeyVerificationResult::kFail); + + ExpectPairedKeyEncryptionFrameSent(); + ExpectPairedKeyResultFrameSent(PairedKeyResultFrame::FAIL); +} + TEST_F(PairedKeyVerificationRunnerTest, ValidPairedKeyEncryptionFrame_ResultFrameTimedOut) { SetUpPairedKeyEncryptionFrame(ReturnFrameType::kValid); @@ -435,14 +474,40 @@ TEST_P(ParameterisedPairedKeyVerificationRunnerTest, PairedKeyResultFrame result_frame = std::get<1>(GetParam()); PairedKeyVerificationRunner::VisibilityHistory visibility_history = std::get<2>(GetParam()); + PairedKeyVerificationRunner::PairedKeyVerificationResult result = + params.result; + // If our visibility has no certificates, then downgrade expected result to + // kUnable if it is not expected to fail. + if ((visibility_history.visibility == + DeviceVisibility::DEVICE_VISIBILITY_EVERYONE) && + !(visibility_history.last_visibility != + DeviceVisibility::DEVICE_VISIBILITY_EVERYONE && + (params.encryption_frame_type == + PairedKeyVerificationRunnerTest::ReturnFrameType::kOptionalValid || + params.encryption_frame_type == + PairedKeyVerificationRunnerTest::ReturnFrameType::kValid))) { + if (result == + PairedKeyVerificationRunner::PairedKeyVerificationResult::kSuccess) { + result = + PairedKeyVerificationRunner::PairedKeyVerificationResult::kUnable; + } + } + if (params.is_incoming && + params.encryption_frame_type == + PairedKeyVerificationRunnerTest::ReturnFrameType::kEmpty && + visibility_history.visibility != + DeviceVisibility::DEVICE_VISIBILITY_EVERYONE) { + result = + PairedKeyVerificationRunner::PairedKeyVerificationResult::kFail; + } PairedKeyVerificationRunner::PairedKeyVerificationResult expected_result = - Merge(params.result, result_frame.status()); + Merge(result, result_frame.status()); LOG(ERROR) << "ValidEncryptionFrame_ValidResultFrame: " << "is_incoming=" << params.is_incoming << ", has_valid_cert=" << params.has_valid_certificate << ", encryption_frame_type=" << (int)params.encryption_frame_type - << ", result=" << (int)params.result + << ", result=" << (int)result << ", expected_result=" << (int)expected_result << ", result_frame=" << (int)result_frame.status() << ", visibility=" << (int)visibility_history.visibility @@ -461,22 +526,6 @@ TEST_P(ParameterisedPairedKeyVerificationRunnerTest, : OSType::UNKNOWN_OS_TYPE); } - // If our visibility has no certificates, then downgrade expected result to - // kUnable if it is not expected to fail. - if ((visibility_history.visibility == - DeviceVisibility::DEVICE_VISIBILITY_EVERYONE || - visibility_history.visibility == - DeviceVisibility::DEVICE_VISIBILITY_HIDDEN) && - (visibility_history.last_visibility == - DeviceVisibility::DEVICE_VISIBILITY_EVERYONE || - visibility_history.last_visibility == - DeviceVisibility::DEVICE_VISIBILITY_HIDDEN)) { - if (expected_result == - PairedKeyVerificationRunner::PairedKeyVerificationResult::kSuccess) { - expected_result = - PairedKeyVerificationRunner::PairedKeyVerificationResult::kUnable; - } - } visibility_history.last_visibility_time = GetFakeClock()->Now(); RunVerification( /*is_incoming=*/params.is_incoming, diff --git a/sharing/payload_tracker.cc b/sharing/payload_tracker.cc index e05b4818..345a87a6 100644 --- a/sharing/payload_tracker.cc +++ b/sharing/payload_tracker.cc @@ -107,7 +107,7 @@ void PayloadTracker::OnStatusUpdate( payload_update_queue_->Queue(std::move(update)); } -std::optional PayloadTracker::ProcessPayloadUpdate( +std::optional PayloadTracker::ProcessPayloadUpdate( std::unique_ptr update) { auto it = payload_state_.find(update->payload_id); if (it == payload_state_.end()) { @@ -139,34 +139,31 @@ std::optional PayloadTracker::ProcessPayloadUpdate( return OnTransferUpdate(state); } -std::optional PayloadTracker::OnTransferUpdate( +std::optional PayloadTracker::OnTransferUpdate( const State& state) { if (IsComplete()) { VLOG(1) << __func__ << ": All payloads are complete."; - return TransferMetadataBuilder() + return std::move(TransferMetadataBuilder() .set_status(TransferMetadata::Status::kComplete) .set_progress(100) .set_total_attachments_count(payload_state_.size()) - .set_transferred_attachments_count(transferred_attachments_count_) - .build(); + .set_transferred_attachments_count(transferred_attachments_count_)); } if (IsCancelled(state)) { VLOG(1) << __func__ << ": Payloads cancelled."; - return TransferMetadataBuilder() + return std::move(TransferMetadataBuilder() .set_status(TransferMetadata::Status::kCancelled) .set_total_attachments_count(payload_state_.size()) - .set_transferred_attachments_count(transferred_attachments_count_) - .build(); + .set_transferred_attachments_count(transferred_attachments_count_)); } if (HasFailed(state)) { VLOG(1) << __func__ << ": Payloads failed."; - return TransferMetadataBuilder() + return std::move(TransferMetadataBuilder() .set_status(TransferMetadata::Status::kFailed) .set_total_attachments_count(payload_state_.size()) - .set_transferred_attachments_count(transferred_attachments_count_) - .build(); + .set_transferred_attachments_count(transferred_attachments_count_)); } double percent = CalculateProgressPercent(state); @@ -220,7 +217,7 @@ std::optional PayloadTracker::OnTransferUpdate( last_update_progress_ = current_progress; - return TransferMetadataBuilder() + return std::move(TransferMetadataBuilder() .set_status(TransferMetadata::Status::kInProgress) .set_progress(percent) .set_transferred_bytes(current_transferred_size) @@ -230,8 +227,7 @@ std::optional PayloadTracker::OnTransferUpdate( .set_transferred_attachments_count(transferred_attachments_count_) .set_in_progress_attachment_id(state.attachment_id) .set_in_progress_attachment_total_bytes(state.total_size) - .set_in_progress_attachment_transferred_bytes(state.amount_transferred) - .build(); + .set_in_progress_attachment_transferred_bytes(state.amount_transferred)); } bool PayloadTracker::IsComplete() const { diff --git a/sharing/payload_tracker.h b/sharing/payload_tracker.h index 4f73e4e6..e27025e0 100644 --- a/sharing/payload_tracker.h +++ b/sharing/payload_tracker.h @@ -27,7 +27,7 @@ #include "sharing/attachment_container.h" #include "sharing/nearby_connections_manager.h" #include "sharing/nearby_connections_types.h" -#include "sharing/transfer_metadata.h" +#include "sharing/transfer_metadata_builder.h" #include "sharing/worker_queue.h" namespace nearby { @@ -46,7 +46,7 @@ class PayloadTracker : public NearbyConnectionsManager::PayloadStatusListener { std::unique_ptr payload_queue); ~PayloadTracker() override; - std::optional ProcessPayloadUpdate( + std::optional ProcessPayloadUpdate( std::unique_ptr update); // NearbyConnectionsManager::PayloadStatusListener: @@ -64,7 +64,7 @@ class PayloadTracker : public NearbyConnectionsManager::PayloadStatusListener { PayloadStatus status = PayloadStatus::kInProgress; }; - std::optional OnTransferUpdate(const State& state); + std::optional OnTransferUpdate(const State& state); bool IsComplete() const; bool IsCancelled(const State& state) const; diff --git a/sharing/payload_tracker_test.cc b/sharing/payload_tracker_test.cc index 73cac929..8b21d2ba 100644 --- a/sharing/payload_tracker_test.cc +++ b/sharing/payload_tracker_test.cc @@ -32,6 +32,7 @@ #include "sharing/nearby_connections_types.h" #include "sharing/proto/wire_format.pb.h" #include "sharing/transfer_metadata.h" +#include "sharing/transfer_metadata_builder.h" namespace nearby::sharing { namespace { @@ -69,7 +70,12 @@ class PayloadTrackerTest : public ::testing::Test { auto transfer_update = std::make_unique( /*payload_id=*/kFileId, PayloadStatus::kInProgress, /*total_bytes=*/kFileSize, /*bytes_transferred=*/bytes_transferred); - return payload_tracker_->ProcessPayloadUpdate(std::move(transfer_update)); + std::optional metadata_builder = + payload_tracker_->ProcessPayloadUpdate(std::move(transfer_update)); + if (!metadata_builder.has_value()) { + return std::nullopt; + } + return metadata_builder->build(); } private: diff --git a/sharing/proto/analytics/nearby_sharing_log.proto b/sharing/proto/analytics/nearby_sharing_log.proto deleted file mode 100644 index 0f7b30f6..00000000 --- a/sharing/proto/analytics/nearby_sharing_log.proto +++ /dev/null @@ -1,1128 +0,0 @@ -// 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. - -syntax = "proto2"; - -package nearby.sharing.analytics.proto; - -import "google/protobuf/duration.proto"; - -// "privacy/pattributes/annotations/proto_field.proto"; -// import "storage/datapol/annotations/proto/semantic_annotations.proto"; -// import "storage/googlesql/public/proto/type_annotation.proto"; -import "proto/sharing_enums.proto"; - -option optimize_for = LITE_RUNTIME; -option java_package = "nearby.sharing.analytics.proto"; -option java_outer_classname = "SharingLogProto"; -option objc_class_prefix = "GNCP"; - -// Top-level log proto for all NearbySharing logging. -// Each log contains a key (event_type), value (a verb-noun event) pair. -// Next Tag: 90 -// LINT.IfChange -message SharingLog { - /* justification = { - collection_basis: CB_CHECKBOX - purposes: [ INFRASTRUCTURE_METRICS, BUSINESS_ANALYSIS ] - } */ - - reserved 71; // Deprecated TransferUIEvent. - - optional location.nearby.proto.sharing.EventType event_type = 1; - - optional UnknownEvent unknown_event = 2; - - optional AcceptAgreements accept_agreements = 3; - - optional EnableNearbySharing enable_nearby_sharing = 4; - - optional SetVisibility set_visibility = 5; - - optional DescribeAttachments describe_attachments = 6; - - optional ScanForShareTargetsStart scan_for_share_targets_start = 7; - - optional ScanForShareTargetsEnd scan_for_share_targets_end = 8; - - optional AdvertiseDevicePresenceStart advertise_device_presence_start = 9; - - optional AdvertiseDevicePresenceEnd advertise_device_presence_end = 10; - - optional SendFastInitialization send_initialization = 11; - - optional ReceiveFastInitialization receive_initialization = 12; - - optional DiscoverShareTarget discover_share_target = 13; - - optional SendIntroduction send_introduction = 14; - - optional ReceiveIntroduction receive_introduction = 15; - - optional RespondToIntroduction respond_introduction = 16; - - optional SendAttachmentsStart send_attachments_start = 17; - - optional SendAttachmentsEnd send_attachments_end = 18; - - optional ReceiveAttachmentsStart receive_attachments_start = 19; - - optional ReceiveAttachmentsEnd receive_attachments_end = 20; - - optional CancelSendingAttachments cancel_sending_attachments = 21; - - optional CancelReceivingAttachments cancel_receiving_attachments = 22; - - optional OpenReceivedAttachments open_received_attachments = 23; - - optional LaunchActivity launch_activity = 24; - - optional AddContact add_contact = 25; - - optional RemoveContact remove_contact = 26; - - optional location.nearby.proto.sharing.LogSource log_source = 27; - - optional FastShareServerResponse fast_share_server_response = 28; - - optional SendStart send_start = 29; - - optional AcceptFastInitialization accept_fast_initialization = 30; - - optional SetDataUsage set_data_usage = 31; - - // The version of Nearby Sharing. E.g. "v1.0.2". - optional string version = 32 /* type = ST_SOFTWARE_ID */; - - optional location.nearby.proto.sharing.EventCategory event_category = 33; - - optional DismissFastInitialization dismiss_fast_initialization = 34; - - optional CancelConnection cancel_connection = 35; - - optional DismissPrivacyNotification dismiss_privacy_notification = 36; - - // Tap privacy notification to update visibility setting. - // http://shortn/_LMJHzPFZM0 - optional TapPrivacyNotification tap_privacy_notification = 37; - - optional TapHelp tap_help = 38; - - optional TapFeedback tap_feedback = 39; - - optional AddQuickSettingsTile add_quick_settings_tile = 40; - - optional RemoveQuickSettingsTile remove_quick_settings_tile = 41; - - optional LaunchPhoneConsent launch_phone_consent = 42; - - optional TapQuickSettingsTile tap_quick_settings_tile = 43; - - optional InstallAPKStatus install_apk_status = 44; - - optional VerifyAPKStatus verify_apk_status = 45; - - optional LaunchConsent launch_consent = 46; - - optional ProcessReceivedAttachmentsEnd process_received_attachments_end = 47; - - optional ToggleShowNotification toggle_show_notification = 48; - - optional SetDeviceName set_device_name = 49; - - // This is a temporary logging field for FilesGo migration phase based on - // device geolocation. Example values are "Phase 1", "Phase 2", etc. - // Reference: http://shortn/_BkSTmDjzWc - optional string files_migration_phase = 50; - - optional DeclineAgreements decline_agreements = 51; - - optional RequestSettingPermissions request_setting_permissions = 52; - - optional DeviceSettings device_settings = 53; - - optional EstablishConnection establish_connection = 54; - - optional AutoDismissFastInitialization auto_dismiss_fast_initialization = 55; - - optional EventMetadata event_metadata = 56; - - // Used only for Nearby Share Windows app now, e.g. "1.0.408". Deprecated and - // move it to the AppInfo below. - optional string app_version = 57 - /* type = ST_SOFTWARE_ID */[deprecated = true]; - - // Used only for Nearby Share Windows app now - optional AppCrash app_crash = 58; - - // Used only for Nearby Share android app now - optional TapQuickSettingsFileShare tap_quick_settings_file_share = 59; - - // Used only for Nearby Share Windows app now. - // TODO(b/260732897): To deprecate, and will be replaced by - // NearbyClientLog.AppInfo. - optional AppInfo app_info = 60; - - // Used only for Nearby Share android app now - optional DisplayPrivacyNotification display_privacy_notification = 61; - - // Used only for Nearby Share android app now - optional DisplayPhoneConsent display_phone_consent = 62; - - // Used only for Nearby Share Windows app now. - optional PreferencesUsage preferences_usage = 63; - - // Used only for Nearby Share android app now. - optional DefaultOptIn default_opt_in = 64; - - optional SetupWizard setup_wizard = 65; - - // Used only for Nearby Share android app now. - optional TapQrCode tap_qr_code = 66; - - optional QrCodeLinkShown qr_code_link_shown = 67; - - optional ParsingFailedEndpointId parsing_failed_endpoint_id = 68; - - optional FastInitDiscoverDevice fast_init_discover_device = 69; - - optional SendDesktopNotification send_desktop_notification = 70; - - optional SendDesktopTransferEvent send_desktop_transfer_event = 72; - - optional SetAccount set_account = 73; - - optional DecryptCertificateFailure decrypt_certificate_failure = 74; - - // Used only for Nearby Share android app now. - optional ShowAllowPermissionAutoAccess show_allow_permission_auto_access = 75; - - optional ShowWaitingForAccept show_waiting_for_accept = 76; - - optional HighQualityMediumSetup high_quality_medium_setup = 77; - - optional RpcCallStatus rpc_call_status = 78; - - optional StartQrCodeSession start_qr_code_session = 79; - - optional QrCodeOpenedInWebClient qr_code_opened_in_web_client = 80; - - optional HatsJointEvent hats_joint_event = 81; - - optional ReceivePreviews receive_previews = 82; - - // Two types of QR code sharing: P2P and Cloud-based - // P2P QR code flow re-uses a lot of the existing events from scan to send. - // Cloud-based QR code flow has the following new events. - // QR code events for sender side. - optional CloudCreateSharingRequest cloud_create_sharing_request = 83; - - // QR code CLOUD_REGISTER_RECEIVER event for receiver side. - optional CloudRegisterReceiver cloud_register_receiver = 84; - - optional CloudUploadStart cloud_upload_start = 85; - - optional CloudUploadEnd cloud_upload_end = 86; - - optional CloudDownloadStart cloud_download_start = 87; - - optional CloudDownloadEnd cloud_download_end = 88; - - // Cloud sharing RPC call event. - optional CloudSharingRpcResult cloud_sharing_rpc_result = 89; - - // Used only for Nearby Share Windows app now. - message AppInfo { - // e.g. "1.0.408" - optional string app_version = 1 /* type = ST_SOFTWARE_ID */; - // e.g. en. In Windows app, it's from the registry value. - optional string app_language = 2 - /* type = ST_DEMOGRAPHIC_INFO */; - optional string update_track = - 3; // e.g. "developer". In Windows app, it's from the registry value. - } - - message DeviceSettings { - // Device visibility setting at Nearby Share settings page, e.g. Contacts. - optional location.nearby.proto.sharing.Visibility visibility = 1; - // Device data usage preference at Nearby Share settings page, e.g.Wi-Fi - // only, Data, etc. - optional location.nearby.proto.sharing.DataUsage data_usage = 2; - // Device name length - optional int32 device_name_size = 3; - // Whether device allows show notification when devices are sharing nearby. - optional bool is_show_notification_enabled = 4; - // True if the BlueTooth setting is enabled - optional bool is_bt_enabled = 5; - // True if the location setting is enabled - optional bool is_location_enabled = 6; - // True if the wifi setting is enabled - optional bool is_wifi_enabled = 7; - // Date(YYYYMMDD) in decimal format for the first successful transfer in - // America/Los_Angeles timezone. If no successful transfer exists, the value - // is 0. - optional int32 first_successful_transfer_date = 8 - /* format = googlesql.format */; - // Date(YYYYMMDD) in decimal format for the previous successful transfer in - // America/Los_Angeles timezone. If no successful transfer exists, the value - // is 0. - optional int32 previous_successful_transfer_date = 9 - /* format = googlesql.format */; - // The cumulative number of transfers the device has completed. - // Exact transfer count stored on device, but when logging put the transfer - // count in buckets, initially we use buckets: 1, 2, 3, …, 10, 11+ - optional int32 lifetime_transfer_count = 10; - // Upload Contact data according to Device consent or Quick Share consent. - optional location.nearby.proto.sharing.ContactAccess contact_access = 11; - // Phone number verification. - optional location.nearby.proto.sharing.IdentityVerification - identity_verification = 12; - } - - // Used only for Nearby Share Windows app now. Here is the screenshot about - // where preferences are set: - // https://screenshot.googleplex.com/6HFrEfKCPxuSiYz. - message PreferencesUsage { - optional location.nearby.proto.sharing.PreferencesAction action = 1; - optional location.nearby.proto.sharing.PreferencesActionStatus - action_status = 2; - optional location.nearby.proto.sharing.PreferencesAction prev_sub_action = - 3; - optional location.nearby.proto.sharing.PreferencesAction next_sub_action = - 4; - } - - // EventType: UNKNOWN_EVENT_TYPE - message UnknownEvent {} - - // EventType: ESTABLISH_CONNECTION - message EstablishConnection { - // The result status of the attempt to establish a connection. - optional location.nearby.proto.sharing.EstablishConnectionStatus status = 1; - - optional int64 session_id = 2 /* type = ST_SESSION_ID */; - // For group share, 1-based number for transfer position. - optional int32 transfer_position = 3; - // For group share. - optional int32 concurrent_connections = 4; - // For calculating latency. - optional int64 duration_millis = 5; - optional ShareTargetInfo share_target_info = 6; - optional string referrer_name = 7; - // Deprecated. Use share_target_info.has_matching_qr_code instead. - optional bool qr_code_flow = 8 [deprecated = true]; - // True if the connection established from receiver - optional bool is_incoming_connection = 9; - // Duration from when the receiver attempts to receive from a QR code to - // when the QR code sender successfully connects to this receiver. Only set - // for QR-code based receive flows. - optional google.protobuf.Duration qr_code_receiver_connect_latency = 10; - } - - // EventType: ACCEPT_AGREEMENTS - message AcceptAgreements {} - - // EventType: DECLINE_AGREEMENTS - message DeclineAgreements {} - - // EventType: ENABLE_NEARBY_SHARING - message EnableNearbySharing { - optional location.nearby.proto.sharing.NearbySharingStatus status = 1; - optional bool has_opted_in = 2; - } - - // EventType: SET_ACCOUNT - // Activity Name: SETUP_ACTIVITY or SETTINGS_ACTIVITY - message SetAccount { - optional location.nearby.proto.sharing.ActivityName activity_name = 1; - } - - // EventType: SET_VISIBILITY - message SetVisibility { - // The new visibility that the device is set to. - optional location.nearby.proto.sharing.Visibility visibility = 1; - - // The current visibility of the device. - optional location.nearby.proto.sharing.Visibility source_visibility = 2; - - // The duration in millis of this visibility setting. - optional int64 duration_millis = 3; - - optional location.nearby.proto.sharing.ActivityName source_activity_name = 4 - /* type = ST_NOT_REQUIRED */; - } - - // EventType: SET_DATA_USAGE - message SetDataUsage { - // The current data usage preference of the device. - optional location.nearby.proto.sharing.DataUsage original_preference = 1; - - // The new data usage preference that the device is set to. - optional location.nearby.proto.sharing.DataUsage preference = 2; - } - - // EventType: SCAN_FOR_SHARE_TARGETS_START - message ScanForShareTargetsStart { - // A randomly generated number to be used to join the start and end of a - // session (mostly used to compute the duration of the session, e.g. how - // long does it take for attachments to be shared/sent via the Nearby - // Connections api). A same number is used twice for the start and - // end of a session. It is not designed to be associated to user or device, - // and can only be used to join the start and end of a particular session. - // Each session itself does not contain user or device information, and is - // not designed to be joined with other sessions/events of the same user to - // reconstruct particular user's activity pattern. - optional int64 session_id = 1 /* type = ST_SESSION_ID */; - optional location.nearby.proto.sharing.SessionStatus status = 2; - optional location.nearby.proto.sharing.ScanType scan_type = 3; - optional int64 flow_id = 4 /* type = ST_SESSION_ID */; - optional string referrer_name = 5; - // Represents whether this scan was started with an active QR code session - // that can be used to help discover targets that have scanned the same QR - // code - optional bool use_qr_code = 6; - } - - // EventType: SCAN_FOR_SHARE_TARGETS_END - message ScanForShareTargetsEnd { - optional int64 session_id = 1 /* type = ST_SESSION_ID */; - } - - // EventType: ADVERTISE_DEVICE_PRESENCE_START - message AdvertiseDevicePresenceStart { - // No longer needed for advertisement. - optional int64 session_id = 1 - /* type = ST_SESSION_ID */[deprecated = true]; - optional location.nearby.proto.sharing.Visibility visibility = 2; - optional location.nearby.proto.sharing.SessionStatus status = 3; - optional location.nearby.proto.sharing.DataUsage data_usage = 4; - // No longer needed for advertisement, replace this with - // SET_NAME_DEVICE. - optional int32 device_name_size = 5 [deprecated = true]; - optional string referrer_name = 6; - optional location.nearby.proto.sharing.AdvertisingMode advertising_mode = 7; - optional bool qr_code_flow = 8; - } - - // EventType: ADVERTISE_DEVICE_PRESENCE_END - message AdvertiseDevicePresenceEnd { - // No longer needed for advertisement. - optional int64 session_id = 1 - /* type = ST_SESSION_ID */[deprecated = true]; - } - - // EventType: SEND_FAST_INITIALIZATION - message SendFastInitialization {} - - // EventType: RECEIVE_FAST_INITIALIZATION - message ReceiveFastInitialization { - // The time elapse from the beginning of screen unlock to the time - // when the FastInitialization is received. - optional int64 time_elapse_since_screen_unlock_millis = 1; - // True if the notification is enabled - optional bool notifications_enabled = 2; - // True if the notification is being filtered when being shown - optional bool notifications_filtered = 3; - } - - // EventType: DISMISS_FAST_INITIALIZATION - message DismissFastInitialization {} - - // EventType: AUTO_DISMISS_FAST_INITIALIZATION - message AutoDismissFastInitialization {} - - // TODO(b/302987763): We need to deprecate flow_id and session_id in each - // event once these two fields in metadata are released to prod and - // pipelines are updated to read them. - message EventMetadata { - optional location.nearby.proto.sharing.SharingUseCase use_case = 1; - // The opt-in status before the user enters the first opt-in screen in each - // time file share or it is always “true” if the user has opted in before. - // Deprecated. - optional bool initial_opt_in = 2 [deprecated = true]; - // The opt-in status after the user leaves the first opt-in screen in each - // time file share or it is always “true” if the user has opted in before. - // Deprecated. - optional bool opt_in = 3 [deprecated = true]; - // The Nearby Share enable status before the user enters the first - // opt-in screen in each time file share. - // Deprecated. - optional bool initial_enable_status = 4 [deprecated = true]; - // The same id means it is in the same sharing file flow of sender side. - // Ex: when sender share file to 2 receivers, the flow_id in sender side is - // the same for all the discovery/connection/transfer events. - optional int64 flow_id = 5 /* type = ST_SESSION_ID */; - // A randomly generated number to be used to join the start and end of a - // session (mostly used to compute the duration of the session, e.g. how - // long does it take for attachments to be shared/sent via the Nearby - // Connections api). A same number is used twice for the start and - // end of a session. It is not designed to be associated to user or device, - // and can only be used to join the start and end of a particular session. - // Each session itself does not contain user or device information, and is - // not designed to be joined with other sessions/events of the same user to - // reconstruct particular user's activity pattern. - optional int64 session_id = 6 /* type = ST_SESSION_ID */; - optional int32 vendor_id = 7 /* type = ST_PARTNER_ID */; - - // The cloud_sharing_id: used by both sender and receiver during QR code - // cloud sharing flow to join sender and receiver events. - optional string cloud_sharing_id = 8 - /* type = ST_SESSION_ID */; - // receiver session id: used by QR code cloud receiver. - optional string cloud_receiver_session_id = 9 - /* type = ST_SESSION_ID */; - - // The name of the external provider, it will not be set if the provider is - // not external. - optional string external_provider_name = 10; - // The service id of the external provider, it will not be set if the - // provider is not external. - optional string external_provider_id = 11 - /* type = ST_SESSION_ID */; - // Is this file transfer from a direct share target. This is only available - // for the sender log. - optional bool is_direct_share = 12; - } - - // TODO(fdi): may consider adding a field about decipherability later. - // EventType: DISCOVER_SHARE_TARGET - message DiscoverShareTarget { - optional ShareTargetInfo share_target_info = 1; - // The time elapse from the beginning of an scanning session to the time - // when the share target is discovered. - optional google.protobuf.Duration duration_since_scanning = 2; - optional int64 session_id = 3 /* type = ST_SESSION_ID */; - optional int64 flow_id = 4 /* type = ST_SESSION_ID */; - optional string referrer_name = 5; - // The time elapse from the share sheet activity starts (foreground - // send surface) to the time when the share target is discovered. - // Only uses foreground send surfaces, since this is when users - // directly engage with NS to send. - optional int64 latency_since_activity_start_millis = 6 [default = -1]; - optional location.nearby.proto.sharing.ScanType scan_type = 7; - - // receiver session id: used by QR code cloud receiver. - optional string cloud_receiver_session_id = 8 - /* type = ST_SESSION_ID */; - } - - // EventType: PARSING_FAILED_ENDPOINT_ID - message ParsingFailedEndpointId { - optional string endpoint_id = 1 /* type = ST_SESSION_ID */; - // The time elapse from the beginning of an scanning session to the time - // when the share target is discovered. - optional google.protobuf.Duration duration_since_scanning = 2; - optional int64 session_id = 3 /* type = ST_SESSION_ID */; - optional int64 flow_id = 4 /* type = ST_SESSION_ID */; - optional string referrer_name = 5 - /* type = ST_REFERER_URL */; - // The time elapse from the share sheet activity starts to the time - // when the share target is discovered. - optional int64 latency_since_activity_start_millis = 6 [default = -1]; - optional location.nearby.proto.sharing.ScanType scan_type = 7; - // The time elapse from the beginning of sync to download the certificates - // to the time when the scanning fails in parsing. - optional google.protobuf.Duration duration_since_last_sync = 8; - optional location.nearby.proto.sharing.ParsingFailedType - parsing_failed_type = 9; - optional location.nearby.proto.sharing.DiscoveryMode discovery_mode = 10; - } - - // EventType: DESCRIBE_ATTACHMENTS - message DescribeAttachments { - optional AttachmentsInfo attachments_info = 1; - - // Time taken to download the attachments before sending - optional google.protobuf.Duration download_duration = 2; - } - - // TODO(fdi): may want to add duration_from_scanning_millis later. - // EventType: SEND_INTRODUCTION - message SendIntroduction { - optional ShareTargetInfo share_target_info = 1; - optional int64 session_id = 2 /* type = ST_SESSION_ID */; - // 1-based number for transfer position. - optional int32 transfer_position = 3; - optional int32 concurrent_connections = 4; - } - - // EventType: RECEIVE_INTRODUCTION - message ReceiveIntroduction { - optional int64 session_id = 1 /* type = ST_SESSION_ID */; - optional ShareTargetInfo share_target_info = 2; - optional string referrer_name = 3; - } - - // TODO(fdi): may add AttachmentInfo, or ShareTargetInfo later. - // EventType: RESPOND_TO_INTRODUCTION - message RespondToIntroduction { - optional location.nearby.proto.sharing.ResponseToIntroduction action = 1; - optional int64 session_id = 2 /* type = ST_SESSION_ID */; - optional bool qr_code_flow = 3; - } - - // EventType: SEND_ATTACHMENTS_START - message SendAttachmentsStart { - optional int64 session_id = 1 /* type = ST_SESSION_ID */; - optional AttachmentsInfo attachments_info = 2; - // 1-based number for transfer position. - optional int32 transfer_position = 3; - optional int32 concurrent_connections = 4; - // Deprecated. Use share_target_info.has_matching_qr_code instead. - optional bool qr_code_flow = 5 [deprecated = true]; - optional ShareTargetInfo share_target_info = 6; - // True if the advanced protection is enabled and the sender needed to - // confirm the transfer. - optional bool advanced_protection_enabled = 7; - // True if the advanced protection flag from NearbyService BE is different - // from the mendel flag. - optional bool advanced_protection_mismatch = 8; - } - - // EventType: SEND_ATTACHMENTS_END - message SendAttachmentsEnd { - optional int64 session_id = 1 /* type = ST_SESSION_ID */; - optional int64 sent_bytes = 2; - optional location.nearby.proto.sharing.AttachmentTransmissionStatus status = - 3; - // 1-based number for transfer position. - optional int32 transfer_position = 4; - optional int32 concurrent_connections = 5; - optional AttachmentsInfo attachments_info = 6; - // the duration from transfer start to transfer is finished. - optional int64 duration_millis = 7; - optional ShareTargetInfo share_target_info = 8; - optional string referrer_name = 9; - // connection status from nearby connections layer - optional location.nearby.proto.sharing.ConnectionLayerStatus - connection_layer_status = 10; - // Date(YYYYMMDD) in decimal format for the first successful transfer in - // America/Los_Angeles timezone. If no successful transfer exists, the value - // is 0. - optional int32 first_successful_transfer_date = 11 - /* format = googlesql.format */; - // Date(YYYYMMDD) in decimal format for the previous successful transfer in - // America/Los_Angeles timezone. If no successful transfer exists, the value - // is 0. - optional int32 previous_successful_transfer_date = 12 - /* format = googlesql.format */; - // The cumulative number of transfers the device has completed. - // Exact transfer count stored on device, but when logging put the transfer - // count in buckets, initially we use buckets: 1, 2, 3, …, 10, 11+ - optional int32 lifetime_transfer_count = 13; - // The medium used for the connection. - optional int32 connection_medium = 14; - // The data usage of the user settings. - optional location.nearby.proto.sharing.DataUsage data_usage = 15; - - // True if the sender and receiver are mutual contacts. - optional bool is_mutual_contact = 16; - } - - // EventType: RECEIVE_ATTACHMENTS_START - message ReceiveAttachmentsStart { - optional int64 session_id = 1 /* type = ST_SESSION_ID */; - optional AttachmentsInfo attachments_info = 2; - optional ShareTargetInfo share_target_info = 3; - } - - // EventType: RECEIVE_ATTACHMENTS_END - message ReceiveAttachmentsEnd { - optional int64 session_id = 1 /* type = ST_SESSION_ID */; - optional int64 received_bytes = 2; - optional location.nearby.proto.sharing.AttachmentTransmissionStatus status = - 3; - optional string referrer_name = 4; - optional ShareTargetInfo share_target_info = 5; - // Date(YYYYMMDD) in decimal format for the first successful transfer in - // America/Los_Angeles timezone. If no successful transfer exists, the value - // is 0. - optional int32 first_successful_transfer_date = 6 - /* format = googlesql.format */; - // Date(YYYYMMDD) in decimal format for the previous successful transfer in - // America/Los_Angeles timezone. If no successful transfer exists, the value - // is 0. - optional int32 previous_successful_transfer_date = 7 - /* format = googlesql.format */; - // The cumulative number of transfers the device has completed. - // Exact transfer count stored on device, but when logging put the transfer - // count in buckets, initially we use buckets: 1, 2, 3, …, 10, 11+ - optional int32 lifetime_transfer_count = 8; - // The medium used for the connection. - optional int32 connection_medium = 14; - // The data usage of the user settings. - optional location.nearby.proto.sharing.DataUsage data_usage = 15; - } - - // EventType: CANCEL_CONNECTION - message CancelConnection { - optional int64 session_id = 1 /* type = ST_SESSION_ID */; - // 1-based number for transfer position. 1 if log is from receiver side. - optional int32 transfer_position = 2; - optional int32 concurrent_connections = 3; - } - - // EventType: CANCEL_SENDING_ATTACHMENTS - message CancelSendingAttachments {} - - // EventType: CANCEL_RECEIVING_ATTACHMENTS - message CancelReceivingAttachments {} - - // EventType: PROCESS_RECEIVED_ATTACHMENTS_END - message ProcessReceivedAttachmentsEnd { - optional int64 session_id = 1 /* type = ST_SESSION_ID */; - optional location.nearby.proto.sharing.ProcessReceivedAttachmentsStatus - status = 2; - } - - // EventType: OPEN_RECEIVED_ATTACHMENTS - message OpenReceivedAttachments { - optional AttachmentsInfo attachments_info = 3; - optional int64 session_id = 4 /* type = ST_SESSION_ID */; - } - - // EventType: LAUNCH_SETUP_ACTIVITY - message LaunchSetupActivity {} - - // EventType: ADD_CONTACT - message AddContact { - optional bool was_phone_added = 1; - optional bool was_email_added = 2; - } - - // EventType: REMOVE_CONTACT - message RemoveContact { - optional bool was_phone_removed = 1; - optional bool was_email_removed = 2; - } - - // EventType: FAST_SHARE_SERVER_RESPONSE - message FastShareServerResponse { - optional location.nearby.proto.sharing.ServerResponseState status = 1; - optional location.nearby.proto.sharing.ServerActionName name = 2; - optional int64 latency_millis = 3; - optional location.nearby.proto.sharing.SyncPurpose purpose = 4; - optional location.nearby.proto.sharing.ClientRole requester = 5; - optional location.nearby.proto.sharing.DeviceType device_type = 6; - } - - // EventType: SEND_START - message SendStart { - optional int64 session_id = 1 /* type = ST_SESSION_ID */; - // 1-based number for transfer position. - optional int32 transfer_position = 2; - optional int32 concurrent_connections = 3; - optional ShareTargetInfo share_target_info = 4; - } - - // EventType: ACCEPT_FAST_INITIALIZATION - message AcceptFastInitialization {} - - // EventType: LAUNCH_ACTIVITY - message LaunchActivity { - optional location.nearby.proto.sharing.ActivityName activity_name = 1; - // Elapsed time in milliseconds between startActivity and stopActivity. - optional int64 duration_millis = 2; - // The name of the package that launched the activity - optional string referrer_name = 3; - // Is previous transfer in progress. - optional bool previous_transfer_in_progress = 4; - // Whether user has opted in before. For SETUP_ACTIVITY (Opt-In half sheet) - // and SETTINGS_ACTIVITY (Settings page). b/202415050, b/203248230 - optional bool has_opted_in = 5; - // Indicate which UI interaction triggers the opt-in half sheet. Currently - // this only applies to SETUP_ACTIVITY - optional location.nearby.proto.sharing.ActivityName source_activity_name = - 6; - // Is the activity simply pausing or completely finishing. - optional bool is_finishing = 7; - } - - // EventType: DISMISS_PRIVACY_NOTIFICATION - message DismissPrivacyNotification {} - - // EventType: TAP_PRIVACY_NOTIFICATION - message TapPrivacyNotification {} - - // EventType: TAP_HELP - message TapHelp {} - - // EventType: TAP_FEEDBACK - message TapFeedback {} - - // EventType: ADD_QUICK_SETTINGS_TILE - message AddQuickSettingsTile {} - - // EventType: REMOVE_QUICK_SETTINGS_TILE - message RemoveQuickSettingsTile {} - - // EventType: LAUNCH_PHONE_CONSENT - message LaunchPhoneConsent {} - - // EventType: DISPLAY_PHONE_CONSENT - message DisplayPhoneConsent {} - - // EventType: TAP_QUICK_SETTINGS_TILE - message TapQuickSettingsTile {} - - // EventType: TAP_QUICK_SETTINGS_FILE_SHARE - message TapQuickSettingsFileShare {} - - // EventType: DISPLAY_PRIVACY_NOTIFICATION - message DisplayPrivacyNotification {} - - // EventType: DEFAULT_OPT_IN - message DefaultOptIn {} - - // EventType: SET_DEVICE_NAME - message SetDeviceName { - optional int32 device_name_size = 1; - } - - // EventType: REQUEST_SETTING_PERMISSIONS - message RequestSettingPermissions { - optional location.nearby.proto.sharing.PermissionRequestType - permission_type = 1; - optional location.nearby.proto.sharing.PermissionRequestResult - permission_request_result = 2; - } - - // EventType: LAUNCH_CONSENT - message LaunchConsent { - optional location.nearby.proto.sharing.ConsentType consent_type = 1; - optional location.nearby.proto.sharing.ConsentAcceptanceStatus status = 2; - } - - // EventType: INSTALL_APK_STATUS - message InstallAPKStatus { - repeated location.nearby.proto.sharing.InstallAPKStatus status = 1 - [packed = true]; - repeated location.nearby.proto.sharing.ApkSource source = 2 [packed = true]; - } - - // EventType: VERIFY_APK_STATUS - message VerifyAPKStatus { - repeated location.nearby.proto.sharing.VerifyAPKStatus status = 1 - [packed = true]; - repeated location.nearby.proto.sharing.ApkSource source = 2 [packed = true]; - } - - // EventType: TOGGLE_SHOW_NOTIFICATION - message ToggleShowNotification { - optional location.nearby.proto.sharing.ShowNotificationStatus - previous_status = 1; - optional location.nearby.proto.sharing.ShowNotificationStatus - current_status = 2; - } - - // EventType: DECRYPT_CERTIFICATE_FAILURE - message DecryptCertificateFailure { - optional location.nearby.proto.sharing.DecryptCertificateFailureStatus - status = 1; - } - - // EventType: SHOW_ALLOW_PERMISSION_AUTO_ACCESS - message ShowAllowPermissionAutoAccess { - // Auto permission UI activity name - // Shows the auto permission UI if the device lacks Wifi or Bluetooth - // permission and the user has not allowed Nearby Share to automatically - // enable these permissions. Once the user allows access, the UI will - // not be shown again. - // Currently, only the SHARE_SHEET_ACTIVITY and RECEIVE_SURFACE_ACTIVITY - // show the UI. - optional location.nearby.proto.sharing.ActivityName activity_name = 1; - // True if user allowed NS to auto enable Wifi/BT permissions during file - // transfer and NS will recover the permissions after transffer is complete. - optional bool allowed_auto_access = 2; - // True if the device lacks Wifi permission. - optional bool is_wifi_missing = 3; - // True if the device lacks Bluetooth permission. - optional bool is_bt_missing = 4; - } - - // EventType: TAP_QR_CODE - message TapQrCode {} - - // QR_CODE_LINK_SHOWN - message QrCodeLinkShown {} - - // EventType: FAST_INIT_DISCOVER_DEVICE - message FastInitDiscoverDevice { - reserved 1; - // The advertisement type is NOTIFY or SILENT. - optional location.nearby.proto.sharing.FastInitType fast_init_type = 2; - // The distance of the found nearby fast init advertisement. - optional location.nearby.proto.sharing.FastInitState fast_init_state = 3; - } - - // The metadata of a share target. - message ShareTargetInfo { - optional location.nearby.proto.sharing.DeviceType device_type = 1; - optional location.nearby.proto.sharing.OSType os_type = 2; - optional location.nearby.proto.sharing.DeviceRelationship - device_relationship = 3; - // Represents whether the share target has the same QR code as the local - // device. - // In sender side events, this returns whether the receiver represented by - // this ShareTargetInfo has scanned the QR code generated by this sender. - // In receiver side events, this returns whether the sender represented by - // this ShareTargetInfo is the one that generated the QR code scanned by - // this receiver. - optional bool has_matching_qr_code = 4; - - // Represents whether the share target is from external provider. - optional bool is_external = 5; - } - - // The metadata of attachments to be shared. - message AttachmentsInfo { - repeated TextAttachment text_attachment = 1; - repeated FileAttachment file_attachment = 2; - // The App required by sender to open the attachments. - optional string required_app = 3; - repeated WifiCredentialsAttachment wifi_credentials_attachment = 4; - repeated AppAttachment app_attachment = 5; - repeated StreamAttachment stream_attachment = 6; - repeated FolderAttachment folder_attachment = 7; - } - - message TextAttachment { - optional Type type = 1; - optional int64 size_bytes = 2; - // attachments are batched together by some source - optional int64 batch_id = 3 /* type = ST_SESSION_ID */; - optional location.nearby.proto.sharing.AttachmentSourceType source_type = 4; - - enum Type { - UNKNOWN_TEXT_TYPE = 0; - URL = 1; - ADDRESS = 2; - PHONE_NUMBER = 3; - } - } - - message FileAttachment { - optional Type type = 1; - optional int64 size_bytes = 2; - reserved 3; // optional string mime_type = 3 - optional int64 offset_bytes = 4; - // attachments are batched together by some source - optional int64 batch_id = 5 /* type = ST_SESSION_ID */; - optional location.nearby.proto.sharing.AttachmentSourceType source_type = 6; - - enum Type { - UNKNOWN_FILE_TYPE = 0; - IMAGE = 1; - VIDEO = 2; - ANDROID_APP = 3; - AUDIO = 4; - DOCUMENT = 5; - CONTACT_CARD = 6; - } - } - - message WifiCredentialsAttachment { - optional int32 security_type = 1; - // attachments are batched together by some source - optional int64 batch_id = 2 /* type = ST_SESSION_ID */; - optional location.nearby.proto.sharing.AttachmentSourceType source_type = 3; - } - - message AppAttachment { - optional string package_name = 1 /* type = ST_SOFTWARE_ID */; - // App size in bytes. - optional int64 size = 2; - // attachments are batched together by some source - optional int64 batch_id = 3 /* type = ST_SESSION_ID */; - optional location.nearby.proto.sharing.AttachmentSourceType source_type = 4; - } - - message StreamAttachment { - optional string package_name = 1 /* type = ST_SOFTWARE_ID */; - // attachments are batched together by some source - optional int64 batch_id = 2 /* type = ST_SESSION_ID */; - optional location.nearby.proto.sharing.AttachmentSourceType source_type = 3; - } - - message CloudAttachmentInfo { - optional location.nearby.proto.sharing.AttachmentTransmissionStatus status = - 1; - oneof CloudAttachment { - TextAttachment text_attachment = 2; - FileAttachment file_attachment = 3; - WifiCredentialsAttachment wifi_credentials_attachment = 4; - AppAttachment app_attachment = 5; - StreamAttachment stream_attachment = 6; - } - // The total bytes of all attachments transferred. - optional int64 transferred_bytes = 7; - // The duration from transfer start to transfer end. - optional int64 duration_millis = 8; - } - - message FolderAttachment {} - - // EventType: APP_CRASH - // Used only for Nearby Share Windows App now - message AppCrash { - optional location.nearby.proto.sharing.AppCrashReason crash_reason = 1; - } - - // EventType: SETUP_WIZARD - // The results of a setup wizard flow - message SetupWizard { - // The new visibility of the device. - optional location.nearby.proto.sharing.Visibility visibility = 1; - // The previous visibility of the device. - optional location.nearby.proto.sharing.Visibility previous_visibility = 2; - } - - message SendDesktopNotification { - reserved 2; - optional location.nearby.proto.sharing.DesktopNotification event = 1; - } - - message SendDesktopTransferEvent { - optional location.nearby.proto.sharing.DesktopTransferEventType event = 1; - } - - message ShowWaitingForAccept { - optional location.nearby.proto.sharing.ButtonStatus button_status = 1; - } - - message HighQualityMediumSetup { - optional ShareTargetInfo share_target_info = 1; - optional int64 session_id = 2 /* type = ST_SESSION_ID */; - optional int64 duration_millis = 3; - optional bool is_timeout = 4; - optional int32 original_quality = 5; - optional int32 connection_medium = 6; - optional int32 connection_mode = 7; - optional int32 instant_connection_result = 8; - } - - message RpcCallStatus { - enum RpcDirection { - UNKNOWN_RPC_DIRECTION = 0; - INCOMING = 1; - OUTGOING = 2; - } - - optional RpcDirection direction = 1; - // Name of RPC in . format. - optional string rpc_name = 2; - // Canonical error code of RPC. - optional int32 error_code = 3; - // Latency of RPC call in milliseconds. - optional int64 latency_millis = 4; - } - - // EventType: START_QR_CODE_SESSION - message StartQrCodeSession {} - - // EventType: QR_CODE_OPENED_IN_WEB_CLIENT - message QrCodeOpenedInWebClient { - enum ClientPlatform { - UNKNOWN_CLIENT_PLATFORM = 0; - // Used when the client platform is not one of the types below - GENERIC = 1; - ANDROID = 2; - IOS = 3; - CHROME_OS = 4; - WINDOWS = 5; - } - optional ClientPlatform client_platform = 1; - - // Whether the /qrcode page was opened in the browser again after the user - // clicked on the "Try again" button - optional bool is_retry = 2; - } - // EventType: HATS_JOINT_EVENT - message HatsJointEvent { - optional int64 flow_id = 1 /* type = ST_SESSION_ID */; - optional string hats_session_id = 2 - /* type = ST_SESSION_ID */; - } - - // EventType: RECEIVE_PREVIEWS - message ReceivePreviews { - optional int32 num_previews = 1; - } - - // EventType: CLOUD_CREATE_SHARING_REQUEST - message CloudCreateSharingRequest { - optional AttachmentsInfo attachments_info = 1; - optional location.nearby.proto.sharing.CloudCreateSharingResult result = 2; - // Time taken to download attachments from the Intent in milliseconds. - optional int64 attachment_download_latency_millis = 3; - // Time taken to generate and encrypt the preview thumbnail in milliseconds. - optional int64 preview_thumbnail_latency_millis = 4; - // Time taken to make the CreateSharing RPC call in milliseconds. - optional int64 rpc_latency_millis = 5; - } - - // EventType: CLOUD_REGISTER_RECEIVER used by QR code cloud web receiver - message CloudRegisterReceiver { - optional location.nearby.proto.sharing.CloudRegisterReceiverResult result = - 1; - } - - // EventType: CLOUD_UPLOAD_START used by QR code cloud web sender - // The cloud_sharing_id and flow_id are logged in the event metadata. - message CloudUploadStart { - optional AttachmentsInfo attachments_info = 1; - optional location.nearby.proto.sharing.CloudActionType action_type = 2; - } - - // EventType: CLOUD_UPLOAD_END used by QR code cloud web sender - // The cloud_sharing_id and flow_id are logged in the event metadata. - message CloudUploadEnd { - repeated CloudAttachmentInfo upload_infos = 1; - optional location.nearby.proto.sharing.CloudActionType action_type = 2; - } - - // EventType: CLOUD_DOWNLOAD_START used by QR code cloud web receiver - // The cloud_sharing_id and cloud_receiver_session_id are logged in the - // event_metadata into the anonymous logs. - message CloudDownloadStart { - optional AttachmentsInfo attachments_info = 1; - optional location.nearby.proto.sharing.CloudActionType action_type = 2; - } - - // EventType: CLOUD_DOWNLOAD_END used by QR code cloud web receiver - // The cloud_sharing_id and cloud_receiver_session_id are logged in the - // event_metadata into the anonymous logs. - message CloudDownloadEnd { - repeated CloudAttachmentInfo download_infos = 1; - optional location.nearby.proto.sharing.CloudActionType action_type = 2; - } - - // EventType: CLOUD_SHARING_RPC_RESULT - // Event logging the result of a Cloud Sharing RPC call. This event captures - // the type of RPC, its outcome, and performance metrics. - message CloudSharingRpcResult { - // The specific RPC method that was called. - optional string rpc_name = 1; - // The canonical gRPC status code (io.grpc.Status.Code) resulting from the - // call. - optional int32 status_code = 2; - // The total time taken for the RPC call to complete, in milliseconds. - optional int64 latency_millis = 3; - // The unique identifier for the cloud sharing session, if available at the - // time of the call. This helps correlate RPC events to a specific sharing - // session. - optional string cloud_sharing_id = 4; - } -} -// LINT.ThenChange(//depot/google3/logs/proto/location/nearby/nearby_client_log.proto) diff --git a/sharing/proto/wire_format.proto b/sharing/proto/wire_format.proto index 2d35c6a7..01f5a4d1 100644 --- a/sharing/proto/wire_format.proto +++ b/sharing/proto/wire_format.proto @@ -196,7 +196,7 @@ message V1Frame { CANCEL = 6; // No longer used. PROGRESS_UPDATE = 7; - FILE_SYNC = 8; + BINDINGS = 8; } optional FrameType type = 1; @@ -208,17 +208,19 @@ message V1Frame { optional PairedKeyResultFrame paired_key_result = 5; optional CertificateInfoFrame certificate_info = 6 [deprecated = true]; optional ProgressUpdateFrame progress_update = 7 [deprecated = true]; - optional SyncFrame file_sync = 8; + optional BindingFrame bindings = 8; } // An introduction packet sent by the sending side. Contains a list of files // they'd like to share. -// NEXT_ID=10 +// NEXT_ID=11 message IntroductionFrame { enum SharingUseCase { UNKNOWN = 0; NEARBY_SHARE = 1; REMOTE_COPY = 2; + TAP_TO_SHARE = 9; + FILE_SYNC = 10; } repeated FileMetadata file_metadata = 1; @@ -243,41 +245,35 @@ message ProgressUpdateFrame { optional bool start_transfer = 2; } -// A packet for file sync messages. -// NEXT_ID=3 -message SyncFrame { +// Messages used to create pair bindings between devices. +// An initiator device requests a new bindingId from the BE using the +// InitiateBinding rpc. This new bindingId is passed to the peer device using +// a BindingRequest frame. The peer device will use this bindingId to call +// JoinBinding rpc. If successful, the peer device is response with a +// BindingResponse frame with status of SUCCESS. +message BindingFrame { oneof content { - SyncHandshake handshake = 1; - SyncConfig config = 2; + BindingRequest binding_request = 1; + BindingResponse binding_response = 2; } } -// A packet for file sync handshake messages. -// NEXT_ID=1 -message SyncHandshake {} - -// A packet for file sync config messages. -// NEXT_ID=2 -message SyncConfig { - repeated SyncFolder folders = 1; +message BindingRequest { + enum Type { + UNKNOWN = 0; + FILESYNC = 1; + } + optional string binding_id = 1; + optional Type type = 2; } -// A packet for file sync folder messages. -// NEXT_ID=5 -message SyncFolder { - // An identifier of the folder for the pair of source and target devices to - // uniquely identify it among all folders that are being synced. - optional string id = 1; - // Human readable name of the folder. - optional string label = 2; - // A randomly generated id when the index is created. Regenerate when the - // index is reset. - optional int32 index_id = 3; - // The maximum sequence number of the folder. Each number represents an update - // to a file in the folder. Sequence numbers are only valid within the scope - // of a valid index_id. If an index is reset, all sequence numbers need to be - // regenerated, including max_sequence. - optional int64 max_sequence = 4; +message BindingResponse { + enum Status { + UNKNOWN = 0; + SUCCESS = 1; + FAILURE = 2; // TODO: b/485307320 - Add more specific error codes. + } + optional Status status = 1; } // A response packet sent by the receiving side. Accepts or rejects the list of diff --git a/sharing/share_session.cc b/sharing/share_session.cc index 885e8396..3c7d1676 100644 --- a/sharing/share_session.cc +++ b/sharing/share_session.cc @@ -176,7 +176,10 @@ void ShareSession::Abort(TransferMetadata::Status status) { // First invoke the appropriate transfer callback with the final // |status|. - UpdateTransferMetadata(TransferMetadataBuilder().set_status(status).build()); + UpdateTransferMetadata(TransferMetadataBuilder() + .set_usage(session_usage()) + .set_status(status) + .build()); Disconnect(); } @@ -232,12 +235,6 @@ bool ShareSession::ProcessKeyVerificationResult( // share flag. self_share_ = false; break; - - case PairedKeyVerificationRunner::PairedKeyVerificationResult::kUnknown: - LOG(WARNING) << __func__ - << ": Unknown PairedKeyVerificationResult for target " - << share_target().id << ". Disconnecting."; - return false; } return true; } @@ -246,7 +243,10 @@ void ShareSession::OnDisconnect() { OnConnectionDisconnected(); if (disconnect_status_ != TransferMetadata::Status::kUnknown) { UpdateTransferMetadata( - TransferMetadataBuilder().set_status(disconnect_status_).build()); + TransferMetadataBuilder() + .set_usage(session_usage()) + .set_status(disconnect_status_) + .build()); } connection_ = nullptr; } diff --git a/sharing/share_session.h b/sharing/share_session.h index d0dc0f03..0a6675d7 100644 --- a/sharing/share_session.h +++ b/sharing/share_session.h @@ -38,6 +38,7 @@ #include "sharing/paired_key_verification_runner.h" #include "sharing/payload_tracker.h" #include "sharing/proto/wire_format.pb.h" +#include "sharing/share_session_usage.h" #include "sharing/share_target.h" #include "sharing/transfer_metadata.h" @@ -96,6 +97,11 @@ class ShareSession { const ShareTarget& share_target() const { return share_target_; } + ShareSessionUsage session_usage() const { return session_usage_; } + void set_session_usage(ShareSessionUsage session_usage) { + session_usage_ = session_usage; + } + // Sets the status to send in the TransferMetadataUpdate on connection // disconnect. If |status| is kUnknown, then no TransferMetadataUpdate will be // sent. If |status| is set, it must be a final status. @@ -221,6 +227,7 @@ class ShareSession { absl::flat_hash_map attachment_payload_map_; PayloadTracker::PayloadUpdateQueue* payload_updates_queue_ = nullptr; bool is_cancelled_ = false; + ShareSessionUsage session_usage_ = ShareSessionUsage::kUnknown; }; } // namespace nearby::sharing diff --git a/sharing/share_session_test.cc b/sharing/share_session_test.cc index fc6d9211..98a969fb 100644 --- a/sharing/share_session_test.cc +++ b/sharing/share_session_test.cc @@ -21,6 +21,8 @@ #include #include +#include "location/nearby/analytics/cpp/logging/mock_event_logger.h" +#include "location/nearby/sharing/lib/analytics/analytics_recorder_impl.h" #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" @@ -28,11 +30,9 @@ #include "absl/synchronization/notification.h" #include "absl/time/clock.h" #include "absl/time/time.h" -#include "internal/analytics/mock_event_logger.h" #include "internal/test/fake_clock.h" #include "internal/test/fake_device_info.h" #include "internal/test/fake_task_runner.h" -#include "sharing/analytics/analytics_recorder.h" #include "sharing/certificates/fake_nearby_share_certificate_manager.h" #include "sharing/fake_nearby_connections_manager.h" #include "sharing/nearby_connection.h" @@ -97,8 +97,8 @@ class TestShareSession : public ShareSession { FakeNearbyConnectionsManager connections_manager_; FakeDeviceInfo device_info_; nearby::analytics::MockEventLogger mock_event_logger_; - analytics::AnalyticsRecorder analytics_recorder_{/*vendor_id=*/0, - &mock_event_logger_}; + analytics::AnalyticsRecorderImpl analytics_recorder_{/*vendor_id=*/0, + &mock_event_logger_}; const bool is_incoming_; }; @@ -389,20 +389,6 @@ TEST(ShareSessionTest, ProcessKeyVerificationResultNotSelfShareUnable) { EXPECT_FALSE(session.token().empty()); } -TEST(ShareSessionTest, ProcessKeyVerificationResultUnknown) { - ShareTarget share_target; - TestShareSession session(std::string(kEndpointId), share_target); - NearbyConnectionImpl connection(session.device_info()); - session.SetNearbyConnection(&connection); - session.SetTokenForTests("9876"); - - EXPECT_FALSE(session.ProcessKeyVerificationResult( - PairedKeyVerificationRunner::PairedKeyVerificationResult::kUnknown, - OSType::WINDOWS)); - EXPECT_EQ(session.os_type(), OSType::WINDOWS); - EXPECT_FALSE(session.token().empty()); -} - TEST(ShareSessionTest, AbortNotConnected) { ShareTarget share_target; TestShareSession session(std::string(kEndpointId), share_target); diff --git a/sharing/share_session_usage.h b/sharing/share_session_usage.h new file mode 100644 index 00000000..0a2e2ded --- /dev/null +++ b/sharing/share_session_usage.h @@ -0,0 +1,45 @@ +// Copyright 2026 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 THIRD_PARTY_NEARBY_SHARING_SHARE_SESSION_USAGE_H_ +#define THIRD_PARTY_NEARBY_SHARING_SHARE_SESSION_USAGE_H_ + +#include + +namespace nearby::sharing { + +enum class ShareSessionUsage { + kUnknown, + kSharing, // Connection is used for quick share. + kPairing, // Connection is used for setting up a binding. + kFileSync, // Connection is used for file sync. +}; + +inline std::string ShareSessionUsageToString( + ShareSessionUsage transfer_usage) { + switch (transfer_usage) { + case ShareSessionUsage::kSharing: + return "Sharing"; + case ShareSessionUsage::kPairing: + return "Pairing"; + case ShareSessionUsage::kFileSync: + return "FileSync"; + case ShareSessionUsage::kUnknown: + return "Unknown"; + } +} + +} // namespace nearby::sharing + +#endif // THIRD_PARTY_NEARBY_SHARING_SHARE_SESSION_USAGE_H_ diff --git a/sharing/transfer_manager.cc b/sharing/transfer_manager.cc index dede2870..fa38384b 100644 --- a/sharing/transfer_manager.cc +++ b/sharing/transfer_manager.cc @@ -14,15 +14,15 @@ #include "sharing/transfer_manager.h" -#include #include #include -#include +#include +#include "absl/base/nullability.h" +#include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" -#include "absl/time/time.h" -#include "sharing/internal/public/context.h" +#include "internal/platform/task_runner.h" #include "sharing/internal/public/logging.h" #include "sharing/nearby_connections_types.h" #include "sharing/thread_timer.h" @@ -43,28 +43,32 @@ bool IsHighQualityMedium(Medium medium) { } // namespace -TransferManager::TransferManager(Context* context, - absl::string_view endpoint_id) - : context_(context), endpoint_id_(endpoint_id) {} +TransferManager::TransferManager( + TaskRunner* absl_nonnull runner, absl::string_view endpoint_id, + absl::AnyInvocable payload)> + deferred_send_function) + : runner_(*runner), + endpoint_id_(endpoint_id), + deferred_send_function_(std::move(deferred_send_function)) {} TransferManager::~TransferManager() { absl::MutexLock lock(mutex_); timeout_timer_.reset(); - pending_tasks_.clear(); } -void TransferManager::Send(std::function task) { +void TransferManager::Send(std::unique_ptr payload) { absl::MutexLock lock(mutex_); if (is_waiting_for_high_quality_medium_) { LOG(INFO) << "Connection to endpoint " << endpoint_id_ << " is waiting for a high quality medium, delaying payload transfer."; - pending_tasks_.push_back(task); + pending_payloads_.push(std::move(payload)); return; } - task(); + deferred_send_function_(endpoint_id_, std::move(payload)); } void TransferManager::OnMediumQualityChanged(Medium current_medium) { @@ -101,8 +105,8 @@ bool TransferManager::StartTransfer() { } timeout_timer_ = std::make_unique( - *context_->GetTaskRunner(), "transfer_manager_timeout_timer", - kMediumUpgradeTimeout, [this]() { + runner_, "transfer_manager_timeout_timer", kMediumUpgradeTimeout, + [this]() { absl::MutexLock lock(mutex_); LOG(INFO) << "Timed out for endpoint " << endpoint_id_ << " after " @@ -113,8 +117,7 @@ bool TransferManager::StartTransfer() { LOG(INFO) << "Attempting to upgrade the bandwidth for endpoint " + endpoint_id_ + ". Large payloads will be delayed" + " until either bandwidth is upgraded or a timeout of " - << (kMediumUpgradeTimeout / absl::Milliseconds(1)) - << " milliseconds is reached"; + << kMediumUpgradeTimeout << " is reached"; return true; } @@ -132,15 +135,16 @@ bool TransferManager::CancelTransfer() { } void TransferManager::StopWaitingForHighQualityMedium() { + timeout_timer_.reset(); is_waiting_for_high_quality_medium_ = false; - for (const auto& task : pending_tasks_) { - LOG(INFO) << "Sending delayed payload to endpoint " << endpoint_id_; - task(); + LOG(INFO) << "Sending " << pending_payloads_.size() + << " delayed payloads to endpoint " << endpoint_id_; + while (!pending_payloads_.empty()) { + auto payload = std::move(pending_payloads_.front()); + pending_payloads_.pop(); + deferred_send_function_(endpoint_id_, std::move(payload)); } - - pending_tasks_.clear(); - timeout_timer_.reset(); } } // namespace sharing diff --git a/sharing/transfer_manager.h b/sharing/transfer_manager.h index e33d1f33..619b2b0e 100644 --- a/sharing/transfer_manager.h +++ b/sharing/transfer_manager.h @@ -15,16 +15,17 @@ #ifndef THIRD_PARTY_NEARBY_SHARING_TRANSFER_MANAGER_H_ #define THIRD_PARTY_NEARBY_SHARING_TRANSFER_MANAGER_H_ -#include #include +#include #include -#include +#include "absl/base/nullability.h" #include "absl/base/thread_annotations.h" +#include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" -#include "sharing/internal/public/context.h" +#include "internal/platform/task_runner.h" #include "sharing/nearby_connections_types.h" #include "sharing/thread_timer.h" @@ -39,11 +40,15 @@ class TransferManager { // Used to wait for the medium upgrade. static constexpr absl::Duration kMediumUpgradeTimeout = absl::Seconds(10); - TransferManager(Context* context, absl::string_view endpoint_id); + TransferManager(TaskRunner* absl_nonnull runner, + absl::string_view endpoint_id, + absl::AnyInvocable payload)> + deferred_send_function); ~TransferManager(); - void Send(std::function task) ABSL_LOCKS_EXCLUDED(mutex_); + void Send(std::unique_ptr payload) ABSL_LOCKS_EXCLUDED(mutex_); void OnMediumQualityChanged(Medium current_medium) ABSL_LOCKS_EXCLUDED(mutex_); bool StartTransfer() ABSL_LOCKS_EXCLUDED(mutex_); @@ -52,11 +57,15 @@ class TransferManager { private: void StopWaitingForHighQualityMedium() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - Context* context_; - std::string endpoint_id_; + TaskRunner& runner_; + const std::string endpoint_id_; + absl::AnyInvocable payload)> + deferred_send_function_; absl::Mutex mutex_; bool is_waiting_for_high_quality_medium_ ABSL_GUARDED_BY(mutex_) = true; - std::vector> pending_tasks_ ABSL_GUARDED_BY(mutex_); + std::queue> pending_payloads_ + ABSL_GUARDED_BY(mutex_); std::unique_ptr timeout_timer_ ABSL_GUARDED_BY(mutex_) = nullptr; }; diff --git a/sharing/transfer_manager_test.cc b/sharing/transfer_manager_test.cc index f6ed6fc0..63f0616c 100644 --- a/sharing/transfer_manager_test.cc +++ b/sharing/transfer_manager_test.cc @@ -14,6 +14,7 @@ #include "sharing/transfer_manager.h" +#include #include #include "gtest/gtest.h" @@ -21,7 +22,7 @@ #include "absl/synchronization/notification.h" #include "absl/time/time.h" #include "internal/test/fake_clock.h" -#include "sharing/internal/test/fake_context.h" +#include "internal/test/fake_task_runner.h" #include "sharing/nearby_connections_types.h" namespace nearby { @@ -32,15 +33,20 @@ constexpr absl::string_view kEndpointId = "endpoint"; constexpr absl::Duration kNotificationTimeout = absl::Milliseconds(200); TEST(TransferManager, MediumUpgradeSuccess) { - FakeContext context; + FakeClock fake_clock; + FakeTaskRunner executor(&fake_clock, /*concurrent_count=*/1); absl::Notification notification; bool is_called = false; - TransferManager transfer_manager{&context, kEndpointId}; - transfer_manager.Send([&]() { - is_called = true; - notification.Notify(); - }); + TransferManager transfer_manager{ + &executor, kEndpointId, + [&](absl::string_view endpoint_id, std::unique_ptr payload) { + is_called = true; + if (!notification.HasBeenNotified()) { + notification.Notify(); + } + }}; + transfer_manager.Send(std::make_unique()); ASSERT_FALSE(is_called); ASSERT_TRUE(transfer_manager.StartTransfer()); @@ -53,15 +59,20 @@ TEST(TransferManager, MediumUpgradeSuccess) { } TEST(TransferManager, SendAfterMediumUpgradeSuccess) { - FakeContext context; + FakeClock fake_clock; + FakeTaskRunner executor(&fake_clock, /*concurrent_count=*/1); absl::Notification notification; bool is_called = false; - TransferManager transfer_manager{&context, kEndpointId}; - transfer_manager.Send([&]() { - is_called = true; - notification.Notify(); - }); + TransferManager transfer_manager{ + &executor, kEndpointId, + [&](absl::string_view endpoint_id, std::unique_ptr payload) { + is_called = true; + if (!notification.HasBeenNotified()) { + notification.Notify(); + } + }}; + transfer_manager.Send(std::make_unique()); ASSERT_FALSE(is_called); ASSERT_TRUE(transfer_manager.StartTransfer()); @@ -70,20 +81,25 @@ TEST(TransferManager, SendAfterMediumUpgradeSuccess) { notification.WaitForNotificationWithTimeout(kNotificationTimeout)); ASSERT_TRUE(is_called); is_called = false; - transfer_manager.Send([&]() { is_called = true; }); + transfer_manager.Send(std::make_unique()); ASSERT_TRUE(is_called); } TEST(TransferManager, MediumUpgradeFailed) { - FakeContext context; + FakeClock fake_clock; + FakeTaskRunner executor(&fake_clock, /*concurrent_count=*/1); absl::Notification notification; bool is_called = false; - TransferManager transfer_manager{&context, kEndpointId}; - transfer_manager.Send([&]() { - is_called = true; - notification.Notify(); - }); + TransferManager transfer_manager{ + &executor, kEndpointId, + [&](absl::string_view endpoint_id, std::unique_ptr payload) { + is_called = true; + if (!notification.HasBeenNotified()) { + notification.Notify(); + } + }}; + transfer_manager.Send(std::make_unique()); ASSERT_FALSE(is_called); ASSERT_TRUE(transfer_manager.StartTransfer()); @@ -94,20 +110,24 @@ TEST(TransferManager, MediumUpgradeFailed) { } TEST(TransferManager, MediumUpgradeTimeout) { - FakeContext context; + FakeClock fake_clock; + FakeTaskRunner executor(&fake_clock, /*concurrent_count=*/1); absl::Notification notification; bool is_called = false; - TransferManager transfer_manager{&context, kEndpointId}; - transfer_manager.Send([&]() { - is_called = true; - notification.Notify(); - }); + TransferManager transfer_manager{ + &executor, kEndpointId, + [&](absl::string_view endpoint_id, std::unique_ptr payload) { + is_called = true; + if (!notification.HasBeenNotified()) { + notification.Notify(); + } + }}; + transfer_manager.Send(std::make_unique()); ASSERT_FALSE(is_called); ASSERT_TRUE(transfer_manager.StartTransfer()); - FakeClock* clock = static_cast(context.GetClock()); - clock->FastForward(TransferManager::kMediumUpgradeTimeout); + fake_clock.FastForward(TransferManager::kMediumUpgradeTimeout); ASSERT_TRUE( notification.WaitForNotificationWithTimeout(kNotificationTimeout)); @@ -115,20 +135,24 @@ TEST(TransferManager, MediumUpgradeTimeout) { } TEST(TransferManager, CancelStartedTransfer) { - FakeContext context; + FakeClock fake_clock; + FakeTaskRunner executor(&fake_clock, /*concurrent_count=*/1); absl::Notification notification; bool is_called = false; - TransferManager transfer_manager{&context, kEndpointId}; - transfer_manager.Send([&]() { - is_called = true; - notification.Notify(); - }); + TransferManager transfer_manager{ + &executor, kEndpointId, + [&](absl::string_view endpoint_id, std::unique_ptr payload) { + is_called = true; + if (!notification.HasBeenNotified()) { + notification.Notify(); + } + }}; + transfer_manager.Send(std::make_unique()); ASSERT_FALSE(is_called); ASSERT_TRUE(transfer_manager.StartTransfer()); - FakeClock* clock = static_cast(context.GetClock()); - clock->FastForward(absl::Seconds(5)); + fake_clock.FastForward(absl::Seconds(5)); ASSERT_TRUE(transfer_manager.CancelTransfer()); ASSERT_FALSE( @@ -137,20 +161,24 @@ TEST(TransferManager, CancelStartedTransfer) { } TEST(TransferManager, CancelTimedOutMediumUpgrade) { - FakeContext context; + FakeClock fake_clock; + FakeTaskRunner executor(&fake_clock, /*concurrent_count=*/1); absl::Notification notification; bool is_called = false; - TransferManager transfer_manager{&context, kEndpointId}; - transfer_manager.Send([&]() { - is_called = true; - notification.Notify(); - }); + TransferManager transfer_manager{ + &executor, kEndpointId, + [&](absl::string_view endpoint_id, std::unique_ptr payload) { + is_called = true; + if (!notification.HasBeenNotified()) { + notification.Notify(); + } + }}; + transfer_manager.Send(std::make_unique()); ASSERT_FALSE(is_called); ASSERT_TRUE(transfer_manager.StartTransfer()); - FakeClock* clock = static_cast(context.GetClock()); - clock->FastForward(TransferManager::kMediumUpgradeTimeout); + fake_clock.FastForward(TransferManager::kMediumUpgradeTimeout); ASSERT_TRUE( notification.WaitForNotificationWithTimeout(kNotificationTimeout)); @@ -159,15 +187,20 @@ TEST(TransferManager, CancelTimedOutMediumUpgrade) { } TEST(TransferManager, MediumUpgradeBeforeStartTransfer) { - FakeContext context; + FakeClock fake_clock; + FakeTaskRunner executor(&fake_clock, /*concurrent_count=*/1); absl::Notification notification; bool is_called = false; - TransferManager transfer_manager{&context, kEndpointId}; - transfer_manager.Send([&]() { - is_called = true; - notification.Notify(); - }); + TransferManager transfer_manager{ + &executor, kEndpointId, + [&](absl::string_view endpoint_id, std::unique_ptr payload) { + is_called = true; + if (!notification.HasBeenNotified()) { + notification.Notify(); + } + }}; + transfer_manager.Send(std::make_unique()); transfer_manager.OnMediumQualityChanged(Medium::kWifiLan); diff --git a/sharing/transfer_metadata.cc b/sharing/transfer_metadata.cc index 82462b89..d62e2a9e 100644 --- a/sharing/transfer_metadata.cc +++ b/sharing/transfer_metadata.cc @@ -24,9 +24,10 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" +#include "absl/strings/string_view.h" +#include "sharing/share_session_usage.h" -namespace nearby { -namespace sharing { +namespace nearby::sharing { // static bool TransferMetadata::IsFinalStatus(Status status) { @@ -90,15 +91,17 @@ std::string TransferMetadata::StatusToString(Status status) { // LINT.ThenChange(//depot/google3/location/nearby/cpp/sharing/clients/dart/platform/lib/types/transfer_status.dart) TransferMetadata::TransferMetadata( - Status status, float progress, std::optional token, - bool is_original, bool is_final_status, bool is_self_share, - uint64_t transferred_bytes, uint64_t transfer_speed, + ShareSessionUsage usage, Status status, float progress, + std::optional token, bool is_original, bool is_final_status, + bool is_self_share, uint64_t transferred_bytes, uint64_t transfer_speed, uint64_t estimated_time_remaining, int total_attachments_count, int transferred_attachments_count, std::optional in_progress_attachment_id, std::optional in_progress_attachment_transferred_bytes, - std::optional in_progress_attachment_total_bytes) - : status_(status), + std::optional in_progress_attachment_total_bytes, + absl::string_view binding_id) + : usage_(usage), + status_(status), progress_(progress), token_(std::move(token)), is_original_(is_original), @@ -112,7 +115,8 @@ TransferMetadata::TransferMetadata( in_progress_attachment_id_(in_progress_attachment_id), in_progress_attachment_transferred_bytes_( in_progress_attachment_transferred_bytes), - in_progress_attachment_total_bytes_(in_progress_attachment_total_bytes) {} + in_progress_attachment_total_bytes_(in_progress_attachment_total_bytes), + binding_id_(binding_id) {} TransferMetadata::~TransferMetadata() = default; @@ -123,22 +127,26 @@ TransferMetadata& TransferMetadata::operator=(const TransferMetadata&) = std::string TransferMetadata::ToString() const { std::vector fmt; - + fmt.push_back( + absl::StrFormat("usage: %s", ShareSessionUsageToString(usage_))); fmt.push_back(absl::StrFormat("status: %s", StatusToString(status_))); - fmt.push_back(absl::StrFormat("progress: %.2f", progress_)); - if (token_) { - fmt.push_back(absl::StrFormat("token: %s", *token_)); - } - fmt.push_back(absl::StrFormat("is_original: %d", is_original_)); fmt.push_back(absl::StrFormat("is_final_status: %d", is_final_status_)); fmt.push_back(absl::StrFormat("is_self_share: %d", is_self_share_)); - fmt.push_back(absl::StrFormat("transferred_bytes: %d", transferred_bytes_)); - fmt.push_back(absl::StrFormat("transfer_speed: %d", transfer_speed_)); - fmt.push_back(absl::StrFormat("estimated_time_remaining: %d", - estimated_time_remaining_)); + if (usage_ != ShareSessionUsage::kPairing) { + fmt.push_back(absl::StrFormat("progress: %.2f", progress_)); + if (token_) { + fmt.push_back(absl::StrFormat("token: %s", *token_)); + } + fmt.push_back(absl::StrFormat("is_original: %d", is_original_)); + fmt.push_back(absl::StrFormat("transferred_bytes: %d", transferred_bytes_)); + fmt.push_back(absl::StrFormat("transfer_speed: %d", transfer_speed_)); + fmt.push_back(absl::StrFormat("estimated_time_remaining: %d", + estimated_time_remaining_)); + } else { + fmt.push_back(absl::StrFormat("binding_id: %s", binding_id_)); + } return absl::StrCat("TransferMetadata<", absl::StrJoin(fmt, ", "), ">"); } -} // namespace sharing -} // namespace nearby +} // namespace nearby::sharing diff --git a/sharing/transfer_metadata.h b/sharing/transfer_metadata.h index 03171a78..bc665e1f 100644 --- a/sharing/transfer_metadata.h +++ b/sharing/transfer_metadata.h @@ -19,6 +19,10 @@ #include #include +#include + +#include "absl/strings/string_view.h" +#include "sharing/share_session_usage.h" namespace nearby { namespace sharing { @@ -50,23 +54,31 @@ class TransferMetadata { // LINT.ThenChange(//depot/google3/location/nearby/cpp/sharing/clients/dart/platform/lib/types/transfer_status.dart) static bool IsFinalStatus(Status status); - static std::string StatusToString(TransferMetadata::Status status); + static std::string StatusToString(Status status); TransferMetadata( - Status status, float progress, std::optional token, - bool is_original, bool is_final_status, bool is_self_share, - uint64_t transferred_bytes, uint64_t transfer_speed, + ShareSessionUsage usage, Status status, float progress, + std::optional token, bool is_original, bool is_final_status, + bool is_self_share, uint64_t transferred_bytes, uint64_t transfer_speed, uint64_t estimated_time_remaining, int total_attachments_count, int transferred_attachments_count, std::optional in_progress_attachment_id, std::optional in_progress_attachment_transferred_bytes, - std::optional in_progress_attachment_total_bytes); + std::optional in_progress_attachment_total_bytes, + absl::string_view binding_id + ); ~TransferMetadata(); TransferMetadata(const TransferMetadata&); TransferMetadata& operator=(const TransferMetadata&); + ShareSessionUsage usage() const { return usage_; } Status status() const { return status_; } + std::string binding_id() const { return binding_id_; } + void set_binding_id(std::string binding_id) { + binding_id_ = std::move(binding_id); + } + // Returns transfer progress as percentage. float progress() const { return progress_; } @@ -118,6 +130,7 @@ class TransferMetadata { } private: + ShareSessionUsage usage_; Status status_; float progress_; std::optional token_; @@ -132,6 +145,9 @@ class TransferMetadata { std::optional in_progress_attachment_id_; std::optional in_progress_attachment_transferred_bytes_; std::optional in_progress_attachment_total_bytes_; + // If usage_ is kPairing and status_ is kComplete, this will be set to the + // binding id. Otherwise, this will be empty. + std::string binding_id_; }; } // namespace sharing diff --git a/sharing/transfer_metadata_builder.cc b/sharing/transfer_metadata_builder.cc index 3f849a2c..0fde6cdf 100644 --- a/sharing/transfer_metadata_builder.cc +++ b/sharing/transfer_metadata_builder.cc @@ -19,6 +19,8 @@ #include #include +#include "absl/strings/string_view.h" +#include "sharing/share_session_usage.h" #include "sharing/transfer_metadata.h" namespace nearby { @@ -27,6 +29,7 @@ namespace sharing { TransferMetadataBuilder TransferMetadataBuilder::Clone( const TransferMetadata& metadata) { TransferMetadataBuilder builder; + builder.usage_ = metadata.usage(); builder.is_original_ = metadata.is_original(); builder.progress_ = metadata.progress(); builder.status_ = metadata.status(); @@ -54,6 +57,18 @@ TransferMetadataBuilder& TransferMetadataBuilder::operator=( TransferMetadataBuilder::~TransferMetadataBuilder() = default; +TransferMetadataBuilder& TransferMetadataBuilder::set_usage( + ShareSessionUsage usage) { + usage_ = usage; + return *this; +} + +TransferMetadataBuilder& TransferMetadataBuilder::set_binding_id( + absl::string_view binding_id) { + binding_id_ = binding_id; + return *this; +} + TransferMetadataBuilder& TransferMetadataBuilder::set_is_original( bool is_original) { is_original_ = is_original; @@ -138,12 +153,12 @@ TransferMetadataBuilder::set_in_progress_attachment_total_bytes( TransferMetadata TransferMetadataBuilder::build() const { return TransferMetadata( - status_, progress_, token_, is_original_, + usage_, status_, progress_, token_, is_original_, TransferMetadata::IsFinalStatus(status_), is_self_share_, transferred_bytes_, transfer_speed_, estimated_time_remaining_, total_attachments_count_, transferred_attachments_count_, in_progress_attachment_id_, in_progress_attachment_transferred_bytes_, - in_progress_attachment_total_bytes_); + in_progress_attachment_total_bytes_, binding_id_); } } // namespace sharing diff --git a/sharing/transfer_metadata_builder.h b/sharing/transfer_metadata_builder.h index 208a8cca..aaaebf42 100644 --- a/sharing/transfer_metadata_builder.h +++ b/sharing/transfer_metadata_builder.h @@ -20,6 +20,8 @@ #include #include +#include "absl/strings/string_view.h" +#include "sharing/share_session_usage.h" #include "sharing/transfer_metadata.h" namespace nearby { @@ -34,6 +36,10 @@ class TransferMetadataBuilder { TransferMetadataBuilder& operator=(TransferMetadataBuilder&&); ~TransferMetadataBuilder(); + TransferMetadataBuilder& set_usage(ShareSessionUsage usage); + + TransferMetadataBuilder& set_binding_id(absl::string_view binding_id); + TransferMetadataBuilder& set_is_original(bool is_original); TransferMetadataBuilder& set_progress(double progress); @@ -69,6 +75,7 @@ class TransferMetadataBuilder { TransferMetadata build() const; private: + ShareSessionUsage usage_ = ShareSessionUsage::kUnknown; bool is_original_ = false; double progress_ = 0; TransferMetadata::Status status_ = TransferMetadata::Status::kInProgress; @@ -83,6 +90,7 @@ class TransferMetadataBuilder { std::optional in_progress_attachment_transferred_bytes_ = std::nullopt; std::optional in_progress_attachment_total_bytes_ = std::nullopt; + std::string binding_id_; }; } // namespace sharing diff --git a/sharing/transfer_metadata_matchers.h b/sharing/transfer_metadata_matchers.h index 64921792..9e85348b 100644 --- a/sharing/transfer_metadata_matchers.h +++ b/sharing/transfer_metadata_matchers.h @@ -23,6 +23,11 @@ MATCHER_P(HasStatus, status, "has status") { return arg.status() == status; } +MATCHER_P(HasUsage, usage, "has usage") { + return arg.usage() == usage; +} + + MATCHER(IsFinalStatus, "is final") { return arg.is_final_status(); } diff --git a/sharing/transfer_metadata_test.cc b/sharing/transfer_metadata_test.cc index ef2d7493..09721dcc 100644 --- a/sharing/transfer_metadata_test.cc +++ b/sharing/transfer_metadata_test.cc @@ -19,6 +19,7 @@ #include #include "gtest/gtest.h" +#include "sharing/share_session_usage.h" namespace nearby { namespace sharing { @@ -34,6 +35,7 @@ std::vector GetTestData() { kTransferMetadataToStringTestData = new std::vector({ {TransferMetadata( + ShareSessionUsage::kSharing, TransferMetadata::Status::kConnecting, /*progress=*/12.321f, /*token=*/std::nullopt, /*is_original=*/true, /*is_final_status=*/false, @@ -45,12 +47,14 @@ std::vector GetTestData() { /*transferred_attachments_count=*/0, /*in_progress_attachment_id=*/std::nullopt, /*in_progress_attachment_transferred_bytes=*/std::nullopt, - /*in_progress_attachment_total_bytes=*/std::nullopt), - "TransferMetadata"}, + /*in_progress_attachment_total_bytes=*/std::nullopt, + /*binding_id=*/""), + "TransferMetadata"}, {TransferMetadata( + ShareSessionUsage::kSharing, TransferMetadata::Status::kCancelled, /*progress=*/77.795f, std::optional{"test_token"}, @@ -62,11 +66,51 @@ std::vector GetTestData() { /*transferred_attachments_count=*/0, /*in_progress_attachment_id=*/std::nullopt, /*in_progress_attachment_transferred_bytes=*/std::nullopt, - /*in_progress_attachment_total_bytes=*/std::nullopt), - "TransferMetadata"}, + {TransferMetadata( + ShareSessionUsage::kFileSync, + TransferMetadata::Status::kCancelled, + /*progress=*/77.795f, + std::optional{"test_token"}, + /*is_original=*/false, + /*is_final_status=*/true, /*is_self_share=*/true, + /*transferred_bytes=*/123456789, /*transfer_speed=*/0, + /*estimated_time_remaining=*/123456789, + /*total_attachments_count=*/1, + /*transferred_attachments_count=*/0, + /*in_progress_attachment_id=*/std::nullopt, + /*in_progress_attachment_transferred_bytes=*/std::nullopt, + /*in_progress_attachment_total_bytes=*/std::nullopt, + /*binding_id=*/""), + "TransferMetadata"}, + {TransferMetadata( + ShareSessionUsage::kPairing, + TransferMetadata::Status::kComplete, + /*progress=*/0.0f, + std::optional{"test_token"}, + /*is_original=*/false, + /*is_final_status=*/true, /*is_self_share=*/true, + /*transferred_bytes=*/0, /*transfer_speed=*/0, + /*estimated_time_remaining=*/0, + /*total_attachments_count=*/0, + /*transferred_attachments_count=*/0, + /*in_progress_attachment_id=*/std::nullopt, + /*in_progress_attachment_transferred_bytes=*/std::nullopt, + /*in_progress_attachment_total_bytes=*/std::nullopt, + /*binding_id=*/"test_binding_id"), + "TransferMetadata"}, }); return *kTransferMetadataToStringTestData; @@ -80,7 +124,7 @@ TEST_P(TransferMetadataToStringTest, ToStringResultMatches) { GetParam().transfer_metadata.ToString()); } -INSTANTIATE_TEST_CASE_P(TransferMetadataToStringTest, +INSTANTIATE_TEST_SUITE_P(TransferMetadataToStringTest, TransferMetadataToStringTest, testing::ValuesIn(GetTestData())); diff --git a/sharing/worker_queue.h b/sharing/worker_queue.h index 0fe8a928..b9c4d958 100644 --- a/sharing/worker_queue.h +++ b/sharing/worker_queue.h @@ -16,6 +16,7 @@ #define THIRD_PARTY_NEARBY_SHARING_WORKER_QUEUE_H_ #include +#include #include #include @@ -38,7 +39,11 @@ namespace nearby::sharing { template class WorkerQueue { public: - explicit WorkerQueue(TaskRunner* task_runner) : task_runner_(task_runner) {} + explicit WorkerQueue(TaskRunner* task_runner) + : task_runner_(task_runner), + run_data_(std::make_shared()) { + run_data_->is_stopped = false; + } ~WorkerQueue() { Stop(); } @@ -52,11 +57,11 @@ class WorkerQueue { LOG(ERROR) << "WorkerQueue is already started."; return false; } - if (is_stopped_) { + if (run_data_->is_stopped) { LOG(ERROR) << "WorkerQueue is already stopped, cannot restart."; return false; } - callback_ = std::move(callback); + run_data_->callback = std::move(callback); { absl::MutexLock lock(mutex_); if (!queue_.empty()) { @@ -67,12 +72,22 @@ class WorkerQueue { } // Stops the queue. No new callback will be scheduled. + // This method will block until the callback finishes if it is currently + // running. void Stop() { - bool already_stopped = is_stopped_.exchange(true); + bool already_stopped = run_data_->is_stopped.exchange(true); if (already_stopped || !is_started_) { return; } + // Prevent new callbacks from being scheduled. is_scheduled_ = true; + // Wait for inflight callback to finish. + absl::MutexLock lock(run_data_->running_mutex); + auto stopped_running = + [this]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(run_data_->running_mutex) { + return !run_data_->is_running; + }; + run_data_->running_mutex.Await(absl::Condition(&stopped_running)); } // Queues an item to be processed by the callback. @@ -95,9 +110,16 @@ class WorkerQueue { } private: + struct RunData { + std::atomic is_stopped; + absl::AnyInvocable callback; + absl::Mutex running_mutex; + bool is_running ABSL_GUARDED_BY(running_mutex) = false; + }; + void ScheduleCallback() { // Skip if not started or stopped - if (!is_started_ || is_stopped_) { + if (!is_started_ || run_data_->is_stopped) { return; } if (is_scheduled_.exchange(true)) { @@ -106,20 +128,26 @@ class WorkerQueue { return; } VLOG(1) << "Scheduling callback"; - task_runner_->PostTask([this]() { - if (is_stopped_) { - return; + task_runner_->PostTask([run_data = run_data_]() { + { + absl::MutexLock lock(run_data->running_mutex); + run_data->is_running = true; + } + if (!run_data->is_stopped) { + run_data->callback(); + } + { + absl::MutexLock lock(run_data->running_mutex); + run_data->is_running = false; } - callback_(); }); } TaskRunner* const task_runner_ = nullptr; - absl::AnyInvocable callback_; + std::shared_ptr run_data_; // Tracks whether Start() has been called. std::atomic is_started_ = false; // Tracks whether Stop() has been called. - std::atomic is_stopped_ = false; absl::Mutex mutex_; std::queue queue_ ABSL_GUARDED_BY(mutex_); // This is used track whether the callback is already scheduled so as to avoid diff --git a/sharing/worker_queue_test.cc b/sharing/worker_queue_test.cc index e9b6e3b0..a3671d61 100644 --- a/sharing/worker_queue_test.cc +++ b/sharing/worker_queue_test.cc @@ -14,10 +14,12 @@ #include "sharing/worker_queue.h" +#include #include #include "gtest/gtest.h" #include "absl/synchronization/notification.h" +#include "absl/time/time.h" #include "internal/test/fake_clock.h" #include "internal/test/fake_task_runner.h" @@ -91,12 +93,13 @@ TEST(WorkerQueueTest, QueueItemsWhileCallbackRunning) { TEST(WorkerQueueTest, StopStopsCallback) { FakeClock fake_clock; FakeTaskRunner task_runner(&fake_clock, 1); - WorkerQueue queue(&task_runner); - queue.Queue(1); - queue.Queue(2); + auto queue = std::make_unique>(&task_runner); + queue->Queue(1); + queue->Queue(2); absl::Notification notification; - EXPECT_TRUE(queue.Start([&queue, ¬ification]() { - std::queue items = queue.ReadAll(); + auto queue_ptr = queue.get(); + EXPECT_TRUE(queue->Start([queue_ptr, ¬ification]() { + std::queue items = queue_ptr->ReadAll(); EXPECT_EQ(items.size(), 2); EXPECT_EQ(items.front(), 1); EXPECT_EQ(items.back(), 2); @@ -104,8 +107,34 @@ TEST(WorkerQueueTest, StopStopsCallback) { })); // Wait for the callback to start. notification.WaitForNotification(); - queue.Stop(); - queue.Queue(3); + queue->Stop(); + queue->Queue(3); + queue.reset(); + task_runner.Sync(); + // No more callbacks. +} + +TEST(WorkerQueueTest, StopWaitsForInFlightCallback) { + FakeClock fake_clock; + FakeTaskRunner task_runner(&fake_clock, 1); + auto queue = std::make_unique>(&task_runner); + absl::Notification notification1; + absl::Notification notification2; + auto queue_ptr = queue.get(); + EXPECT_TRUE(queue->Start([queue_ptr, ¬ification1, ¬ification2]() { + std::queue items = queue_ptr->ReadAll(); + notification1.Notify(); + notification2.WaitForNotificationWithTimeout(absl::Milliseconds(500)); + EXPECT_EQ(items.size(), 2); + EXPECT_EQ(items.front(), 1); + EXPECT_EQ(items.back(), 2); + })); + queue->Queue(1); + queue->Queue(2); + // Wait for the callback to start. + notification1.WaitForNotification(); + queue->Queue(3); + queue.reset(); task_runner.Sync(); // No more callbacks. } diff --git a/util/hash/BUILD b/util/hash/BUILD new file mode 100644 index 00000000..e92c3ccb --- /dev/null +++ b/util/hash/BUILD @@ -0,0 +1,11 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +cc_library( + name = "highway_fingerprint", + hdrs = ["highway_fingerprint.h"], + visibility = ["//visibility:public"], + deps = [ + "@com_google_absl//absl/hash", + "@com_google_absl//absl/strings:string_view", + ], +) diff --git a/sharing/linux/stubs/highway_fingerprint.h b/util/hash/highway_fingerprint.h similarity index 100% rename from sharing/linux/stubs/highway_fingerprint.h rename to util/hash/highway_fingerprint.h